diff --git a/engine/qclib/Comprout.c b/engine/qclib/Comprout.c new file mode 100644 index 000000000..7b528b40c --- /dev/null +++ b/engine/qclib/Comprout.c @@ -0,0 +1,238 @@ +//compile routines + +#include "qcc.h" +#undef progfuncs + +char errorfile[128]; +int errorline; + +progfuncs_t *qccprogfuncs; + +#include + +extern int qcc_compileactive; +jmp_buf qcccompileerror; +char qcc_gamedir[128]; + + + +#ifdef MINIMAL + +#else + +int qccalloced; +int qcchunksize; +char *qcchunk; +void *qccHunkAlloc(size_t mem) +{ + qccalloced+=mem; + if (qccalloced > qcchunksize) + QCC_Error(ERR_INTERNAL, "Compile hunk was filled"); + + memset(qcchunk+qccalloced-mem, 0, mem); + return qcchunk+qccalloced-mem; +} +void qccClearHunk(void) +{ + if (qcchunk) + { + free(qcchunk); + qcchunk=NULL; + } +} +void PostCompile(void) +{ +#ifndef QCCONLY //QCCONLY has a frontend that browses defs. + qccClearHunk(); +#endif + + if (asmfile) + { + fclose(asmfile); + asmfile = NULL; + } +} +void PreCompile(void) +{ + qccClearHunk(); + strcpy(qcc_gamedir, ""); + qcchunk = malloc(qcchunksize=16*1024*1024); + qccalloced=0; +} + +void QCC_main (int argc, char **argv); +void QCC_ContinueCompile(void); +void QCC_FinishCompile(void); + +int comp_nump;char **comp_parms; +//void Editor(char *fname, int line, int numparms, char **compileparms); +pbool CompileParams(progfuncs_t *progfuncs, int doall, int nump, char **parms) +{ + comp_nump = nump; + comp_parms = parms; + *errorfile = '\0'; + qccprogfuncs = progfuncs; + if (setjmp(qcccompileerror)) + { + PostCompile(); + if (*errorfile) + { + if (!externs->useeditor) + printf("Error in %s on line %i\n", errorfile, errorline); + else + externs->useeditor(errorfile, errorline, nump, parms); + } + return false; + } + + PreCompile(); + QCC_main(nump, parms); + + while(qcc_compileactive) + QCC_ContinueCompile(); + + PostCompile(); + + return true; +} +int Comp_Begin(progfuncs_t *progfuncs, int nump, char **parms) +{ + comp_nump = nump; + comp_parms = parms; + qccprogfuncs = progfuncs; + *errorfile = '\0'; + if (setjmp(qcccompileerror)) + { + PostCompile(); + if (*errorfile) + externs->useeditor(errorfile, errorline, nump, parms); + return false; + } + + PreCompile(); + QCC_main(nump, parms); + + return true; +} +int Comp_Continue(progfuncs_t *progfuncs) +{ + qccprogfuncs = progfuncs; + if (setjmp(qcccompileerror)) + { + PostCompile(); + if (*errorfile) + externs->useeditor(errorfile, errorline, comp_nump, comp_parms); + return false; + } + + if (qcc_compileactive) + QCC_ContinueCompile(); + else + { + PostCompile(); + + if (*errorfile) + externs->useeditor(errorfile, errorline, comp_nump, comp_parms); + + return false; + } + + return true; +} +#endif +pbool CompileFile(progfuncs_t *progfuncs, char *filename) +{ +#ifdef MINIMAL + return false; +#else + char srcfile[32]; + char newname[32]; + static char *p[5]; + int parms; + char *s, *s2; + + p[0] = NULL; + parms = 1; + + strcpy(newname, filename); + s = newname; + if (strchr(s+1, '/')) + { + while(1) + { + s2 = strchr(s+1, '/'); + if (!s2) + { + *s = '\0'; + break; + } + s = s2; + } + p[parms] = "-src"; + p[parms+1] = newname; + parms+=2; + + strcpy(srcfile, s+1); + srcfile[strlen(srcfile)-4] = '\0'; + strcat(srcfile, ".src"); + + if (externs->FileSize(qcva("%s/%s", newname, srcfile))>0) + { + p[parms] = "-srcfile"; + p[parms+1] = srcfile; + parms+=2; + } + } + else + { + p[parms] = "-srcfile"; + p[parms+1] = newname; + newname[strlen(newname)-4] = '\0'; + strcat(newname, ".src"); + parms+=2; + } +// p[2][strlen(p[2])-4] = '\0'; +// strcat(p[2], "/"); + + while (!CompileParams(progfuncs, true, parms, p)) + { + return false; + } + return true; +#endif +} + +int QC_strncasecmp(const char *s1, const char *s2, int n) +{ + int c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + + if (!n--) + return 0; // strings are equal until end point + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return -1; // strings not equal + } + if (!c1) + return 0; // strings are equal + } + + return -1; +} + +void editbadfile(char *fname, int line) +{ + strcpy(errorfile, fname); + errorline = line; +} + diff --git a/engine/qclib/PR_COMP.H b/engine/qclib/PR_COMP.H new file mode 100644 index 000000000..98e6a45db --- /dev/null +++ b/engine/qclib/PR_COMP.H @@ -0,0 +1,490 @@ +// this file is shared by the execution and compiler + +/*i'm part way through making this work +I've given up now that I can't work out a way to load pointers. +Setting them should be fine. +*/ +#ifndef __PR_COMP_H__ +#define __PR_COMP_H__ + + +/*this distinction is made as the execution uses c pointers while compiler uses pointers from the start of the string table of the current progs*/ +#ifdef COMPILER +typedef int QCC_string_t; +#else +//typedef char *string_t; +#endif + +//typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_integer, ev_struct, ev_union} etype_t; +// 0 1 2 3 4 5 6 7 8 9 10 + +#define OFS_NULL 0 +#define OFS_RETURN 1 +#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors +#define OFS_PARM1 7 +#define OFS_PARM2 10 +#define OFS_PARM3 13 +#define OFS_PARM4 16 +#define OFS_PARM5 19 +#define OFS_PARM6 22 +#define OFS_PARM7 25 +#define RESERVED_OFS 28 + + +enum { + OP_DONE, //0 + OP_MUL_F, + OP_MUL_V, + OP_MUL_FV, + OP_MUL_VF, + OP_DIV_F, + OP_ADD_F, + OP_ADD_V, + OP_SUB_F, + OP_SUB_V, + + OP_EQ_F, //10 + OP_EQ_V, + OP_EQ_S, + OP_EQ_E, + OP_EQ_FNC, + + OP_NE_F, + OP_NE_V, + OP_NE_S, + OP_NE_E, + OP_NE_FNC, + + OP_LE, //20 + OP_GE, + OP_LT, + OP_GT, + + OP_LOAD_F, + OP_LOAD_V, + OP_LOAD_S, + OP_LOAD_ENT, + OP_LOAD_FLD, + OP_LOAD_FNC, + + OP_ADDRESS, //30 + + OP_STORE_F, + OP_STORE_V, + OP_STORE_S, + OP_STORE_ENT, + OP_STORE_FLD, + OP_STORE_FNC, + + OP_STOREP_F, + OP_STOREP_V, + OP_STOREP_S, + OP_STOREP_ENT, //40 + OP_STOREP_FLD, + OP_STOREP_FNC, + + OP_RETURN, + OP_NOT_F, + OP_NOT_V, + OP_NOT_S, + OP_NOT_ENT, + OP_NOT_FNC, + OP_IF, + OP_IFNOT, //50 + OP_CALL0, //careful... hexen2 and q1 have different calling conventions + OP_CALL1, //remap hexen2 calls to OP_CALL2H + OP_CALL2, + OP_CALL3, + OP_CALL4, + OP_CALL5, + OP_CALL6, + OP_CALL7, + OP_CALL8, + OP_STATE, //60 + OP_GOTO, + OP_AND, + OP_OR, + + OP_BITAND, + OP_BITOR, + + + //these following ones are Hexen 2 constants. + + OP_MULSTORE_F, + OP_MULSTORE_V, + OP_MULSTOREP_F, + OP_MULSTOREP_V, + + OP_DIVSTORE_F, //70 + OP_DIVSTOREP_F, + + OP_ADDSTORE_F, + OP_ADDSTORE_V, + OP_ADDSTOREP_F, + OP_ADDSTOREP_V, + + OP_SUBSTORE_F, + OP_SUBSTORE_V, + OP_SUBSTOREP_F, + OP_SUBSTOREP_V, + + OP_FETCH_GBL_F, //80 + OP_FETCH_GBL_V, + OP_FETCH_GBL_S, + OP_FETCH_GBL_E, + OP_FETCH_GBL_FNC, + + OP_CSTATE, + OP_CWSTATE, + + OP_THINKTIME, + + OP_BITSET, + OP_BITSETP, + OP_BITCLR, //90 + OP_BITCLRP, + + OP_RAND0, + OP_RAND1, + OP_RAND2, + OP_RANDV0, + OP_RANDV1, + OP_RANDV2, + + OP_SWITCH_F, + OP_SWITCH_V, + OP_SWITCH_S, //100 + OP_SWITCH_E, + OP_SWITCH_FNC, + + OP_CASE, + OP_CASERANGE, + + + + + + //the rest are added + //mostly they are various different ways of adding two vars with conversions. + + OP_CALL1H, + OP_CALL2H, + OP_CALL3H, + OP_CALL4H, + OP_CALL5H, + OP_CALL6H, //110 + OP_CALL7H, + OP_CALL8H, + + + OP_STORE_I, + OP_STORE_IF, + OP_STORE_FI, + + OP_ADD_I, + OP_ADD_FI, + OP_ADD_IF, //110 + + OP_SUB_I, + OP_SUB_FI, + OP_SUB_IF, + + OP_CONV_ITOF, + OP_CONV_FTOI, + OP_CP_ITOF, + OP_CP_FTOI, + OP_LOAD_I, + OP_STOREP_I, + OP_STOREP_IF, //120 + OP_STOREP_FI, + + OP_BITAND_I, + OP_BITOR_I, + + OP_MUL_I, + OP_DIV_I, + OP_EQ_I, + OP_NE_I, + + OP_IFNOTS, + OP_IFS, + + OP_NOT_I, //130 + + OP_DIV_VF, + + OP_POWER_I, + OP_RSHIFT_I, + OP_LSHIFT_I, + + OP_GLOBALADDRESS, + OP_POINTER_ADD, //32 bit pointers + + OP_LOADA_F, + OP_LOADA_V, + OP_LOADA_S, + OP_LOADA_ENT, //140 + OP_LOADA_FLD, + OP_LOADA_FNC, + OP_LOADA_I, + + OP_STORE_P, + OP_LOAD_P, + + OP_LOADP_F, + OP_LOADP_V, + OP_LOADP_S, + OP_LOADP_ENT, + OP_LOADP_FLD, //150 + OP_LOADP_FNC, + OP_LOADP_I, + + OP_LE_I, + OP_GE_I, + OP_LT_I, + OP_GT_I, + + OP_LE_IF, + OP_GE_IF, + OP_LT_IF, + OP_GT_IF, //160 + + OP_LE_FI, + OP_GE_FI, + OP_LT_FI, + OP_GT_FI, + + OP_EQ_IF, + OP_EQ_FI, + + //------------------------------------- + //string manipulation. + OP_ADD_SF, //(char*)c = (char*)a + (float)b + OP_SUB_S, //(float)c = (char*)a - (char*)b + OP_STOREP_C,//(float)c = *(char*)b = (float)a + OP_LOADP_C, //(float)c = *(char*) //170 + //------------------------------------- + + + OP_MUL_IF, + OP_MUL_FI, + OP_MUL_VI, + OP_MUL_IV, + OP_DIV_IF, + OP_DIV_FI, + OP_BITAND_IF, + OP_BITOR_IF, + OP_BITAND_FI, + OP_BITOR_FI, //180 + OP_AND_I, + OP_OR_I, + OP_AND_IF, + OP_OR_IF, + OP_AND_FI, + OP_OR_FI, + OP_NE_IF, + OP_NE_FI, + OP_GSTOREP_I, + OP_GSTOREP_F, //190 + OP_GSTOREP_ENT, + OP_GSTOREP_FLD, // integers + OP_GSTOREP_S, + OP_GSTOREP_FNC, // pointers + OP_GSTOREP_V, + OP_GADDRESS, + OP_GLOAD_I, + OP_GLOAD_F, + OP_GLOAD_FLD, + OP_GLOAD_ENT, //200 + OP_GLOAD_S, + OP_GLOAD_FNC, + OP_BOUNDCHECK, + + OP_NUMOPS +}; + + +#ifndef COMPILER +typedef struct statement16_s +{ + unsigned short op; + unsigned short a,b,c; +} dstatement16_t; +typedef struct statement32_s +{ + unsigned int op; + unsigned int a,b,c; +} dstatement32_t; +#else +typedef struct QCC_statement16_s +{ + unsigned short op; + unsigned short a,b,c; +} QCC_dstatement16_t; +typedef struct QCC_statement32_s +{ + unsigned int op; + unsigned int a,b,c; +} QCC_dstatement32_t; +#define QCC_dstatement_t QCC_dstatement32_t +#endif + +//these should be the same except the string type +#ifndef COMPILER +typedef struct ddef16_s +{ + unsigned short type; // if DEF_SAVEGLOBAL bit is set + // the variable needs to be saved in savegames + unsigned short ofs; + string_t s_name; +} ddef16_t; + +typedef struct ddef32_s +{ + unsigned int type; // if DEF_SAVEGLOBAL bit is set + // the variable needs to be saved in savegames + unsigned int ofs; + string_t s_name; +} ddef32_t; + +typedef struct fdef_s +{ + unsigned int type; // if DEF_SAVEGLOBAL bit is set + // the variable needs to be saved in savegames + unsigned int ofs; + unsigned int requestedofs; + string_t s_name; +} fdef_t; + +typedef void *ddefXX_t; +#else +typedef struct QCC_ddef16_s +{ + unsigned short type; // if DEF_SAVEGLOBAL bit is set + // the variable needs to be saved in savegames + unsigned short ofs; + QCC_string_t s_name; +} QCC_ddef16_t; + +typedef struct QCC_ddef32_s +{ + unsigned int type; // if DEF_SAVEGLOBAL bit is set + // the variable needs to be saved in savegames + unsigned int ofs; + QCC_string_t s_name; +} QCC_ddef32_t; + +#define QCC_ddef_t QCC_ddef32_t +#endif + +#define DEF_SAVEGLOBAL (1<<15) +#define DEF_SHARED (1<<14) + +#define MAX_PARMS 8 + +#ifndef COMPILER +typedef struct +{ + int first_statement; // negative numbers are builtins + int parm_start; + int locals; // total ints of parms + locals + + int profile; // runtime + + string_t s_name; + string_t s_file; // source file defined in + + int numparms; + qbyte parm_size[MAX_PARMS]; +} dfunction_t; +#else +typedef struct +{ + unsigned int first_statement; // negative numbers are builtins + unsigned int parm_start; + int locals; // total ints of parms + locals + + int profile; // runtime + + QCC_string_t s_name; + QCC_string_t s_file; // source file defined in + + int numparms; + qbyte parm_size[MAX_PARMS]; +} QCC_dfunction_t; +#endif + + +#define PROG_VERSION 6 +#define PROG_DEBUGVERSION 7 +#define PROG_SECONDARYVERSION16 (*(int*)"1FTE" ^ *(int*)"PROG") //something unlikly and still meaningful (to me) +#define PROG_SECONDARYVERSION32 (*(int*)"1FTE" ^ *(int*)"32B ") //something unlikly and still meaningful (to me) +typedef struct +{ + int version; + int crc; // check of header file + + unsigned int ofs_statements; //comp 1 + unsigned int numstatements; // statement 0 is an error + + unsigned int ofs_globaldefs; //comp 2 + unsigned int numglobaldefs; + + unsigned int ofs_fielddefs; //comp 4 + unsigned int numfielddefs; + + unsigned int ofs_functions; //comp 8 + unsigned int numfunctions; // function 0 is an empty + + unsigned int ofs_strings; //comp 16 + unsigned int numstrings; // first string is a null string + + unsigned int ofs_globals; //comp 32 + unsigned int numglobals; + + unsigned int entityfields; + + //debug / version 7 extensions + unsigned int ofsfiles; //non list format. no comp + unsigned int ofslinenums; //numstatements big //comp 64 + unsigned int ofsbodylessfuncs; //no comp + unsigned int numbodylessfuncs; + + unsigned int ofs_types; //comp 128 + unsigned int numtypes; + unsigned int blockscompressed; + + int secondaryversion; //Constant - to say that any version 7 progs are actually ours, not someone else's alterations. +} dprograms_t; + + +#endif + + + + + +typedef struct { + char filename[128]; + int size; + int compsize; + int compmethod; + int ofs; +} includeddatafile_t; + + + + +typedef struct typeinfo_s +{ + etype_t type; + + int next; + int aux_type; + int num_parms; + + int ofs; //inside a structure. + int size; + char *name; +} typeinfo_t; diff --git a/engine/qclib/Pr_exec.c b/engine/qclib/Pr_exec.c new file mode 100644 index 000000000..c5077f584 --- /dev/null +++ b/engine/qclib/Pr_exec.c @@ -0,0 +1,1090 @@ +#define PROGSUSED +#include "progsint.h" +//#include "editor.h" + +#define HunkAlloc BADGDFG sdfhhsf FHS + + +#define Host_Error Sys_Error + + +//============================================================================= + +/* +================= +PR_PrintStatement +================= +*/ +void PR_PrintStatement (progfuncs_t *progfuncs, dstatement16_t *s) +{ + int i; +printf("PR_PrintStatement is unsupported\n"); +return; + if ( (unsigned)s->op < OP_NUMOPS) + { + printf ("%s ", pr_opcodes[s->op].name); + i = strlen(pr_opcodes[s->op].name); + for ( ; i<10 ; i++) + printf (" "); + } + + if (s->op == OP_IF || s->op == OP_IFNOT) + printf ("%sbranch %i",PR_GlobalString(progfuncs, s->a),s->b); + else if (s->op == OP_GOTO) + { + printf ("branch %i",s->a); + } + else if ( (unsigned)(s->op - OP_STORE_F) < 6) + { + printf ("%s",PR_GlobalString(progfuncs, s->a)); + printf ("%s", PR_GlobalStringNoContents(progfuncs, s->b)); + } + else + { + if (s->a) + printf ("%s",PR_GlobalString(progfuncs, s->a)); + if (s->b) + printf ("%s",PR_GlobalString(progfuncs, s->b)); + if (s->c) + printf ("%s", PR_GlobalStringNoContents(progfuncs, s->c)); + } + printf ("\n"); +} + +/* +============ +PR_StackTrace +============ +*/ +char *QC_ucase(char *str) +{ + static char s[1024]; + strcpy(s, str); + str = s; + + while(*str) + { + if (*str >= 'a' && *str <= 'z') + *str = *str - 'a' + 'A'; + str++; + } + return s; +} + +void PR_StackTrace (progfuncs_t *progfuncs) +{ + dfunction_t *f; + int i; + int progs; + +#ifdef STACKTRACE + int arg; + int *globalbase; +#endif + progs = -1; + + if (pr_depth == 0) + { + printf ("\n"); + return; + } + +#ifdef STACKTRACE + globalbase = (int *)pr_globals + pr_xfunction->parm_start - pr_xfunction->locals; +#endif + + pr_stack[pr_depth].f = pr_xfunction; + for (i=pr_depth ; i>0 ; i--) + { + f = pr_stack[i].f; + + if (!f) + { + printf ("\n"); + } + else + { + if (pr_stack[i].progsnum != progs) + { + progs = pr_stack[i].progsnum; + + printf ("<%s>\n", pr_progstate[progs].filename); + } + if (!*f->s_file) + printf ("stripped : %s\n", f->s_name); + else + printf ("%12s : %s\n", f->s_file, f->s_name); + +#ifdef STACKTRACE + + for (arg = 0; arg < f->locals; arg++) + { + ddef16_t *local; + local = ED_GlobalAtOfs16(progfuncs, f->parm_start+arg); + if (!local) + { + printf(" ofs %i: %f : %i\n", f->parm_start+arg, *(float *)(globalbase - f->locals+arg), *(int *)(globalbase - f->locals+arg) ); + } + else + { + printf(" %s: %s\n", local->s_name, PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg))); + if (local->type == ev_vector) + arg+=2; + } + } + + if (i == pr_depth) + globalbase = localstack + localstack_used; + else + globalbase -= f->locals; +#endif + } + } +} + +/* +============ +PR_Profile_f + +============ +*/ +/* +void PR_Profile_f (void) +{ + dfunction_t *f, *best; + int max; + int num; + unsigned int i; + + num = 0; + do + { + max = 0; + best = NULL; + for (i=0 ; inumfunctions ; i++) + { + f = &pr_functions[i]; + if (f->profile > max && f->first_statement >=0) + { + max = f->profile; + best = f; + } + } + if (best) + { + if (num < 10) + printf ("%7i %s\n", best->profile, best->s_name); + num++; + best->profile = 0; + } + } while (best); +} +*/ + + +/* +============ +PR_RunError + +Aborts the currently executing function +============ +*/ +void VARGS PR_RunError (progfuncs_t *progfuncs, 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); + printf ("%s\n", string); + +//editbadfile(pr_strings + pr_xfunction->s_file, -1); + + pr_depth = 0; // dump the stack so host_error can shutdown functions + prinst->exitdepth = 0; + + Abort ("Program error %s", string); +} + +/* +============================================================================ +PR_ExecuteProgram + +The interpretation main loop +============================================================================ +*/ + +/* +==================== +PR_EnterFunction + +Returns the new program statement counter +==================== +*/ +int PR_EnterFunction (progfuncs_t *progfuncs, dfunction_t *f, int progsnum) +{ + int i, j, c, o; + + pr_stack[pr_depth].s = pr_xstatement; + pr_stack[pr_depth].f = pr_xfunction; + pr_stack[pr_depth].progsnum = progsnum; + pr_depth++; + if (pr_depth >= MAX_STACK_DEPTH) + { + printf ("stack overflow"); + PR_StackTrace (progfuncs); + pr_depth--; + return pr_xstatement; + } + +// save off any locals that the new function steps on (to a side place, fromwhere they are restored on exit) + c = f->locals; + if (localstack_used + c > LOCALSTACK_SIZE) + PR_RunError (progfuncs, "PR_ExecuteProgram: locals stack overflow\n"); + + for (i=0 ; i < c ; i++) + localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i]; + 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++; + } + } + + pr_xfunction = f; + return f->first_statement - 1; // offset the s++ +} + +/* +==================== +PR_LeaveFunction +==================== +*/ +int PR_LeaveFunction (progfuncs_t *progfuncs) +{ + int i, c; + + if (pr_depth <= 0) + Sys_Error ("prog stack underflow"); + +// restore locals from the stack + c = pr_xfunction->locals; + localstack_used -= c; + if (localstack_used < 0) + PR_RunError (progfuncs, "PR_ExecuteProgram: locals stack underflow\n"); + + for (i=0 ; i < c ; i++) + ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i]; + +// up stack + pr_depth--; + PR_MoveParms(progfuncs, pr_stack[pr_depth].progsnum, pr_typecurrent); + PR_SwitchProgs(progfuncs, pr_stack[pr_depth].progsnum); + pr_xfunction = pr_stack[pr_depth].f; + return pr_stack[pr_depth].s; +} + +ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, char *name, eval_t **val) +{ + static ddef32_t def; + ddef32_t *def32; + ddef16_t *def16; + int i; + + switch (pr_progstate[pr_typecurrent].intsize) + { + case 16: + case 24: + //this gets parms fine, but not locals + if (pr_xfunction) + for (i = 0; i < pr_xfunction->numparms; i++) + { + def16 = ED_GlobalAtOfs16(progfuncs, pr_xfunction->parm_start+i); + if (!def16) + continue; + if (!strcmp(def16->s_name, name)) + { + *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[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 32: + //this gets parms fine, but not locals + if (pr_xfunction) + for (i = 0; i < pr_xfunction->numparms; i++) + { + def32 = ED_GlobalAtOfs32(progfuncs, pr_xfunction->parm_start+i); + if (!def32) + continue; + if (!strcmp(def32->s_name, name)) + { + *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[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: + Sys_Error("Bad int size in ED_FindLocalOrGlobal"); + def32 = NULL; + } + + *val = (eval_t *)&pr_progstate[pr_typecurrent].globals[def32->ofs]; + return &def; +} + +char *COM_TrimString(char *str) +{ + int i; + static char buffer[256]; + while (*str <= ' ' && *str>'\0') + str++; + + for (i = 0; i < 255; i++) + { + if (*str <= ' ') + break; + buffer[i] = *str++; + } + buffer[i] = '\0'; + return buffer; +} + +char *EvaluateDebugString(progfuncs_t *progfuncs, char *key) +{ + static char buf[256]; + char *c, *c2; + ddef32_t *def; + fdef_t *fdef; + eval_t *val; + char *assignment; + int type; + + assignment = strchr(key, '='); + if (assignment) + *assignment = '\0'; + + c = strchr(key, '.'); + if (c) *c = '\0'; + def = ED_FindLocalOrGlobal(progfuncs, key, &val); + if (c) *c = '.'; + if (!def) + { + return "(Bad string)"; + } + //go through ent vars + + c = strchr(key, '.'); + while(c) + { + c2 = c+1; + c = strchr(c2, '.'); + type = def->type &~DEF_SAVEGLOBAL; + if (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)"; + val = (eval_t *) (((char *)PROG_TO_EDICT(val->_int) + externs->edictsize) + fdef->ofs*4); + def->type = fdef->type; + } + + if (assignment) + { + assignment++; + switch (def->type&~DEF_SAVEGLOBAL) + { + case ev_string: + *(string_t *)val = ED_NewString (progfuncs, assignment)-progfuncs->stringtable; + break; + + case ev_float: + *(float *)val = (float)atof (assignment); + break; + + case ev_integer: + *(int *)val = atoi (assignment); + break; + +/* case ev_vector: + strcpy (string, assignment); + v = string; + w = string; + for (i=0 ; i<3 ; i++) + { + while (*v && *v != ' ') + v++; + *v = 0; + ((float *)d)[i] = (float)atof (w); + w = v = v+1; + } + break; +*/ + case ev_entity: + *(int *)val = EDICT_TO_PROG(EDICT_NUM(progfuncs, atoi (assignment))); + break; + + case ev_field: + fdef = ED_FindField (progfuncs, assignment); + if (!fdef) + { + sprintf(buf, "Can't find field %s\n", assignment); + return buf; + } + *(int *)val = G_INT(fdef->ofs); + break; +/* + case ev_function: + if (s[1]==':'&&s[2]=='\0') + { + *(func_t *)val = 0; + return true; + } + func = ED_FindFunction (assignment, &i, -1); + if (!func) + { + printf ("Can't find function %s\n", assignment); + return false; + } + *(func_t *)val = (func - pr_progstate[i].functions) | (i<<24); + break; +*/ + default: + break; + + } + } + strcpy(buf, PR_ValueString(progfuncs, def->type, val)); + + return buf; +} + +int debugstatement; +//int EditorHighlightLine(window_t *wnd, int line); +void SetExecutionToLine(progfuncs_t *progfuncs, int linenum) +{ + int pn = pr_typecurrent; + int snum; + dfunction_t *f = pr_xfunction; + + switch(current_progstate->intsize) + { + case 16: + for (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++) + { + if (pr_statements16[snum].op == OP_DONE) + return; + } + break; + case 24: + case 32: + for (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++) + { + if (pr_statements32[snum].op == OP_DONE) + return; + } + break; + default: + Sys_Error("Bad intsize"); + snum = 0; + } + debugstatement = snum; +// EditorHighlightLine(editwnd, pr_progstate[pn].linenums[snum]); +} + +//0 clear. 1 set, 2 toggle, 3 check +int PR_ToggleBreakpoint(progfuncs_t *progfuncs, char *filename, int linenum, int flag) //write alternate route to work by function name. +{ + int ret=0; + unsigned int fl; + unsigned int i; + int pn = pr_typecurrent; + dfunction_t *f; + int op; + + for (pn = 0; pn < maxprogs; pn++) + { + if (!pr_progstate || !pr_progstate[pn].progs) + continue; + + if (linenum) //linenum is set means to set the breakpoint on a file and line + { + if (!pr_progstate[pn].linenums) + continue; + + for (f = pr_progstate[pn].functions, fl = 0; fl < pr_progstate[pn].progs->numfunctions; f++, fl++) + { + if (!strcmp(f->s_file, filename)) + { + for (i = f->first_statement; ; i++) + { + if (pr_progstate[pn].linenums[i] >= linenum) + { + fl = pr_progstate[pn].linenums[i]; + for (; ; i++) + { + if ((unsigned int)pr_progstate[pn].linenums[i] > fl) + break; + + switch(pr_progstate[pn].intsize) + { + case 16: + op = ((dstatement16_t*)pr_progstate[pn].statements + i)->op; + break; + case 24: + case 32: + op = ((dstatement32_t*)pr_progstate[pn].statements + i)->op; + break; + default: + Sys_Error("Bad intsize"); + op = 0; + } + 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(pr_progstate[pn].intsize) + { + case 16: + ((dstatement16_t*)pr_progstate[pn].statements + i)->op = op; + break; + case 24: + case 32: + ((dstatement32_t*)pr_progstate[pn].statements + i)->op = op; + break; + default: + Sys_Error("Bad intsize"); + op = 0; + } + } + goto cont; + } + } + } + } + } + else //set the breakpoint on the first statement of the function specified. + { + for (f = pr_progstate[pn].functions, fl = 0; fl < pr_progstate[pn].progs->numfunctions; f++, fl++) + { + if (!strcmp(f->s_name, filename)) + { + i = f->first_statement; + switch(pr_progstate[pn].intsize) + { + case 16: + op = ((dstatement16_t*)pr_progstate[pn].statements + i)->op; + break; + case 24: + case 32: + op = ((dstatement32_t*)pr_progstate[pn].statements + i)->op; + break; + default: + Sys_Error("Bad intsize"); + } + 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(pr_progstate[pn].intsize) + { + case 16: + ((dstatement16_t*)pr_progstate[pn].statements + i)->op = op; + break; + case 24: + case 32: + ((dstatement32_t*)pr_progstate[pn].statements + i)->op = op; + break; + default: + Sys_Error("Bad intsize"); + } + break; + } + } + } +cont: + continue; + } + + return ret; +} + +int ShowStep(progfuncs_t *progfuncs, int statement) +{ +// return statement; +// texture realcursortex; +static int lastline = 0; +static char *lastfile = NULL; + + int pn = pr_typecurrent; + int i; + dfunction_t *f = pr_xfunction; + + if (f && pr_progstate[pn].linenums) + { + if (lastline == pr_progstate[pn].linenums[statement] && lastfile == f->s_file) + return statement; //no info/same line as last time + + lastline = pr_progstate[pn].linenums[statement]; + lastfile = f->s_file; + + lastline = externs->useeditor(lastfile, lastline, 0, NULL); + + if (pr_progstate[pn].linenums[statement] != lastline) + { + for (i = f->first_statement; ; i++) + { + if (lastline == pr_progstate[pn].linenums[i]) + { + return i; + } + else if (lastline <= pr_progstate[pn].linenums[i]) + { + return statement; + } + } + } + } + else if (f) //annoying. + { + externs->useeditor(f->s_file, -1, 0, &f->s_name); + return statement; + } + + + return statement; +} + +//DMW: all pointer functions are modified to be absoloute pointers from NULL not sv_edicts +/* +==================== +PR_ExecuteProgram +==================== +*/ +void PR_ExecuteCode (progfuncs_t *progfuncs, int s) +{ + static dstatement16_t fakeop16; + static dstatement32_t fakeop32; + + eval_t *t, *swtch=NULL; + + int swtchtype; + dstatement16_t *st16; + dstatement32_t *st32; + dfunction_t *newf; + int runaway; + int i; + int p; + edictrun_t *ed; + eval_t *ptr; + + float *glob; + + int fnum; + + runaway = 100000; + + prinst->continuestatement = -1; + +#define PRBOUNDSCHECK +#define RUNAWAYCHECK() if (!--runaway) { pr_xstatement = st-pr_statements; PR_StackTrace(progfuncs); printf ("runaway loop error"); pr_depth = prinst->exitdepth;return;} + +#define OPA ((eval_t *)&glob[st->a]) +#define OPB ((eval_t *)&glob[st->b]) +#define OPC ((eval_t *)&glob[st->c]) + +restart: //jumped to when the progs might have changed. + glob = pr_globals; + switch (current_progstate->intsize) + { + case 16: +#define INTSIZE 16 + st16 = &pr_statements16[s]; + while (pr_trace) + { + #define DEBUGABLE + #include "execloop16d.h" + #undef DEBUGABLE + } + + while(1) + { + #include "execloop.h" + } +#undef INTSIZE + Sys_Error("PR_ExecuteProgram - should be unreachable"); + break; + case 24: + case 32: +#define INTSIZE 32 + st32 = &pr_statements32[s]; + while (pr_trace) + { + #define DEBUGABLE + #include "execloop32d.h" + #undef DEBUGABLE + } + + while(1) + { + #include "execloop32.h" + } +#undef INTSIZE + Sys_Error("PR_ExecuteProgram - should be unreachable"); + break; + default: + Sys_Error("PR_ExecuteProgram - bad intsize"); + } +} + + +void PR_ExecuteProgram (progfuncs_t *progfuncs, func_t fnum) +{ + dfunction_t *f; + int i; + progsnum_t initial_progs; + int oldexitdepth; + + int s; + + int newprogs = (fnum & 0xff000000)>>24; + + initial_progs = pr_typecurrent; + if (newprogs != initial_progs) + { + if (newprogs >= maxprogs || !&pr_progstate[newprogs].globals) //can happen with hexen2... + { + printf("PR_ExecuteProgram: tried branching into invalid progs\n"); + return; + } + PR_MoveParms(progfuncs, newprogs, pr_typecurrent); + PR_SwitchProgs(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)); + printf("PR_ExecuteProgram: NULL function from exe\n"); +// Host_Error ("PR_ExecuteProgram: NULL function from exe"); + +// PR_MoveParms(0, pr_typecurrent); + PR_SwitchProgs(progfuncs, 0); + return; + } + + oldexitdepth = prinst->exitdepth; + + f = &pr_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, (struct globalvars_s *)current_progstate->globals); + else + { + i -= externs->numglobalbuiltins; + if (i > current_progstate->numbuiltins) + { + printf ("Bad builtin call number %i (from exe)\n", -f->first_statement); + // PR_MoveParms(p, pr_typecurrent); + PR_SwitchProgs(progfuncs, initial_progs); + return; + } + current_progstate->builtins [i] (progfuncs, (struct globalvars_s *)current_progstate->globals); + } + PR_MoveParms(progfuncs, initial_progs, pr_typecurrent); + PR_SwitchProgs(progfuncs, initial_progs); + return; + } + + if (pr_trace) + pr_trace--; + +// make a stack frame + prinst->exitdepth = pr_depth; + + s = PR_EnterFunction (progfuncs, f, initial_progs); + + PR_ExecuteCode(progfuncs, s); + + + PR_MoveParms(progfuncs, initial_progs, pr_typecurrent); + PR_SwitchProgs(progfuncs, initial_progs); + + 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 *PR_ForkStack(progfuncs_t *progfuncs) +{ //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. + int i, l; + int ed = prinst->exitdepth; + int localsoffset, baselocalsoffset; + qcthread_t *thread = memalloc(sizeof(qcthread_t)); + dfunction_t *f; + + //copy out the functions stack. + for (i = 0,localsoffset=0; i < ed; i++) + { + if (i+1 == pr_depth) + f = pr_xfunction; + else + f = pr_stack[i+1].f; + localsoffset += f->locals; //this is where it crashes + } + baselocalsoffset = localsoffset; + for (i = ed; i < pr_depth; i++) + { + thread->fstack[i-ed].fnum = pr_stack[i].f - pr_progstate[pr_stack[i].progsnum].functions; + thread->fstack[i-ed].progsnum = pr_stack[i].progsnum; + thread->fstack[i-ed].statement = pr_stack[i].s; + + if (i+1 == pr_depth) + f = pr_xfunction; + else + f = pr_stack[i+1].f; + localsoffset += f->locals; + } + thread->fstackdepth = pr_depth - ed; + + for (i = pr_depth - 1; i >= ed ; i--) + { + if (i+1 == pr_depth) + f = pr_xfunction; + else + f = 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] = localstack[localsoffset+l]; //copy the old value into the globals (so the older functions have the correct locals. + } + } + + for (i = ed; i < pr_depth ; i++) //we need to get the locals back to how they were. + { + if (i+1 == pr_depth) + f = pr_xfunction; + else + f = 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 = pr_xstatement; + thread->xfunction = pr_xfunction - pr_progstate[pr_typecurrent].functions; + thread->xprogs = pr_typecurrent; + + return thread; +} + +void PR_ResumeThread (progfuncs_t *progfuncs, struct qcthread_s *thread) +{ + dfunction_t *f, *oldf; + int i,l,ls; + progsnum_t initial_progs; + int oldexitdepth; + + int s; + + progsnum_t prnum = thread->xprogs; + int fnum = thread->xfunction; + + if (localstack_used + thread->lstackused > LOCALSTACK_SIZE) + PR_RunError(progfuncs, "Too many locals on resumtion of QC thread\n"); + + if (pr_depth + thread->fstackdepth > MAX_STACK_DEPTH) + PR_RunError(progfuncs, "Too large stack on resumtion of QC thread\n"); + + + //do progs switching stuff as appropriate. (fteqw only) + initial_progs = pr_typecurrent; + PR_MoveParms(progfuncs, prnum, pr_typecurrent); + PR_SwitchProgs(progfuncs, prnum); + + + oldexitdepth = prinst->exitdepth; + prinst->exitdepth = pr_depth; + + ls = 0; + //add on the callstack. + for (i = 0; i < thread->fstackdepth; i++) + { + if (pr_depth == prinst->exitdepth) + { + pr_stack[pr_depth].f = pr_xfunction; + pr_stack[pr_depth].s = pr_xstatement; + pr_stack[pr_depth].progsnum = initial_progs; + } + else + { + pr_stack[pr_depth].progsnum = thread->fstack[i].progsnum; + pr_stack[pr_depth].f = pr_progstate[thread->fstack[i].progsnum].functions + thread->fstack[i].fnum; + pr_stack[pr_depth].s = thread->fstack[i].statement; + } + + if (i+1 == thread->fstackdepth) + f = &pr_functions[fnum]; + else + f = pr_progstate[thread->fstack[i+1].progsnum].functions + thread->fstack[i+1].fnum; + for (l = 0; l < f->locals; l++) + { + localstack[localstack_used++] = ((int *)pr_globals)[f->parm_start + l]; + ((int *)pr_globals)[f->parm_start + l] = thread->lstack[ls++]; + } + + pr_depth++; + } + + if (ls != thread->lstackused) + PR_RunError(progfuncs, "Thread stores incorrect locals count\n"); + + + f = &pr_functions[fnum]; + +// thread->lstackused -= f->locals; //the current function is the odd one out. + + //add on the locals stack + memcpy(localstack+localstack_used, thread->lstack, sizeof(int)*thread->lstackused); + 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 = pr_xfunction; + pr_xfunction = f; + s = thread->xstatement; + + PR_ExecuteCode(progfuncs, s); + + + PR_MoveParms(progfuncs, initial_progs, pr_typecurrent); + PR_SwitchProgs(progfuncs, initial_progs); + + prinst->exitdepth = oldexitdepth; + pr_xfunction = oldf; +} + +void PR_AbortStack (progfuncs_t *progfuncs) +{ + while(pr_depth > prinst->exitdepth+1) + PR_LeaveFunction(progfuncs); + prinst->continuestatement = 0; +} + diff --git a/engine/qclib/Progsint.h b/engine/qclib/Progsint.h new file mode 100644 index 000000000..0c724c3bf --- /dev/null +++ b/engine/qclib/Progsint.h @@ -0,0 +1,446 @@ +#ifdef WIN32 + +#ifndef AVAIL_ZLIB +#ifdef _MSC_VER +//#define AVAIL_ZLIB +#endif +#endif + +#include + +enum{false, true}; +#else +#include +#include + +#include +#include +#include +#include + +#ifndef __declspec +#define __declspec(mode) +#endif +typedef enum{false, true} boolean; +//#define _inline inline +#endif +typedef unsigned char qbyte; +#include + +#define DLL_PROG +#ifndef PROGSUSED +#define PROGSUSED +#endif + +#define DYNAMIC_ENTS + +extern int maxedicts; +extern int maxprogs; +extern int hunksize; + +#include "progtype.h" +#include "progslib.h" + +//extern progfuncs_t *progfuncs; + +#define prinst progfuncs->prinst +#define externs progfuncs->parms + +#include "pr_comp.h" + +#include "qcd.h" + +typedef struct +{ + int targetflags; //weather we need to mark the progs as a newer version + char *name; + char *opname; + int priority; + enum {ASSOC_LEFT, ASSOC_RIGHT, ASSOC_RIGHT_RESULT} associative; + struct QCC_type_s **type_a, **type_b, **type_c; +} QCC_opcode_t; +extern QCC_opcode_t pr_opcodes[]; // sized by initialization + + + + +#ifdef _MSC_VER +#define Q_vsnprintf _vsnprintf +#else +#define Q_vsnprintf vsnprintf +#endif + + +#define sv_num_edicts (*externs->sv_num_edicts) +#define sv_edicts (*externs->sv_edicts) + +#define printf externs->printf +#define Sys_Error externs->Sys_Error +#define Abort externs->Abort + +#define memalloc externs->memalloc +#define memfree externs->memfree + +int PRHunkMark(progfuncs_t *progfuncs); +void PRHunkFree(progfuncs_t *progfuncs, int mark); +void *PRHunkAlloc(progfuncs_t *progfuncs, int size); + +//void *HunkAlloc (int size); +char *VARGS qcva (char *text, ...); +void QC_InitShares(progfuncs_t *progfuncs); +void QC_StartShares(progfuncs_t *progfuncs); +void QC_AddSharedVar(progfuncs_t *progfuncs, int num, int type); +void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num); +int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, int requestedpos, int origionalofs); +pbool Decompile(progfuncs_t *progfuncs, char *fname); +int PR_ToggleBreakpoint(progfuncs_t *progfuncs, char *filename, int linenum, int flag); + + +#define edvars(ed) (((char *)ed)+externs->edictsize) //pointer to the field vars, given an edict + + + +extern short (*BigShort) (short l); +extern short (*LittleShort) (short l); +extern long (*BigLong) (long l); +extern long (*LittleLong) (long l); +extern float (*BigFloat) (float l); +extern float (*LittleFloat) (float l); + + + +/* +#ifndef COMPILER +typedef union eval_s +{ + string_t string; + float _float; + float vector[3]; + func_t function; + int _int; + int edict; + progsnum_t prog; //so it can easily be changed +} eval_t; +#endif +*/ + + +#define MAX_ENT_LEAFS 16 +typedef struct edictrun_s +{ + pbool isfree; + + float freetime; // realtime when the object was freed + int entnum; + pbool readonly; //causes error when QC tries writing to it. (quake's world entity) + +// other fields from progs come immediately after +} edictrun_t; +#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edictrun_t,area) + + +int Comp_Begin(progfuncs_t *progfuncs, int nump, char **parms); +int Comp_Continue(progfuncs_t *progfuncs); + +char *EvaluateDebugString(progfuncs_t *progfuncs, char *key); +char *SaveEnts(progfuncs_t *progfuncs, char *mem, int *size, int mode); +int LoadEnts(progfuncs_t *progfuncs, char *file, float killonspawnflags); +char *SaveEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed); +struct edict_s *RestoreEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed); +char *PF_VarString (int first); +void PR_StackTrace (progfuncs_t *progfuncs); + +extern int outputversion; +extern int noextensions; + +#ifndef COMPILER +typedef struct progstate_s +{ + dprograms_t *progs; + dfunction_t *functions; + char *strings; + union { + ddefXX_t *globaldefs; + ddef16_t *globaldefs16; + ddef32_t *globaldefs32; + }; + union { + ddefXX_t *fielddefs; + ddef16_t *fielddefs16; + ddef32_t *fielddefs32; + }; + void *statements; +// void *global_struct; + float *globals; // same as pr_global_struct + + typeinfo_t *types; + + int edict_size; // in bytes + + char filename[128]; + + builtin_t *builtins; + int numbuiltins; + + int *linenums; //debug versions only + + int intsize; //16 for standard (more limiting) versions +} progstate_t; + +typedef struct extensionbuiltin_s { + char *name; + builtin_t func; + struct extensionbuiltin_s *prev; +} extensionbuiltin_t; + +//============================================================================ + + +#define pr_progs current_progstate->progs +#define pr_functions current_progstate->functions +#define pr_strings current_progstate->strings +#define pr_globaldefs16 ((ddef16_t*)current_progstate->globaldefs) +#define pr_globaldefs32 ((ddef32_t*)current_progstate->globaldefs) +#define pr_fielddefs16 ((ddef16_t*)current_progstate->fielddefs) +#define pr_fielddefs32 ((ddef32_t*)current_progstate->fielddefs) +#define pr_statements16 ((dstatement16_t*)current_progstate->statements) +#define pr_statements32 ((dstatement32_t*)current_progstate->statements) +//#define pr_global_struct current_progstate->global_struct +#define pr_globals current_progstate->globals +#define pr_linenums current_progstate->linenums +#define pr_types current_progstate->types + + + +//============================================================================ + +void PR_Init (void); + +void PR_ExecuteProgram (progfuncs_t *progfuncs, func_t fnum); +int PR_LoadProgs(progfuncs_t *progfncs, char *s, int headercrc, builtin_t *builtins, int numbuiltins); +int PR_ReallyLoadProgs (progfuncs_t *progfuncs, char *filename, int headercrc, progstate_t *progstate, pbool complain); + +void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount); + +void PR_Profile_f (void); + +struct edict_s *ED_Alloc (progfuncs_t *progfuncs); +void ED_Free (progfuncs_t *progfuncs, struct edict_s *ed); + +char *ED_NewString (progfuncs_t *progfuncs, char *string); +// returns a copy of the string allocated from the server's string heap + +void ED_Print (progfuncs_t *progfuncs, struct edict_s *ed); +//void ED_Write (FILE *f, edictrun_t *ed); +char *ED_ParseEdict (progfuncs_t *progfuncs, char *data, edictrun_t *ent); + +//void ED_WriteGlobals (FILE *f); +void ED_ParseGlobals (char *data); + +//void ED_LoadFromFile (char *data); + +//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size)) +//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size) + +struct edict_s *EDICT_NUM(progfuncs_t *progfuncs, int n); +int NUM_FOR_EDICT(progfuncs_t *progfuncs, struct edict_s *e); + +//#define NEXT_EDICT(e) ((edictrun_t *)( (byte *)e + pr_edict_size)) + +#define EDICT_TO_PROG(e) ((qbyte *)e - (qbyte *)sv_edicts) +#define PROG_TO_EDICT(e) ((edictrun_t *)((qbyte *)sv_edicts + e)) + +//============================================================================ + +#define G_FLOAT(o) (pr_globals[o]) +#define G_FLOAT2(o) (pr_globals[OFS_PARM0 + o*3]) +#define G_INT(o) (*(int *)&pr_globals[o]) +#define G_EDICT(o) ((edict_t *)((qbyte *)sv_edicts+ *(int *)&pr_globals[o])) +#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o)) +#define G_VECTOR(o) (&pr_globals[o]) +#define G_STRING(o) (*(string_t *)&pr_globals[o]) +#define G_STRING2(o) ((char*)*(string_t *)&pr_globals[o]) +#define GQ_STRING(o) (*(QCC_string_t *)&pr_globals[o]) +#define GQ_STRING2(o) ((char*)*(QCC_string_t *)&pr_globals[o]) +#define G_FUNCTION(o) (*(func_t *)&pr_globals[o]) +#define G_PROG(o) (*(progsnum_t *)&pr_globals[o]) //simply so it's nice and easy to change... + +#define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) + +#define E_FLOAT(e,o) (((float*)&e->v)[o]) +#define E_INT(e,o) (*(int *)&((float*)&e->v)[o]) +#define E_VECTOR(e,o) (&((float*)&e->v)[o]) +#define E_STRING(e,o) (*(string_t *)&((float*)(e+1))[o]) + +extern int type_size[9]; + + +extern unsigned short pr_crc; + +void VARGS PR_RunError (progfuncs_t *progfuncs, char *error, ...); + +void ED_PrintEdicts (progfuncs_t *progfuncs); +void ED_PrintNum (progfuncs_t *progfuncs, int ent); + + +pbool PR_SwitchProgs(progfuncs_t *progfuncs, progsnum_t type); +void PR_MoveParms(progfuncs_t *progfuncs, progsnum_t progs1, progsnum_t progs2); + + + + +eval_t *GetEdictFieldValue(progfuncs_t *progfuncs, struct edict_s *ed, char *name, evalc_t *cache); + +#endif + + + + +#ifndef COMPILER + +//this is windows - all files are written with this endian standard +//optimisation +//leave undefined if in doubt over os. +#ifndef WIN32 +#define NOENDIAN +#endif + + +typedef struct { + int varofs; + int size; +} sharedvar_t; +typedef struct +{ + int s; + dfunction_t *f; + int progsnum; +} prstack_t; + + + +//pr_multi.c +void PR_SetBuiltins(int type); + +#define var(type, name) type name +#define vars(type, name, size) type name[size] + +typedef struct prinst_s { +var(progstate_t *, pr_progstate); +#define pr_progstate prinst->pr_progstate + +var(progsnum_t, pr_typecurrent); +#define pr_typecurrent prinst->pr_typecurrent +var(int, maxprogs); +#define maxprogs prinst->maxprogs + +var(progstate_t *,current_progstate); +#define current_progstate prinst->current_progstate + +var(unsigned int, numshares); +#define numshares prinst->numshares +var(sharedvar_t *,shares); //shared globals, not including parms +#define shares prinst->shares +var(unsigned int, maxshares); +#define maxshares prinst->maxshares + +var(struct prmemb_s *, memblocks); +#define memb prinst->memblocks + +var(unsigned int, maxfields); +#define maxfields prinst->maxfields +var(unsigned int, numfields); +#define numfields prinst->numfields +var(fdef_t*, field); //biggest size +#define field prinst->field + +int reorganisefields; + + +//pr_exec.c +#define MAX_STACK_DEPTH 64 +vars(prstack_t, pr_stack, MAX_STACK_DEPTH); +#define pr_stack prinst->pr_stack +var(int, pr_depth); +#define pr_depth prinst->pr_depth + +#define LOCALSTACK_SIZE 16384 +vars(int, localstack, LOCALSTACK_SIZE); +#define localstack prinst->localstack +var(int, localstack_used); +#define localstack_used prinst->localstack_used + +var(int, continuestatement); +var(int, exitdepth); + +var(int, pr_trace); +#define pr_trace prinst->pr_trace +var(dfunction_t *, pr_xfunction); +#define pr_xfunction prinst->pr_xfunction +var(int, pr_xstatement); +#define pr_xstatement prinst->pr_xstatement + +var(int, pr_argc); +#define pr_argc prinst->pr_argc + +//pr_edict.c + +var(int, maxedicts); +#define maxedicts prinst->maxedicts + +var(evalc_t, spawnflagscache); +#define spawnflagscache prinst->spawnflagscache + + + + +var(int, pr_edict_size); // in bytes +#define pr_edict_size prinst->pr_edict_size +var(int, pr_max_edict_size); +#define pr_max_edict_size prinst->pr_max_edict_size + + +//initlib.c +var(char *, progshunk); +#define progshunk prinst->progshunk +var(int, hunkused); +#define hunkused prinst->hunkused +var(int, hunksize); +#define hunksize prinst->hunksize + + +var(extensionbuiltin_t *, extensionbuiltin); +#define extensionbuiltin prinst->extensionbuiltin + +#ifdef DYNAMIC_ENTS + struct edict_s **edicttable; +#endif + +} prinst_t; +extern vec3_t vec3_origin; + +eval_t *PR_FindGlobal(progfuncs_t *prfuncs, char *globname, progsnum_t pnum); +ddef16_t *ED_FindTypeGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type); +ddef32_t *ED_FindTypeGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type); +ddef16_t *ED_FindGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum); +ddef32_t *ED_FindGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum); +fdef_t *ED_FindField (progfuncs_t *progfuncs, char *name); +dfunction_t *ED_FindFunction (progfuncs_t *progfuncs, char *name, int *pnum, int fromprogs); +func_t PR_FindFunc(progfuncs_t *progfncs, char *funcname, progsnum_t pnum); +void PR_Configure (progfuncs_t *progfncs, void *mem, int mem_size, int max_progs); +int PR_InitEnts(progfuncs_t *progfncs, int maxents); +char *PR_ValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val); + +ddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs); +ddef16_t *ED_FindGlobal16 (progfuncs_t *progfuncs, char *name); +ddef32_t *ED_FindGlobal32 (progfuncs_t *progfuncs, char *name); +ddef32_t *ED_GlobalAtOfs32 (progfuncs_t *progfuncs, unsigned int ofs); + +char *PR_GlobalString (progfuncs_t *progfuncs, int ofs); +char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs); + +pbool CompileFile(progfuncs_t *progfuncs, char *filename); + +char *QCC_COM_Parse (char *data); +extern char qcc_token[1024]; +#endif diff --git a/engine/qclib/Progslib.h b/engine/qclib/Progslib.h new file mode 100644 index 000000000..72d3fd28c --- /dev/null +++ b/engine/qclib/Progslib.h @@ -0,0 +1,278 @@ +/*#define true 1 +#define false 0 + +#define PITCH 0 +#define YAW 1 +#define ROLL 2 + +typedef char bool; +//typedef float vec3_t[3]; +typedef int progsnum_t; +typedef int func_t; +#ifndef COMPILER +typedef char *string_t; +#endif +//typedef struct globalvars_s globalvars_t; +//typedef struct edict_s edict_t; +#define globalvars_t void +#define edict_t void +*/ + +#ifdef _MSC_VER +#define VARGS __cdecl +#endif +#ifndef VARGS +#define VARGS +#endif + + +struct edict_s; +struct globalvars_s; +typedef struct progfuncs_s progfuncs_t; +typedef void (*builtin_t) (progfuncs_t *prinst, struct globalvars_s *gvars); + +//used by progs engine. All nulls is reset. +typedef struct { + char *varname; + struct fdef_s *ofs32; + + int spare[2]; +} evalc_t; +#define sizeofevalc sizeof(evalc_t) +typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_integer, ev_struct, ev_union} etype_t; + +struct progfuncs_s { + int progsversion; //PROGSTRUCT_VERSION + + + void (*PR_Configure) (progfuncs_t *prinst, void *mem, int memsize, int max_progs); //configure buffers and memory. Used to reset and must be called first. + progsnum_t (*PR_LoadProgs) (progfuncs_t *prinst, char *s, int headercrc, builtin_t *builtins, int numbuiltins); //load a progs + int (*PR_InitEnts) (progfuncs_t *prinst, int max_ents); //returns size of edicts for use with nextedict macro + void (*PR_ExecuteProgram) (progfuncs_t *prinst, func_t fnum); //start execution + pbool (*PR_SwitchProgs) (progfuncs_t *prinst, progsnum_t num); //switch to a different progs - my aim is to make this obsolete + struct globalvars_s *(*globals) (progfuncs_t *prinst, progsnum_t num); //get the globals of a progs + struct entvars_s *(*entvars) (progfuncs_t *prinst, struct edict_s *ent); //return a pointer to the entvars of an ent + + void (VARGS *PR_RunError) (progfuncs_t *prinst, char *msg, ...); //builtins call this to say there was a problem + void (*PR_PrintEdict) (progfuncs_t *prinst, struct edict_s *ed); //get a listing of all vars on an edict (sent back via 'print') + + struct edict_s *(*ED_Alloc) (progfuncs_t *prinst); + void (*ED_Free) (progfuncs_t *prinst, struct edict_s *ed); + + struct edict_s *(*EDICT_NUM) (progfuncs_t *prinst, int n); //get the nth edict + int (*NUM_FOR_EDICT) (progfuncs_t *prinst, struct edict_s *e); //so you can find out what that 'n' will be + + void (*SetGlobalEdict) (progfuncs_t *prinst, struct edict_s *ed, int ofs); //set a global to an edict (partially obsolete) + + char *(*PR_VarString) (progfuncs_t *prinst, int first); //returns a string made up of multiple arguments + + struct progstate_s **progstate; //these are so the macros work properly +// struct edict_s **sv_edicts; + +// int *sv_num_edicts; + + func_t (*PR_FindFunction) (progfuncs_t *prinst, char *funcname, progsnum_t num); + + int (*PR_StartCompile) (progfuncs_t *prinst, int argv, char **argc); //1 if can compile, 0 if failed to compile + int (*PR_ContinueCompile) (progfuncs_t *prinst); //2 if finished, 1 if more to go, 0 if failed + + char *(*filefromprogs) (progfuncs_t *prinst, progsnum_t prnum, char *fname, int *size, char *buffer); //reveals encoded/added files from already loaded progs + char *(*filefromnewprogs) (progfuncs_t *prinst, char *prname, char *fname, int *size, char *buffer); //reveals encoded/added files from a progs on the disk somewhere + + char *(*save_ents) (progfuncs_t *prinst, char *buf, int *size, int mode); //dump the entire progs info into one big self allocated string + int (*load_ents) (progfuncs_t *prinst, char *s, float killonspawnflags); //restore the entire progs state (or just add some more ents) (returns edicts ize) + + char *(*saveent) (progfuncs_t *prinst, char *buf, int *size, struct edict_s *ed); //will save just one entities vars + struct edict_s *(*restoreent) (progfuncs_t *prinst, char *buf, int *size, struct edict_s *ed); //will restore the entity that had it's values saved (can use NULL for ed) + + union eval_s *(*FindGlobal) (progfuncs_t *prinst, char *name, progsnum_t num); //find a pointer to the globals value + char *(*AddString) (progfuncs_t *prinst, char *val); //dump a string into the progs memory (for setting globals and whatnot) + void *(*Tempmem) (progfuncs_t *prinst, int ammount, char *whatfor); //grab some mem for as long as the progs stays loaded (for strings) + + union eval_s *(*GetEdictFieldValue) (progfuncs_t *prinst, struct edict_s *ent, char *name, evalc_t *s); //get an entityvar (cache it) and return the possible values + struct edict_s *(*ProgsToEdict) (progfuncs_t *prinst, int progs); //edicts are stored as ints and need to be adjusted + int (*EdictToProgs) (progfuncs_t *prinst, struct edict_s *ed); //edicts are stored as ints and need to be adjusted + + char *(*EvaluateDebugString) (progfuncs_t *prinst, char *key); //evaluate a string and return it's value (according to current progs) (expands edict vars) + + int *pr_trace; //start calling the editor for each line executed + + void (*PR_StackTrace) (progfuncs_t *prinst); + + int (*ToggleBreak) (progfuncs_t *prinst, char *filename, int linenum, int mode); + + int numprogs; + + struct progexterns_s *parms; //these are the initial parms, they may be changed + + pbool (*Decompile) (progfuncs_t *prinst, char *fname); + + + struct prinst_s *prinst; //internal variables. Leave alone. + + int *callargc; //number of args of built-in call + void (*RegisterBuiltin) (progfuncs_t *prinst, char *, builtin_t); + + int stringtable; //qc strings are all relative. add to a qc string. this is required for support of frikqcc progs that strip string immediates. + int fieldadjust; //FrikQCC style arrays can cause problems due to field remapping. This causes us to leave gaps but offsets identical. + + struct qcthread_s *(*Fork) (progfuncs_t *prinst); + void (*RunThread) (progfuncs_t *prinst, struct qcthread_s *thread); + void (*AbortStack) (progfuncs_t *prinst); +}; + +typedef struct progexterns_s { + int progsversion; //PROGSTRUCT_VERSION + + unsigned char *(*ReadFile) (char *fname, void *buffer, int len); + int (*FileSize) (char *fname); //-1 if file does not exist + pbool (*WriteFile) (char *name, void *data, int len); + int (VARGS *printf) (const char *, ...); + void (VARGS *Sys_Error) (const char *, ...); + void (VARGS *Abort) (char *, ...); + int edictsize; //size of edict_t + + void (*entspawn) (struct edict_s *ent); //ent has been spawned, but may not have all the extra variables (that may need to be set) set + pbool (*entcanfree) (struct edict_s *ent); //return true to stop ent from being freed + void (*stateop) (progfuncs_t *prinst, float var, func_t func); + void (*cstateop) (progfuncs_t *prinst, float vara, float varb, func_t currentfunc); + void (*cwstateop) (progfuncs_t *prinst, float vara, float varb, func_t currentfunc); + void (*thinktimeop) (progfuncs_t *prinst, struct edict_s *ent, float varb); + + + //used when loading a game + builtin_t *(*builtinsfor) (int num, int headercrc); //must return a pointer to the builtins that were used before the state was saved. + void (*loadcompleate) (int edictsize); //notification to reset any pointers. + + void *(VARGS *memalloc) (int size); //small string allocation malloced and freed randomly by the executor. (use malloc if you want) + void (VARGS *memfree) (void * mem); + + + builtin_t *globalbuiltins; //these are available to all progs + int numglobalbuiltins; + + enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS, PR_COMPILEIGNORE} autocompile; + + double *gametime; + + struct edict_s **sv_edicts; + int *sv_num_edicts; + + int (*useeditor) (char *filename, int line, int nump, char **parms); +} progparms_t, progexterns_t; + +void QC_AddSharedVar(progfuncs_t *progfuncs, int start, int size); + + +#if defined(QCLIBDLL_EXPORTS) +__declspec(dllexport) +#endif +progfuncs_t * InitProgs(progparms_t *ext); +#if defined(QCLIBDLL_EXPORTS) +__declspec(dllexport) +#endif +void CloseProgs(progfuncs_t *inst); + +#ifndef COMPILER +typedef union eval_s +{ + string_t string; + float _float; + float vector[3]; + func_t function; + int _int; + int edict; + progsnum_t prog; //so it can easily be changed +} eval_t; +#endif + +#define PR_CURRENT -1 +#define PR_ANY -2 //not always valid. Use for finding funcs +#define PROGSTRUCT_VERSION 1 + + +#ifndef DLL_PROG +#define PR_Configure(pf, mem, memsize, max_progs) (*pf->PR_Configure) (pf, mem, memsize, max_progs) +#define PR_LoadProgs(pf, s, headercrc, builtins, numb) (*pf->PR_LoadProgs) (pf, s, headercrc, builtins, numb) +#define PR_InitEnts(pf, maxents) (*pf->PR_InitEnts) (pf, maxents) +#define PR_ExecuteProgram(pf, fnum) (*pf->PR_ExecuteProgram) (pf, fnum) +#define PR_SwitchProgs(pf, num) (*pf->PR_SwitchProgs) (pf, num); +#define PR_globals(pf, num) (*pf->globals) (pf, num) +#define PR_entvars(pf, ent) (*pf->entvars) (pf, ent) + +#define ED_Alloc(pf) (*pf->ED_Alloc) (pf) +#define ED_Free(pf, ed) (*pf->ED_Free) (pf, ed) + +#define PR_LoadEnts(pf, s, kf) (*pf->load_ents) (pf, s, kf) +#define PR_SaveEnts(pf, buf, size, mode) (*pf->save_ents) (pf, buf, size, mode) + +#define EDICT_NUM(pf, num) (*pf->EDICT_NUM) (pf, num) +#define NUM_FOR_EDICT(pf, e) (*pf->NUM_FOR_EDICT) (pf, e) +#define SetGlobalEdict(pf, ed, ofs) (*pf->SetGlobalEdict) (pf, ed, ofs) +#define PR_VarString (*progfuncs->PR_VarString) + +//#define sv_edicts (*progfuncs->sv_edicts) +#define current_progstate (*progfuncs->progstate) + +//#define pr_num_edicts (*progfuncs->sv_num_edicts) + +#define PR_FindFunction(pf, name, num) (*pf->PR_FindFunction) (pf, name, num) +#define PR_FindGlobal(pf, name, progs) (*pf->FindGlobal) (pf, name, progs) +#define PR_AddString(pf, ed) (*pf->AddString) (pf, ed) +#define PR_Alloc (*progfuncs->Tempmem) + +#define PROG_TO_EDICT(pf, ed) (*pf->ProgsToEdict) (pf, ed) +#define EDICT_TO_PROG(pf, ed) (*pf->EdictToProgs) (pf, ed) + +#define PR_RunError (*progfuncs->PR_RunError) +#define PR_PrintEdict (*progfuncs->PR_PrintEdict) + +#define PR_RegisterBuiltin(pf, name, func) (*pf->RegisterBuiltin) (pf, name, func) + +//#ifdef DYNAMIC_ENTS +#define NEXT_EDICT(pf,o) EDICT_NUM(pf, NUM_FOR_EDICT(pf, o)+1) +/*#else +#define NEXT_EDICT(pf, o) (edict_t *)(((char *)o)+ pr_edict_size) +#endif*/ +#define RETURN_EDICT(pf, e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(pf, e)) + + +//builtin funcs (which operate on globals) +#define G_FLOAT(o) (((float *)pr_globals)[o]) +#define G_FLOAT2(o) (((float *)pr_globals)[OFS_PARM0 + o*3]) +#define G_INT(o) (((int *)pr_globals)[o]) +#define G_EDICT(pf, o) PROG_TO_EDICT(pf, G_INT(o)) //((edict_t *)((char *) sv.edicts+ *(int *)&((float *)pr_globals)[o])) +#define G_EDICTNUM(pf, o) NUM_FOR_EDICT(pf, G_EDICT(pf, o)) +#define G_VECTOR(o) (&((float *)pr_globals)[o]) +#define G_FUNCTION(o) (*(func_t *)&((float *)pr_globals)[o]) +#define G_PROG(o) (*(progsnum_t *)&((float *)pr_globals)[o]) //simply so it's nice and easy to change... + +#define PR_GetString(p,s) (s?s + p->stringtable:"") +#define PR_GetStringOfs(p,o) (G_INT(o)?(char *)G_INT(o) + p->stringtable:"") +#define PR_SetString(p, s) ((s&&*s)?(s - p->stringtable):0) +#define PR_NewString(p, s) (PR_AddString(p, s) - p->stringtable) + +#define ev_prog ev_integer + +#define E_STRING(o) (char *)(((int *)((char *)ed) + progparms.edictsize)[o]) + +//#define pr_global_struct pr_globals + +#endif + + +#define OFS_NULL 0 +#define OFS_RETURN 1 +#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors +#define OFS_PARM1 7 +#define OFS_PARM2 10 +#define OFS_PARM3 13 +#define OFS_PARM4 16 +#define OFS_PARM5 19 +#define OFS_PARM6 22 +#define OFS_PARM7 25 +#define RESERVED_OFS 28 + + +#undef edict_t +#undef globalvars_t diff --git a/engine/qclib/QccMain.c b/engine/qclib/QccMain.c new file mode 100644 index 000000000..434c1a620 --- /dev/null +++ b/engine/qclib/QccMain.c @@ -0,0 +1,3198 @@ +#ifndef MINIMAL + +#define PROGSUSED +#include "qcc.h" +int mkdir(const char *path); + +int outputversion; + +char QCC_copyright[1024]; +int QCC_packid; +char QCC_Packname[5][128]; + +extern QCC_def_t *functemps; //floats/strings/funcs/ents... + +extern int optres_test1; +extern int optres_test2; + +int writeasm; + + +pbool QCC_PR_SimpleGetToken (void); +void QCC_PR_LexWhitespace (void); + +void *FS_ReadToMem(char *fname, void *membuf, int *len); +void FS_CloseFromMem(void *mem); + +struct qcc_includechunk_s *currentchunk; + +unsigned int MAX_REGS = 32768; + +int MAX_STRINGS = 1000000; +int MAX_GLOBALS = 16384; +int MAX_FIELDS = 1024; +int MAX_STATEMENTS = 65536; +int MAX_FUNCTIONS = 8192; +int MAX_CONSTANTS; +int max_temps; + +int *qcc_tempofs; +int tempsstart; +int numtemps; + + +pbool newstylesource; +char destfile[1024]; + +float *qcc_pr_globals; +unsigned int numpr_globals; + +char *strings; +int strofs; + +QCC_dstatement_t *statements; +int numstatements; +int *statement_linenums; + +QCC_dfunction_t *functions; +int numfunctions; + +QCC_ddef_t *qcc_globals; +int numglobaldefs; + +QCC_ddef_t *fields; +int numfielddefs; + +//typedef char PATHSTRING[MAX_DATA_PATH]; + +PATHSTRING *precache_sounds; +int *precache_sounds_block; +int numsounds; + +PATHSTRING *precache_textures; +int *precache_textures_block; +int numtextures; + +PATHSTRING *precache_models; +int *precache_models_block; +int nummodels; + +PATHSTRING *precache_files; +int *precache_files_block; +int numfiles; + +extern int numCompilerConstants; +hashtable_t compconstantstable; +hashtable_t globalstable; +hashtable_t localstable; +hashtable_t floatconstdefstable; +hashtable_t stringconstdefstable; + +pbool qccwarningdisabled[WARN_MAX]; + +qcc_targetformat_t qcc_targetformat; + +pbool bodylessfuncs; + +QCC_type_t *qcc_typeinfo; +int numtypeinfos; +int maxtypeinfos; + + +struct { + char *name; + int index; +} warningnames[] = +{ + {"Q302", WARN_NOTREFERENCED}, +// {"", WARN_NOTREFERENCEDCONST}, +// {"", WARN_CONFLICTINGRETURNS}, + {"Q105", WARN_TOOFEWPARAMS}, + {"Q101", WARN_TOOMANYPARAMS}, +// {"", WARN_UNEXPECTEDPUNCT}, + {"Q106", WARN_ASSIGNMENTTOCONSTANT}, + {"Q203", WARN_MISSINGRETURNVALUE}, + {"Q204", WARN_WRONGRETURNTYPE}, + {"Q205", WARN_POINTLESSSTATEMENT}, + {"Q206", WARN_MISSINGRETURN}, + {"Q207", WARN_DUPLICATEDEFINITION}, + {"Q100", WARN_PRECOMPILERMESSAGE}, +// {"", WARN_STRINGTOOLONG}, +// {"", WARN_BADTARGET}, + {"Q120", WARN_BADPRAGMA}, +// {"", WARN_HANGINGSLASHR}, +// {"", WARN_NOTDEFINED}, +// {"", WARN_SWITCHTYPEMISMATCH}, +// {"", WARN_CONFLICTINGUNIONMEMBER}, +// {"", WARN_KEYWORDDISABLED}, +// {"", WARN_ENUMFLAGS_NOTINTEGER}, +// {"", WARN_ENUMFLAGS_NOTBINARY}, +// {"", WARN_CASEINSENSATIVEFRAMEMACRO}, + {"Q111", WARN_DUPLICATELABEL}, + {"Q201", WARN_ASSIGNMENTINCONDITIONAL}, + {"F300", WARN_DEADCODE}, + {NULL} +}; + +optimisations_t optimisations[] = +{ + //level 0 = no optimisations + //level 1 = size optimisations + //level 2 = speed optimisations + //level 3 = dodgy optimisations. + //level 4 = experimental... + {&opt_assignments, "t", 1, 2, "assignments"}, + {&opt_shortenifnots, "i", 1, 2, "shortenifs"}, + {&opt_nonvec_parms, "p", 1, 2, "nonvec_parms"}, + {&opt_constant_names, "c", 2, 1, "constant_names"}, + {&opt_constant_names_strings, "cs", 3, 1, "constant_names_strings"}, + {&opt_dupconstdefs, "d", 1, 2, "dupconstdefs"}, + {&opt_noduplicatestrings, "s", 1, 0, "noduplicatestrings"}, + {&opt_locals, "l", 1, 1, "locals"}, + {&opt_function_names, "n", 1, 1, "function_names"}, + {&opt_filenames, "f", 1, 1, "filenames"}, + {&opt_unreferenced, "u", 1, 2, "unreferenced"}, + {&opt_overlaptemps, "r", 1, 2, "overlaptemps"}, + {&opt_constantarithmatic, "a", 1, 2, "constantarithmatic"}, + {&opt_precache_file, "pf", 2, 0, "precache_file"}, + {&opt_return_only, "ro", 3, 0, "return_only"}, + {&opt_compound_jumps, "cj", 3, 0, "compound_jumps"}, +// {&opt_comexprremoval, "cer", 4, 0, "expression_removal"}, //this would be too hard... + {&opt_stripfunctions, "sf", 3, 0, "strip_functions"}, + {&opt_locals_marshalling, "lm", 4, 1, "locals_marshalling"}, + {&opt_logicops, "o", 2, 0, "logicops"}, + {NULL} +}; + + +struct { + pbool *enabled; + pbool defaultval; + char *name; +} compiler_flag[] = { + //keywords + {&keyword_var, true, "var"}, + {&keyword_thinktime, false, "thinktime"}, + {&keyword_switch, true, "switch"}, + {&keyword_for, true, "for"}, + {&keyword_case, true, "case"}, + {&keyword_default, true, "default"}, + {&keyword_do, true, "do"}, + {&keyword_asm, true, "asm"}, + {&keyword_goto, true, "goto"}, + {&keyword_break, true, "break"}, + {&keyword_continue, true, "continue"}, + {&keyword_state, false, "state"}, + {&keyword_string, true, "string"}, + {&keyword_float, true, "float"}, + {&keyword_entity, true, "entity"}, + {&keyword_vector, true, "vector"}, + {&keyword_const, true, "const"}, + {&keyword_integer, true, "integer"}, + {&keyword_int, true, "int"}, + {&keyword_class, true, "class"}, + + //options + {&keywords_coexist, true, "kce"}, + {&output_parms, false, "parms"}, //controls weather to define PARMx for the parms + {&autoprototype, false, "autoproto"}, + {&writeasm, false, "wasm"}, + {NULL} +}; + +struct { + qcc_targetformat_t target; + char *name; +} targets[] = { + {QCF_STANDARD, "q1"}, + {QCF_STANDARD, "standard"}, + {QCF_STANDARD, "quakec"}, + {QCF_HEXEN2, "h2"}, + {QCF_HEXEN2, "hexen2"}, + {QCF_KK7, "kk7"}, + {QCF_KK7, "bigprogs"}, + {QCF_KK7, "version7"}, + {QCF_KK7, "kkqwsv"}, + {QCF_FTE, "fte"}, + {0, NULL} +}; + +/* +================= +BspModels + +Runs qbsp and light on all of the models with a .bsp extension +================= +*/ +int QCC_CheckParm (char *check); + +void QCC_BspModels (void) +{ + int p; + char *gamedir; + int i; + char *m; + char cmd[1024]; + char name[256]; + + p = QCC_CheckParm ("-bspmodels"); + if (!p) + return; + if (p == myargc-1) + QCC_Error (ERR_BADPARMS, "-bspmodels must preceed a game directory"); + gamedir = myargv[p+1]; + + for (i=0 ; istrings ; s--) + if (!strcmp(s, str)) + return s-strings; + + old = strofs; + strcpy (strings+strofs, str); + strofs += strlen(str)+1; + return old; +} + +void QCC_PrintStrings (void) +{ + int i, l, j; + + for (i=0 ; is_file, strings + d->s_name, d->first_statement, d->parm_start); + for (j=0 ; jnumparms ; j++) + printf ("%i ",d->parm_size[j]); + printf (")\n"); + } +}*/ + +void QCC_PrintFields (void) +{ + int i; + QCC_ddef_t *d; + + for (i=0 ; iofs, d->type, strings + d->s_name); + } +} + +void QCC_PrintGlobals (void) +{ + int i; + QCC_ddef_t *d; + + for (i=0 ; iofs, d->type, strings + d->s_name); + } +} + +int encode(int len, int method, char *in, int handle); +int WriteSourceFiles(int h, dprograms_t *progs, pbool sourceaswell) +{ + includeddatafile_t *idf; + qcc_cachedsourcefile_t *f; + int num=0; + int ofs; + + /* + for (f = qcc_sourcefile; f ; f=f->next) + { + if (f->type == FT_CODE && !sourceaswell) + continue; + + SafeWrite(h, f->filename, strlen(f->filename)+1); + i = LittleLong(f->size); + SafeWrite(h, &i, sizeof(int)); + + i = LittleLong(encrpytmode); + SafeWrite(h, &i, sizeof(int)); + + if (encrpytmode) + for (i = 0; i < f->size; i++) + f->file[i] ^= 0xA5; + + SafeWrite(h, f->file, f->size); + }*/ + + for (f = qcc_sourcefile,num=0; f ; f=f->next) + { + if (f->type == FT_CODE && !sourceaswell) + continue; + + num++; + } + if (!num) + return 0; + idf = qccHunkAlloc(sizeof(includeddatafile_t)*num); + for (f = qcc_sourcefile,num=0; f ; f=f->next) + { + if (f->type == FT_CODE && !sourceaswell) + continue; + + strcpy(idf[num].filename, f->filename); + idf[num].size = f->size; +#ifdef AVAIL_ZLIB + idf[num].compmethod = 2; +#else + idf[num].compmethod = 1; +#endif + idf[num].ofs = SafeSeek(h, 0, SEEK_CUR); + idf[num].compsize = QC_encode(progfuncs, f->size, idf[num].compmethod, f->file, h); + num++; + } + + ofs = SafeSeek(h, 0, SEEK_CUR); + SafeWrite(h, &num, sizeof(int)); + SafeWrite(h, idf, sizeof(includeddatafile_t)*num); + + qcc_sourcefile = NULL; + + return ofs; +} + +void QCC_InitData (void) +{ + static char parmname[12][MAX_PARMS]; + static temp_t ret_temp; + int i; + + qcc_sourcefile = NULL; + + numstatements = 1; + strofs = 1; + numfunctions = 1; + numglobaldefs = 1; + numfielddefs = 1; + + def_ret.ofs = OFS_RETURN; + def_ret.name = "return"; + def_ret.temp = &ret_temp; + def_ret.constant = false; + ret_temp.ofs = def_ret.ofs; + ret_temp.scope = NULL; + ret_temp.size = 3; + ret_temp.next = NULL; + for (i=0 ; inext) + { + if (d->type->type == ev_function && !d->scope)// function parms are ok + { + if (d->initialized != 1) + { + SafeWrite(handle, d->name, strlen(d->name)+1); + ret++; + } + } + } + + return ret; +} + +//marshalled locals remaps all the functions to use the range MAX_REGS onwards for the offset to thier locals. +//this function remaps all the locals back into the function. +void QCC_UnmarshalLocals(void) +{ + QCC_def_t *def; + unsigned int ofs; + unsigned int maxo; + int i; + + ofs = numpr_globals; + maxo = ofs; + + for (def = pr.def_head.next ; def ; def = def->next) + { + if (def->ofs >= MAX_REGS) //unmap defs. + { + def->ofs = def->ofs + ofs - MAX_REGS; + if (maxo < def->ofs) + maxo = def->ofs; + } + } + + for (i = 0; i < numfunctions; i++) + { + if (functions[i].parm_start == MAX_REGS) + functions[i].parm_start = ofs; + } + + QCC_RemapOffsets(0, numstatements, MAX_REGS, MAX_REGS + maxo-numpr_globals + 3, ofs); + + numpr_globals = maxo+3; + if (numpr_globals > MAX_REGS) + QCC_Error(ERR_TOOMANYGLOBALS, "Too many globals are in use to unmarshal all locals"); + + if (maxo-ofs) + printf("Total of %i marshalled globals\n", maxo-ofs); +} + + +CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def); +void QCC_WriteData (int crc) +{ + char element[MAX_NAME]; + QCC_def_t *def, *comp_x, *comp_y, *comp_z; + QCC_ddef_t *dd; + dprograms_t progs; + int h; + int i, len; + pbool debugdefined = false; + pbool types = false; + int outputsize = 16; + + progs.blockscompressed=0; + + if (numstatements > MAX_STATEMENTS) + QCC_Error(ERR_TOOMANYSTATEMENTS, "Too many statements - %i\nAdd \"MAX_STATEMENTS\" \"%i\" to qcc.cfg", numstatements, (numstatements+32768)&~32767); + + if (strofs > MAX_STRINGS) + QCC_Error(ERR_TOOMANYSTRINGS, "Too many strings - %i\nAdd \"MAX_STRINGS\" \"%i\" to qcc.cfg", strofs, (strofs+32768)&~32767); + + QCC_UnmarshalLocals(); + + if (QCC_PR_CheckCompConstDefined("DEBUG")) //debug forces it if standard + { + if (!qcc_targetformat) + { + printf("Forcing target due to debugging\n"); + qcc_targetformat = QCF_FTEDEBUG; + } + else if (qcc_targetformat != QCF_FTE && qcc_targetformat != QCF_FTEDEBUG) + printf("Target does not support debugging info\n"); + } + + switch (qcc_targetformat) + { + case QCF_HEXEN2: + case QCF_STANDARD: + if (bodylessfuncs) + printf("Warning: There are some functions without bodies.\n"); + + if (outputversion > PROG_VERSION) + { + printf("Forcing target to FTE due to additional opcodes\n"); + qcc_targetformat = QCF_FTE; + outputversion = PROG_DEBUGVERSION; //force it. + } + else if (numpr_globals > 65530 ) + { + printf("Forcing target to FTE32 due to numpr_globals\n"); + qcc_targetformat = QCF_FTE32; + outputsize = 32; + outputversion = PROG_DEBUGVERSION; //force it. + } + else if (qcc_targetformat == QCF_HEXEN2) + { + outputversion = PROG_VERSION; + printf("Progs execution requires a Hexen2 compatable engine\n"); + break; + } + else + { + if (numpr_globals >= 32768) //not much of a different format. Rewrite output to get it working on origional executors? + printf("An enhanced executor will be required (FTE/QF/KK)\n"); + else + printf("Progs should run on any Quake executor\n"); + break; + } + //intentional + case QCF_FTEDEBUG: + case QCF_FTEDEBUG32: + case QCF_FTE: + case QCF_FTE32: + if (qcc_targetformat == QCF_FTEDEBUG || qcc_targetformat == QCF_FTEDEBUG32) + debugdefined = true; + if (qcc_targetformat == QCF_FTE32 || qcc_targetformat == QCF_FTEDEBUG32) + outputsize = 32; + else if (numpr_globals > 65530) + { + printf("Forcing 32 bit target due to numpr_globals\n"); + } + + //compression of blocks? + if (QCC_PR_CheckCompConstDefined("OP_COMP_STATEMENTS")) progs.blockscompressed |=1; + if (QCC_PR_CheckCompConstDefined("OP_COMP_DEFS")) progs.blockscompressed |=2; + if (QCC_PR_CheckCompConstDefined("OP_COMP_FIELDS")) progs.blockscompressed |=4; + if (QCC_PR_CheckCompConstDefined("OP_COMP_FUNCTIONS")) progs.blockscompressed |=8; + if (QCC_PR_CheckCompConstDefined("OP_COMP_STRINGS")) progs.blockscompressed |=16; + if (QCC_PR_CheckCompConstDefined("OP_COMP_GLOBALS")) progs.blockscompressed |=32; + if (QCC_PR_CheckCompConstDefined("OP_COMP_LINES")) progs.blockscompressed |=64; + if (QCC_PR_CheckCompConstDefined("OP_COMP_TYPES")) progs.blockscompressed |=128; + //include a type block? + types = !!QCC_PR_CheckCompConstDefined("TYPES"); //useful for debugging and saving (maybe, anyway...). + + outputversion = PROG_DEBUGVERSION; + + printf("An FTE executor will be required\n"); + break; + case QCF_KK7: + outputversion = PROG_DEBUGVERSION; + if (bodylessfuncs) + printf("Warning: There are some functions without bodies.\n"); + + printf("A KK compatable executor will be required (FTE/KK)\n"); + break; + } + + //part of how compilation works. This def is always present, and never used. + def = QCC_PR_GetDef(NULL, "end_sys_globals", NULL, false, 0); + if (def) + def->references++; + + def = QCC_PR_GetDef(NULL, "end_sys_fields", NULL, false, 0); + if (def) + def->references++; + + for (def = pr.def_head.next ; def ; def = def->next) + { + if (def->type->type == ev_vector || (def->type->type == ev_field && def->type->aux_type->type == ev_vector)) + { //do the references, so we don't get loadsa not referenced VEC_HULL_MINS_x + sprintf(element, "%s_x", def->name); + comp_x = QCC_PR_GetDef(NULL, element, def->scope, false, 0); + sprintf(element, "%s_y", def->name); + comp_y = QCC_PR_GetDef(NULL, element, def->scope, false, 0); + sprintf(element, "%s_z", def->name); + comp_z = QCC_PR_GetDef(NULL, element, def->scope, false, 0); + + h = def->references; + if (comp_x && comp_y && comp_z) + { + h += comp_x->references; + h += comp_y->references; + h += comp_z->references; + + if (!def->references) + if (!comp_x->references || !comp_y->references || !comp_z->references) //one of these vars is useless... + h=0; + + def->references = h; + + + if (!h) + h = 1; + if (comp_x) + comp_x->references = h; + if (comp_y) + comp_y->references = h; + if (comp_z) + comp_z->references = h; + } + } + if (def->references<=0) + { + if (def->constant) + QCC_PR_Warning(WARN_NOTREFERENCEDCONST, strings + def->s_file, def->s_line, "%s no references", def->name); + else + QCC_PR_Warning(WARN_NOTREFERENCED, strings + def->s_file, def->s_line, "%s no references", def->name); + + if (opt_unreferenced && def->type->type != ev_field) + { + optres_unreferenced++; + continue; + } + } + + if (def->type->type == ev_function) + { + if (opt_function_names && functions[G_FUNCTION(def->ofs)].first_statement<0) + { + optres_function_names++; + def->name = ""; + } + if (!def->timescalled) + { + if (def->references<=1) + QCC_PR_Warning(WARN_DEADCODE, strings + def->s_file, def->s_line, "%s is never directly called or referenced (spawn function or dead code)", def->name); + else + QCC_PR_Warning(WARN_DEADCODE, strings + def->s_file, def->s_line, "%s is never directly called", def->name); + } + if (opt_stripfunctions && def->timescalled >= def->references-1) //make sure it's not copied into a different var. + { //if it ever does self.think then it could be needed for saves. + optres_stripfunctions++; //if it's only ever called explicitly, the engine doesn't need to know. + continue; + } + +// df = &functions[numfunctions]; +// numfunctions++; + + } + else if (def->type->type == ev_field)// && !def->constant) + { + dd = &fields[numfielddefs]; + numfielddefs++; + dd->type = def->type->aux_type->type; + dd->s_name = QCC_CopyString (def->name); + dd->ofs = G_INT(def->ofs); + } + else if ((def->scope||def->constant) && (def->type->type != ev_string || opt_constant_names_strings)) + { + if (opt_constant_names) + { + if (def->type->type == ev_string) + optres_constant_names_strings += strlen(def->name); + else + optres_constant_names += strlen(def->name); + continue; + } + } + +// if (!def->saved && def->type->type != ev_string) +// continue; + dd = &qcc_globals[numglobaldefs]; + numglobaldefs++; + + if (types) + dd->type = def->type-qcc_typeinfo; + else + dd->type = def->type->type; +#ifdef DEF_SAVEGLOBAL + if ( def->saved && ((!def->initialized || def->type->type == ev_function) +// && def->type->type != ev_function + && def->type->type != ev_field + && def->scope == NULL)) + dd->type |= DEF_SAVEGLOBAL; +#endif + if (def->shared) + dd->type |= DEF_SHARED; + + if (opt_locals && (def->scope || !strcmp(def->name, "IMMEDIATE"))) + { + dd->s_name = 0; + optres_locals += strlen(def->name); + } + else + dd->s_name = QCC_CopyString (def->name); + dd->ofs = def->ofs; + } + + if (numglobaldefs > MAX_GLOBALS) + QCC_Error(ERR_TOOMANYGLOBALS, "Too many globals - %i\nAdd \"MAX_GLOBALS\" \"%i\" to qcc.cfg", numglobaldefs, (numglobaldefs+32768)&~32767); + + + for (i = 0; i < nummodels; i++) + { + if (!precache_models_used[i]) + QCC_PR_Warning(WARN_EXTRAPRECACHE, NULL, 0, "Model %s was precached but not directly used", precache_models[i]); + else if (!precache_models_block[i]) + QCC_PR_Warning(WARN_NOTPRECACHED, NULL, 0, "Model %s was used but not precached", precache_models[i]); + } +//PrintStrings (); +//PrintFunctions (); +//PrintFields (); +//PrintGlobals (); +strofs = (strofs+3)&~3; + + printf ("%6i strofs (of %i)\n", strofs, MAX_STRINGS); + printf ("%6i numstatements (of %i)\n", numstatements, MAX_STATEMENTS); + printf ("%6i numfunctions (of %i)\n", numfunctions, MAX_FUNCTIONS); + printf ("%6i numglobaldefs (of %i)\n", numglobaldefs, MAX_GLOBALS); + printf ("%6i numfielddefs (%i unique) (of %i)\n", numfielddefs, pr.size_fields, MAX_FIELDS); + printf ("%6i numpr_globals (of %i)\n", numpr_globals, MAX_REGS); + + if (!*destfile) + strcpy(destfile, "progs.dat"); + printf("Writing %s\n", destfile); + h = SafeOpenWrite (destfile, 2*1024*1024); + SafeWrite (h, &progs, sizeof(progs)); + SafeWrite (h, "\r\n", 2); + SafeWrite (h, QCC_copyright, strlen(QCC_copyright)+1); + SafeWrite (h, "\r\n\r\n", 4); + + progs.ofs_strings = SafeSeek (h, 0, SEEK_CUR); + progs.numstrings = strofs; + + if (progs.blockscompressed&16) + { + SafeWrite (h, &len, sizeof(int)); //save for later + len = QC_encode(progfuncs, strofs*sizeof(char), 2, (char *)strings, h); //write + i = SafeSeek (h, 0, SEEK_CUR); + SafeSeek(h, progs.ofs_strings, SEEK_SET);//seek back + len = LittleLong(len); + SafeWrite (h, &len, sizeof(int)); //write size. + SafeSeek(h, i, SEEK_SET); + } + else + SafeWrite (h, strings, strofs); + + progs.ofs_statements = SafeSeek (h, 0, SEEK_CUR); + progs.numstatements = numstatements; + + if (qcc_targetformat == QCF_HEXEN2) + { + for (i=0 ; i= OP_CALL1 && statements[i].op <= OP_CALL8) + QCC_Error(ERR_BADTARGETSWITCH, "Target switching produced incompatable instructions"); + else if (statements[i].op >= OP_CALL1H && statements[i].op <= OP_CALL8H) + statements[i].op = statements[i].op - OP_CALL1H + OP_CALL1; + } + } + + for (i=0 ; iMAX_PARMS)?MAX_PARMS:functions[i].numparms); + functions[i].locals = LittleLong (functions[i].locals); + } + + if (progs.blockscompressed&8) + { + SafeWrite (h, &len, sizeof(int)); //save for later + len = QC_encode(progfuncs, numfunctions*sizeof(QCC_dfunction_t), 2, (char *)functions, h); //write + i = SafeSeek (h, 0, SEEK_CUR); + SafeSeek(h, progs.ofs_functions, SEEK_SET);//seek back + len = LittleLong(len); + SafeWrite (h, &len, sizeof(int)); //write size. + SafeSeek(h, i, SEEK_SET); + } + else + SafeWrite (h, functions, numfunctions*sizeof(QCC_dfunction_t)); + + switch(outputsize) + { + case 32: + progs.ofs_globaldefs = SafeSeek (h, 0, SEEK_CUR); + progs.numglobaldefs = numglobaldefs; + for (i=0 ; i= PROG_DEBUGVERSION) + { + if (outputsize == 32) + progs.secondaryversion = PROG_SECONDARYVERSION32; + else + progs.secondaryversion = PROG_SECONDARYVERSION16; + + progs.ofsbodylessfuncs = SafeSeek (h, 0, SEEK_CUR); + progs.numbodylessfuncs = WriteBodylessFuncs(h); + + if (debugdefined) + { + progs.ofslinenums = SafeSeek (h, 0, SEEK_CUR); + if (progs.blockscompressed&64) + { + SafeWrite (h, &len, sizeof(int)); //save for later + len = QC_encode(progfuncs, numstatements*sizeof(int), 2, (char *)statement_linenums, h); //write + i = SafeSeek (h, 0, SEEK_CUR); + SafeSeek(h, progs.ofslinenums, SEEK_SET);//seek back + len = LittleLong(len); + SafeWrite (h, &len, sizeof(int)); //write size. + SafeSeek(h, i, SEEK_SET); + } + else + SafeWrite (h, statement_linenums, numstatements*sizeof(int)); + } + else + progs.ofslinenums = 0; + + if (types) + { + progs.ofs_types = SafeSeek (h, 0, SEEK_CUR); + if (progs.blockscompressed&128) + { + SafeWrite (h, &len, sizeof(int)); //save for later + len = QC_encode(progfuncs, sizeof(QCC_type_t)*numtypeinfos, 2, (char *)qcc_typeinfo, h); //write + i = SafeSeek (h, 0, SEEK_CUR); + SafeSeek(h, progs.ofs_types, SEEK_SET);//seek back# + len = LittleLong(len); + SafeWrite (h, &len, sizeof(int)); //write size. + SafeSeek(h, i, SEEK_SET); + } + else + SafeWrite (h, qcc_typeinfo, sizeof(QCC_type_t)*numtypeinfos); + progs.numtypes = numtypeinfos; + } + else + { + progs.ofs_types = 0; + progs.numtypes = 0; + } + + progs.ofsfiles = WriteSourceFiles(h, &progs, debugdefined); + } + + progs.version = outputversion; + + printf ("%6i TOTAL SIZE\n", (int)SafeSeek (h, 0, SEEK_CUR)); + + progs.entityfields = pr.size_fields; + + progs.crc = crc; + +// qbyte swap the header and write it out + + for (i=0 ; i 60) + { + *s++ = '.'; + *s++ = '.'; + *s++ = '.'; + break; + } + } + *s++ = '"'; + *s++ = 0; + return buf; +} + + + +QCC_def_t *QCC_PR_DefForFieldOfs (gofs_t ofs) +{ + QCC_def_t *d; + + for (d=pr.def_head.next ; d ; d=d->next) + { + if (d->type->type != ev_field) + continue; + if (*((unsigned int *)&qcc_pr_globals[d->ofs]) == ofs) + return d; + } + QCC_Error (ERR_NOTDEFINED, "PR_DefForFieldOfs: couldn't find %i",ofs); + return NULL; +} + +/* +============ +PR_ValueString + +Returns a string describing *data in a type specific manner +============= +*/ +char *QCC_PR_ValueString (etype_t type, void *val) +{ + static char line[256]; + QCC_def_t *def; + QCC_dfunction_t *f; + + switch (type) + { + case ev_string: + sprintf (line, "%s", QCC_PR_String(strings + *(int *)val)); + break; + case ev_entity: + sprintf (line, "entity %i", *(int *)val); + break; + case ev_function: + f = functions + *(int *)val; + if (!f) + sprintf (line, "undefined function"); + else + sprintf (line, "%s()", strings + f->s_name); + break; + case ev_field: + def = QCC_PR_DefForFieldOfs ( *(int *)val ); + sprintf (line, ".%s", def->name); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + sprintf (line, "%5.1f", *(float *)val); + break; + case ev_integer: + sprintf (line, "%i", *(int *)val); + break; + case ev_vector: + sprintf (line, "'%5.1f %5.1f %5.1f'", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2]); + break; + case ev_pointer: + sprintf (line, "pointer"); + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +/* +============ +PR_GlobalString + +Returns a string with a description and the contents of a global, +padded to 20 field width +============ +*/ +/*char *QCC_PR_GlobalStringNoContents (gofs_t ofs) +{ + int i; + QCC_def_t *def; + void *val; + static char line[128]; + + val = (void *)&qcc_pr_globals[ofs]; + def = pr_global_defs[ofs]; + if (!def) +// Error ("PR_GlobalString: no def for %i", ofs); + sprintf (line,"%i(?""?""?)", ofs); + else + sprintf (line,"%i(%s)", ofs, def->name); + + i = strlen(line); + for ( ; i<16 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +} + +char *QCC_PR_GlobalString (gofs_t ofs) +{ + char *s; + int i; + QCC_def_t *def; + void *val; + static char line[128]; + + val = (void *)&qcc_pr_globals[ofs]; + def = pr_global_defs[ofs]; + if (!def) + return QCC_PR_GlobalStringNoContents(ofs); + if (def->initialized && def->type->type != ev_function) + { + s = QCC_PR_ValueString (def->type->type, &qcc_pr_globals[ofs]); + sprintf (line,"%i(%s)", ofs, s); + } + else + sprintf (line,"%i(%s)", ofs, def->name); + + i = strlen(line); + for ( ; i<16 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +}*/ + +/* +============ +PR_PrintOfs +============ +*/ +/*void QCC_PR_PrintOfs (gofs_t ofs) +{ + printf ("%s\n",QCC_PR_GlobalString(ofs)); +}*/ + +/* +================= +PR_PrintStatement +================= +*/ +/*void QCC_PR_PrintStatement (QCC_dstatement_t *s) +{ + int i; + + printf ("%4i : %4i : %s ", (int)(s - statements), statement_linenums[s-statements], pr_opcodes[s->op].opname); + i = strlen(pr_opcodes[s->op].opname); + for ( ; i<10 ; i++) + printf (" "); + + if (s->op == OP_IF || s->op == OP_IFNOT) + printf ("%sbranch %i",QCC_PR_GlobalString(s->a),s->b); + else if (s->op == OP_GOTO) + { + printf ("branch %i",s->a); + } + else if ( (unsigned)(s->op - OP_STORE_F) < 6) + { + printf ("%s",QCC_PR_GlobalString(s->a)); + printf ("%s", QCC_PR_GlobalStringNoContents(s->b)); + } + else + { + if (s->a) + printf ("%s",QCC_PR_GlobalString(s->a)); + if (s->b) + printf ("%s",QCC_PR_GlobalString(s->b)); + if (s->c) + printf ("%s", QCC_PR_GlobalStringNoContents(s->c)); + } + printf ("\n"); +}*/ + + +/* +============ +PR_PrintDefs +============ +*/ +/*void QCC_PR_PrintDefs (void) +{ + QCC_def_t *d; + + for (d=pr.def_head.next ; d ; d=d->next) + QCC_PR_PrintOfs (d->ofs); +}*/ + +QCC_type_t *QCC_PR_NewType (char *name, int basictype) +{ + if (numtypeinfos>= maxtypeinfos) + QCC_Error(ERR_TOOMANYTYPES, "Too many types"); + memset(&qcc_typeinfo[numtypeinfos], 0, sizeof(QCC_type_t)); + qcc_typeinfo[numtypeinfos].type = basictype; + qcc_typeinfo[numtypeinfos].name = name; + qcc_typeinfo[numtypeinfos].num_parms = 0; + qcc_typeinfo[numtypeinfos].param = NULL; + qcc_typeinfo[numtypeinfos].size = type_size[basictype]; + + + numtypeinfos++; + + return &qcc_typeinfo[numtypeinfos-1]; +} + +/* +============== +PR_BeginCompilation + +called before compiling a batch of files, clears the pr struct +============== +*/ +void QCC_PR_BeginCompilation (void *memory, int memsize) +{ + extern struct freeoffset_s *freeofs; + int i; + char name[16]; + + pr.memory = memory; + pr.max_memory = memsize; + + pr.def_tail = &pr.def_head; + +/* numpr_globals = RESERVED_OFS; + + for (i=0 ; iaux_type = type_float; + type_pointer->aux_type = QCC_PR_NewType("pointeraux", ev_float); + + type_function->aux_type = type_void; + + //type_field->aux_type = type_float; + + if (keyword_int) + QCC_PR_NewType("int", ev_integer); + if (keyword_integer) + QCC_PR_NewType("integer", ev_integer); + + + + if (output_parms) + { //this tends to confuse the brains out of decompilers. :) + numpr_globals = 1; + QCC_PR_GetDef(type_vector, "RETURN", NULL, true, 1)->references++; + for (i = 0; i < MAX_PARMS; i++) + { + sprintf(name, "PARM%i", i); + QCC_PR_GetDef(type_vector, name, NULL, true, 1)->references++; + } + } + else + { + numpr_globals = RESERVED_OFS; +// for (i=0 ; inext = NULL; + pr_error_count = 0; + + freeofs = NULL; +} + +/* +============== +PR_FinishCompilation + +called after all files are compiled to check for errors +Returns false if errors were detected. +============== +*/ +int QCC_PR_FinishCompilation (void) +{ + QCC_def_t *d; + int errors; + + errors = false; + +// check to make sure all functions prototyped have code + for (d=pr.def_head.next ; d ; d=d->next) + { + if (d->type->type == ev_function && !d->scope)// function parms are ok + { +// f = G_FUNCTION(d->ofs); +// if (!f || (!f->code && !f->builtin) ) + if (d->initialized==0) + { + if (!strncmp(d->name, "ArrayGet*", 9)) + { + G_FUNCTION(d->ofs) = QCC_PR_EmitArrayGetFunction(d->name+9); + } + else if (!strncmp(d->name, "ArraySet*", 9)) + { + G_FUNCTION(d->ofs) = QCC_PR_EmitArraySetFunction(d->name+9); + } + else if (!strncmp(d->name, "Class*", 6)) + { + G_FUNCTION(d->ofs) = QCC_PR_EmitClassFromFunction(d, d->name+6); + } + else + { + QCC_PR_Warning(WARN_NOTDEFINED, strings + d->s_file, d->s_line, "function %s was not defined\n",d->name); + bodylessfuncs = true; + } +// errors = true; + } + else if (d->initialized==2) + bodylessfuncs = true; + } + } + + return !errors; +} + +//============================================================================= + +// FIXME: byte swap? + +// this is a 16 bit, non-reflected CRC using the polynomial 0x1021 +// and the initial and final xor values shown below... in other words, the +// CCITT standard CRC used by XMODEM + + +#define CRC_INIT_VALUE 0xffff +#define CRC_XOR_VALUE 0x0000 + +static unsigned short QCC_crctable[256] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +void QCC_CRC_Init(unsigned short *crcvalue) +{ + *crcvalue = CRC_INIT_VALUE; +} + +void QCC_CRC_ProcessByte(unsigned short *crcvalue, qbyte data) +{ + *crcvalue = (*crcvalue << 8) ^ QCC_crctable[(*crcvalue >> 8) ^ data]; +} + +unsigned short QCC_CRC_Value(unsigned short crcvalue) +{ + return crcvalue ^ CRC_XOR_VALUE; +} +//============================================================================= + + +/* +============ +PR_WriteProgdefs + +Writes the global and entity structures out +Returns a crc of the header, to be stored in the progs file for comparison +at load time. +============ +*/ +/* +char *Sva(char *msg, ...) +{ + va_list l; + static char buf[1024]; + + va_start(l, msg); + QC_vsnprintf (buf,sizeof(buf)-1, msg, l); + va_end(l); + + return buf; +} +*/ + +//write (to file buf) and add to the crc +void inline Add(char *p, unsigned short *crc, char *file) +{ + char *s; + int i = strlen(file); + for(s=p;*s;s++,i++) + { + QCC_CRC_ProcessByte(crc, *s); + file[i] = *s; + } + file[i]='\0'; +} +#define ADD(p) Add(p, &crc, file) +//#define ADD(p) {char *s;int i = strlen(p);for(s=p;*s;s++,i++){QCC_CRC_ProcessByte(&crc, *s);file[i] = *s;}file[i]='\0';} + +void inline Add3(char *p, unsigned short *crc, char *file) +{ + char *s; + for(s=p;*s;s++) + QCC_CRC_ProcessByte(crc, *s); +} +#define ADD3(p) Add3(p, &crc, file) + +unsigned short QCC_PR_WriteProgdefs (char *filename) +{ + extern int ForcedCRC; +#define ADD2(p) strcat(file, p) //no crc (later changes) + char file[16384]; + QCC_def_t *d; + int f; + unsigned short crc; +// int c; + + file[0] = '\0'; + + QCC_CRC_Init (&crc); + +// print global vars until the first field is defined + ADD("\n/* "); + if (qcc_targetformat == QCF_HEXEN2) + ADD3("generated by hcc, do not modify"); + else + ADD3("file generated by qcc, do not modify"); + ADD2("File generated by FTEQCC, relevent for engine modding only, the generated crc must be the same as your engine expects."); + ADD(" */\n\ntypedef struct"); + ADD2(" globalvars_s"); + ADD(qcva("\n{")); + ADD2("\tint pad;\n" + "\tint ofs_return[3];\n" //makes it easier with the get globals func + "\tint ofs_parm0[3];\n" + "\tint ofs_parm1[3];\n" + "\tint ofs_parm2[3];\n" + "\tint ofs_parm3[3];\n" + "\tint ofs_parm4[3];\n" + "\tint ofs_parm5[3];\n" + "\tint ofs_parm6[3];\n" + "\tint ofs_parm7[3];\n"); + ADD3(qcva("\tint\tpad[%i];\n", RESERVED_OFS)); + for (d=pr.def_head.next ; d ; d=d->next) + { + if (!strcmp (d->name, "end_sys_globals")) + break; + if (d->ofstype->type) + { + case ev_float: + ADD(qcva("\tfloat\t%s;\n",d->name)); + break; + case ev_vector: + ADD(qcva("\tvec3_t\t%s;\n",d->name)); + d=d->next->next->next; // skip the elements + break; + case ev_string: + ADD(qcva("\tstring_t\t%s;\n",d->name)); + break; + case ev_function: + ADD(qcva("\tfunc_t\t%s;\n",d->name)); + break; + case ev_entity: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + case ev_integer: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + default: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + } + } + ADD("} globalvars_t;\n\n"); + +// print all fields + ADD("typedef struct"); + ADD2(" entvars_s"); + ADD("\n{\n"); + for (d=pr.def_head.next ; d ; d=d->next) + { + if (!strcmp (d->name, "end_sys_fields")) + break; + + if (d->type->type != ev_field) + continue; + + switch (d->type->aux_type->type) + { + case ev_float: + ADD(qcva("\tfloat\t%s;\n",d->name)); + break; + case ev_vector: + ADD(qcva("\tvec3_t\t%s;\n",d->name)); + d=d->next->next->next; // skip the elements + break; + case ev_string: + ADD(qcva("\tstring_t\t%s;\n",d->name)); + break; + case ev_function: + ADD(qcva("\tfunc_t\t%s;\n",d->name)); + break; + case ev_entity: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + case ev_integer: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + default: + ADD(qcva("\tint\t%s;\n",d->name)); + break; + } + } + ADD("} entvars_t;\n\n"); + + ///temp + ADD2("//with this the crc isn't needed for fields.\n#ifdef FIELDSSTRUCT\nstruct fieldvars_s {\n\tint ofs;\n\tint type;\n\tchar *name;\n} fieldvars[] = {\n"); + f=0; + for (d=pr.def_head.next ; d ; d=d->next) + { + if (!strcmp (d->name, "end_sys_fields")) + break; + + if (d->type->type != ev_field) + continue; + if (f) + ADD2(",\n"); + ADD2(qcva("\t{%i,\t%i,\t\"%s\"}",G_INT(d->ofs), d->type->aux_type->type, d->name)); + f = 1; + } + ADD2("\n};\n#endif\n\n"); + //end temp + + ADD2(qcva("#define PROGHEADER_CRC %i\n", crc)); + + if (QCC_CheckParm("-progdefs")) + { + printf ("writing %s\n", filename); + f = SafeOpenWrite("progdefs.h", 16384); + SafeWrite(f, file, strlen(file)); + SafeClose(f); + } + + + if (ForcedCRC) + return ForcedCRC; + + return crc; +} + + +/*void QCC_PrintFunction (char *name) +{ + int i; + QCC_dstatement_t *ds; + QCC_dfunction_t *df; + + for (i=0 ; ifirst_statement; + while (1) + { + QCC_PR_PrintStatement (ds); + if (!ds->op) + break; + ds++; + } +}*/ +/* +void QCC_PrintOfs(unsigned int ofs) +{ + int i; + bool printfunc; + QCC_dstatement_t *ds; + QCC_dfunction_t *df; + + for (i=0 ; ifirst_statement; + printfunc = false; + while (1) + { + if (!ds->op) + break; + if (ds->a == ofs || ds->b == ofs || ds->c == ofs) + { + QCC_PR_PrintStatement (ds); + printfunc = true; + } + ds++; + } + if (printfunc) + { + QCC_PrintFunction(strings + functions[i].s_name); + printf(" \n \n"); + } + } + +} +*/ +/* +============================================================================== + +DIRECTORY COPYING / PACKFILE CREATION + +============================================================================== +*/ + +typedef struct +{ + char name[56]; + int filepos, filelen; +} packfile_t; + +typedef struct +{ + char id[4]; + int dirofs; + int dirlen; +} packheader_t; + +packfile_t pfiles[4096], *pf; +int packhandle; +int packbytes; + + +/* +============ +CreatePath +============ +*/ +void QCC_CreatePath (char *path) +{ + /* + char *ofs; + + for (ofs = path+1 ; *ofs ; ofs++) + { + if (*ofs == '/') + { // create the directory + *ofs = 0; +#ifdef QCC + mkdir(path); +#else + QCC_mkdir (path); +#endif + *ofs = '/'; + } + } + */ +} + + +/* +=========== +PackFile + +Copy a file into the pak file +=========== +*/ +void QCC_PackFile (char *src, char *name) +{ + int remaining; +#if 1 + char *f; +#else + int in; + int count; + char buf[4096]; +#endif + + + if ( (qbyte *)pf - (qbyte *)pfiles > sizeof(pfiles) ) + QCC_Error (ERR_TOOMANYPAKFILES, "Too many files in pak file"); + +#if 1 + f = FS_ReadToMem(src, NULL, &remaining); + if (!f) + { + printf ("%64s : %7s\n", name, ""); +// QCC_Error("Failed to open file %s", src); + return; + } + + pf->filepos = LittleLong (SafeSeek (packhandle, 0, SEEK_CUR)); + pf->filelen = LittleLong (remaining); + strcpy (pf->name, name); + printf ("%64s : %7i\n", pf->name, remaining); + + packbytes += remaining; + + SafeWrite (packhandle, f, remaining); + + FS_CloseFromMem(f); +#else + in = SafeOpenRead (src); + remaining = filelength (in); + + pf->filepos = LittleLong (lseek (packhandle, 0, SEEK_CUR)); + pf->filelen = LittleLong (remaining); + strcpy (pf->name, name); + printf ("%64s : %7i\n", pf->name, remaining); + + packbytes += remaining; + + while (remaining) + { + if (remaining < sizeof(buf)) + count = remaining; + else + count = sizeof(buf); + SafeRead (in, buf, count); + SafeWrite (packhandle, buf, count); + remaining -= count; + } + + close (in); +#endif + pf++; +} + + +/* +=========== +CopyFile + +Copies a file, creating any directories needed +=========== +*/ +void QCC_CopyFile (char *src, char *dest) +{ + /* + int in, out; + int remaining, count; + char buf[4096]; + + print ("%s to %s\n", src, dest); + + in = SafeOpenRead (src); + remaining = filelength (in); + + QCC_CreatePath (dest); + out = SafeOpenWrite (dest, remaining+10); + + while (remaining) + { + if (remaining < sizeof(buf)) + count = remaining; + else + count = sizeof(buf); + SafeRead (in, buf, count); + SafeWrite (out, buf, count); + remaining -= count; + } + + close (in); + SafeClose (out); + */ +} + + +/* +=========== +CopyFiles +=========== +*/ + +void _QCC_CopyFiles (int blocknum, int copytype, char *srcdir, char *destdir) +{ + int i; + int dirlen; + unsigned short crc; + packheader_t header; + char name[1024]; + char srcfile[1024], destfile[1024]; + + packbytes = 0; + + if (copytype == 2) + { + pf = pfiles; + packhandle = SafeOpenWrite (destdir, 1024*1024); + SafeWrite (packhandle, &header, sizeof(header)); + } + + for (i=0 ; i 0) + printf ("%3i unique precache_sounds\n", numsounds); + if (nummodels > 0) + printf ("%3i unique precache_models\n", nummodels); + if (numtextures > 0) + printf ("%3i unique precache_textures\n", numtextures); + if (numfiles > 0) + printf ("%3i unique precache_files\n", numfiles); + + p = QCC_CheckParm ("-copy"); + if (p && p < myargc-2) + { // create a new directory tree + + strcpy (srcdir, myargv[p+1]); + strcpy (destdir, myargv[p+2]); + if (srcdir[strlen(srcdir)-1] != '/') + strcat (srcdir, "/"); + if (destdir[strlen(destdir)-1] != '/') + strcat (destdir, "/"); + + _QCC_CopyFiles(0, 1, srcdir, destdir); + return; + } + + for ( p = 0; p < 5; p++) + { + s = QCC_Packname[p]; + if (!*s) + continue; + strcpy(destdir, s); + strcpy(srcdir, ""); + _QCC_CopyFiles(p+1, 2, srcdir, destdir); + } + return; + /* + + blocknum = 1; + p = QCC_CheckParm ("-pak2"); + if (p && p = sizeof(cnst->value)) + QCC_Error(ERR_PRECOMPILERCONSTANTTOOLONG, "Compiler constant value is too long\n"); + strncpy(cnst->value, val, sizeof(cnst->value)-1); + cnst->value[sizeof(cnst->value)-1] = '\0'; + } + } + + //optimisations. + else if ( !strnicmp(myargv[i], "-O", 2) || !strnicmp(myargv[i], "/O", 2) ) + { + if (myargv[i][2] >= '0' && myargv[i][2] <= '3') + { + p=0; + } + else if (!strnicmp(myargv[i]+2, "no-", 3)) + { + if (myargv[i][5]) + for (p = 0; optimisations[p].enabled; p++) + if ((*optimisations[p].abbrev && !stricmp(myargv[i]+5, optimisations[p].abbrev)) || !stricmp(myargv[i]+5, optimisations[p].fullname)) + { + *optimisations[p].enabled = false; + break; + } + } + else + { + if (myargv[i][2]) + for (p = 0; optimisations[p].enabled; p++) + if ((*optimisations[p].abbrev && !stricmp(myargv[i]+2, optimisations[p].abbrev)) || !stricmp(myargv[i]+2, optimisations[p].fullname)) + { + *optimisations[p].enabled = true; + break; + } + } + if (!optimisations[p].enabled) + QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised optimisation parameter (%s)", myargv[i]); + } + + else if ( !strnicmp(myargv[i], "-K", 2) || !strnicmp(myargv[i], "/K", 2) ) + { + if (!strnicmp(myargv[i]+2, "no-", 3)) + { + for (p = 0; compiler_flag[p].enabled; p++) + if (!stricmp(myargv[i]+5, compiler_flag[p].name)) + { + *compiler_flag[p].enabled = false; + break; + } + } + else + { + for (p = 0; compiler_flag[p].enabled; p++) + if (!stricmp(myargv[i]+2, compiler_flag[p].name)) + { + *compiler_flag[p].enabled = true; + break; + } + } + + if (!compiler_flag[p].enabled) + QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised keyword parameter (%s)", myargv[i]); + } + else if ( !strnicmp(myargv[i], "-F", 2) || !strnicmp(myargv[i], "/F", 2) ) + { + if (!strnicmp(myargv[i]+2, "no-", 3)) + { + for (p = 0; compiler_flag[p].enabled; p++) + if (!stricmp(myargv[i]+5, compiler_flag[p].name)) + { + *compiler_flag[p].enabled = false; + break; + } + } + else + { + for (p = 0; compiler_flag[p].enabled; p++) + if (!stricmp(myargv[i]+2, compiler_flag[p].name)) + { + *compiler_flag[p].enabled = true; + break; + } + } + + if (!compiler_flag[p].enabled) + QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised flag parameter (%s)", myargv[i]); + } + + + else if ( !strncmp(myargv[i], "-T", 2) || !strncmp(myargv[i], "/T", 2) ) + { + for (p = 0; targets[p].name; p++) + if (!stricmp(myargv[i]+2, targets[p].name)) + { + qcc_targetformat = targets[p].target; + break; + } + + if (!targets[p].name) + QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised target parameter (%s)", myargv[i]); + } + + else if ( !strnicmp(myargv[i], "-W", 2) || !strnicmp(myargv[i], "/W", 2) ) + { + if (!stricmp(myargv[i]+2, "all")) + memset(qccwarningdisabled, 0, sizeof(qccwarningdisabled)); + else if (!stricmp(myargv[i]+2, "none")) + memset(qccwarningdisabled, 1, sizeof(qccwarningdisabled)); + else + { + if (!strnicmp(myargv[i]+2, "no-", 3)) + { + for (p = 0; warningnames[p].name; p++) + if (!strcmp(myargv[i]+5, warningnames[p].name)) + { + qccwarningdisabled[warningnames[p].index] = true; + break; + } + } + else + { + for (p = 0; warningnames[p].name; p++) + if (!stricmp(myargv[i]+2, warningnames[p].name)) + { + qccwarningdisabled[warningnames[p].index] = false; + break; + } + } + + if (!warningnames[p].name) + QCC_PR_Warning(0, NULL, WARN_BADPARAMS, "Unrecognised warning parameter (%s)", myargv[i]); + } + } + } +} + +/* +============ +main +============ +*/ + +char *qccmsrc; +char *qccmsrc2; +char qccmfilename[1024]; +char qccmprogsdat[1024]; +char qccmsourcedir[1024]; + +void QCC_FinishCompile(void); + + +void SetEndian(void); + + + +void QCC_SetDefaultProperties (void) +{ + int level; + int i; + QCC_PR_DefineName("FTEQCC"); + + if (QCC_CheckParm("/Oz")) + { + qcc_targetformat = QCF_FTE; + QCC_PR_DefineName("OP_COMP_STATEMENTS"); + QCC_PR_DefineName("OP_COMP_DEFS"); + QCC_PR_DefineName("OP_COMP_FIELDS"); + QCC_PR_DefineName("OP_COMP_FUNCTIONS"); + QCC_PR_DefineName("OP_COMP_STRINGS"); + QCC_PR_DefineName("OP_COMP_GLOBALS"); + QCC_PR_DefineName("OP_COMP_LINES"); + QCC_PR_DefineName("OP_COMP_TYPES"); + } + + if (QCC_CheckParm("/O0") || QCC_CheckParm("-O0")) + level = 0; + else if (QCC_CheckParm("/O1") || QCC_CheckParm("-O1")) + level = 1; + else if (QCC_CheckParm("/O2") || QCC_CheckParm("-O2")) + level = 2; + else if (QCC_CheckParm("/O3") || QCC_CheckParm("-O3")) + level = 3; + else + level = -1; + + if (level == -1) + { + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].flags & 2) + *optimisations[i].enabled = true; + else + *optimisations[i].enabled = false; + } + } + else + { + for (i = 0; optimisations[i].enabled; i++) + { + if (level >= optimisations[i].optimisationlevel) + *optimisations[i].enabled = true; + else + *optimisations[i].enabled = false; + } + } + + if (QCC_CheckParm ("-h2")) + qcc_targetformat = QCF_HEXEN2; + else if (QCC_CheckParm ("-fte")) + qcc_targetformat = QCF_FTE; + else + qcc_targetformat = QCF_STANDARD; + + + //enable all warnings + memset(qccwarningdisabled, 0, sizeof(qccwarningdisabled)); + + //play with default warnings. + qccwarningdisabled[WARN_NOTREFERENCEDCONST] = true; + qccwarningdisabled[WARN_MACROINSTRING] = true; + qccwarningdisabled[WARN_ASSIGNMENTTOCONSTANT] = true; + qccwarningdisabled[WARN_FIXEDRETURNVALUECONFLICT] = true; + qccwarningdisabled[WARN_EXTRAPRECACHE] = true; + qccwarningdisabled[WARN_DEADCODE] = true; + + if (QCC_CheckParm("-nowarn") || QCC_CheckParm("-Wnone")) + memset(qccwarningdisabled, 1, sizeof(qccwarningdisabled)); + if (QCC_CheckParm("-Wall")) + memset(qccwarningdisabled, 0, sizeof(qccwarningdisabled)); + + if (QCC_CheckParm("-h2")) + qccwarningdisabled[WARN_CASEINSENSATIVEFRAMEMACRO] = true; + + //Check the command line + QCC_PR_CommandLinePrecompilerOptions(); + + + if (qcc_targetformat == QCF_HEXEN2) + keyword_thinktime = true; + + if (QCC_CheckParm("/Debug")) //disable any debug optimisations + { + for (i = 0; optimisations[i].enabled; i++) + { + if (optimisations[i].flags & 1) + *optimisations[i].enabled = false; + } + } +} + +int qcc_compileactive = false; +char *origionalqccmsrc; //for autoprototype. +void QCC_main (int argc, char **argv) //as part of the quake engine +{ + extern int ForcedCRC; + + int p; + + extern char qcc_gamedir[]; +#ifndef QCCONLY + char destfile2[1024], *s2; +#endif + char *s; + + myargc = argc; + myargv = argv; + + qcc_compileactive = true; + + MAX_REGS = 65536; + MAX_STRINGS = 1000000; + MAX_GLOBALS = 16384; + MAX_FIELDS = 2048; + MAX_STATEMENTS = 0x20000; + MAX_FUNCTIONS = 16384; + maxtypeinfos = 16384; + MAX_CONSTANTS = 2048; + + p = externs->FileSize("qcc.cfg"); + if (p < 0) + p = externs->FileSize("src/qcc.cfg"); + if (p>0) + { + s = qccHunkAlloc(p+1); + s = externs->ReadFile("qcc.cfg", s, p); + + while(1) + { + s = QCC_COM_Parse(s); + if (!strcmp(qcc_token, "MAX_REGS")) + { + s = QCC_COM_Parse(s); + MAX_REGS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_STRINGS")) { + s = QCC_COM_Parse(s); + MAX_STRINGS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_GLOBALS")) { + s = QCC_COM_Parse(s); + MAX_GLOBALS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_FIELDS")) { + s = QCC_COM_Parse(s); + MAX_FIELDS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_STATEMENTS")) { + s = QCC_COM_Parse(s); + MAX_STATEMENTS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_FUNCTIONS")) { + s = QCC_COM_Parse(s); + MAX_FUNCTIONS = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_TYPES")) { + s = QCC_COM_Parse(s); + maxtypeinfos = atoi(qcc_token); + } else if (!strcmp(qcc_token, "MAX_TEMPS")) { + s = QCC_COM_Parse(s); + max_temps = atoi(qcc_token); + } else if (!strcmp(qcc_token, "CONSTANTS")) { + s = QCC_COM_Parse(s); + MAX_CONSTANTS = atoi(qcc_token); + } + else if (!s) + break; + else + printf("Bad token in qcc.cfg file\n"); + } + } + /* don't try to be clever + else if (p < 0) + { + s = qccHunkAlloc(8192); + sprintf(s, "MAX_REGS\t%i\r\nMAX_STRINGS\t%i\r\nMAX_GLOBALS\t%i\r\nMAX_FIELDS\t%i\r\nMAX_STATEMENTS\t%i\r\nMAX_FUNCTIONS\t%i\r\nMAX_TYPES\t%i\r\n", + MAX_REGS, MAX_STRINGS, MAX_GLOBALS, MAX_FIELDS, MAX_STATEMENTS, MAX_FUNCTIONS, maxtypeinfos); + externs->WriteFile("qcc.cfg", s, strlen(s)); + } + */ + + SetEndian(); + + strcpy(QCC_copyright, "This file was created with ForeThought's modified QuakeC compiler\nThanks to ID Software"); + for (p = 0; p < 5; p++) + strcpy(QCC_Packname[p], ""); + + outputversion = PROG_VERSION; + + for (p = 0; compiler_flag[p].enabled; p++) + { + *compiler_flag[p].enabled = compiler_flag[p].defaultval; + } + + Hash_InitTable(&compconstantstable, MAX_CONSTANTS, qccHunkAlloc(Hash_BytesForBuckets(MAX_CONSTANTS))); + QCC_SetDefaultProperties(); + + optres_shortenifnots = 0; + optres_overlaptemps = 0; + optres_noduplicatestrings = 0; + optres_constantarithmatic = 0; + optres_nonvec_parms = 0; + optres_constant_names = 0; + optres_constant_names_strings = 0; + optres_precache_file = 0; + optres_filenames = 0; + optres_assignments = 0; + optres_unreferenced = 0; + optres_function_names = 0; + optres_locals = 0; + optres_dupconstdefs = 0; + optres_return_only = 0; + optres_compound_jumps = 0; +// optres_comexprremoval = 0; + optres_stripfunctions = 0; + optres_locals_marshalling = 0; + optres_logicops = 0; + + optres_test1 = 0; + optres_test2 = 0; + + + numtemps = 0; + + functemps=NULL; + + strings = (void *)qccHunkAlloc(sizeof(char) * MAX_STRINGS); + strofs = 1; + + statements = (void *)qccHunkAlloc(sizeof(QCC_dstatement_t) * MAX_STATEMENTS); + numstatements = 0; + statement_linenums = (void *)qccHunkAlloc(sizeof(int) * MAX_STATEMENTS); + + functions = (void *)qccHunkAlloc(sizeof(QCC_dfunction_t) * MAX_FUNCTIONS); + numfunctions=0; + + qcc_pr_globals = (void *)qccHunkAlloc(sizeof(float) * MAX_REGS); + numpr_globals=0; + + Hash_InitTable(&globalstable, MAX_REGS, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS))); + Hash_InitTable(&localstable, MAX_REGS, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS))); + Hash_InitTable(&floatconstdefstable, MAX_REGS+1, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS+1))); + Hash_InitTable(&stringconstdefstable, MAX_REGS, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS))); + +// pr_global_defs = (QCC_def_t **)qccHunkAlloc(sizeof(QCC_def_t *) * MAX_REGS); + + qcc_globals = (void *)qccHunkAlloc(sizeof(QCC_ddef_t) * MAX_GLOBALS); + numglobaldefs=0; + + fields = (void *)qccHunkAlloc(sizeof(QCC_ddef_t) * MAX_FIELDS); + numfielddefs=0; + +memset(pr_immediate_string, 0, sizeof(pr_immediate_string)); + + precache_sounds = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_SOUNDS); + precache_sounds_block = (void *)qccHunkAlloc(sizeof(int)*MAX_SOUNDS); + precache_sounds_used = (void *)qccHunkAlloc(sizeof(int)*MAX_SOUNDS); + numsounds=0; + + precache_textures = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_TEXTURES); + precache_textures_block = (void *)qccHunkAlloc(sizeof(int)*MAX_TEXTURES); + numtextures=0; + + precache_models = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_MODELS); + precache_models_block = (void *)qccHunkAlloc(sizeof(int)*MAX_MODELS); + precache_models_used = (void *)qccHunkAlloc(sizeof(int)*MAX_MODELS); + nummodels=0; + + precache_files = (void *)qccHunkAlloc(sizeof(char)*MAX_DATA_PATH*MAX_FILES); + precache_files_block = (void *)qccHunkAlloc(sizeof(int)*MAX_FILES); + numfiles = 0; + + qcc_typeinfo = (void *)qccHunkAlloc(sizeof(QCC_type_t)*maxtypeinfos); + numtypeinfos = 0; + + qcc_tempofs = qccHunkAlloc(sizeof(int) * max_temps); + tempsstart = 0; + + bodylessfuncs=0; + + memset(&pr, 0, sizeof(pr)); +#ifdef MAX_EXTRA_PARMS + memset(&extra_parms, 0, sizeof(extra_parms)); +#endif + + ForcedCRC = 0; + + if ( QCC_CheckParm ("/?") || QCC_CheckParm ("?") || QCC_CheckParm ("-?") || QCC_CheckParm ("-help") || QCC_CheckParm ("--help")) + { + printf ("qcc looks for progs.src in the current directory.\n"); + printf ("to look in a different directory: qcc -src \n"); +// printf ("to build a clean data tree: qcc -copy \n"); +// printf ("to build a clean pak file: qcc -pak \n"); +// printf ("to bsp all bmodels: qcc -bspmodels \n"); + printf ("-Fwasm causes FTEQCC to dump all asm to qc.asm\n"); + printf ("-O0 to disable optimisations\n"); + printf ("-O1 to optimise for size\n"); + printf ("-O2 to optimise more - some behaviours may change\n"); + printf ("-O3 to optimise lots - experimental or non-future-proof\n"); + printf ("-Oname to enable an optimisation\n"); + printf ("-Ono-name to disable optimisations\n"); + printf ("-Kkeyword to activate keyword\n"); + printf ("-Kno-keyword to disable keyword\n"); + printf ("-Wall to give a stupid number of warnings\n"); + printf ("-Ttarget to set a output format\n"); + printf ("-Fautoproto to enable automatic prototyping\n"); + + qcc_compileactive = false; + return; + } + + if (opt_locals_marshalling) + printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\nLocals marshalling might be buggy. Use with caution\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + + p = QCC_CheckParm ("-src"); + if (p && p < argc-1 ) + { + strcpy (qccmsourcedir, argv[p+1]); + strcat (qccmsourcedir, "/"); + printf ("Source directory: %s\n", qccmsourcedir); + } + else +#ifndef QCCONLY + if (!*qcc_gamedir) + sprintf (qccmsourcedir, "src/"); + else + sprintf (qccmsourcedir, "%s/src/", qcc_gamedir); +#else + sprintf (qccmsourcedir, ""); +#endif + + QCC_InitData (); +#if 0 + p = QCC_CheckParm ("-base"); + if (p && p < argc-1 ) + sprintf (qccmprogsdat, "%s%s", qccmsourcedir, argv[p+1]); + else + { //look for a preprogs.src... :o) + sprintf (qccmprogsdat, "%spreprogs.src", qccmsourcedir); + if (externs->FileSize(qccmprogsdat) <= 0) + sprintf (qccmprogsdat, "%sprogs.src", qccmsourcedir); + } +#endif + QCC_PR_BeginCompilation ((void *)qccHunkAlloc (0x100000), 0x100000); +#if 0 + QCC_ReadPoff(qccmprogsdat); +#endif + p = QCC_CheckParm ("-srcfile"); + if (p && p < argc-1 ) + sprintf (qccmprogsdat, "%s%s", qccmsourcedir, argv[p+1]); + else + { //look for a preprogs.src... :o) + sprintf (qccmprogsdat, "%spreprogs.src", qccmsourcedir); + if (externs->FileSize(qccmprogsdat) <= 0) + sprintf (qccmprogsdat, "%sprogs.src", qccmsourcedir); + } + + printf ("Source file: %s\n", qccmprogsdat); + + if (QCC_LoadFile (qccmprogsdat, (void *)&qccmsrc) == -1) + { + return; + } + + newstylesource = false; + + while(*qccmsrc && *qccmsrc < ' ') + qccmsrc++; + + pr_file_p = QCC_COM_Parse(qccmsrc); + +#ifdef WRITEASM + if (writeasm) + { + asmfile = fopen("qc.asm", "wb"); + if (!asmfile) + QCC_Error (ERR_INTERNAL, "Couldn't open file for asm output."); + } +#endif + + if (*qcc_token == '#') + { + void StartNewStyleCompile(void); + newstylesource = true; + StartNewStyleCompile(); + return; + } + + pr_file_p = qccmsrc; + QCC_PR_LexWhitespace(); + qccmsrc = pr_file_p; + + s = qccmsrc; + pr_file_p = qccmsrc; + QCC_PR_SimpleGetToken (); + strcpy(qcc_token, pr_token); + qccmsrc = pr_file_p; + + if (!qccmsrc) + QCC_Error (ERR_NOOUTPUT, "No destination filename. qcc -help for info."); + strcpy (destfile, qcc_token); + +#ifndef QCCONLY + p=1; + s2 = strcpy(destfile2, destfile); + if (!strncmp(s2, "./", 2)) + s2+=2; + else + { + while(!strncmp(s2, "../", 3)) + { + s2+=3; + p++; + } + } + strcpy(qccmfilename, qccmsourcedir); + for (s=qccmfilename+strlen(qccmfilename);p && s>=qccmfilename; s--) + { + if (*s == '/' || *s == '\\') + { + *(s+1) = '\0'; + p--; + } + } + if (s>=qccmfilename) + sprintf(destfile, "%s%s", qccmfilename, s2); + else + sprintf(destfile, "%s", s2); +#endif + + printf ("outputfile: %s\n", destfile); + + pr_dumpasm = false; + + currentchunk = NULL; + + origionalqccmsrc = qccmsrc; +} + +void new_QCC_ContinueCompile(void); +//called between exe frames - won't loose net connection (is the theory)... +void QCC_ContinueCompile(void) +{ + char *s, *s2; + currentchunk = NULL; + if (!qcc_compileactive) + //HEY! + return; + + if (newstylesource) + { + new_QCC_ContinueCompile(); + return; + } + + qccmsrc = QCC_COM_Parse(qccmsrc); + if (!qccmsrc) + { + if (autoprototype) + { + qccmsrc = origionalqccmsrc; + autoprototype = false; + return; + } + QCC_FinishCompile(); + return; + } + s = qcc_token; + + strcpy (qccmfilename, qccmsourcedir); + while(1) + { + if (!strncmp(s, "..\\", 3)) + { + s2 = qccmfilename + strlen(qccmfilename)-2; + while (s2>=qccmfilename) + { + if (*s2 == '/' || *s2 == '\\') + { + s2[1] = '\0'; + break; + } + s2--; + } + s+=3; + continue; + } + if (!strncmp(s, ".\\", 2)) + { + s+=2; + continue; + } + + break; + } + strcat (qccmfilename, s); + if (autoprototype) + printf ("prototyping %s\n", qccmfilename); + else + printf ("compiling %s\n", qccmfilename); + QCC_LoadFile (qccmfilename, (void *)&qccmsrc2); + + if (!QCC_PR_CompileFile (qccmsrc2, qccmfilename) ) + QCC_Error (ERR_PARSEERRORS, "Errors have occured\n"); +} +void QCC_FinishCompile(void) +{ + int crc; +// int p; + currentchunk = NULL; + + if (setjmp(pr_parse_abort)) + QCC_Error(ERR_INTERNAL, ""); + + if (!QCC_PR_FinishCompilation ()) + { + QCC_Error (ERR_PARSEERRORS, "compilation errors"); + } + +/* p = QCC_CheckParm ("-asm"); + if (p) + { + for (p++ ; p MAX_ERRORS) + return; + QCC_PR_SkipToSemicolon (); + if (pr_token_type == tt_eof) + return; + } + + + QCC_PR_ClearGrabMacros (); // clear the frame macros + + compilingfile = qccmprogsdat; + + pr_file_p = qccmsrc; + s_file = s_file2 = QCC_CopyString (compilingfile); + + pr_source_line = 0; + + QCC_PR_NewLine (false); + + QCC_PR_Lex (); // read first token +} +void new_QCC_ContinueCompile(void) +{ + if (setjmp(pr_parse_abort)) + { +// if (pr_error_count != 0) + { + QCC_Error (ERR_PARSEERRORS, "Errors have occured"); + return; + } + QCC_PR_SkipToSemicolon (); + if (pr_token_type == tt_eof) + return; + } + + if (pr_token_type == tt_eof) + { + if (pr_error_count) + QCC_Error (ERR_PARSEERRORS, "Errors have occured"); + QCC_FinishCompile(); + return; + } + + pr_scope = NULL; // outside all functions + + QCC_PR_ParseDefs (NULL); +} + +/*void new_QCC_ContinueCompile(void) +{ + char *s, *s2; + if (!qcc_compileactive) + //HEY! + return; + +// compile all the files + + qccmsrc = QCC_COM_Parse(qccmsrc); + if (!qccmsrc) + { + QCC_FinishCompile(); + return; + } + s = qcc_token; + + strcpy (qccmfilename, qccmsourcedir); + while(1) + { + if (!strncmp(s, "..\\", 3)) + { + s2 = qccmfilename + strlen(qccmfilename)-2; + while (s2>=qccmfilename) + { + if (*s2 == '/' || *s2 == '\\') + { + s2[1] = '\0'; + break; + } + s2--; + } + s+=3; + continue; + } + if (!strncmp(s, ".\\", 2)) + { + s+=2; + continue; + } + + break; + } +// strcat (qccmfilename, s); +// printf ("compiling %s\n", qccmfilename); +// QCC_LoadFile (qccmfilename, (void *)&qccmsrc2); + +// if (!new_QCC_PR_CompileFile (qccmsrc2, qccmfilename) ) +// QCC_Error ("Errors have occured\n"); + + + { + + if (!pr.memory) + QCC_Error ("PR_CompileFile: Didn't clear"); + + QCC_PR_ClearGrabMacros (); // clear the frame macros + + compilingfile = filename; + + pr_file_p = qccmsrc2; + s_file = QCC_CopyString (filename); + + pr_source_line = 0; + + QCC_PR_NewLine (); + + QCC_PR_Lex (); // read first token + + while (pr_token_type != tt_eof) + { + if (setjmp(pr_parse_abort)) + { + if (++pr_error_count > MAX_ERRORS) + return false; + QCC_PR_SkipToSemicolon (); + if (pr_token_type == tt_eof) + return false; + } + + pr_scope = NULL; // outside all functions + + QCC_PR_ParseDefs (); + } + } + return (pr_error_count == 0); + +}*/ + + + + + + + + + + + + +#ifdef QCCONLY +progfuncs_t *progfuncs; + +short (*BigShort) (short l); +short (*LittleShort) (short l); +long (*BigLong) (long l); +long (*LittleLong) (long l); +float (*BigFloat) (float l); +float (*LittleFloat) (float l); + +/* +============== +LoadFile +============== +*/ +char *QCC_ReadFile (char *fname, void *buffer, int len) +{ + long length; + FILE *f; + f = fopen(fname, "rb"); + if (!f) + return NULL; + length = fread(buffer, 1, len, f); + fclose(f); + + if (length != len) + return NULL; + + return buffer; +} +int QCC_FileSize (char *fname) +{ + long length; + FILE *f; + f = fopen(fname, "rb"); + if (!f) + return -1; + fseek(f, 0, SEEK_END); + length = ftell(f); + fclose(f); + + return length; +} + +pbool QCC_WriteFile (char *name, void *data, int len) +{ + long length; + FILE *f; + f = fopen(name, "wb"); + if (!f) + return false; + length = fwrite(data, 1, len, f); + fclose(f); + + if (length != len) + return false; + + return true; +} + +#undef printf +#undef Sys_Error + +void Sys_Error(const char *text, ...) +{ + va_list argptr; + static char msg[2048]; + + va_start (argptr,text); + QC_vsnprintf (msg,sizeof(msg)-1, text,argptr); + va_end (argptr); + + QCC_Error(ERR_INTERNAL, "%s", msg); +} + +int main (int argc, char **argv) +{ + progexterns_t ext; + progfuncs_t funcs; + progfuncs = &funcs; + memset(&funcs, 0, sizeof(funcs)); + funcs.parms = &ext; + memset(&ext, 0, sizeof(progexterns_t)); + funcs.parms->ReadFile = QCC_ReadFile; + funcs.parms->FileSize = QCC_FileSize; + funcs.parms->WriteFile = QCC_WriteFile; + funcs.parms->printf = printf; + funcs.parms->Sys_Error = Sys_Error; + CompileParams(&funcs, true, argc, argv); + qccClearHunk(); + +#ifdef _WIN32 + fgetc(stdin); //wait for keypress +#endif + return 0; +} +#endif + +#endif \ No newline at end of file diff --git a/engine/qclib/cmdlib.h b/engine/qclib/cmdlib.h new file mode 100644 index 000000000..95a15e107 --- /dev/null +++ b/engine/qclib/cmdlib.h @@ -0,0 +1,83 @@ +// cmdlib.h + +#ifndef __CMDLIB__ +#define __CMDLIB__ + +#include "progsint.h" + +/*#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef NeXT +#include +#endif +*/ + +// the dec offsetof macro doesn't work very well... +#define myoffsetof(type,identifier) ((size_t)&((type *)NULL)->identifier) + + +// set these before calling CheckParm +extern int myargc; +extern char **myargv; + +//char *strupr (char *in); +//char *strlower (char *in); +int QCC_filelength (int handle); +int QCC_tell (int handle); + +int QC_strcasecmp (const char *s1, const char *s2); + +#ifdef _MSC_VER +#define QC_vsnprintf _vsnprintf +#else +#define QC_vsnprintf vsnprintf +#endif + +double I_FloatTime (void); + +void VARGS QCC_Error (int errortype, const char *error, ...); +int CheckParm (char *check); + + +int SafeOpenWrite (char *filename, int maxsize); +int SafeOpenRead (char *filename); +void SafeRead (int handle, void *buffer, long count); +void SafeWrite (int handle, void *buffer, long count); +void SafeClose(int handle); +int SafeSeek(int hand, int ofs, int mode); +void *SafeMalloc (long size); + + +long QCC_LoadFile (char *filename, void **bufferptr); +void QCC_SaveFile (char *filename, void *buffer, long count); + +void DefaultExtension (char *path, char *extension); +void DefaultPath (char *path, char *basepath); +void StripFilename (char *path); +void StripExtension (char *path); + +void ExtractFilePath (char *path, char *dest); +void ExtractFileBase (char *path, char *dest); +void ExtractFileExtension (char *path, char *dest); + +long ParseNum (char *str); + + +char *QCC_COM_Parse (char *data); + +extern char qcc_token[1024]; +extern int qcc_eof; + + + +#endif diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h new file mode 100644 index 000000000..f02f873bd --- /dev/null +++ b/engine/qclib/execloop.h @@ -0,0 +1,973 @@ +//qc execution code. +//we have two conditions. +//one allows us to debug and trace through our code, the other doesn't. + +//hopefully, the compiler will do a great job at optimising this code for us, where required. +//if it dosn't, then bum. + +//the general overhead should be reduced significantly, and I would be supprised if it did run slower. + +//run away loops are checked for ONLY on gotos and function calls. This might give a poorer check, but it will run faster overall. + +//Appears to work fine. + +#if INTSIZE == 16 +#define cont cont16 +#define reeval reeval16 +#define st st16 +#define pr_statements pr_statements16 +#define fakeop fakeop16 +#define dstatement_t dstatement16_t +#define sofs signed short +#define uofs unsigned short +#elif INTSIZE == 32 +#define cont cont32 +#define reeval reeval32 +#define st st32 +#define pr_statements pr_statements32 +#define fakeop fakeop32 +#define dstatement_t dstatement32_t +#define sofs signed int +#define uofs unsigned int +#elif INTSIZE == 24 +#error INTSIZE should be set to 32. +#else +#error Bad cont size +#endif + + +//rely upon just st +{ +#ifdef DEBUGABLE +cont: //last statement may have been a breakpoint + s = st-pr_statements; + s+=1; + s=ShowStep(progfuncs, s); + st = pr_statements + s; + +reeval: +#else + 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_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 = (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 = (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 = (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 = (float)(OPA->_float && OPB->_float); + break; + case OP_OR: + OPC->_float = (float)(OPA->_float || OPB->_float); + break; + + case OP_NOT_F: + OPC->_float = (float)(!OPA->_float); + break; + case OP_NOT_V: + OPC->_float = (float)(!OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2]); + break; + case OP_NOT_S: + OPC->_float = (float)(!(OPA->string) || !*(OPA->string+progfuncs->stringtable)); + break; + case OP_NOT_FNC: + OPC->_float = (float)(!(OPA->function & ~0xff000000)); + break; + case OP_NOT_ENT: + OPC->_float = (float)(PROG_TO_EDICT(OPA->edict) == (edictrun_t *)sv_edicts); + 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 = (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; + else if (!OPA->string) + { + if (!OPB->string || !*(OPB->string+progfuncs->stringtable)) + OPC->_float = true; + else + OPC->_float = false; + } + else if (!OPB->string) + { + if (!OPA->string || !*(OPA->string+progfuncs->stringtable)) + OPC->_float = true; + else + OPC->_float = false; + } + else + OPC->_float = (float)(!strcmp(OPA->string+progfuncs->stringtable,OPB->string+progfuncs->stringtable)); + break; + case OP_EQ_E: + OPC->_float = (float)(OPA->_int == OPB->_int); + break; + case OP_EQ_FNC: + OPC->_float = (float)(OPA->function == OPB->function); + break; + + + case OP_NE_F: + OPC->_float = (float)(OPA->_float != OPB->_float); + break; + case OP_NE_V: + OPC->_float = (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; + else if (!OPA->string) + { + if (!OPB->string || !*(OPB->string+progfuncs->stringtable)) + OPC->_float = false; + else + OPC->_float = true; + } + else if (!OPB->string) + { + if (!OPA->string || !*(OPA->string+progfuncs->stringtable)) + OPC->_float = false; + else + OPC->_float = true; + } + else + OPC->_float = (float)(strcmp(OPA->string+progfuncs->stringtable,OPB->string+progfuncs->stringtable)); + 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_I: + OPB->_int = OPA->_int; + 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->vector[0] = OPA->vector[0]; + OPB->vector[1] = OPA->vector[1]; + OPB->vector[2] = OPA->vector[2]; + break; + + //store a value to a pointer + case OP_STOREP_IF: + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)OPA->_int; + break; + case OP_STOREP_FI: + ptr = (eval_t *)(OPB->_int); + ptr->_int = (int)OPA->_float; + break; + case OP_STOREP_I: + ptr = (eval_t *)(OPB->_int); + 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 + ptr = (eval_t *)(OPB->_int); + ptr->_int = OPA->_int; + break; + case OP_STOREP_V: + ptr = (eval_t *)(OPB->_int); + 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 + ptr = (eval_t *)(OPB->_int); + *(unsigned char *)ptr = (char)OPA->_float; + break; + + case OP_MULSTORE_F: // f *= f + OPB->_float *= OPA->_float; + break; + case OP_MULSTORE_V: // v *= f + OPB->vector[0] *= OPA->_float; + OPB->vector[1] *= OPA->_float; + OPB->vector[2] *= OPA->_float; + break; + case OP_MULSTOREP_F: // e.f *= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float *= OPA->_float); + break; + case OP_MULSTOREP_V: // e.v *= f + ptr = (eval_t *)(OPB->_int); + 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: // f /= f + OPB->_float /= OPA->_float; + break; + case OP_DIVSTOREP_F: // e.f /= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float /= OPA->_float); + break; + + case OP_ADDSTORE_F: // f += f + OPB->_float += OPA->_float; + break; + case OP_ADDSTORE_V: // v += v + OPB->vector[0] += OPA->vector[0]; + OPB->vector[1] += OPA->vector[1]; + OPB->vector[2] += OPA->vector[2]; + break; + case OP_ADDSTOREP_F: // e.f += f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float += OPA->_float); + break; + case OP_ADDSTOREP_V: // e.v += v + ptr = (eval_t *)(OPB->_int); + 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: // f -= f + OPB->_float -= OPA->_float; + break; + case OP_SUBSTORE_V: // v -= v + OPB->vector[0] -= OPA->vector[0]; + OPB->vector[1] -= OPA->vector[1]; + OPB->vector[2] -= OPA->vector[2]; + break; + case OP_SUBSTOREP_F: // e.f -= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float -= OPA->_float); + break; + case OP_SUBSTOREP_V: // e.v -= v + ptr = (eval_t *)(OPB->_int); + 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; + + + //get a pointer to a field var + case OP_ADDRESS: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + if (ed->readonly) + PR_RunError (progfuncs, "assignment to read-only entity"); + OPC->_int = (int)(((int *)edvars(ed)) + OPB->_int + progfuncs->fieldadjust); + break; + + //load a field to a value + case OP_LOAD_I: + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int + progfuncs->fieldadjust); + OPC->_int = ptr->_int; + break; + + case OP_LOAD_V: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int + progfuncs->fieldadjust); + OPC->vector[0] = ptr->vector[0]; + OPC->vector[1] = ptr->vector[1]; + OPC->vector[2] = ptr->vector[2]; + break; + +//================== + + case OP_IFNOTS: + RUNAWAYCHECK(); + if (!OPA->string || !*OPA->string) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFNOT: + RUNAWAYCHECK(); + if (!OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFS: + RUNAWAYCHECK(); + if (OPA->string && *OPA->string) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IF: + RUNAWAYCHECK(); + if (OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_GOTO: + RUNAWAYCHECK(); + st += (sofs)st->a - 1; // offset the s++ + break; + + case OP_CALL8H: + case OP_CALL7H: + case OP_CALL6H: + case OP_CALL5H: + case OP_CALL4H: + case OP_CALL3H: + case OP_CALL2H: + G_VECTOR(OFS_PARM1)[0] = OPC->vector[0]; + G_VECTOR(OFS_PARM1)[1] = OPC->vector[1]; + G_VECTOR(OFS_PARM1)[2] = OPC->vector[2]; + case OP_CALL1H: + G_VECTOR(OFS_PARM0)[0] = OPB->vector[0]; + G_VECTOR(OFS_PARM0)[1] = OPB->vector[1]; + G_VECTOR(OFS_PARM0)[2] = OPB->vector[2]; + + case OP_CALL8: + case OP_CALL7: + case OP_CALL6: + case OP_CALL5: + case OP_CALL4: + case OP_CALL3: + case OP_CALL2: + case OP_CALL1: + case OP_CALL0: + RUNAWAYCHECK(); + pr_xstatement = st-pr_statements; + + + if (st->op > OP_CALL8) + pr_argc = st->op - (OP_CALL1H-1); + else + pr_argc = st->op - OP_CALL0; + fnum = OPA->function; + if ((fnum & ~0xff000000)<=0) + { + pr_trace++; + printf("NULL function from qc.\n"); +#ifndef DEBUGABLE + goto cont; +#endif + break; + } +/* +{ + static char buffer[1024*1024*8]; + int size = sizeof buffer; + progfuncs->save_ents(progfuncs, buffer, &size, 0); +}*/ + + + p=pr_typecurrent; +//about to switch. needs caching. + + //if it's an external call, switch now (before any function pointers are used) + PR_MoveParms(progfuncs, (fnum & 0xff000000)>>24, p); + PR_SwitchProgs(progfuncs, (fnum & 0xff000000)>>24); + + newf = &pr_functions[fnum & ~0xff000000]; + + if (newf->first_statement < 0) + { // negative statements are built in functions + i = -newf->first_statement; +// p = pr_typecurrent; + if (i < externs->numglobalbuiltins) + { + (*externs->globalbuiltins[i]) (progfuncs, (struct globalvars_s *)current_progstate->globals); + if (prinst->continuestatement!=-1) + { + st=&pr_statements[prinst->continuestatement]; + prinst->continuestatement=-1; + break; + } + } + else + { + i -= externs->numglobalbuiltins; + if (i > current_progstate->numbuiltins) + { + if (newf->first_statement == -0x7fffffff) + ((builtin_t)newf->profile) (progfuncs, (struct globalvars_s *)current_progstate->globals); + else + PR_RunError (progfuncs, "Bad builtin call number - %i", -newf->first_statement); + } + else + current_progstate->builtins [i] (progfuncs, (struct globalvars_s *)current_progstate->globals); + } + PR_MoveParms(progfuncs, p, pr_typecurrent); +// memcpy(&pr_progstate[p].globals[OFS_RETURN], ¤t_progstate->globals[OFS_RETURN], sizeof(vec3_t)); + PR_SwitchProgs(progfuncs, (progsnum_t)p); + +//#ifndef DEBUGABLE //decide weather non debugger wants to start debugging. + s = st-pr_statements; + goto restart; +//#endif +// break; + } +// PR_MoveParms((OPA->function & 0xff000000)>>24, pr_typecurrent); +// PR_SwitchProgs((OPA->function & 0xff000000)>>24); + s = PR_EnterFunction (progfuncs, newf, p); + st = &pr_statements[s]; + + goto restart; +// break; + + case OP_DONE: + case OP_RETURN: + + RUNAWAYCHECK(); + + pr_globals[OFS_RETURN] = pr_globals[st->a]; + pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; + pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; +/* +{ + static char buffer[1024*1024*8]; + int size = sizeof buffer; + progfuncs->save_ents(progfuncs, buffer, &size, 0); +}*/ + + s = PR_LeaveFunction (progfuncs); + st = &pr_statements[s]; + if (pr_depth == prinst->exitdepth) + { + return; // all done + } + goto restart; +// break; + + case OP_STATE: + externs->stateop(progfuncs, OPA->_float, OPB->function); + 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 = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + OPC->_float = (float)ptr->_int; + break; + + case OP_CP_FTOI: + ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + 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: + if (OPB->_int == 0) //no division by zero allowed... + 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; + + + //array/structure reading/riting. + case OP_GLOBALADDRESS: + OPC->_int = (int)(&((int)(OPA->_int)) + OPB->_int); + break; + case OP_POINTER_ADD: //pointer to 32 bit (remember to *3 for vectors) + 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 = (eval_t *)(&((int)(OPA->_int)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADA_V: + ptr = (eval_t *)(&((int)(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: //(char*)c = (char*)a + (float)b + OPC->_int = OPA->_int + (int)OPB->_float; + break; + case OP_SUB_S: //(float)c = (char*)a - (char*)b + OPC->_int = OPA->_int - OPB->_int; + break; + case OP_LOADP_C: //load character from a string + ptr = (eval_t *)(((int)(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: +#ifdef PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int >= pr_edict_size/4) + { + Host_Error("Progs attempted to read an invalid field in an edict (%i)\n", OPB->_int); + return; + } +#endif + ptr = (eval_t *)(((int)(OPA->_int)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADP_V: +#ifdef PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int + 2 >= pr_edict_size/4) + { + Host_Error("Progs attempted to read an invalid field in an edict (%i)\n", OPB->_int); + return; + } +#endif + + ptr = (eval_t *)(((int)(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_GBL_FNC: + i = (int)OPB->_float; + if(i < 0 || i > G_INT((uofs)st->a - 1)) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + i]; + OPC->_int = t->_int; + break; + case OP_FETCH_GBL_V: + i = (int)OPB->_float; + if(i < 0 || i > G_INT((uofs)st->a - 1)) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + +((int)OPB->_float)*3]; + OPC->vector[0] = t->vector[0]; + OPC->vector[1] = t->vector[1]; + OPC->vector[2] = t->vector[2]; + break; + + case OP_CSTATE: + externs->cstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_CWSTATE: + externs->cwstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_THINKTIME: + externs->thinktimeop(progfuncs, (struct edict_s *)PROG_TO_EDICT(OPA->edict), OPB->_float); + break; + + + case OP_BITSET: // b (+) a + OPB->_float = (float)((int)OPB->_float | (int)OPA->_float); + break; + case OP_BITSETP: // .b (+) a + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)((int)ptr->_float | (int)OPA->_float); + break; + case OP_BITCLR: // b (-) a + OPB->_float = (float)((int)OPB->_float & ~((int)OPA->_float)); + break; + case OP_BITCLRP: // .b (-) a + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)((int)ptr->_float & ~((int)OPA->_float)); + break; + + case OP_RAND0: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RAND1: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff)*OPA->_float; + break; + case OP_RAND2: + if(OPA->_float < OPB->_float) + { + G_FLOAT(OFS_RETURN) = OPA->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPB->_float-OPA->_float)); + } + else + { + G_FLOAT(OFS_RETURN) = OPB->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPA->_float-OPB->_float)); + } + break; + case OP_RANDV0: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RANDV1: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[0]; + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[1]; + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[2]; + break; + case OP_RANDV2: + for(i = 0; i < 3; i++) + { + if(OPA->vector[i] < OPB->vector[i]) + { + G_FLOAT(OFS_RETURN+i) = OPA->vector[i]+((rand()&0x7fff)/((float)0x7fff) + *(OPB->vector[i]-OPA->vector[i])); + } + else + { + G_FLOAT(OFS_RETURN+i) = OPB->vector[i]+(rand()*(1.0f/RAND_MAX) + *(OPA->vector[i]-OPB->vector[i])); + } + } + break; + + + case OP_SWITCH_F: + case OP_SWITCH_V: + case OP_SWITCH_S: + case OP_SWITCH_E: + case OP_SWITCH_FNC: + swtch = OPA; + swtchtype = st->op; + RUNAWAYCHECK(); + st += (sofs)st->b - 1; // offset the st++ + break; + case OP_CASE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float == OPA->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_E: + case OP_SWITCH_FNC: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_S: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + if ((!swtch->_int && progfuncs->stringtable[OPA->string]) || (!OPA->_int && progfuncs->stringtable[swtch->string])) //one is null (cannot be not both). + break; + if (!strcmp(progfuncs->stringtable+swtch->string, progfuncs->stringtable+OPA->string)) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_V: + if (swtch->vector[0] == OPA->vector[0] && swtch->vector[1] == OPA->vector[1] && swtch->vector[2] == OPA->vector[2]) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASE with bad/missing OP_SWITCH %i", swtchtype); + break; + } + break; + case OP_CASERANGE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float >= OPA->_float && swtch->_float <= OPB->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->c-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASERANGE with bad/missing OP_SWITCH %i", swtchtype); + } + break; + + + + + + + + + + + + case OP_MUL_IF: + case OP_MUL_FI: + case OP_MUL_VI: + case OP_DIV_IF: + case OP_DIV_FI: + case OP_BITAND_IF: + case OP_BITOR_IF: + case OP_BITAND_FI: + case OP_BITOR_FI: + case OP_AND_I: + case OP_OR_I: + case OP_AND_IF: + case OP_OR_IF: + case OP_AND_FI: + case OP_OR_FI: + case OP_NOT_I: + case OP_NE_IF: + case OP_NE_FI: + 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 + case OP_GSTOREP_V: + case OP_GADDRESS: + case OP_GLOAD_I: + case OP_GLOAD_F: + case OP_GLOAD_FLD: + case OP_GLOAD_ENT: + case OP_GLOAD_S: + case OP_GLOAD_FNC: + case OP_BOUNDCHECK: +PR_RunError(progfuncs, "Extra opcode not implemented\n"); + break; + + default: + if (st->op & 0x8000) //break point! + { + pr_xstatement = s = st-pr_statements; + + printf("Break point hit.\n"); + if (pr_trace<1) + pr_trace=1; //this is what it's for + + s = ShowStep(progfuncs, s); + st = &pr_statements[s]; //let the user move execution + pr_xstatement = s = st-pr_statements; + + memcpy(&fakeop, st, sizeof(dstatement_t)); //don't hit the new statement as a break point, cos it's probably the same one. + fakeop.op &= ~0x8000; + st = &fakeop; //a little remapping... + + goto reeval; //reexecute + } + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "Bad opcode %i", st->op); + } +} + + +#undef cont +#undef reeval +#undef st +#undef pr_statements +#undef fakeop +#undef dstatement_t +#undef sofs +#undef uofs diff --git a/engine/qclib/execloop16.h b/engine/qclib/execloop16.h new file mode 100644 index 000000000..157726f1d --- /dev/null +++ b/engine/qclib/execloop16.h @@ -0,0 +1,914 @@ +//qc execution code. +//we have two conditions. +//one allows us to debug and trace through our code, the other doesn't. + +//hopefully, the compiler will do a great job at optimising this code for us, where required. +//if it dosn't, then bum. + +//the general overhead should be reduced significantly, and I would be supprised if it did run slower. + +//run away loops are checked for ONLY on gotos and function calls. This might give a poorer check, but it will run faster overall. + +//Appears to work fine. + +#if INTSIZE == 16 +#define cont cont16 +#define reeval reeval16 +#define st st16 +#define pr_statements pr_statements16 +#define fakeop fakeop16 +#define dstatement_t dstatement16_t +#define sofs signed short +#define uofs unsigned short +#elif INTSIZE == 32 +#define cont cont32 +#define reeval reeval32 +#define st st32 +#define pr_statements pr_statements32 +#define fakeop fakeop32 +#define dstatement_t dstatement32_t +#define sofs signed int +#define uofs unsigned int +#elif INTSIZE == 24 +#error INTSIZE should be set to 32. +#else +#error Bad cont size +#endif + + +//rely upon just st +{ +#ifdef DEBUGABLE +cont: //last statement may have been a breakpoint + s = st-pr_statements; + s+=1; + s=ShowStep(progfuncs, s); + st = pr_statements + s; + +reeval: +#else + 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_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 = (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 = (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 = (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 = (float)(OPA->_float && OPB->_float); + break; + case OP_OR: + OPC->_float = (float)(OPA->_float || OPB->_float); + break; + + case OP_NOT_F: + OPC->_float = (float)(!OPA->_float); + break; + case OP_NOT_V: + OPC->_float = (float)(!OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2]); + break; + case OP_NOT_S: + OPC->_float = (float)(!(OPA->string) || !*(OPA->string+progfuncs->stringtable)); + break; + case OP_NOT_FNC: + OPC->_float = (float)(!(OPA->function & ~0xff000000)); + break; + case OP_NOT_ENT: + OPC->_float = (float)(PROG_TO_EDICT(OPA->edict) == (edictrun_t *)sv_edicts); + 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 = (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; + else if (!OPA->string) + { + if (!OPB->string || !*(OPB->string+progfuncs->stringtable)) + OPC->_float = true; + else + OPC->_float = false; + } + else if (!OPB->string) + { + if (!OPA->string || !*(OPA->string+progfuncs->stringtable)) + OPC->_float = true; + else + OPC->_float = false; + } + else + OPC->_float = (float)(!strcmp(OPA->string+progfuncs->stringtable,OPB->string+progfuncs->stringtable)); + break; + case OP_EQ_E: + OPC->_float = (float)(OPA->_int == OPB->_int); + break; + case OP_EQ_FNC: + OPC->_float = (float)(OPA->function == OPB->function); + break; + + + case OP_NE_F: + OPC->_float = (float)(OPA->_float != OPB->_float); + break; + case OP_NE_V: + OPC->_float = (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; + else if (!OPA->string) + { + if (!OPB->string || !*(OPB->string+progfuncs->stringtable)) + OPC->_float = false; + else + OPC->_float = true; + } + else if (!OPB->string) + { + if (!OPA->string || !*(OPA->string+progfuncs->stringtable)) + OPC->_float = false; + else + OPC->_float = true; + } + else + OPC->_float = (float)(strcmp(OPA->string+progfuncs->stringtable,OPB->string+progfuncs->stringtable)); + 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_I: + OPB->_int = OPA->_int; + 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->vector[0] = OPA->vector[0]; + OPB->vector[1] = OPA->vector[1]; + OPB->vector[2] = OPA->vector[2]; + break; + + //store a value to a pointer + case OP_STOREP_IF: + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)OPA->_int; + break; + case OP_STOREP_FI: + ptr = (eval_t *)(OPB->_int); + ptr->_int = (int)OPA->_float; + break; + case OP_STOREP_I: + ptr = (eval_t *)(OPB->_int); + 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 + ptr = (eval_t *)(OPB->_int); + ptr->_int = OPA->_int; + break; + case OP_STOREP_V: + ptr = (eval_t *)(OPB->_int); + 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 + ptr = (eval_t *)(OPB->_int); + *(unsigned char *)ptr = (char)OPA->_float; + break; + + case OP_MULSTORE_F: // f *= f + OPB->_float *= OPA->_float; + break; + case OP_MULSTORE_V: // v *= f + OPB->vector[0] *= OPA->_float; + OPB->vector[1] *= OPA->_float; + OPB->vector[2] *= OPA->_float; + break; + case OP_MULSTOREP_F: // e.f *= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float *= OPA->_float); + break; + case OP_MULSTOREP_V: // e.v *= f + ptr = (eval_t *)(OPB->_int); + 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: // f /= f + OPB->_float /= OPA->_float; + break; + case OP_DIVSTOREP_F: // e.f /= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float /= OPA->_float); + break; + + case OP_ADDSTORE_F: // f += f + OPB->_float += OPA->_float; + break; + case OP_ADDSTORE_V: // v += v + OPB->vector[0] += OPA->vector[0]; + OPB->vector[1] += OPA->vector[1]; + OPB->vector[2] += OPA->vector[2]; + break; + case OP_ADDSTOREP_F: // e.f += f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float += OPA->_float); + break; + case OP_ADDSTOREP_V: // e.v += v + ptr = (eval_t *)(OPB->_int); + 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: // f -= f + OPB->_float -= OPA->_float; + break; + case OP_SUBSTORE_V: // v -= v + OPB->vector[0] -= OPA->vector[0]; + OPB->vector[1] -= OPA->vector[1]; + OPB->vector[2] -= OPA->vector[2]; + break; + case OP_SUBSTOREP_F: // e.f -= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float -= OPA->_float); + break; + case OP_SUBSTOREP_V: // e.v -= v + ptr = (eval_t *)(OPB->_int); + 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; + + + //get a pointer to a field var + case OP_ADDRESS: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + if (ed->readonly) + PR_RunError (progfuncs, "assignment to read-only entity"); + OPC->_int = (int)(((int *)edvars(ed)) + OPB->_int); + break; + + //load a field to a value + case OP_LOAD_I: + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOAD_V: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int); + OPC->vector[0] = ptr->vector[0]; + OPC->vector[1] = ptr->vector[1]; + OPC->vector[2] = ptr->vector[2]; + break; + +//================== + + case OP_IFNOTS: + RUNAWAYCHECK(); + if (!OPA->string || !*OPA->string) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFNOT: + RUNAWAYCHECK(); + if (!OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFS: + RUNAWAYCHECK(); + if (OPA->string && *OPA->string) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IF: + RUNAWAYCHECK(); + if (OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_GOTO: + RUNAWAYCHECK(); + st += (sofs)st->a - 1; // offset the s++ + break; + + case OP_CALL8H: + case OP_CALL7H: + case OP_CALL6H: + case OP_CALL5H: + case OP_CALL4H: + case OP_CALL3H: + case OP_CALL2H: + G_VECTOR(OFS_PARM1)[0] = OPC->vector[0]; + G_VECTOR(OFS_PARM1)[1] = OPC->vector[1]; + G_VECTOR(OFS_PARM1)[2] = OPC->vector[2]; + case OP_CALL1H: + G_VECTOR(OFS_PARM0)[0] = OPB->vector[0]; + G_VECTOR(OFS_PARM0)[1] = OPB->vector[1]; + G_VECTOR(OFS_PARM0)[2] = OPB->vector[2]; + + case OP_CALL8: + case OP_CALL7: + case OP_CALL6: + case OP_CALL5: + case OP_CALL4: + case OP_CALL3: + case OP_CALL2: + case OP_CALL1: + case OP_CALL0: + RUNAWAYCHECK(); + pr_xstatement = st-pr_statements; + + + if (st->op > OP_CALL8) + pr_argc = st->op - (OP_CALL1H-1); + else + pr_argc = st->op - OP_CALL0; + fnum = OPA->function; + if ((fnum & ~0xff000000)<=0) + { + pr_trace++; + printf("NULL function from qc.\n"); +#ifndef DEBUGABLE + goto cont; +#endif + break; + } + + p=pr_typecurrent; +//about to switch. needs caching. + + //if it's an external call, switch now (before any function pointers are used) + PR_MoveParms(progfuncs, (fnum & 0xff000000)>>24, p); + PR_SwitchProgs(progfuncs, (fnum & 0xff000000)>>24); + + newf = &pr_functions[fnum & ~0xff000000]; + + if (newf->first_statement < 0) + { // negative statements are built in functions + i = -newf->first_statement; +// p = pr_typecurrent; + if (i < externs->numglobalbuiltins) + { + (*externs->globalbuiltins[i]) (progfuncs, (struct globalvars_s *)current_progstate->globals); + if (prinst->continuestatement!=-1) + { + st=&pr_statements[prinst->continuestatement]; + prinst->continuestatement=-1; + break; + } + } + else + { + i -= externs->numglobalbuiltins; + if (i > current_progstate->numbuiltins) + { + if (newf->first_statement == -0x7fffffff) + ((builtin_t)newf->profile) (progfuncs, (struct globalvars_s *)current_progstate->globals); + else + PR_RunError (progfuncs, "Bad builtin call number"); + } + else + current_progstate->builtins [i] (progfuncs, (struct globalvars_s *)current_progstate->globals); + } + PR_MoveParms(progfuncs, p, pr_typecurrent); +// memcpy(&pr_progstate[p].globals[OFS_RETURN], ¤t_progstate->globals[OFS_RETURN], sizeof(vec3_t)); + PR_SwitchProgs(progfuncs, (progsnum_t)p); + +//#ifndef DEBUGABLE //decide weather non debugger wants to start debugging. + s = st-pr_statements; + goto restart; +//#endif +// break; + } +// PR_MoveParms((OPA->function & 0xff000000)>>24, pr_typecurrent); +// PR_SwitchProgs((OPA->function & 0xff000000)>>24); + s = PR_EnterFunction (progfuncs, newf, p); + st = &pr_statements[s]; + + goto restart; +// break; + + case OP_DONE: + case OP_RETURN: + + RUNAWAYCHECK(); + + pr_globals[OFS_RETURN] = pr_globals[st->a]; + pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; + pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; + + s = PR_LeaveFunction (progfuncs); + st = &pr_statements[s]; + if (pr_depth == exitdepth) + { + PR_MoveParms(progfuncs, initial_progs, pr_typecurrent); + PR_SwitchProgs(progfuncs, initial_progs); + return; // all done + } + goto restart; +// break; + + case OP_STATE: + externs->stateop(progfuncs, OPA->_float, OPB->function); + 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_C_ITOF: + OPC->_float = (float)OPA->_int; + break; + case OP_C_FTOI: + OPC->_int = (int)OPA->_float; + break; + + case OP_CP_ITOF: + ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + OPC->_float = (float)ptr->_int; + break; + + case OP_CP_FTOI: + ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + 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: + if (OPB->_int == 0) //no division by zero allowed... + 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; + + + //array/structure reading/riting. + case OP_GLOBALADDRESS: + OPC->_int = (int)(&((int)(OPA->_int)) + OPB->_int); + break; + case OP_POINTER_ADD: //pointer to 32 bit (remember to *3 for vectors) + 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 = (eval_t *)(&((int)(OPA->_int)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADA_V: + ptr = (eval_t *)(&((int)(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: //(char*)c = (char*)a + (float)b + OPC->_int = OPA->_int + (int)OPB->_float; + break; + case OP_SUB_S: //(float)c = (char*)a - (char*)b + OPC->_int = OPA->_int - OPB->_int; + break; + case OP_LOADP_C: //load character from a string + ptr = (eval_t *)(((int)(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: +#ifdef PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int >= pr_edict_size/4) + { + Host_Error("Progs attempted to read an invalid field in an edict (%i)\n", OPB->_int); + return; + } +#endif + ptr = (eval_t *)(((int)(OPA->_int)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADP_V: +#ifdef PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int + 2 >= pr_edict_size/4) + { + Host_Error("Progs attempted to read an invalid field in an edict (%i)\n", OPB->_int); + return; + } +#endif + + ptr = (eval_t *)(((int)(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_GBL_FNC: + i = (int)OPB->_float; + if(i < 0 || i > G_INT((uofs)st->a - 1)) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + i]; + OPC->_int = t->_int; + break; + case OP_FETCH_GBL_V: + i = (int)OPB->_float; + if(i < 0 || i > G_INT((uofs)st->a - 1)) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + +((int)OPB->_float)*3]; + OPC->vector[0] = t->vector[0]; + OPC->vector[1] = t->vector[1]; + OPC->vector[2] = t->vector[2]; + break; + + case OP_CSTATE: + externs->cstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_CWSTATE: + externs->cwstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_THINKTIME: + externs->thinktimeop(progfuncs, (struct edict_s *)PROG_TO_EDICT(OPA->edict), OPB->_float); + break; + + + case OP_BITSET: // b (+) a + OPB->_float = (float)((int)OPB->_float | (int)OPA->_float); + break; + case OP_BITSETP: // .b (+) a + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)((int)ptr->_float | (int)OPA->_float); + break; + case OP_BITCLR: // b (-) a + OPB->_float = (float)((int)OPB->_float & ~((int)OPA->_float)); + break; + case OP_BITCLRP: // .b (-) a + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)((int)ptr->_float & ~((int)OPA->_float)); + break; + + case OP_RAND0: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RAND1: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff)*OPA->_float; + break; + case OP_RAND2: + if(OPA->_float < OPB->_float) + { + G_FLOAT(OFS_RETURN) = OPA->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPB->_float-OPA->_float)); + } + else + { + G_FLOAT(OFS_RETURN) = OPB->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPA->_float-OPB->_float)); + } + break; + case OP_RANDV0: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RANDV1: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[0]; + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[1]; + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[2]; + break; + case OP_RANDV2: + for(i = 0; i < 3; i++) + { + if(OPA->vector[i] < OPB->vector[i]) + { + G_FLOAT(OFS_RETURN+i) = OPA->vector[i]+((rand()&0x7fff)/((float)0x7fff) + *(OPB->vector[i]-OPA->vector[i])); + } + else + { + G_FLOAT(OFS_RETURN+i) = OPB->vector[i]+(rand()*(1.0f/RAND_MAX) + *(OPA->vector[i]-OPB->vector[i])); + } + } + break; + + + case OP_SWITCH_F: + case OP_SWITCH_V: + case OP_SWITCH_S: + case OP_SWITCH_E: + case OP_SWITCH_FNC: + swtch = OPA; + swtchtype = st->op; + RUNAWAYCHECK(); + st += (sofs)st->b - 1; // offset the st++ + break; + case OP_CASE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float == OPA->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_E: + case OP_SWITCH_FNC: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_S: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + if ((!swtch->_int && progfuncs->stringtable[OPA->string]) || (!OPA->_int && progfuncs->stringtable[swtch->string])) //one is null (cannot be not both). + break; + if (!strcmp(progfuncs->stringtable+swtch->string, progfuncs->stringtable+OPA->string)) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_V: + if (swtch->vector[0] == OPA->vector[0] && swtch->vector[1] == OPA->vector[1] && swtch->vector[2] == OPA->vector[2]) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASE with bad/missing OP_SWITCH %i", swtchtype); + break; + } + break; + case OP_CASERANGE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float >= OPA->_float && swtch->_float <= OPB->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->c-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASERANGE with bad/missing OP_SWITCH %i", swtchtype); + } + break; + + default: + if (st->op & 0x8000) //break point! + { + pr_xstatement = s = st-pr_statements; + + printf("Break point hit.\n"); + if (pr_trace<1) + pr_trace=1; //this is what it's for + + s = ShowStep(progfuncs, s); + st = &pr_statements[s]; //let the user move execution + pr_xstatement = s = st-pr_statements; + + memcpy(&fakeop, st, sizeof(dstatement_t)); //don't hit the new statement as a break point, cos it's probably the same one. + fakeop.op &= ~0x8000; + st = &fakeop; //a little remapping... + + goto reeval; //reexecute + } + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "Bad opcode %i", st->op); + } +} + + +#undef cont +#undef reeval +#undef st +#undef pr_statements +#undef fakeop +#undef dstatement_t +#undef sofs +#undef uofs diff --git a/engine/qclib/execloop16d.h b/engine/qclib/execloop16d.h new file mode 100644 index 000000000..9c0d53743 --- /dev/null +++ b/engine/qclib/execloop16d.h @@ -0,0 +1,912 @@ +//qc execution code. +//we have two conditions. +//one allows us to debug and trace through our code, the other doesn't. + +//hopefully, the compiler will do a great job at optimising this code for us, where required. +//if it dosn't, then bum. + +//the general overhead should be reduced significantly, and I would be supprised if it did run slower. + +//run away loops are checked for ONLY on gotos and function calls. This might give a poorer check, but it will run faster overall. + +//Appears to work fine. + +#if INTSIZE == 16 +#define cont cont16 +#define reeval reeval16 +#define st st16 +#define pr_statements pr_statements16 +#define fakeop fakeop16 +#define dstatement_t dstatement16_t +#define sofs signed short +#define uofs unsigned short +#elif INTSIZE == 32 +#define cont cont32 +#define reeval reeval32 +#define st st32 +#define pr_statements pr_statements32 +#define fakeop fakeop32 +#define dstatement_t dstatement32_t +#define sofs signed int +#define uofs unsigned int +#elif INTSIZE == 24 +#error INTSIZE should be set to 32. +#else +#error Bad cont size +#endif + + +//rely upon just st +{ +#ifdef DEBUGABLE +cont: //last statement may have been a breakpoint + s = st-pr_statements; + s+=1; + s=ShowStep(progfuncs, s); + st = pr_statements + s; + +reeval: +#else + 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_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 = (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 = (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 = (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 = (float)(OPA->_float && OPB->_float); + break; + case OP_OR: + OPC->_float = (float)(OPA->_float || OPB->_float); + break; + + case OP_NOT_F: + OPC->_float = (float)(!OPA->_float); + break; + case OP_NOT_V: + OPC->_float = (float)(!OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2]); + break; + case OP_NOT_S: + OPC->_float = (float)(!(OPA->string) || !*(OPA->string+progfuncs->stringtable)); + break; + case OP_NOT_FNC: + OPC->_float = (float)(!(OPA->function & ~0xff000000)); + break; + case OP_NOT_ENT: + OPC->_float = (float)(PROG_TO_EDICT(OPA->edict) == (edictrun_t *)sv_edicts); + 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 = (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; + else if (!OPA->string) + { + if (!OPB->string || !*(OPB->string+progfuncs->stringtable)) + OPC->_float = true; + else + OPC->_float = false; + } + else if (!OPB->string) + { + if (!OPA->string || !*(OPA->string+progfuncs->stringtable)) + OPC->_float = true; + else + OPC->_float = false; + } + else + OPC->_float = (float)(!strcmp(OPA->string+progfuncs->stringtable,OPB->string+progfuncs->stringtable)); + break; + case OP_EQ_E: + OPC->_float = (float)(OPA->_int == OPB->_int); + break; + case OP_EQ_FNC: + OPC->_float = (float)(OPA->function == OPB->function); + break; + + + case OP_NE_F: + OPC->_float = (float)(OPA->_float != OPB->_float); + break; + case OP_NE_V: + OPC->_float = (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; + else if (!OPA->string) + { + if (!OPB->string || !*(OPB->string+progfuncs->stringtable)) + OPC->_float = false; + else + OPC->_float = true; + } + else if (!OPB->string) + { + if (!OPA->string || !*(OPA->string+progfuncs->stringtable)) + OPC->_float = false; + else + OPC->_float = true; + } + else + OPC->_float = (float)(strcmp(OPA->string+progfuncs->stringtable,OPB->string+progfuncs->stringtable)); + 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_I: + OPB->_int = OPA->_int; + 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->vector[0] = OPA->vector[0]; + OPB->vector[1] = OPA->vector[1]; + OPB->vector[2] = OPA->vector[2]; + break; + + //store a value to a pointer + case OP_STOREP_IF: + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)OPA->_int; + break; + case OP_STOREP_FI: + ptr = (eval_t *)(OPB->_int); + ptr->_int = (int)OPA->_float; + break; + case OP_STOREP_I: + ptr = (eval_t *)(OPB->_int); + 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 + ptr = (eval_t *)(OPB->_int); + ptr->_int = OPA->_int; + break; + case OP_STOREP_V: + ptr = (eval_t *)(OPB->_int); + 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 + ptr = (eval_t *)(OPB->_int); + *(unsigned char *)ptr = (char)OPA->_float; + break; + + case OP_MULSTORE_F: // f *= f + OPB->_float *= OPA->_float; + break; + case OP_MULSTORE_V: // v *= f + OPB->vector[0] *= OPA->_float; + OPB->vector[1] *= OPA->_float; + OPB->vector[2] *= OPA->_float; + break; + case OP_MULSTOREP_F: // e.f *= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float *= OPA->_float); + break; + case OP_MULSTOREP_V: // e.v *= f + ptr = (eval_t *)(OPB->_int); + 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: // f /= f + OPB->_float /= OPA->_float; + break; + case OP_DIVSTOREP_F: // e.f /= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float /= OPA->_float); + break; + + case OP_ADDSTORE_F: // f += f + OPB->_float += OPA->_float; + break; + case OP_ADDSTORE_V: // v += v + OPB->vector[0] += OPA->vector[0]; + OPB->vector[1] += OPA->vector[1]; + OPB->vector[2] += OPA->vector[2]; + break; + case OP_ADDSTOREP_F: // e.f += f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float += OPA->_float); + break; + case OP_ADDSTOREP_V: // e.v += v + ptr = (eval_t *)(OPB->_int); + 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: // f -= f + OPB->_float -= OPA->_float; + break; + case OP_SUBSTORE_V: // v -= v + OPB->vector[0] -= OPA->vector[0]; + OPB->vector[1] -= OPA->vector[1]; + OPB->vector[2] -= OPA->vector[2]; + break; + case OP_SUBSTOREP_F: // e.f -= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float -= OPA->_float); + break; + case OP_SUBSTOREP_V: // e.v -= v + ptr = (eval_t *)(OPB->_int); + 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; + + + //get a pointer to a field var + case OP_ADDRESS: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + if (ed->readonly) + PR_RunError (progfuncs, "assignment to world entity"); + OPC->_int = (int)(((int *)edvars(ed)) + OPB->_int); + break; + + //load a field to a value + case OP_LOAD_I: + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOAD_V: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int); + OPC->vector[0] = ptr->vector[0]; + OPC->vector[1] = ptr->vector[1]; + OPC->vector[2] = ptr->vector[2]; + break; + +//================== + + case OP_IFNOTS: + RUNAWAYCHECK(); + if (!OPA->string || !*OPA->string) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFNOT: + RUNAWAYCHECK(); + if (!OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFS: + RUNAWAYCHECK(); + if (OPA->string && *OPA->string) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IF: + RUNAWAYCHECK(); + if (OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_GOTO: + RUNAWAYCHECK(); + st += (sofs)st->a - 1; // offset the s++ + break; + + case OP_CALL8H: + case OP_CALL7H: + case OP_CALL6H: + case OP_CALL5H: + case OP_CALL4H: + case OP_CALL3H: + case OP_CALL2H: + G_VECTOR(OFS_PARM1)[0] = OPC->vector[0]; + G_VECTOR(OFS_PARM1)[1] = OPC->vector[1]; + G_VECTOR(OFS_PARM1)[2] = OPC->vector[2]; + case OP_CALL1H: + G_VECTOR(OFS_PARM0)[0] = OPB->vector[0]; + G_VECTOR(OFS_PARM0)[1] = OPB->vector[1]; + G_VECTOR(OFS_PARM0)[2] = OPB->vector[2]; + + case OP_CALL8: + case OP_CALL7: + case OP_CALL6: + case OP_CALL5: + case OP_CALL4: + case OP_CALL3: + case OP_CALL2: + case OP_CALL1: + case OP_CALL0: + RUNAWAYCHECK(); + pr_xstatement = st-pr_statements; + + + if (st->op > OP_CALL8) + pr_argc = st->op - (OP_CALL1H-1); + else + pr_argc = st->op - OP_CALL0; + fnum = OPA->function; + if ((fnum & ~0xff000000)<=0) + { + pr_trace++; + printf("NULL function from qc.\n"); +#ifndef DEBUGABLE + goto cont; +#endif + break; + } + + p=pr_typecurrent; +//about to switch. needs caching. + + //if it's an external call, switch now (before any function pointers are used) + PR_MoveParms(progfuncs, (fnum & 0xff000000)>>24, p); + PR_SwitchProgs(progfuncs, (fnum & 0xff000000)>>24); + + newf = &pr_functions[fnum & ~0xff000000]; + + if (newf->first_statement < 0) + { // negative statements are built in functions + i = -newf->first_statement; +// p = pr_typecurrent; + if (i < externs->numglobalbuiltins) + { + (*externs->globalbuiltins[i]) (progfuncs, (struct globalvars_s *)current_progstate->globals); + if (prinst->continuestatement!=-1) + { + st=&pr_statements[prinst->continuestatement]; + prinst->continuestatement=-1; + break; + } + } + else + { + i -= externs->numglobalbuiltins; + if (i > current_progstate->numbuiltins) + { + if (newf->first_statement == -0x7fffffff) + ((builtin_t)newf->profile) (progfuncs, (struct globalvars_s *)current_progstate->globals); + else + PR_RunError (progfuncs, "Bad builtin call number"); + } + else + current_progstate->builtins [i] (progfuncs, (struct globalvars_s *)current_progstate->globals); + } + PR_MoveParms(progfuncs, p, pr_typecurrent); +// memcpy(&pr_progstate[p].globals[OFS_RETURN], ¤t_progstate->globals[OFS_RETURN], sizeof(vec3_t)); + PR_SwitchProgs(progfuncs, (progsnum_t)p); + +//#ifndef DEBUGABLE //decide weather non debugger wants to start debugging. + s = st-pr_statements; + goto restart; +//#endif +// break; + } +// PR_MoveParms((OPA->function & 0xff000000)>>24, pr_typecurrent); +// PR_SwitchProgs((OPA->function & 0xff000000)>>24); + s = PR_EnterFunction (progfuncs, newf, p); + st = &pr_statements[s]; + + goto restart; +// break; + + case OP_DONE: + case OP_RETURN: + + RUNAWAYCHECK(); + + pr_globals[OFS_RETURN] = pr_globals[st->a]; + pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; + pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; + + s = PR_LeaveFunction (progfuncs); + st = &pr_statements[s]; + if (pr_depth == prinst->exitdepth) + { + return; // all done + } + goto restart; +// break; + + case OP_STATE: + externs->stateop(progfuncs, OPA->_float, OPB->function); + 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 = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + OPC->_float = (float)ptr->_int; + break; + + case OP_CP_FTOI: + ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + 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: + if (OPB->_int == 0) //no division by zero allowed... + 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; + + + //array/structure reading/riting. + case OP_GLOBALADDRESS: + OPC->_int = (int)(&((int)(OPA->_int)) + OPB->_int); + break; + case OP_POINTER_ADD: //pointer to 32 bit (remember to *3 for vectors) + 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 = (eval_t *)(&((int)(OPA->_int)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADA_V: + ptr = (eval_t *)(&((int)(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: //(char*)c = (char*)a + (float)b + OPC->_int = OPA->_int + (int)OPB->_float; + break; + case OP_SUB_S: //(float)c = (char*)a - (char*)b + OPC->_int = OPA->_int - OPB->_int; + break; + case OP_LOADP_C: //load character from a string + ptr = (eval_t *)(((int)(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: +#ifdef PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int >= pr_edict_size/4) + { + Host_Error("Progs attempted to read an invalid field in an edict (%i)\n", OPB->_int); + return; + } +#endif + ptr = (eval_t *)(((int)(OPA->_int)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADP_V: +#ifdef PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int + 2 >= pr_edict_size/4) + { + Host_Error("Progs attempted to read an invalid field in an edict (%i)\n", OPB->_int); + return; + } +#endif + + ptr = (eval_t *)(((int)(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_GBL_FNC: + i = (int)OPB->_float; + if(i < 0 || i > G_INT((uofs)st->a - 1)) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + i]; + OPC->_int = t->_int; + break; + case OP_FETCH_GBL_V: + i = (int)OPB->_float; + if(i < 0 || i > G_INT((uofs)st->a - 1)) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + +((int)OPB->_float)*3]; + OPC->vector[0] = t->vector[0]; + OPC->vector[1] = t->vector[1]; + OPC->vector[2] = t->vector[2]; + break; + + case OP_CSTATE: + externs->cstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_CWSTATE: + externs->cwstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_THINKTIME: + externs->thinktimeop(progfuncs, (struct edict_s *)PROG_TO_EDICT(OPA->edict), OPB->_float); + break; + + + case OP_BITSET: // b (+) a + OPB->_float = (float)((int)OPB->_float | (int)OPA->_float); + break; + case OP_BITSETP: // .b (+) a + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)((int)ptr->_float | (int)OPA->_float); + break; + case OP_BITCLR: // b (-) a + OPB->_float = (float)((int)OPB->_float & ~((int)OPA->_float)); + break; + case OP_BITCLRP: // .b (-) a + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)((int)ptr->_float & ~((int)OPA->_float)); + break; + + case OP_RAND0: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RAND1: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff)*OPA->_float; + break; + case OP_RAND2: + if(OPA->_float < OPB->_float) + { + G_FLOAT(OFS_RETURN) = OPA->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPB->_float-OPA->_float)); + } + else + { + G_FLOAT(OFS_RETURN) = OPB->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPA->_float-OPB->_float)); + } + break; + case OP_RANDV0: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RANDV1: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[0]; + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[1]; + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[2]; + break; + case OP_RANDV2: + for(i = 0; i < 3; i++) + { + if(OPA->vector[i] < OPB->vector[i]) + { + G_FLOAT(OFS_RETURN+i) = OPA->vector[i]+((rand()&0x7fff)/((float)0x7fff) + *(OPB->vector[i]-OPA->vector[i])); + } + else + { + G_FLOAT(OFS_RETURN+i) = OPB->vector[i]+(rand()*(1.0f/RAND_MAX) + *(OPA->vector[i]-OPB->vector[i])); + } + } + break; + + + case OP_SWITCH_F: + case OP_SWITCH_V: + case OP_SWITCH_S: + case OP_SWITCH_E: + case OP_SWITCH_FNC: + swtch = OPA; + swtchtype = st->op; + RUNAWAYCHECK(); + st += (sofs)st->b - 1; // offset the st++ + break; + case OP_CASE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float == OPA->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_E: + case OP_SWITCH_FNC: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_S: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + if ((!swtch->_int && progfuncs->stringtable[OPA->string]) || (!OPA->_int && progfuncs->stringtable[swtch->string])) //one is null (cannot be not both). + break; + if (!strcmp(progfuncs->stringtable+swtch->string, progfuncs->stringtable+OPA->string)) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_V: + if (swtch->vector[0] == OPA->vector[0] && swtch->vector[1] == OPA->vector[1] && swtch->vector[2] == OPA->vector[2]) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASE with bad/missing OP_SWITCH %i", swtchtype); + break; + } + break; + case OP_CASERANGE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float >= OPA->_float && swtch->_float <= OPB->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->c-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASERANGE with bad/missing OP_SWITCH %i", swtchtype); + } + break; + + default: + if (st->op & 0x8000) //break point! + { + pr_xstatement = s = st-pr_statements; + + printf("Break point hit.\n"); + if (pr_trace<1) + pr_trace=1; //this is what it's for + + s = ShowStep(progfuncs, s); + st = &pr_statements[s]; //let the user move execution + pr_xstatement = s = st-pr_statements; + + memcpy(&fakeop, st, sizeof(dstatement_t)); //don't hit the new statement as a break point, cos it's probably the same one. + fakeop.op &= ~0x8000; + st = &fakeop; //a little remapping... + + goto reeval; //reexecute + } + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "Bad opcode %i", st->op); + } +} + + +#undef cont +#undef reeval +#undef st +#undef pr_statements +#undef fakeop +#undef dstatement_t +#undef sofs +#undef uofs diff --git a/engine/qclib/execloop32.h b/engine/qclib/execloop32.h new file mode 100644 index 000000000..1f123e349 --- /dev/null +++ b/engine/qclib/execloop32.h @@ -0,0 +1,912 @@ +//qc execution code. +//we have two conditions. +//one allows us to debug and trace through our code, the other doesn't. + +//hopefully, the compiler will do a great job at optimising this code for us, where required. +//if it dosn't, then bum. + +//the general overhead should be reduced significantly, and I would be supprised if it did run slower. + +//run away loops are checked for ONLY on gotos and function calls. This might give a poorer check, but it will run faster overall. + +//Appears to work fine. + +#if INTSIZE == 16 +#define cont cont16 +#define reeval reeval16 +#define st st16 +#define pr_statements pr_statements16 +#define fakeop fakeop16 +#define dstatement_t dstatement16_t +#define sofs signed short +#define uofs unsigned short +#elif INTSIZE == 32 +#define cont cont32 +#define reeval reeval32 +#define st st32 +#define pr_statements pr_statements32 +#define fakeop fakeop32 +#define dstatement_t dstatement32_t +#define sofs signed int +#define uofs unsigned int +#elif INTSIZE == 24 +#error INTSIZE should be set to 32. +#else +#error Bad cont size +#endif + + +//rely upon just st +{ +#ifdef DEBUGABLE +cont: //last statement may have been a breakpoint + s = st-pr_statements; + s+=1; + s=ShowStep(progfuncs, s); + st = pr_statements + s; + +reeval: +#else + 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_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 = (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 = (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 = (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 = (float)(OPA->_float && OPB->_float); + break; + case OP_OR: + OPC->_float = (float)(OPA->_float || OPB->_float); + break; + + case OP_NOT_F: + OPC->_float = (float)(!OPA->_float); + break; + case OP_NOT_V: + OPC->_float = (float)(!OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2]); + break; + case OP_NOT_S: + OPC->_float = (float)(!(OPA->string) || !*(OPA->string+progfuncs->stringtable)); + break; + case OP_NOT_FNC: + OPC->_float = (float)(!(OPA->function & ~0xff000000)); + break; + case OP_NOT_ENT: + OPC->_float = (float)(PROG_TO_EDICT(OPA->edict) == (edictrun_t *)sv_edicts); + 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 = (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; + else if (!OPA->string) + { + if (!OPB->string || !*(OPB->string+progfuncs->stringtable)) + OPC->_float = true; + else + OPC->_float = false; + } + else if (!OPB->string) + { + if (!OPA->string || !*(OPA->string+progfuncs->stringtable)) + OPC->_float = true; + else + OPC->_float = false; + } + else + OPC->_float = (float)(!strcmp(OPA->string+progfuncs->stringtable,OPB->string+progfuncs->stringtable)); + break; + case OP_EQ_E: + OPC->_float = (float)(OPA->_int == OPB->_int); + break; + case OP_EQ_FNC: + OPC->_float = (float)(OPA->function == OPB->function); + break; + + + case OP_NE_F: + OPC->_float = (float)(OPA->_float != OPB->_float); + break; + case OP_NE_V: + OPC->_float = (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; + else if (!OPA->string) + { + if (!OPB->string || !*(OPB->string+progfuncs->stringtable)) + OPC->_float = false; + else + OPC->_float = true; + } + else if (!OPB->string) + { + if (!OPA->string || !*(OPA->string+progfuncs->stringtable)) + OPC->_float = false; + else + OPC->_float = true; + } + else + OPC->_float = (float)(strcmp(OPA->string+progfuncs->stringtable,OPB->string+progfuncs->stringtable)); + 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_I: + OPB->_int = OPA->_int; + 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->vector[0] = OPA->vector[0]; + OPB->vector[1] = OPA->vector[1]; + OPB->vector[2] = OPA->vector[2]; + break; + + //store a value to a pointer + case OP_STOREP_IF: + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)OPA->_int; + break; + case OP_STOREP_FI: + ptr = (eval_t *)(OPB->_int); + ptr->_int = (int)OPA->_float; + break; + case OP_STOREP_I: + ptr = (eval_t *)(OPB->_int); + 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 + ptr = (eval_t *)(OPB->_int); + ptr->_int = OPA->_int; + break; + case OP_STOREP_V: + ptr = (eval_t *)(OPB->_int); + 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 + ptr = (eval_t *)(OPB->_int); + *(unsigned char *)ptr = (char)OPA->_float; + break; + + case OP_MULSTORE_F: // f *= f + OPB->_float *= OPA->_float; + break; + case OP_MULSTORE_V: // v *= f + OPB->vector[0] *= OPA->_float; + OPB->vector[1] *= OPA->_float; + OPB->vector[2] *= OPA->_float; + break; + case OP_MULSTOREP_F: // e.f *= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float *= OPA->_float); + break; + case OP_MULSTOREP_V: // e.v *= f + ptr = (eval_t *)(OPB->_int); + 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: // f /= f + OPB->_float /= OPA->_float; + break; + case OP_DIVSTOREP_F: // e.f /= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float /= OPA->_float); + break; + + case OP_ADDSTORE_F: // f += f + OPB->_float += OPA->_float; + break; + case OP_ADDSTORE_V: // v += v + OPB->vector[0] += OPA->vector[0]; + OPB->vector[1] += OPA->vector[1]; + OPB->vector[2] += OPA->vector[2]; + break; + case OP_ADDSTOREP_F: // e.f += f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float += OPA->_float); + break; + case OP_ADDSTOREP_V: // e.v += v + ptr = (eval_t *)(OPB->_int); + 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: // f -= f + OPB->_float -= OPA->_float; + break; + case OP_SUBSTORE_V: // v -= v + OPB->vector[0] -= OPA->vector[0]; + OPB->vector[1] -= OPA->vector[1]; + OPB->vector[2] -= OPA->vector[2]; + break; + case OP_SUBSTOREP_F: // e.f -= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float -= OPA->_float); + break; + case OP_SUBSTOREP_V: // e.v -= v + ptr = (eval_t *)(OPB->_int); + 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; + + + //get a pointer to a field var + case OP_ADDRESS: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + if (ed->readonly) + PR_RunError (progfuncs, "assignment to world entity"); + OPC->_int = (int)(((int *)edvars(ed)) + OPB->_int); + break; + + //load a field to a value + case OP_LOAD_I: + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOAD_V: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int); + OPC->vector[0] = ptr->vector[0]; + OPC->vector[1] = ptr->vector[1]; + OPC->vector[2] = ptr->vector[2]; + break; + +//================== + + case OP_IFNOTS: + RUNAWAYCHECK(); + if (!OPA->string || !*OPA->string) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFNOT: + RUNAWAYCHECK(); + if (!OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFS: + RUNAWAYCHECK(); + if (OPA->string && *OPA->string) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IF: + RUNAWAYCHECK(); + if (OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_GOTO: + RUNAWAYCHECK(); + st += (sofs)st->a - 1; // offset the s++ + break; + + case OP_CALL8H: + case OP_CALL7H: + case OP_CALL6H: + case OP_CALL5H: + case OP_CALL4H: + case OP_CALL3H: + case OP_CALL2H: + G_VECTOR(OFS_PARM1)[0] = OPC->vector[0]; + G_VECTOR(OFS_PARM1)[1] = OPC->vector[1]; + G_VECTOR(OFS_PARM1)[2] = OPC->vector[2]; + case OP_CALL1H: + G_VECTOR(OFS_PARM0)[0] = OPB->vector[0]; + G_VECTOR(OFS_PARM0)[1] = OPB->vector[1]; + G_VECTOR(OFS_PARM0)[2] = OPB->vector[2]; + + case OP_CALL8: + case OP_CALL7: + case OP_CALL6: + case OP_CALL5: + case OP_CALL4: + case OP_CALL3: + case OP_CALL2: + case OP_CALL1: + case OP_CALL0: + RUNAWAYCHECK(); + pr_xstatement = st-pr_statements; + + + if (st->op > OP_CALL8) + pr_argc = st->op - (OP_CALL1H-1); + else + pr_argc = st->op - OP_CALL0; + fnum = OPA->function; + if ((fnum & ~0xff000000)<=0) + { + pr_trace++; + printf("NULL function from qc.\n"); +#ifndef DEBUGABLE + goto cont; +#endif + break; + } + + p=pr_typecurrent; +//about to switch. needs caching. + + //if it's an external call, switch now (before any function pointers are used) + PR_MoveParms(progfuncs, (fnum & 0xff000000)>>24, p); + PR_SwitchProgs(progfuncs, (fnum & 0xff000000)>>24); + + newf = &pr_functions[fnum & ~0xff000000]; + + if (newf->first_statement < 0) + { // negative statements are built in functions + i = -newf->first_statement; +// p = pr_typecurrent; + if (i < externs->numglobalbuiltins) + { + (*externs->globalbuiltins[i]) (progfuncs, (struct globalvars_s *)current_progstate->globals); + if (prinst->continuestatement!=-1) + { + st=&pr_statements[prinst->continuestatement]; + prinst->continuestatement=-1; + break; + } + } + else + { + i -= externs->numglobalbuiltins; + if (i > current_progstate->numbuiltins) + { + if (newf->first_statement == -0x7fffffff) + ((builtin_t)newf->profile) (progfuncs, (struct globalvars_s *)current_progstate->globals); + else + PR_RunError (progfuncs, "Bad builtin call number"); + } + else + current_progstate->builtins [i] (progfuncs, (struct globalvars_s *)current_progstate->globals); + } + PR_MoveParms(progfuncs, p, pr_typecurrent); +// memcpy(&pr_progstate[p].globals[OFS_RETURN], ¤t_progstate->globals[OFS_RETURN], sizeof(vec3_t)); + PR_SwitchProgs(progfuncs, (progsnum_t)p); + +//#ifndef DEBUGABLE //decide weather non debugger wants to start debugging. + s = st-pr_statements; + goto restart; +//#endif +// break; + } +// PR_MoveParms((OPA->function & 0xff000000)>>24, pr_typecurrent); +// PR_SwitchProgs((OPA->function & 0xff000000)>>24); + s = PR_EnterFunction (progfuncs, newf, p); + st = &pr_statements[s]; + + goto restart; +// break; + + case OP_DONE: + case OP_RETURN: + + RUNAWAYCHECK(); + + pr_globals[OFS_RETURN] = pr_globals[st->a]; + pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; + pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; + + s = PR_LeaveFunction (progfuncs); + st = &pr_statements[s]; + if (pr_depth == prinst->exitdepth) + { + return; // all done + } + goto restart; +// break; + + case OP_STATE: + externs->stateop(progfuncs, OPA->_float, OPB->function); + 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 = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + OPC->_float = (float)ptr->_int; + break; + + case OP_CP_FTOI: + ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + 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: + if (OPB->_int == 0) //no division by zero allowed... + 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; + + + //array/structure reading/riting. + case OP_GLOBALADDRESS: + OPC->_int = (int)(&((int)(OPA->_int)) + OPB->_int); + break; + case OP_POINTER_ADD: //pointer to 32 bit (remember to *3 for vectors) + 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 = (eval_t *)(&((int)(OPA->_int)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADA_V: + ptr = (eval_t *)(&((int)(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: //(char*)c = (char*)a + (float)b + OPC->_int = OPA->_int + (int)OPB->_float; + break; + case OP_SUB_S: //(float)c = (char*)a - (char*)b + OPC->_int = OPA->_int - OPB->_int; + break; + case OP_LOADP_C: //load character from a string + ptr = (eval_t *)(((int)(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: +#ifdef PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int >= pr_edict_size/4) + { + Host_Error("Progs attempted to read an invalid field in an edict (%i)\n", OPB->_int); + return; + } +#endif + ptr = (eval_t *)(((int)(OPA->_int)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADP_V: +#ifdef PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int + 2 >= pr_edict_size/4) + { + Host_Error("Progs attempted to read an invalid field in an edict (%i)\n", OPB->_int); + return; + } +#endif + + ptr = (eval_t *)(((int)(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_GBL_FNC: + i = (int)OPB->_float; + if(i < 0 || i > G_INT((uofs)st->a - 1)) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + i]; + OPC->_int = t->_int; + break; + case OP_FETCH_GBL_V: + i = (int)OPB->_float; + if(i < 0 || i > G_INT((uofs)st->a - 1)) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + +((int)OPB->_float)*3]; + OPC->vector[0] = t->vector[0]; + OPC->vector[1] = t->vector[1]; + OPC->vector[2] = t->vector[2]; + break; + + case OP_CSTATE: + externs->cstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_CWSTATE: + externs->cwstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_THINKTIME: + externs->thinktimeop(progfuncs, (struct edict_s *)PROG_TO_EDICT(OPA->edict), OPB->_float); + break; + + + case OP_BITSET: // b (+) a + OPB->_float = (float)((int)OPB->_float | (int)OPA->_float); + break; + case OP_BITSETP: // .b (+) a + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)((int)ptr->_float | (int)OPA->_float); + break; + case OP_BITCLR: // b (-) a + OPB->_float = (float)((int)OPB->_float & ~((int)OPA->_float)); + break; + case OP_BITCLRP: // .b (-) a + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)((int)ptr->_float & ~((int)OPA->_float)); + break; + + case OP_RAND0: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RAND1: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff)*OPA->_float; + break; + case OP_RAND2: + if(OPA->_float < OPB->_float) + { + G_FLOAT(OFS_RETURN) = OPA->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPB->_float-OPA->_float)); + } + else + { + G_FLOAT(OFS_RETURN) = OPB->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPA->_float-OPB->_float)); + } + break; + case OP_RANDV0: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RANDV1: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[0]; + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[1]; + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[2]; + break; + case OP_RANDV2: + for(i = 0; i < 3; i++) + { + if(OPA->vector[i] < OPB->vector[i]) + { + G_FLOAT(OFS_RETURN+i) = OPA->vector[i]+((rand()&0x7fff)/((float)0x7fff) + *(OPB->vector[i]-OPA->vector[i])); + } + else + { + G_FLOAT(OFS_RETURN+i) = OPB->vector[i]+(rand()*(1.0f/RAND_MAX) + *(OPA->vector[i]-OPB->vector[i])); + } + } + break; + + + case OP_SWITCH_F: + case OP_SWITCH_V: + case OP_SWITCH_S: + case OP_SWITCH_E: + case OP_SWITCH_FNC: + swtch = OPA; + swtchtype = st->op; + RUNAWAYCHECK(); + st += (sofs)st->b - 1; // offset the st++ + break; + case OP_CASE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float == OPA->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_E: + case OP_SWITCH_FNC: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_S: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + if ((!swtch->_int && progfuncs->stringtable[OPA->string]) || (!OPA->_int && progfuncs->stringtable[swtch->string])) //one is null (cannot be not both). + break; + if (!strcmp(progfuncs->stringtable+swtch->string, progfuncs->stringtable+OPA->string)) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_V: + if (swtch->vector[0] == OPA->vector[0] && swtch->vector[1] == OPA->vector[1] && swtch->vector[2] == OPA->vector[2]) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASE with bad/missing OP_SWITCH %i", swtchtype); + break; + } + break; + case OP_CASERANGE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float >= OPA->_float && swtch->_float <= OPB->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->c-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASERANGE with bad/missing OP_SWITCH %i", swtchtype); + } + break; + + default: + if (st->op & 0x8000) //break point! + { + pr_xstatement = s = st-pr_statements; + + printf("Break point hit.\n"); + if (pr_trace<1) + pr_trace=1; //this is what it's for + + s = ShowStep(progfuncs, s); + st = &pr_statements[s]; //let the user move execution + pr_xstatement = s = st-pr_statements; + + memcpy(&fakeop, st, sizeof(dstatement_t)); //don't hit the new statement as a break point, cos it's probably the same one. + fakeop.op &= ~0x8000; + st = &fakeop; //a little remapping... + + goto reeval; //reexecute + } + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "Bad opcode %i", st->op); + } +} + + +#undef cont +#undef reeval +#undef st +#undef pr_statements +#undef fakeop +#undef dstatement_t +#undef sofs +#undef uofs diff --git a/engine/qclib/execloop32d.h b/engine/qclib/execloop32d.h new file mode 100644 index 000000000..1f123e349 --- /dev/null +++ b/engine/qclib/execloop32d.h @@ -0,0 +1,912 @@ +//qc execution code. +//we have two conditions. +//one allows us to debug and trace through our code, the other doesn't. + +//hopefully, the compiler will do a great job at optimising this code for us, where required. +//if it dosn't, then bum. + +//the general overhead should be reduced significantly, and I would be supprised if it did run slower. + +//run away loops are checked for ONLY on gotos and function calls. This might give a poorer check, but it will run faster overall. + +//Appears to work fine. + +#if INTSIZE == 16 +#define cont cont16 +#define reeval reeval16 +#define st st16 +#define pr_statements pr_statements16 +#define fakeop fakeop16 +#define dstatement_t dstatement16_t +#define sofs signed short +#define uofs unsigned short +#elif INTSIZE == 32 +#define cont cont32 +#define reeval reeval32 +#define st st32 +#define pr_statements pr_statements32 +#define fakeop fakeop32 +#define dstatement_t dstatement32_t +#define sofs signed int +#define uofs unsigned int +#elif INTSIZE == 24 +#error INTSIZE should be set to 32. +#else +#error Bad cont size +#endif + + +//rely upon just st +{ +#ifdef DEBUGABLE +cont: //last statement may have been a breakpoint + s = st-pr_statements; + s+=1; + s=ShowStep(progfuncs, s); + st = pr_statements + s; + +reeval: +#else + 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_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 = (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 = (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 = (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 = (float)(OPA->_float && OPB->_float); + break; + case OP_OR: + OPC->_float = (float)(OPA->_float || OPB->_float); + break; + + case OP_NOT_F: + OPC->_float = (float)(!OPA->_float); + break; + case OP_NOT_V: + OPC->_float = (float)(!OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2]); + break; + case OP_NOT_S: + OPC->_float = (float)(!(OPA->string) || !*(OPA->string+progfuncs->stringtable)); + break; + case OP_NOT_FNC: + OPC->_float = (float)(!(OPA->function & ~0xff000000)); + break; + case OP_NOT_ENT: + OPC->_float = (float)(PROG_TO_EDICT(OPA->edict) == (edictrun_t *)sv_edicts); + 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 = (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; + else if (!OPA->string) + { + if (!OPB->string || !*(OPB->string+progfuncs->stringtable)) + OPC->_float = true; + else + OPC->_float = false; + } + else if (!OPB->string) + { + if (!OPA->string || !*(OPA->string+progfuncs->stringtable)) + OPC->_float = true; + else + OPC->_float = false; + } + else + OPC->_float = (float)(!strcmp(OPA->string+progfuncs->stringtable,OPB->string+progfuncs->stringtable)); + break; + case OP_EQ_E: + OPC->_float = (float)(OPA->_int == OPB->_int); + break; + case OP_EQ_FNC: + OPC->_float = (float)(OPA->function == OPB->function); + break; + + + case OP_NE_F: + OPC->_float = (float)(OPA->_float != OPB->_float); + break; + case OP_NE_V: + OPC->_float = (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; + else if (!OPA->string) + { + if (!OPB->string || !*(OPB->string+progfuncs->stringtable)) + OPC->_float = false; + else + OPC->_float = true; + } + else if (!OPB->string) + { + if (!OPA->string || !*(OPA->string+progfuncs->stringtable)) + OPC->_float = false; + else + OPC->_float = true; + } + else + OPC->_float = (float)(strcmp(OPA->string+progfuncs->stringtable,OPB->string+progfuncs->stringtable)); + 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_I: + OPB->_int = OPA->_int; + 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->vector[0] = OPA->vector[0]; + OPB->vector[1] = OPA->vector[1]; + OPB->vector[2] = OPA->vector[2]; + break; + + //store a value to a pointer + case OP_STOREP_IF: + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)OPA->_int; + break; + case OP_STOREP_FI: + ptr = (eval_t *)(OPB->_int); + ptr->_int = (int)OPA->_float; + break; + case OP_STOREP_I: + ptr = (eval_t *)(OPB->_int); + 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 + ptr = (eval_t *)(OPB->_int); + ptr->_int = OPA->_int; + break; + case OP_STOREP_V: + ptr = (eval_t *)(OPB->_int); + 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 + ptr = (eval_t *)(OPB->_int); + *(unsigned char *)ptr = (char)OPA->_float; + break; + + case OP_MULSTORE_F: // f *= f + OPB->_float *= OPA->_float; + break; + case OP_MULSTORE_V: // v *= f + OPB->vector[0] *= OPA->_float; + OPB->vector[1] *= OPA->_float; + OPB->vector[2] *= OPA->_float; + break; + case OP_MULSTOREP_F: // e.f *= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float *= OPA->_float); + break; + case OP_MULSTOREP_V: // e.v *= f + ptr = (eval_t *)(OPB->_int); + 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: // f /= f + OPB->_float /= OPA->_float; + break; + case OP_DIVSTOREP_F: // e.f /= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float /= OPA->_float); + break; + + case OP_ADDSTORE_F: // f += f + OPB->_float += OPA->_float; + break; + case OP_ADDSTORE_V: // v += v + OPB->vector[0] += OPA->vector[0]; + OPB->vector[1] += OPA->vector[1]; + OPB->vector[2] += OPA->vector[2]; + break; + case OP_ADDSTOREP_F: // e.f += f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float += OPA->_float); + break; + case OP_ADDSTOREP_V: // e.v += v + ptr = (eval_t *)(OPB->_int); + 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: // f -= f + OPB->_float -= OPA->_float; + break; + case OP_SUBSTORE_V: // v -= v + OPB->vector[0] -= OPA->vector[0]; + OPB->vector[1] -= OPA->vector[1]; + OPB->vector[2] -= OPA->vector[2]; + break; + case OP_SUBSTOREP_F: // e.f -= f + ptr = (eval_t *)(OPB->_int); + OPC->_float = (ptr->_float -= OPA->_float); + break; + case OP_SUBSTOREP_V: // e.v -= v + ptr = (eval_t *)(OPB->_int); + 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; + + + //get a pointer to a field var + case OP_ADDRESS: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + if (ed->readonly) + PR_RunError (progfuncs, "assignment to world entity"); + OPC->_int = (int)(((int *)edvars(ed)) + OPB->_int); + break; + + //load a field to a value + case OP_LOAD_I: + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOAD_V: + ed = PROG_TO_EDICT(OPA->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + ptr = (eval_t *)(((int *)edvars(ed)) + OPB->_int); + OPC->vector[0] = ptr->vector[0]; + OPC->vector[1] = ptr->vector[1]; + OPC->vector[2] = ptr->vector[2]; + break; + +//================== + + case OP_IFNOTS: + RUNAWAYCHECK(); + if (!OPA->string || !*OPA->string) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFNOT: + RUNAWAYCHECK(); + if (!OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IFS: + RUNAWAYCHECK(); + if (OPA->string && *OPA->string) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_IF: + RUNAWAYCHECK(); + if (OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + + case OP_GOTO: + RUNAWAYCHECK(); + st += (sofs)st->a - 1; // offset the s++ + break; + + case OP_CALL8H: + case OP_CALL7H: + case OP_CALL6H: + case OP_CALL5H: + case OP_CALL4H: + case OP_CALL3H: + case OP_CALL2H: + G_VECTOR(OFS_PARM1)[0] = OPC->vector[0]; + G_VECTOR(OFS_PARM1)[1] = OPC->vector[1]; + G_VECTOR(OFS_PARM1)[2] = OPC->vector[2]; + case OP_CALL1H: + G_VECTOR(OFS_PARM0)[0] = OPB->vector[0]; + G_VECTOR(OFS_PARM0)[1] = OPB->vector[1]; + G_VECTOR(OFS_PARM0)[2] = OPB->vector[2]; + + case OP_CALL8: + case OP_CALL7: + case OP_CALL6: + case OP_CALL5: + case OP_CALL4: + case OP_CALL3: + case OP_CALL2: + case OP_CALL1: + case OP_CALL0: + RUNAWAYCHECK(); + pr_xstatement = st-pr_statements; + + + if (st->op > OP_CALL8) + pr_argc = st->op - (OP_CALL1H-1); + else + pr_argc = st->op - OP_CALL0; + fnum = OPA->function; + if ((fnum & ~0xff000000)<=0) + { + pr_trace++; + printf("NULL function from qc.\n"); +#ifndef DEBUGABLE + goto cont; +#endif + break; + } + + p=pr_typecurrent; +//about to switch. needs caching. + + //if it's an external call, switch now (before any function pointers are used) + PR_MoveParms(progfuncs, (fnum & 0xff000000)>>24, p); + PR_SwitchProgs(progfuncs, (fnum & 0xff000000)>>24); + + newf = &pr_functions[fnum & ~0xff000000]; + + if (newf->first_statement < 0) + { // negative statements are built in functions + i = -newf->first_statement; +// p = pr_typecurrent; + if (i < externs->numglobalbuiltins) + { + (*externs->globalbuiltins[i]) (progfuncs, (struct globalvars_s *)current_progstate->globals); + if (prinst->continuestatement!=-1) + { + st=&pr_statements[prinst->continuestatement]; + prinst->continuestatement=-1; + break; + } + } + else + { + i -= externs->numglobalbuiltins; + if (i > current_progstate->numbuiltins) + { + if (newf->first_statement == -0x7fffffff) + ((builtin_t)newf->profile) (progfuncs, (struct globalvars_s *)current_progstate->globals); + else + PR_RunError (progfuncs, "Bad builtin call number"); + } + else + current_progstate->builtins [i] (progfuncs, (struct globalvars_s *)current_progstate->globals); + } + PR_MoveParms(progfuncs, p, pr_typecurrent); +// memcpy(&pr_progstate[p].globals[OFS_RETURN], ¤t_progstate->globals[OFS_RETURN], sizeof(vec3_t)); + PR_SwitchProgs(progfuncs, (progsnum_t)p); + +//#ifndef DEBUGABLE //decide weather non debugger wants to start debugging. + s = st-pr_statements; + goto restart; +//#endif +// break; + } +// PR_MoveParms((OPA->function & 0xff000000)>>24, pr_typecurrent); +// PR_SwitchProgs((OPA->function & 0xff000000)>>24); + s = PR_EnterFunction (progfuncs, newf, p); + st = &pr_statements[s]; + + goto restart; +// break; + + case OP_DONE: + case OP_RETURN: + + RUNAWAYCHECK(); + + pr_globals[OFS_RETURN] = pr_globals[st->a]; + pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; + pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; + + s = PR_LeaveFunction (progfuncs); + st = &pr_statements[s]; + if (pr_depth == prinst->exitdepth) + { + return; // all done + } + goto restart; +// break; + + case OP_STATE: + externs->stateop(progfuncs, OPA->_float, OPB->function); + 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 = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + OPC->_float = (float)ptr->_int; + break; + + case OP_CP_FTOI: + ptr = (eval_t *)(((qbyte *)sv_edicts) + OPA->_int); + 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: + if (OPB->_int == 0) //no division by zero allowed... + 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; + + + //array/structure reading/riting. + case OP_GLOBALADDRESS: + OPC->_int = (int)(&((int)(OPA->_int)) + OPB->_int); + break; + case OP_POINTER_ADD: //pointer to 32 bit (remember to *3 for vectors) + 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 = (eval_t *)(&((int)(OPA->_int)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADA_V: + ptr = (eval_t *)(&((int)(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: //(char*)c = (char*)a + (float)b + OPC->_int = OPA->_int + (int)OPB->_float; + break; + case OP_SUB_S: //(float)c = (char*)a - (char*)b + OPC->_int = OPA->_int - OPB->_int; + break; + case OP_LOADP_C: //load character from a string + ptr = (eval_t *)(((int)(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: +#ifdef PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int >= pr_edict_size/4) + { + Host_Error("Progs attempted to read an invalid field in an edict (%i)\n", OPB->_int); + return; + } +#endif + ptr = (eval_t *)(((int)(OPA->_int)) + OPB->_int); + OPC->_int = ptr->_int; + break; + + case OP_LOADP_V: +#ifdef PRBOUNDSCHECK + if (OPB->_int < 0 || OPB->_int + 2 >= pr_edict_size/4) + { + Host_Error("Progs attempted to read an invalid field in an edict (%i)\n", OPB->_int); + return; + } +#endif + + ptr = (eval_t *)(((int)(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_GBL_FNC: + i = (int)OPB->_float; + if(i < 0 || i > G_INT((uofs)st->a - 1)) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + i]; + OPC->_int = t->_int; + break; + case OP_FETCH_GBL_V: + i = (int)OPB->_float; + if(i < 0 || i > G_INT((uofs)st->a - 1)) + { + PR_RunError(progfuncs, "array index out of bounds: %s[%d]", PR_GlobalStringNoContents(progfuncs, st->a), i); + } + t = (eval_t *)&pr_globals[(uofs)st->a + +((int)OPB->_float)*3]; + OPC->vector[0] = t->vector[0]; + OPC->vector[1] = t->vector[1]; + OPC->vector[2] = t->vector[2]; + break; + + case OP_CSTATE: + externs->cstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_CWSTATE: + externs->cwstateop(progfuncs, OPA->_float, OPB->_float, fnum); + break; + + case OP_THINKTIME: + externs->thinktimeop(progfuncs, (struct edict_s *)PROG_TO_EDICT(OPA->edict), OPB->_float); + break; + + + case OP_BITSET: // b (+) a + OPB->_float = (float)((int)OPB->_float | (int)OPA->_float); + break; + case OP_BITSETP: // .b (+) a + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)((int)ptr->_float | (int)OPA->_float); + break; + case OP_BITCLR: // b (-) a + OPB->_float = (float)((int)OPB->_float & ~((int)OPA->_float)); + break; + case OP_BITCLRP: // .b (-) a + ptr = (eval_t *)(OPB->_int); + ptr->_float = (float)((int)ptr->_float & ~((int)OPA->_float)); + break; + + case OP_RAND0: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RAND1: + G_FLOAT(OFS_RETURN) = (rand()&0x7fff)/((float)0x7fff)*OPA->_float; + break; + case OP_RAND2: + if(OPA->_float < OPB->_float) + { + G_FLOAT(OFS_RETURN) = OPA->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPB->_float-OPA->_float)); + } + else + { + G_FLOAT(OFS_RETURN) = OPB->_float+((rand()&0x7fff)/((float)0x7fff) + *(OPA->_float-OPB->_float)); + } + break; + case OP_RANDV0: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff); + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff); + break; + case OP_RANDV1: + G_FLOAT(OFS_RETURN+0) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[0]; + G_FLOAT(OFS_RETURN+1) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[1]; + G_FLOAT(OFS_RETURN+2) = (rand()&0x7fff)/((float)0x7fff)*OPA->vector[2]; + break; + case OP_RANDV2: + for(i = 0; i < 3; i++) + { + if(OPA->vector[i] < OPB->vector[i]) + { + G_FLOAT(OFS_RETURN+i) = OPA->vector[i]+((rand()&0x7fff)/((float)0x7fff) + *(OPB->vector[i]-OPA->vector[i])); + } + else + { + G_FLOAT(OFS_RETURN+i) = OPB->vector[i]+(rand()*(1.0f/RAND_MAX) + *(OPA->vector[i]-OPB->vector[i])); + } + } + break; + + + case OP_SWITCH_F: + case OP_SWITCH_V: + case OP_SWITCH_S: + case OP_SWITCH_E: + case OP_SWITCH_FNC: + swtch = OPA; + swtchtype = st->op; + RUNAWAYCHECK(); + st += (sofs)st->b - 1; // offset the st++ + break; + case OP_CASE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float == OPA->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_E: + case OP_SWITCH_FNC: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_S: + if (swtch->_int == OPA->_int) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + if ((!swtch->_int && progfuncs->stringtable[OPA->string]) || (!OPA->_int && progfuncs->stringtable[swtch->string])) //one is null (cannot be not both). + break; + if (!strcmp(progfuncs->stringtable+swtch->string, progfuncs->stringtable+OPA->string)) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + case OP_SWITCH_V: + if (swtch->vector[0] == OPA->vector[0] && swtch->vector[1] == OPA->vector[1] && swtch->vector[2] == OPA->vector[2]) + { + RUNAWAYCHECK(); + st += (sofs)st->b-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASE with bad/missing OP_SWITCH %i", swtchtype); + break; + } + break; + case OP_CASERANGE: + switch(swtchtype) + { + case OP_SWITCH_F: + if (swtch->_float >= OPA->_float && swtch->_float <= OPB->_float) + { + RUNAWAYCHECK(); + st += (sofs)st->c-1; // -1 to offset the s++ + } + break; + default: + PR_RunError (progfuncs, "OP_CASERANGE with bad/missing OP_SWITCH %i", swtchtype); + } + break; + + default: + if (st->op & 0x8000) //break point! + { + pr_xstatement = s = st-pr_statements; + + printf("Break point hit.\n"); + if (pr_trace<1) + pr_trace=1; //this is what it's for + + s = ShowStep(progfuncs, s); + st = &pr_statements[s]; //let the user move execution + pr_xstatement = s = st-pr_statements; + + memcpy(&fakeop, st, sizeof(dstatement_t)); //don't hit the new statement as a break point, cos it's probably the same one. + fakeop.op &= ~0x8000; + st = &fakeop; //a little remapping... + + goto reeval; //reexecute + } + pr_xstatement = st-pr_statements; + PR_RunError (progfuncs, "Bad opcode %i", st->op); + } +} + + +#undef cont +#undef reeval +#undef st +#undef pr_statements +#undef fakeop +#undef dstatement_t +#undef sofs +#undef uofs diff --git a/engine/qclib/hash.c b/engine/qclib/hash.c new file mode 100644 index 000000000..deab730ca --- /dev/null +++ b/engine/qclib/hash.c @@ -0,0 +1,217 @@ +#include "qcc.h" +void Hash_InitTable(hashtable_t *table, int numbucks, void *mem) +{ + table->numbuckets = numbucks; + table->bucket = (bucket_t **)mem; +} + +int Hash_Key(char *name, int modulus) +{ //fixme: optimize. + unsigned int key; + for (key=0;*name; name++) + key += ((key<<3) + (key>>28) + *name); + + return (int)(key%modulus); +} + +void *Hash_Get(hashtable_t *table, char *name) +{ + int bucknum = Hash_Key(name, table->numbuckets); + bucket_t *buck; + + buck = table->bucket[bucknum]; + + while(buck) + { + if (!STRCMP(name, buck->keystring)) + return buck->data; + + buck = buck->next; + } + return NULL; +} +void *Hash_GetKey(hashtable_t *table, int key) +{ + int bucknum = key%table->numbuckets; + bucket_t *buck; + + buck = table->bucket[bucknum]; + + while(buck) + { + if ((int)buck->keystring == key) + return buck->data; + + buck = buck->next; + } + return NULL; +} +void *Hash_GetNext(hashtable_t *table, char *name, void *old) +{ + int bucknum = Hash_Key(name, table->numbuckets); + bucket_t *buck; + + buck = table->bucket[bucknum]; + + while(buck) + { + if (!STRCMP(name, buck->keystring)) + { + if (buck->data == old) //found the old one + break; + } + + buck = buck->next; + } + if (!buck) + return NULL; + + buck = buck->next;//don't return old + while(buck) + { + if (!STRCMP(name, buck->keystring)) + return buck->data; + + buck = buck->next; + } + return NULL; +} + +#ifndef MINIMAL +void *Hash_Add(hashtable_t *table, char *name, void *data) +{ + int bucknum = Hash_Key(name, table->numbuckets); + bucket_t *buck; + + buck = qccHunkAlloc(sizeof(bucket_t)); + + buck->data = data; + buck->keystring = name; + buck->next = table->bucket[bucknum]; + table->bucket[bucknum] = buck; + + return buck; +} +#endif +void *Hash_Add2(hashtable_t *table, char *name, void *data, bucket_t *buck) +{ + int bucknum = Hash_Key(name, table->numbuckets); + + buck->data = data; + buck->keystring = name; + buck->next = table->bucket[bucknum]; + table->bucket[bucknum] = buck; + + return buck; +} +#ifndef MINIMAL +void *Hash_AddKey(hashtable_t *table, int key, void *data) +{ + int bucknum = key%table->numbuckets; + bucket_t *buck; + + buck = qccHunkAlloc(sizeof(bucket_t)); + + buck->data = data; + (int)buck->keystring = key; + buck->next = table->bucket[bucknum]; + table->bucket[bucknum] = buck; + + return buck; +} +#endif +void *Hash_AddKey2(hashtable_t *table, int key, void *data, bucket_t *buck) +{ + int bucknum = key%table->numbuckets; + + buck->data = data; + (int)buck->keystring = key; + buck->next = table->bucket[bucknum]; + table->bucket[bucknum] = buck; + + return buck; +} + +void Hash_Remove(hashtable_t *table, char *name) +{ + int bucknum = Hash_Key(name, table->numbuckets); + bucket_t *buck; + + buck = table->bucket[bucknum]; + + if (!STRCMP(name, buck->keystring)) + { + table->bucket[bucknum] = buck->next; + return; + } + + + while(buck->next) + { + if (!STRCMP(name, buck->next->keystring)) + { + buck->next = buck->next->next; + return; + } + + buck = buck->next; + } + return; +} + +void Hash_RemoveData(hashtable_t *table, char *name, void *data) +{ + int bucknum = Hash_Key(name, table->numbuckets); + bucket_t *buck; + + buck = table->bucket[bucknum]; + + if (buck->data == data) + if (!STRCMP(name, buck->keystring)) + { + table->bucket[bucknum] = buck->next; + return; + } + + + while(buck->next) + { + if (buck->next->data == data) + if (!STRCMP(name, buck->next->keystring)) + { + buck->next = buck->next->next; + return; + } + + buck = buck->next; + } + return; +} + + +void Hash_RemoveKey(hashtable_t *table, int key) +{ + int bucknum = key%table->numbuckets; + bucket_t *buck; + + buck = table->bucket[bucknum]; + + if ((int)buck->keystring == key) + { + table->bucket[bucknum] = buck->next; + return; + } + + + while(buck->next) + { + if ((int)buck->next->keystring == key) + { + buck->next = buck->next->next; + return; + } + + buck = buck->next; + } + return; +} diff --git a/engine/qclib/hash.h b/engine/qclib/hash.h new file mode 100644 index 000000000..d5d8fb125 --- /dev/null +++ b/engine/qclib/hash.h @@ -0,0 +1,27 @@ +//============================= +//David's hash tables +//string based. + +#define Hash_BytesForBuckets(b) (sizeof(bucket_t)*b) + +#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc +typedef struct bucket_s { + void *data; + char *keystring; + struct bucket_s *next; +} bucket_t; +typedef struct hashtable_s { + int numbuckets; + bucket_t **bucket; +} hashtable_t; + +void Hash_InitTable(hashtable_t *table, int numbucks, void *mem); //mem must be 0 filled. (memset(mem, 0, size)) +int Hash_Key(char *name, int modulus); +void *Hash_Get(hashtable_t *table, char *name); +void *Hash_GetKey(hashtable_t *table, int key); +void *Hash_GetNext(hashtable_t *table, char *name, void *old); +void *Hash_Add(hashtable_t *table, char *name, void *data); +void *Hash_Add2(hashtable_t *table, char *name, void *data, bucket_t *buck); +void *Hash_AddKey(hashtable_t *table, int key, void *data); +void Hash_Remove(hashtable_t *table, char *name); +void Hash_RemoveData(hashtable_t *table, char *name, void *data); diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c new file mode 100644 index 000000000..70e95f70b --- /dev/null +++ b/engine/qclib/initlib.c @@ -0,0 +1,564 @@ +#define PROGSUSED +#include "progsint.h" +#include + +typedef struct prmemb_s { + struct prmemb_s *prev; + int level; +} prmemb_t; +void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount) +{ + if (!progshunk) + { + prmemb_t *mem; + ammount = sizeof(prmemb_t)+((ammount + 3)&~3); + mem = memalloc(ammount); + memset(mem, 0, ammount); + mem->prev = memb; + if (!memb) + mem->level = 1; + else + mem->level = ((prmemb_t *)memb)->level+1; + memb = mem; + + return ((char *)mem)+sizeof(prmemb_t); + } + hunkused+=ammount; + if (hunkused > hunksize) + Sys_Error("QCLIB: Out of hunk"); + + memset(progshunk + hunkused-ammount, 0, ammount); + return progshunk + hunkused-ammount; +} + +int PRHunkMark(progfuncs_t *progfuncs) +{ + if (!progshunk) + { + return ((prmemb_t *)memb)->level; + } + return hunkused; +} +void PRHunkFree(progfuncs_t *progfuncs, int mark) +{ + if (!progshunk) + { + prmemb_t *omem; + while(memb) + { + if (memb->level <= mark) + return; + + omem = memb; + memb = memb->prev; + memfree(omem); + } + return; + } + hunkused = mark; +} + +int PR_InitEnts(progfuncs_t *progfuncs, int max_ents) +{ + maxedicts = max_ents; + + sv_num_edicts = 0; + + pr_edict_size += externs->edictsize; + + pr_max_edict_size = pr_edict_size; + +#ifdef DYNAMIC_ENTS + prinst->edicttable = PRHunkAlloc(progfuncs, maxedicts*sizeof(struct edicts_s *)); + sv_edicts = PRHunkAlloc(progfuncs, pr_edict_size); + prinst->edicttable[0] = sv_edicts; + sv_num_edicts = 1; +#else + sv_edicts = PRHunkAlloc(progfuncs, (pr_edict_size) * maxedicts); + + {int a; + for (a = 1; a < maxedicts; a++) + { + ((edictrun_t*)EDICT_NUM(progfuncs, a))->isfree = true; + ((edictrun_t*)EDICT_NUM(progfuncs, a))->freetime = 0; + } + } +#endif + + + sv_num_edicts = 1; + + return pr_edict_size; +} +char tempedicts[2048]; //used as a safty buffer +void PR_Configure (progfuncs_t *progfuncs, void *mem, int mem_size, int max_progs) //can be used to wipe all memory +{ +#ifdef DYNAMIC_ENTS + int i; + edictrun_t *e; +#endif + +// int a; + pr_max_edict_size=0; + pr_edict_size = 0; + progfuncs->stringtable = 0; + QC_StartShares(progfuncs); + QC_InitShares(progfuncs); + +#ifdef DYNAMIC_ENTS + for ( i=1 ; iedicttable[i]; + prinst->edicttable[i] = NULL; +// e->entnum = i; + if (e) + memfree(e); + } +#endif + + PRHunkFree(progfuncs, 0); //clear mem - our hunk may not be a real hunk. + +//three conditions. +//mem + size uses new hunk space +//size>=0 uses previous hunk space +//size < 0 uses memalloc for mem, then 'emulates' a hunk. + if (mem == NULL) + { + if (mem_size < 0) + { + progshunk = NULL; + hunksize = 0; + } + } + else + { + progshunk = mem; + hunksize = mem_size; + } + hunkused = 0; + + pr_progstate = PRHunkAlloc(progfuncs, sizeof(progstate_t) * max_progs); + +/* for(a = 0; a < max_progs; a++) + { + pr_progstate[a].progs = NULL; + } +*/ + + maxprogs = max_progs; + pr_typecurrent=-1; + + prinst->reorganisefields = false; + + maxedicts = 1; + sv_num_edicts = 1; //set up a safty buffer so things won't go horribly wrong too often + sv_edicts=(struct edict_s *)tempedicts; +} + + + +struct globalvars_s *PR_globals (progfuncs_t *progfuncs, progsnum_t pnum) +{ + if (pnum < 0) + return (struct globalvars_s *)current_progstate->globals; + return (struct globalvars_s *)pr_progstate[pnum].globals; +} + +struct entvars_s *PR_entvars (progfuncs_t *progfuncs, struct edict_s *ed) +{ + if (((edictrun_t *)ed)->isfree) + return NULL; + + return (struct entvars_s *)edvars(ed); +} + +func_t PR_FindFunc(progfuncs_t *progfuncs, char *funcname, progsnum_t pnum) +{ + + dfunction_t *f=NULL; + if (pnum == -2) + { + for (pnum = 0; pnum < maxprogs; pnum ++) + { + if (!pr_progstate[pnum].progs) + continue; + f = ED_FindFunction(progfuncs, funcname, &pnum, pnum); + if (f) + break; + } + } + else + f = ED_FindFunction(progfuncs, funcname, &pnum, pnum); + if (!f) + return 0; + + { + ddef16_t *var16; + ddef32_t *var32; + switch(pr_progstate[pnum].intsize) + { + case 24: + case 16: + var16 = ED_FindTypeGlobalFromProgs16(progfuncs, funcname, pnum, ev_function); //we must make sure we actually have a function def - 'light' is defined as a field before it is defined as a function. + if (!var16) + return (f - pr_progstate[pnum].functions) | (pnum << 24); + return *(int *)&pr_progstate[pnum].globals[var16->ofs]; + case 32: + var32 = ED_FindTypeGlobalFromProgs32(progfuncs, funcname, pnum, ev_function); //we must make sure we actually have a function def - 'light' is defined as a field before it is defined as a function. + if (!var32) + return (f - pr_progstate[pnum].functions) | (pnum << 24);; + return *(int *)&pr_progstate[pnum].globals[var32->ofs]; + } + Sys_Error("Error with def size (PR_FindFunc)"); + } + return 0; +} + +eval_t *PR_FindGlobal(progfuncs_t *progfuncs, char *globname, progsnum_t pnum) +{ + ddef16_t *var16; + ddef32_t *var32; + switch(pr_progstate[pnum].intsize) + { + case 16: + case 24: + if (!(var16 = ED_FindGlobalFromProgs16(progfuncs, globname, pnum))) + return NULL; + + return (eval_t *)&pr_progstate[pnum].globals[var16->ofs]; + case 32: + if (!(var32 = ED_FindGlobalFromProgs32(progfuncs, globname, pnum))) + return NULL; + + return (eval_t *)&pr_progstate[pnum].globals[var32->ofs]; + } + Sys_Error("Error with def size (PR_FindGlobal)"); + return NULL; +} + +void SetGlobalEdict(progfuncs_t *progfuncs, struct edict_s *ed, int ofs) +{ + ((int*)pr_globals)[ofs] = EDICT_TO_PROG(ed); +} + +char *PR_VarString (progfuncs_t *progfuncs, int first) +{ + int i; + static char out[1024]; + char *s; + + out[0] = 0; + for (i=first ; i= sizeof(out)) + Sys_Error("VarString (builtin call ending with strings) exceeded maximum string length of %i chars", sizeof(out)); +//#endif + } + } + return out; +} + +eval_t *GetEdictFieldValue(progfuncs_t *progfuncs, struct edict_s *ed, char *name, evalc_t *cache) +{ + fdef_t *var; + if (!cache) + { + var = ED_FindField(progfuncs, name); + if (!var) + return NULL; + return (eval_t *)&( + ((int*)((char *)ed + externs->edictsize)) + [var->ofs]); + } + if (!cache->varname) + { + cache->varname = name; + var = ED_FindField(progfuncs, name); + if (!var) + { + cache->ofs32 = NULL; + return NULL; + } + cache->ofs32 = var; + return (eval_t *) + &((int*)((char *)ed + externs->edictsize)) + [var->ofs]; + } + if (cache->ofs32 == NULL) + return NULL; + return (eval_t *) + &((int*)((char *)ed + externs->edictsize)) + [cache->ofs32->ofs]; +} + +struct edict_s *ProgsToEdict (progfuncs_t *progfuncs, int progs) +{ + return (struct edict_s *)PROG_TO_EDICT(progs); +} +int EdictToProgs (progfuncs_t *progfuncs, struct edict_s *ed) +{ + return EDICT_TO_PROG(ed); +} + + +struct qcthread_s *PR_ForkStack (progfuncs_t *progfuncs); +void PR_ResumeThread (progfuncs_t *progfuncs, struct qcthread_s *thread); +void PR_AbortStack (progfuncs_t *progfuncs); + + +void RegisterBuiltin(progfuncs_t *progfncs, char *name, builtin_t func); + +progfuncs_t deffuncs = { + PROGSTRUCT_VERSION, + PR_Configure, + PR_LoadProgs, + PR_InitEnts, + PR_ExecuteProgram, + PR_SwitchProgs, + PR_globals, + PR_entvars, + PR_RunError, + ED_Print, + ED_Alloc, + ED_Free, + + EDICT_NUM, + NUM_FOR_EDICT, + + + SetGlobalEdict, + + PR_VarString, + + NULL, + PR_FindFunc, +#ifdef MINIMAL + NULL, + NULL, +#else + Comp_Begin, + Comp_Continue, +#endif + + filefromprogs, + filefromnewprogs, + + SaveEnts, + LoadEnts, + + SaveEnt, + RestoreEnt, + + PR_FindGlobal, + ED_NewString, + (void*)PRHunkAlloc, + + GetEdictFieldValue, + ProgsToEdict, + EdictToProgs, + + EvaluateDebugString, + + NULL, + PR_StackTrace, + + PR_ToggleBreakpoint, + 0, + NULL, +#ifdef MINIMAL + NULL, +#else + Decompile, +#endif + NULL, + NULL, + RegisterBuiltin, + + 0, + 0, + + PR_ForkStack, + PR_ResumeThread, + PR_AbortStack +}; +#undef printf + +//defs incase following structure is not passed. +struct edict_s *safesv_edicts; +int safesv_num_edicts; +double safetime=0; + +progexterns_t defexterns = { + PROGSTRUCT_VERSION, + + NULL, //char *(*ReadFile) (char *fname, void *buffer, int len); + NULL, //int (*FileSize) (char *fname); //-1 if file does not exist + NULL, //bool (*WriteFile) (char *name, void *data, int len); + printf, //void (*printf) (char *, ...); + (void*)exit, //void (*Sys_Error) (char *, ...); + NULL, //void (*Abort) (char *, ...); + sizeof(edictrun_t), //int edictsize; //size of edict_t + + NULL, //void (*entspawn) (struct edict_s *ent); //ent has been spawned, but may not have all the extra variables (that may need to be set) set + NULL, //bool (*entcanfree) (struct edict_s *ent); //return true to stop ent from being freed + NULL, //void (*stateop) (float var, func_t func); + NULL, + NULL, + NULL, + + //used when loading a game + NULL, //builtin_t *(*builtinsfor) (int num); //must return a pointer to the builtins that were used before the state was saved. + NULL, //void (*loadcompleate) (int edictsize); //notification to reset any pointers. + + (void*)malloc, //void *(*memalloc) (int size); //small string allocation malloced and freed randomly by the executor. (use memalloc if you want) + free, //void (*memfree) (void * mem); + + + NULL, //builtin_t *globalbuiltins; //these are available to all progs + 0, //int numglobalbuiltins; + + PR_COMPILENEXIST, + + &safetime, //double *gametime; + + &safesv_edicts, //struct edict_s **sv_edicts; + &safesv_num_edicts, //int *sv_num_edicts; + + NULL, //int (*useeditor) (char *filename, int line, int nump, char **parms); +}; + +//progfuncs_t *progfuncs = NULL; +#undef memfree +#undef prinst +#undef extensionbuiltin +#undef field +#undef shares +#undef sv_num_edicts + + +#ifdef QCLIBDLL_EXPORTS +__declspec(dllexport) +#endif +void CloseProgs(progfuncs_t *inst) +{ + extensionbuiltin_t *eb; + void (VARGS *f) (void *); + +#ifdef DYNAMIC_ENTS + int i; + edictrun_t *e; +#endif + + f = inst->parms->memfree; + +#ifdef DYNAMIC_ENTS + for ( i=1 ; imaxedicts; i++) + { + (struct edict_s *)e = inst->prinst->edicttable[i]; + inst->prinst->edicttable[i] = NULL; + if (e) + { +// e->entnum = i; + f(e); + } + } +#endif + + PRHunkFree(inst, 0); + while(inst->prinst->extensionbuiltin) + { + eb = inst->prinst->extensionbuiltin->prev; + f(inst->prinst->extensionbuiltin); + inst->prinst->extensionbuiltin = eb; + } + + if (inst->prinst->field) + f(inst->prinst->field); + if (inst->prinst->shares) + f(inst->prinst->shares); //free memory + f(inst->prinst); + f(inst); +} + +void RegisterBuiltin(progfuncs_t *progfuncs, char *name, builtin_t func) +{ + extensionbuiltin_t *eb; + eb = memalloc(sizeof(extensionbuiltin_t)); + eb->prev = progfuncs->prinst->extensionbuiltin; + progfuncs->prinst->extensionbuiltin = eb; + eb->name = name; + eb->func = func; +} + +#ifndef WIN32 +#define QCLIBINT //don't use dllspecifications +#endif + +#if defined(QCLIBDLL_EXPORTS) +__declspec(dllexport) +#endif +progfuncs_t * InitProgs(progexterns_t *ext) +{ + progfuncs_t *funcs; + + if (!ext) + ext = &defexterns; + else + { + int i; + if (ext->progsversion > PROGSTRUCT_VERSION) + return NULL; + + for (i=0;imemalloc(sizeof(progfuncs_t)); + memcpy(funcs, &deffuncs, sizeof(progfuncs_t)); + + funcs->prinst = ext->memalloc(sizeof(prinst_t)); + memset(funcs->prinst,0, sizeof(prinst_t)); + + funcs->pr_trace = &funcs->prinst->pr_trace; + funcs->progstate = &funcs->pr_progstate; + funcs->callargc = &funcs->pr_argc; + + funcs->parms = ext; + + return funcs; +} + + + + + + + + + + + + + + + + +#ifdef QCC +void main (int argc, char **argv) +{ + progexterns_t ext; + + progfuncs_t *funcs; + funcs = InitProgs(&ext); + if (funcs->PR_StartCompile(argc, argv)) + while(funcs->PR_ContinueCompile()); +} +#endif diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c new file mode 100644 index 000000000..398f525ff --- /dev/null +++ b/engine/qclib/pr_edict.c @@ -0,0 +1,2803 @@ + + +#define PROGSUSED +struct edict_s; +#include "progsint.h" +#include "crc.h" + +/*int maxedicts; + +evalc_t spawnflagscache; +*/ + +#ifdef _WIN32 +//this is windows all files are written with this endian standard. we do this to try to get a little more speed. +#define NOENDIAN +#endif + + +vec3_t vec3_origin; + +//edictrun_t *sv_edicts; +//int sv_num_edicts; + +//int pr_edict_size; // in bytes +//int pr_max_edict_size; + +//unsigned short pr_crc; + +int type_size[9] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4, 1}; + +fdef_t *ED_FieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs); +pbool ED_ParseEpair (progfuncs_t *progfuncs, void *base, ddefXX_t *key, char *s, int bits); + + + +/* +#define MAX_FIELD_LEN 64 +#define GEFV_CACHESIZE 5 + +typedef struct { + ddef_t *pcache; + char field[MAX_FIELD_LEN]; +} gefv_cache; + +static gefv_cache gefvCache[GEFV_CACHESIZE] = {{NULL, ""}, {NULL, ""}}; +*/ + +/* +================= +ED_ClearEdict + +Sets everything to NULL +================= +*/ +void ED_ClearEdict (progfuncs_t *progfuncs, edictrun_t *e) +{ + int num = e->entnum; + memset (edvars(e), 0, pr_edict_size-externs->edictsize); + e->isfree = false; + e->entnum = num; +} + +/* +================= +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. +================= +*/ +struct edict_s *ED_Alloc (progfuncs_t *progfuncs) +{ + int i; + edictrun_t *e; + + static int lastmax; + + if (lastmax + 2 < sv_num_edicts) + lastmax = lastmax; + lastmax = sv_num_edicts; + + for ( i=0 ; iisfree && ( e->freetime < 2 || *externs->gametime - e->freetime > 0.5 ) )) + { + if (!e) + { + if (e) + memfree(e); +#ifdef DYNAMIC_ENTS + prinst->edicttable[i] = (struct edict_s *)e = (void*)memalloc(pr_edict_size); + e->entnum = i; +#endif + } + + ED_ClearEdict (progfuncs, e); + + if (externs->entspawn) + externs->entspawn((struct edict_s *) e); + return (struct edict_s *)e; + } + } + + if (i >= maxedicts-1) //try again, but use timed out ents. + { + for ( i=0 ; iisfree)) + { + if (!e) + { + if (e) + memfree(e); + #ifdef DYNAMIC_ENTS + prinst->edicttable[i] = (struct edict_s *)e = (void*)memalloc(pr_edict_size); + e->entnum = i; + #endif + } + + ED_ClearEdict (progfuncs, e); + + if (externs->entspawn) + externs->entspawn((struct edict_s *) e); + return (struct edict_s *)e; + } + } + + if (i >= maxedicts-1) + Sys_Error ("ED_Alloc: no free edicts"); + } + + sv_num_edicts++; + e = (edictrun_t*)EDICT_NUM(progfuncs, i); +#ifdef DYNAMIC_ENTS + if (!e) + { + prinst->edicttable[i] = (struct edict_s *)e = (void*)memalloc(pr_edict_size); + e->entnum = i; + } +#endif + ED_ClearEdict (progfuncs, e); + + if (externs->entspawn) + externs->entspawn((struct edict_s *) e); + return (struct edict_s *)e; +} + +/* +================= +ED_Free + +Marks the edict as free +FIXME: walk all entities and NULL out references to this entity +================= +*/ +void ED_Free (progfuncs_t *progfuncs, struct edict_s *ed) +{ + edictrun_t *e = (edictrun_t *)ed; +// SV_UnlinkEdict (ed); // unlink from world bsp + + if (e->isfree) //this happens on start.bsp where an onlyregistered trigger killtargets itself (when all of this sort die after 1 trigger anyway). + { + printf("Tried to free free entity\n"); +// if (developer.value == 1) +// pr_trace = true; + return; + } + + if (externs->entcanfree) + if (!externs->entcanfree(ed)) //can stop an ent from being freed. + return; + + e->isfree = true; + e->freetime = (float)*externs->gametime; + +/* + ed->v.model = 0; + ed->v.takedamage = 0; + ed->v.modelindex = 0; + ed->v.colormap = 0; + ed->v.skin = 0; + ed->v.frame = 0; + VectorCopy (vec3_origin, ed->v.origin); + VectorCopy (vec3_origin, ed->v.angles); + ed->v.nextthink = -1; + ed->v.solid = 0; +*/ +} + +//=========================================================================== + +/* +============ +ED_GlobalAtOfs +============ +*/ +ddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs) +{ + ddef16_t *def; + unsigned int i; + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs16[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} +ddef32_t *ED_GlobalAtOfs32 (progfuncs_t *progfuncs, unsigned int ofs) +{ + ddef32_t *def; + unsigned int i; + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs32[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* +============ +ED_FieldAtOfs +============ +*/ +fdef_t *ED_FieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs) +{ +// ddef_t *def; + unsigned int i; + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs16[i]; + if (!strcmp(def->s_name,name) ) + return def; + } + return NULL; +} +ddef32_t *ED_FindGlobal32 (progfuncs_t *progfuncs, char *name) +{ + ddef32_t *def; + unsigned int i; + + for (i=1 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs32[i]; + if (!strcmp(def->s_name,name) ) + return def; + } + return NULL; +} + +unsigned int ED_FindGlobalOfs (progfuncs_t *progfuncs, char *name) +{ + ddef16_t *d16; + ddef32_t *d32; + switch(current_progstate->intsize) + { + case 24: + case 16: + d16 = ED_FindGlobal16(progfuncs, name); + return d16?d16->ofs:0; + case 32: + d32 = ED_FindGlobal32(progfuncs, name); + return d32?d32->ofs:0; + } + Sys_Error("ED_FindGlobalOfs - bad intsize"); + return 0; +} + +ddef16_t *ED_FindGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum) +{ + ddef16_t *def; + unsigned int i; + + for (i=1 ; inumglobaldefs ; i++) + { + def = &pr_progstate[prnum].globaldefs16[i]; + if (!strcmp(def->s_name,name) ) + return def; + } + return NULL; +} +ddef32_t *ED_FindGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum) +{ + ddef32_t *def; + unsigned int i; + + for (i=1 ; inumglobaldefs ; i++) + { + def = &pr_progstate[prnum].globaldefs32[i]; + if (!strcmp(def->s_name,name) ) + return def; + } + return NULL; +} + +ddef16_t *ED_FindTypeGlobalFromProgs16 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type) +{ + ddef16_t *def; + unsigned int i; + + for (i=1 ; inumglobaldefs ; i++) + { + def = &pr_progstate[prnum].globaldefs16[i]; + if (!strcmp(def->s_name,name) ) + { + if (pr_progstate[prnum].types) + { + if (pr_progstate[prnum].types[def->type&~DEF_SAVEGLOBAL].type != type) + continue; + } + else if ((def->type&(~DEF_SAVEGLOBAL)) != type) + continue; + return def; + } + } + return NULL; +} + + +ddef32_t *ED_FindTypeGlobalFromProgs32 (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type) +{ + ddef32_t *def; + unsigned int i; + + for (i=1 ; inumglobaldefs ; i++) + { + def = &pr_progstate[prnum].globaldefs32[i]; + if (!strcmp(def->s_name,name) ) + { + if (pr_progstate[prnum].types) + { + if (pr_progstate[prnum].types[def->type&~DEF_SAVEGLOBAL].type != type) + continue; + } + else if ((def->type&(~DEF_SAVEGLOBAL)) != (unsigned)type) + continue; + return def; + } + } + return NULL; +} + +unsigned int *ED_FindGlobalOfsFromProgs (progfuncs_t *progfuncs, char *name, progsnum_t prnum, int type) +{ + ddef16_t *def16; + ddef32_t *def32; + static unsigned int pos; + switch(pr_progstate[prnum].intsize) + { + case 16: + case 24: + def16 = ED_FindTypeGlobalFromProgs16(progfuncs, name, prnum, type); + if (!def16) + return NULL; + pos = def16->ofs; + return &pos; + case 32: + def32 = ED_FindTypeGlobalFromProgs32(progfuncs, name, prnum, type); + if (!def32) + return NULL; + return &def32->ofs; + } + Sys_Error("ED_FindGlobalOfsFromProgs - bad intsize"); + return 0; +} + +/* +============ +ED_FindFunction +============ +*/ +dfunction_t *ED_FindFunction (progfuncs_t *progfuncs, char *name, progsnum_t *prnum, int fromprogs) +{ + dfunction_t *func; + unsigned int i; + char *sep; + + int pnum; + + if (prnum) + { + sep = strchr(name, ':'); + if (sep) + { + pnum = atoi(name); + name = sep+1; + } + else + { + if (fromprogs>=0) + pnum = fromprogs; + else + pnum = pr_typecurrent; + } + *prnum = pnum; + } + else + pnum = pr_typecurrent; + + if (!pr_progstate[pnum].progs) + return NULL; + + for (i=1 ; inumfunctions ; i++) + { + func = &pr_progstate[pnum].functions[i]; + if (!strcmp(func->s_name,name) ) + return func; + } + return NULL; +} + +/* +============ +PR_ValueString + +Returns a string describing *data in a type specific manner +============= +*/ +char *PR_ValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val) +{ + static char line[256]; + fdef_t *fielddef; + dfunction_t *f; + +#ifdef DEF_SAVEGLOBAL + type &= ~DEF_SAVEGLOBAL; +#endif + + if (pr_types) + type = pr_types[type].type; + + switch (type) + { + case ev_struct: + sprintf (line, "struct"); + break; + case ev_union: + sprintf (line, "union"); + break; + case ev_string: + sprintf (line, "%s", val->string+progfuncs->stringtable); + break; + case ev_entity: + sprintf (line, "entity %i", NUM_FOR_EDICT(progfuncs, (struct edict_s *)PROG_TO_EDICT(val->edict)) ); + break; + case ev_function: + if (!val->function) + sprintf (line, "NULL function"); + else + { + if ((val->function & 0xff000000)>>24 >= (unsigned)maxprogs || !pr_progstate[(val->function & 0xff000000)>>24].functions) + sprintf (line, "Bad function"); + else + { + f = pr_progstate[(val->function & 0xff000000)>>24].functions + (val->function & ~0xff000000); + sprintf (line, "%i:%s()", (val->function & 0xff000000)>>24, f->s_name); + } + } + break; + case ev_field: + fielddef = ED_FieldAtOfs (progfuncs, val->_int ); + sprintf (line, ".%s (%i)", fielddef->s_name, val->_int); + break; + case ev_void: + sprintf (line, "void type"); + break; + case ev_float: + sprintf (line, "%5.1f", val->_float); + break; + case ev_integer: + sprintf (line, "%i", val->_int); + break; + case ev_vector: + sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]); + break; + case ev_pointer: + sprintf (line, "pointer"); + { + int entnum; + int valofs; + if (val->edict == 0) + { + sprintf (line, "NULL pointer"); + break; + } + entnum = ((qbyte *)val->edict - (qbyte *)sv_edicts) / pr_edict_size; + valofs = (int *)val->edict - (int *)edvars(EDICT_NUM(progfuncs, entnum)); + fielddef = ED_FieldAtOfs (progfuncs, valofs ); + if (!fielddef) + sprintf(line, "ent%i.%s", entnum, "UNKNOWN"); + else + sprintf(line, "ent%i.%s", entnum, fielddef->s_name); + } + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +/* +============ +PR_UglyValueString + +Returns a string describing *data in a type specific manner +Easier to parse than PR_ValueString +============= +*/ +char *PR_UglyValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val) +{ + static char line[256]; + fdef_t *fielddef; + dfunction_t *f; + int i; + +#ifdef DEF_SAVEGLOBAL + type &= ~DEF_SAVEGLOBAL; +#endif + + if (pr_types) + type = pr_types[type].type; + + switch (type) + { + case ev_struct: + sprintf (line, "structures cannot yet be saved"); + break; + case ev_union: + sprintf (line, "unions cannot yet be saved"); + break; + case ev_string: + sprintf (line, "%s", val->string+progfuncs->stringtable); + break; + case ev_entity: + sprintf (line, "%i", NUM_FOR_EDICT(progfuncs, (struct edict_s *)PROG_TO_EDICT(val->edict))); + break; + case ev_function: + i = (val->function & 0xff000000)>>24; + if (i > maxprogs) + sprintf (line, "BAD FUNCTION INDEX: %i", val->function); + else + { + f = pr_progstate[(val->function & 0xff000000)>>24].functions + (val->function & ~0xff000000); + sprintf (line, "%i:%s", (val->function & 0xff000000)>>24, f->s_name); + } + break; + case ev_field: + fielddef = ED_FieldAtOfs (progfuncs, val->_int ); + sprintf (line, "%s", fielddef->s_name); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + if (val->_float == (int)val->_float) + sprintf (line, "%i", (int)val->_float); //an attempt to cut down on the number of .000000 vars.. + else + sprintf (line, "%f", val->_float); + break; + case ev_integer: + sprintf (line, "%i", val->_int); + break; + case ev_vector: + if (val->vector[0] == (int)val->vector[0] && val->vector[1] == (int)val->vector[1] && val->vector[2] == (int)val->vector[2]) + sprintf (line, "%i %i %i", (int)val->vector[0], (int)val->vector[1], (int)val->vector[2]); + else + sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); + break; + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +//compatable with Q1 (for savegames) +char *PR_UglyOldValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val) +{ + static char line[256]; + fdef_t *fielddef; + dfunction_t *f; + +#ifdef DEF_SAVEGLOBAL + type &= ~DEF_SAVEGLOBAL; +#endif + + if (pr_types) + type = pr_types[type].type; + + switch (type) + { + case ev_struct: + sprintf (line, "structures cannot yet be saved"); + break; + case ev_union: + sprintf (line, "unions cannot yet be saved"); + break; + case ev_string: + sprintf (line, "%s", val->string+progfuncs->stringtable); + break; + case ev_entity: + sprintf (line, "%i", NUM_FOR_EDICT(progfuncs, (struct edict_s *)PROG_TO_EDICT(val->edict))); + break; + case ev_function: + f = pr_progstate[(val->function & 0xff000000)>>24].functions + (val->function & ~0xff000000); + sprintf (line, "%s", f->s_name); + break; + case ev_field: + fielddef = ED_FieldAtOfs (progfuncs, val->_int ); + sprintf (line, "%s", fielddef->s_name); + break; + case ev_void: + sprintf (line, "void"); + break; + case ev_float: + if (val->_float == (int)val->_float) + sprintf (line, "%i", (int)val->_float); //an attempt to cut down on the number of .000000 vars.. + else + sprintf (line, "%f", val->_float); + break; + case ev_integer: + sprintf (line, "%i", val->_int); + break; + case ev_vector: + if (val->vector[0] == (int)val->vector[0] && val->vector[1] == (int)val->vector[1] && val->vector[2] == (int)val->vector[2]) + sprintf (line, "%i %i %i", (int)val->vector[0], (int)val->vector[1], (int)val->vector[2]); + else + sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); + break; + break; + default: + sprintf (line, "bad type %i", type); + break; + } + + return line; +} + +char *PR_TypeString(progfuncs_t *progfuncs, etype_t type) +{ +#ifdef DEF_SAVEGLOBAL + type &= ~DEF_SAVEGLOBAL; +#endif + + if (pr_types) + type = pr_types[type].type; + + switch (type) + { + case ev_struct: + return "struct"; + case ev_union: + return "union"; + case ev_string: + return "string"; + case ev_entity: + return "entity"; + case ev_function: + return "function"; + case ev_field: + return "field"; + case ev_void: + return "void"; + case ev_float: + return "float"; + case ev_vector: + return "vector"; + case ev_integer: + return "integer"; + default: + return "BAD TYPE"; + } +} + +/* +============ +PR_GlobalString + +Returns a string with a description and the contents of a global, +padded to 20 field width +============ +*/ +char *PR_GlobalString (progfuncs_t *progfuncs, int ofs) +{ + char *s; + int i; + ddef16_t *def16; + ddef32_t *def32; + void *val; + static char line[128]; + + switch (current_progstate->intsize) + { + case 16: + case 24: + val = (void *)&pr_globals[ofs]; + def16 = ED_GlobalAtOfs16(progfuncs, ofs); + if (!def16) + sprintf (line,"%i(?""?""?)", ofs); + else + { + s = PR_ValueString (progfuncs, def16->type, val); + sprintf (line,"%i(%s)%s", ofs, def16->s_name, s); + } + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + return line; + case 32: + val = (void *)&pr_globals[ofs]; + def32 = ED_GlobalAtOfs32(progfuncs, ofs); + if (!def32) + sprintf (line,"%i(?""?""?)", ofs); + else + { + s = PR_ValueString (progfuncs, def32->type, val); + sprintf (line,"%i(%s)%s", ofs, def32->s_name, s); + } + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + return line; + } + Sys_Error("Bad offset size in PR_GlobalString"); + return ""; +} + +char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs) +{ + int i; + ddef16_t *def16; + ddef32_t *def32; + static char line[128]; + + switch (current_progstate->intsize) + { + case 16: + case 24: + def16 = ED_GlobalAtOfs16(progfuncs, ofs); + if (!def16) + sprintf (line,"%i(?""?""?)", ofs); + else + sprintf (line,"%i(%s)", ofs, def16->s_name); + break; + case 32: + def32 = ED_GlobalAtOfs32(progfuncs, ofs); + if (!def32) + sprintf (line,"%i(?""?""?)", ofs); + else + sprintf (line,"%i(%s)", ofs, def32->s_name); + break; + default: + Sys_Error("Bad offset size in PR_GlobalStringNoContents"); + } + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +} + + +/* +============= +ED_Print + +For debugging +============= +*/ +void ED_Print (progfuncs_t *progfuncs, struct edict_s *ed) +{ + int l; + fdef_t *d; + int *v; + unsigned int i;int j; + char *name; + int type; + + if (((edictrun_t *)ed)->isfree) + { + printf ("FREE\n"); + return; + } + + printf("\nEDICT %i:\n", NUM_FOR_EDICT(progfuncs, (struct edict_s *)ed)); + for (i=1 ; is_name; + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars + + v = (int *)((char *)edvars(ed) + d->ofs*4); + + // if the value is still all 0, skip the field +#ifdef DEF_SAVEGLOBAL + type = d->type & ~DEF_SAVEGLOBAL; +#else + type = d->type; +#endif + + for (j=0 ; jtype, (eval_t *)v)); + } +} + +void ED_PrintNum (progfuncs_t *progfuncs, int ent) +{ + ED_Print (progfuncs, EDICT_NUM(progfuncs, ent)); +} + +/* +============= +ED_PrintEdicts + +For debugging, prints all the entities in the current server +============= +*/ +void ED_PrintEdicts (progfuncs_t *progfuncs) +{ + int i; + + printf ("%i entities\n", sv_num_edicts); + for (i=0 ; iisfree) + continue; + active++; +// if (ent->v.solid) +// solid++; +// if (ent->v.model) +// models++; +// if (ent->v.movetype == MOVETYPE_STEP) +// step++; + } + + printf ("num_edicts:%3i\n", sv_num_edicts); + printf ("active :%3i\n", active); +// Con_Printf ("view :%3i\n", models); +// Con_Printf ("touch :%3i\n", solid); +// Con_Printf ("step :%3i\n", step); + +} + + + +//============================================================================ + + +/* +============= +ED_NewString +============= +*/ +char *ED_NewString (progfuncs_t *progfuncs, char *string) +{ + char *new, *new_p; + int i,l; + + l = strlen(string) + 1; + new = PRHunkAlloc (progfuncs, 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; +} + + +/* +============= +ED_ParseEval + +Can parse either fields or globals +returns false if error +============= +*/ +pbool ED_ParseEpair (progfuncs_t *progfuncs, void *base, ddefXX_t *key, char *s, int bits) +{ + int i; + char string[128]; + fdef_t *def; + char *v, *w; + void *d; + dfunction_t *func; + + int type; + + switch(bits) + { + case 16: + d = (void *)((int *)base + ((ddef16_t*)key)->ofs); + + if (pr_types) + type = pr_types[((ddef16_t*)key)->type & ~DEF_SAVEGLOBAL].type; + else + type = ((ddef16_t*)key)->type & ~DEF_SAVEGLOBAL; + break; + case 32: + d = (void *)((int *)base + ((ddef32_t*)key)->ofs); + + if (pr_types) + type = pr_types[((ddef32_t*)key)->type & ~DEF_SAVEGLOBAL].type; + else + type = ((ddef32_t*)key)->type & ~DEF_SAVEGLOBAL; + break; + default: + Sys_Error("Bad bits in ED_ParseEpair"); + } + + switch (type) + { + case ev_string: + v = ED_NewString (progfuncs, s)-progfuncs->stringtable; + *(string_t *)d = v; + break; + + case ev_float: + *(float *)d = (float)atof (s); + break; + + case ev_integer: + *(int *)d = atoi (s); + break; + + case ev_vector: + strcpy (string, s); + v = string; + w = string; + for (i=0 ; i<3 ; i++) + { + while (*v && *v != ' ') + v++; + *v = 0; + ((float *)d)[i] = (float)atof (w); + w = v = v+1; + } + break; + + case ev_entity: + *(int *)d = EDICT_TO_PROG(EDICT_NUM(progfuncs, atoi (s))); + break; + + case ev_field: + def = ED_FindField (progfuncs, s); + if (!def) + { + printf ("Can't find field %s\n", s); + return false; + } + *(int *)d = G_INT(def->ofs); + break; + + case ev_function: + if (s[1]==':'&&s[2]=='\0') + { + *(func_t *)d = 0; + return true; + } + func = ED_FindFunction (progfuncs, s, &i, -1); + if (!func) + { + printf ("Can't find function %s\n", s); + return false; + } + *(func_t *)d = (func - pr_progstate[i].functions) | (i<<24); + break; + + default: + break; + } + return true; +} + +/* +==================== +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. +==================== +*/ +#if 1 +char *ED_ParseEdict (progfuncs_t *progfuncs, char *data, edictrun_t *ent) +{ + fdef_t *key; + pbool init; + char keyname[256]; + int n; + +// eval_t *val; + + init = false; + +// clear it +// if (ent != (edictrun_t *)sv_edicts) // hack +// memset (ent+1, 0, pr_edict_size - sizeof(edictrun_t)); + +// go through all the dictionary pairs + while (1) + { + // parse key + data = QCC_COM_Parse (data); + if (qcc_token[0] == '}') + break; + if (!data) + Sys_Error ("ED_ParseEntity: EOF without closing brace"); + + strcpy (keyname, qcc_token); + + // another hack to fix heynames with trailing spaces + n = strlen(keyname); + while (n && keyname[n-1] == ' ') + { + keyname[n-1] = 0; + n--; + } + + // parse value + data = QCC_COM_Parse (data); + if (!data) + Sys_Error ("ED_ParseEntity: EOF without closing brace"); + + if (qcc_token[0] == '}') + Sys_Error ("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 = ED_FindField (progfuncs, keyname); + if (!key) + { + if (!strcmp(keyname, "angle")) //Quake anglehack - we've got to leave it in cos it doesn't work for quake otherwise, and this is a QuakeC lib! + { + if ((key = ED_FindField (progfuncs, "angles"))) + { + sprintf (qcc_token, "0 %f 0", atof(qcc_token)); //change it from yaw to 3d angle + goto cont; + } + } + if (!strcmp(keyname, "light")) //Quake lighthack - allows a field name and a classname to go by the same thing in the level editor + if ((key = ED_FindField (progfuncs, "light_lev"))) + goto cont; + printf ("'%s' is not a field\n", keyname); + continue; + } + +cont: + if (!ED_ParseEpair (progfuncs, (void *)(((char *)ent)+externs->edictsize), (ddefXX_t*)key, qcc_token, 32)) + Sys_Error ("ED_ParseEdict: parse error on entities"); + } + + if (!init) + ent->isfree = true; + + return data; +} +#endif + +/* +================ +ED_LoadFromFile + +The entities are directly placed in the array, rather than allocated with +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 ED_CallSpawnFunctions () to let the objects initialize themselves. +================ +*/ + +char *ED_WriteGlobals(progfuncs_t *progfuncs, char *buffer) //switch first. +{ +#define AddS(str) strcpy(buffer, str);buffer+=strlen(str); + int *v; + ddef32_t *def32; + ddef16_t *def16; + unsigned int i; + int j; + char *name; + int type; + int curprogs = pr_typecurrent; + int len; + switch(current_progstate->intsize) + { + case 16: + case 24: + for (i=0 ; inumglobaldefs ; i++) + { + def16 = &pr_globaldefs16[i]; + name = def16->s_name; + len = strlen(name); + if (!*name) + continue; + if (name[len-2] == '_' && (name[len-1] == 'x' || name[len-1] == 'y' || name[len-1] == 'z')) + continue; // skip _x, _y, _z vars (vector componants, which are saved as one vector not 3 floats) + + type = def16->type; + +#ifdef DEF_SAVEGLOBAL + if ( !(def16->type & DEF_SAVEGLOBAL) ) + continue; + type &= ~DEF_SAVEGLOBAL; +#endif + if (current_progstate->types) + type = current_progstate->types[type].type; + if (type == ev_function) + { + v = (int *)¤t_progstate->globals[def16->ofs]; + if ((v[0]&0xff000000)>>24 == (unsigned)curprogs) //same progs + { + if (!*current_progstate->functions[v[0]&0x00ffffff].s_name) + continue; + else if (!strcmp(current_progstate->functions[v[0]&0x00ffffff].s_name, name)) //names match. Assume function is at initial value. + continue; + } + + if (curprogs!=0) + if ((v[0]&0xff000000)>>24 == 0) + if (!ED_FindFunction(progfuncs, name, NULL, curprogs)) //defined as extern + { + if (!*pr_progstate[0].functions[v[0]&0x00ffffff].s_name) + continue; + else if (!strcmp(pr_progstate[0].functions[v[0]&0x00ffffff].s_name, name)) //same name. + continue; + } + + //else function has been redirected externally. + goto add16; + } + else if (type != ev_string //anything other than these is not saved + && type != ev_float + && type != ev_integer + && type != ev_entity + && type != ev_vector) + continue; + + v = (int *)¤t_progstate->globals[def16->ofs]; + + // make sure the value is not null, where there's no point in saving + for (j=0 ; jtype&~DEF_SAVEGLOBAL, (eval_t *)v))); + } + break; + case 32: + for (i=0 ; inumglobaldefs ; i++) + { + def32 = &pr_globaldefs32[i]; + name = def32->s_name; + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars (vector componants, which are saved as one vector not 3 floats) + + type = def32->type; + +#ifdef DEF_SAVEGLOBAL + if ( !(def32->type & DEF_SAVEGLOBAL) ) + continue; + type &= ~DEF_SAVEGLOBAL; +#endif + if (current_progstate->types) + type = current_progstate->types[type].type; + if (type == ev_function) + { + v = (int *)¤t_progstate->globals[def32->ofs]; + if ((v[0]&0xff000000)>>24 == (unsigned)curprogs) //same progs + if (!strcmp(current_progstate->functions[v[0]&0x00ffffff].s_name, name)) //names match. Assume function is at initial value. + continue; + + if (curprogs!=0) + if ((v[0]&0xff000000)>>24 == 0) + if (!ED_FindFunction(progfuncs, name, NULL, curprogs)) //defined as extern + if (!strcmp(pr_progstate[0].functions[v[0]&0x00ffffff].s_name, name)) //same name. + continue; + + //else function has been redirected externally. + goto add32; + } + else if (type != ev_string //anything other than these is not saved + && type != ev_float + && type != ev_integer + && type != ev_entity + && type != ev_vector) + continue; + + v = (int *)¤t_progstate->globals[def32->ofs]; + + // make sure the value is not null, where there's no point in saving + for (j=0 ; jtype&~DEF_SAVEGLOBAL, (eval_t *)v))); + } + break; + default: + Sys_Error("Bad number of bits in SaveEnts"); + } + + return buffer; +} + +char *ED_WriteEdict(progfuncs_t *progfuncs, edictrun_t *ed, char *buffer, pbool q1compatable) +{ + fdef_t *d; + + int *v; + unsigned int i;int j; + char *name; + int type; + int len; + + for (i=0 ; is_name; + len = strlen(name); + if (name[len-2] == '_' && (name[len-1] == 'x' || name[len-1] == 'y' || name[len-1] == 'z')) + continue; // skip _x, _y, _z vars + + v = (int *)(((char *)ed + externs->edictsize) + d->ofs*4); + + // if the value is still all 0, skip the field +#ifdef DEF_SAVEGLOBAL + type = d->type & ~DEF_SAVEGLOBAL; +#else + type = d->type; +#endif + + for (j=0 ; jtype, (eval_t *)v))); + } + + return buffer; +#undef AddS +} + +//there are two ways of saving everything. +//0 is to save just the entities. +//1 is to save the entites, and all the progs info so that all the variables are saved off, and it can be reloaded to exactly how it was (provided no files or data has been changed outside, like the progs.dat for example) +char *SaveEnts(progfuncs_t *progfuncs, char *mem, int *len, int alldata) +{ +#define AddS(str) strcpy(s, str);s+=strlen(str); + char *s, *os; + int a; + int oldprogs; + + if (mem) + { + os = s = mem; + } + else + os = s = memalloc(5*1024*1024); + + if (alldata == 2) + { //special Q1 savegame compatability mode. + //engine will need to store references to progs type and will need to preload the progs and inti the ents itself before loading. + + //Make sure there is only 1 progs loaded. + for (a = 1; a < maxprogs; a++) + { + if (pr_progstate[a].progs) + break; + } + if (!pr_progstate[0].progs || a != maxprogs) //the state of the progs wasn't Q1 compatable. + { + memfree(os); + return NULL; + } + + //write the globals + AddS ("{\n"); + + oldprogs = pr_typecurrent; + PR_SwitchProgs(progfuncs, 0); + + s = ED_WriteGlobals(progfuncs, s); + + PR_SwitchProgs(progfuncs, oldprogs); + + AddS ("}\n"); + + + //write the ents + for (a = 0; a < sv_num_edicts; a++) + { + edictrun_t *ed = (edictrun_t *)EDICT_NUM(progfuncs, a); + + AddS ("{\n"); + + if (!ed->isfree) + s = ED_WriteEdict(progfuncs, ed, s, true); + + AddS ("}\n"); + } + + *len = s - os; + return os; + } + + if (alldata) + { + + AddS("general {\n"); + AddS(qcva("\"maxprogs\" \"%i\"\n", maxprogs)); +// AddS(qcva("\"maxentities\" \"%i\"\n", maxedicts)); +// AddS(qcva("\"mem\" \"%i\"\n", hunksize)); +// AddS(qcva("\"crc\" \"%i\"\n", header_crc)); + AddS(qcva("\"numentities\" \"%i\"\n", sv_num_edicts)); + AddS("}\n"); + + oldprogs = pr_typecurrent; + + for (a = 0; a < maxprogs; a++) + { + if (!pr_progstate[a].progs) + continue; + PR_SwitchProgs(progfuncs, a); + { + AddS (qcva("progs %i {\n", a)); + AddS (qcva("\"filename\" \"%s\"\n", pr_progstate[a].filename)); + AddS (qcva("\"crc\" \"%i\"\n", pr_progs->crc)); + AddS (qcva("\"numbuiltins\" \"%i\"\n", current_progstate->numbuiltins)); + AddS ("}\n"); + } + } + + for (a = 0; a < maxprogs; a++) //I would mix, but external functions rely on other progs being loaded + { + if (!pr_progstate[a].progs) + continue; + + AddS (qcva("globals %i {\n", a)); + + PR_SwitchProgs(progfuncs, a); + + s = ED_WriteGlobals(progfuncs, s); + + AddS ("}\n"); + } + PR_SwitchProgs(progfuncs, oldprogs); + } + for (a = 0; a < sv_num_edicts; a++) + { + edictrun_t *ed = (edictrun_t *)EDICT_NUM(progfuncs, a); + + if (ed->isfree) + continue; + + AddS (qcva("entity %i{\n", a)); + + s = ED_WriteEdict(progfuncs, ed, s, false); + + AddS ("}\n"); + } + + *len = s - os; + return os; + +#undef AddS +} + +int header_crc; + +//if 'general' block is found, this is a compleate state, otherwise, we should spawn entities like +int LoadEnts(progfuncs_t *progfuncs, char *file, float killonspawnflags) +{ + eval_t *fulldata; //this is part of FTE_FULLSPAWNDATA + char *datastart; + + eval_t *var; + + char filename[128]; + int num; + int numbuiltins; + edictrun_t *ed=NULL; + ddef16_t *d16; + ddef32_t *d32; + func_t CheckSpawn=0; + + extern char tempedicts[]; + + int crc = 1; + int entsize = 0; + int numents = 0; + + pbool resethunk=0; + pbool isloadgame; + if (!strncmp(file, "loadgame", 8)) + { + isloadgame = true; + numents = -1; + file+=8; + fulldata = NULL; + } + else + { + isloadgame = false; + + num = ED_FindGlobalOfs(progfuncs, "__fullspawndata"); + if (num) + fulldata = (eval_t *)((int *)pr_globals + num); + else + fulldata = NULL; + } + + while(1) + { + datastart = file; + file = QCC_COM_Parse(file); + if (file == NULL) + break; //finished reading file + else if (!strcmp(qcc_token, "entity")) + { + if (entsize == 0 && resethunk) //edicts have not yet been initialized, and this is a compleate load (memsize has been set) + { + entsize = PR_InitEnts(progfuncs, maxedicts); +// sv_num_edicts = numents; + + for (num = 0; num < numents; num++) + { + ed = (edictrun_t *)EDICT_NUM(progfuncs, num); +#ifdef DYNAMIC_ENTS + if (!ed) + { + prinst->edicttable[num] = (struct edict_s *)ed = (void*)memalloc(pr_edict_size); + ed->entnum = num; + ed->isfree = true; + } +#endif + } + } + + file = QCC_COM_Parse(file); + num = atoi(qcc_token); + file = QCC_COM_Parse(file); + if (qcc_token[0] != '{') + Sys_Error("Progs loading found %s, not '{'", qcc_token); + if (!resethunk) + ed = (edictrun_t *)ED_Alloc(progfuncs); + else + { + ed = (edictrun_t *)EDICT_NUM(progfuncs, num); +#ifdef DYNAMIC_ENTS + if (!ed) + { + Sys_Error("Edict was not allocated\n"); + prinst->edicttable[num] = (struct edict_s *)ed = (void*)memalloc(pr_edict_size); + ed->entnum = num; + } +#endif + } + ed->isfree = false; + file = ED_ParseEdict(progfuncs, file, ed); + + if (killonspawnflags) + { + var = GetEdictFieldValue (progfuncs, (struct edict_s *)ed, "spawnflags", &spawnflagscache); + if (var) + { + if ((int)var->_float & (int)killonspawnflags) + { + ed->isfree = true; + continue; + } + } + } + + if (!resethunk) + { + dfunction_t *f; + if ((var = GetEdictFieldValue (progfuncs, (struct edict_s *)ed, "classname", NULL))) + { + f = ED_FindFunction(progfuncs, var->string, NULL, -1); + if (f) + { + var = (eval_t *)((int *)pr_globals + ED_FindGlobalOfs(progfuncs, "self")); + var->edict = EDICT_TO_PROG(ed); + PR_ExecuteProgram(progfuncs, f-pr_functions); + } + } + } + } + else if (!strcmp(qcc_token, "progs")) + { + file = QCC_COM_Parse(file); + num = atoi(qcc_token); + file = QCC_COM_Parse(file); + if (qcc_token[0] != '{') + Sys_Error("Progs loading found %s, not '{'", qcc_token); + + + filename[0] = '\0'; + header_crc = 0; + numbuiltins = 0; + + while(1) + { + file = QCC_COM_Parse(file); //read the key + if (!file) + Sys_Error("EOF in progs block"); + + if (!strcmp("filename", qcc_token)) //check key get and save values + {file = QCC_COM_Parse(file); strcpy(filename, qcc_token);} + else if (!strcmp("crc", qcc_token)) + {file = QCC_COM_Parse(file); header_crc = atoi(qcc_token);} + else if (!strcmp("numbuiltins", qcc_token)) + {file = QCC_COM_Parse(file); numbuiltins = atoi(qcc_token);} + else if (qcc_token[0] == '}') //end of block + break; + else + Sys_Error("Bad key \"%s\" in progs block", qcc_token); + } + + PR_ReallyLoadProgs(progfuncs, filename, header_crc, &pr_progstate[num], true); + if (!externs->builtinsfor) + { + // Sys_Error("Couldn't reset the builtin functions"); + current_progstate->builtins = NULL; //these are specific, we assume the global ones were set via pr_configure + current_progstate->numbuiltins = 0; + } + else + { + current_progstate->builtins = externs->builtinsfor(num, header_crc); + current_progstate->numbuiltins = numbuiltins; + } + } + else if (!strcmp(qcc_token, "globals")) + { + if (entsize == 0 && resethunk) //by the time we parse some globals, we MUST have loaded all progs + { + entsize = PR_InitEnts(progfuncs, maxedicts); +// sv_num_edicts = numents; + + for (num = 0; num < numents; num++) + { + ed = (edictrun_t *)EDICT_NUM(progfuncs, num); +#ifdef DYNAMIC_ENTS + if (!ed) + { + prinst->edicttable[num] = (struct edict_s *)ed = (void*)memalloc(pr_edict_size); + ed->entnum = num; + ed->isfree = true; + } +#endif + } + } + + file = QCC_COM_Parse(file); + num = atoi(qcc_token); + + file = QCC_COM_Parse(file); + if (qcc_token[0] != '{') + Sys_Error("Globals loading found \'%s\', not '{'", qcc_token); + + PR_SwitchProgs(progfuncs, num); + while (1) + { + file = QCC_COM_Parse(file); + if (qcc_token[0] == '}') + break; + else if (!qcc_token[0] || !file) + Sys_Error("EOF when parsing global values"); + + switch(current_progstate->intsize) + { + case 16: + case 24: + if (!(d16 = ED_FindGlobal16(progfuncs, qcc_token))) + { + file = QCC_COM_Parse(file); + printf("global value %s not found", qcc_token); + } + else + { + file = QCC_COM_Parse(file); + ED_ParseEpair(progfuncs, pr_globals, (ddefXX_t*)d16, qcc_token, 16); + } + break; + case 32: + if (!(d32 = ED_FindGlobal32(progfuncs, qcc_token))) + { + file = QCC_COM_Parse(file); + printf("global value %s not found", qcc_token); + } + else + { + file = QCC_COM_Parse(file); + ED_ParseEpair(progfuncs, pr_globals, (ddefXX_t*)d32, qcc_token, 32); + } + break; + default: + Sys_Error("Bad intsize in LoadEnts"); + } + } + +// file = QCC_COM_Parse(file); +// if (com_token[0] != '}') +// Sys_Error("Progs loading found %s, not '}'", qcc_token); + } + else if (!strcmp(qcc_token, "general")) + { + QC_StartShares(progfuncs); +// QC_InitShares(); //forget stuff +// pr_edict_size = 0; + pr_max_edict_size=0; + + file = QCC_COM_Parse(file); + if (qcc_token[0] != '{') + Sys_Error("Progs loading found %s, not '{'", qcc_token); + + while(1) + { + file = QCC_COM_Parse(file); //read the key + if (!file) + Sys_Error("EOF in general block"); + + if (!strcmp("maxprogs", qcc_token)) //check key get and save values + {file = QCC_COM_Parse(file); maxprogs = atoi(qcc_token);} +// else if (!strcmp("maxentities", com_token)) +// {file = QCC_COM_Parse(file); maxedicts = atoi(qcc_token);} +// else if (!strcmp("mem", com_token)) +// {file = QCC_COM_Parse(file); memsize = atoi(qcc_token);} +// else if (!strcmp("crc", com_token)) +// {file = QCC_COM_Parse(file); crc = atoi(qcc_token);} + else if (!strcmp("numentities", qcc_token)) + {file = QCC_COM_Parse(file); numents = atoi(qcc_token);} + else if (qcc_token[0] == '}') //end of block + break; + else + Sys_Error("Bad key \"%s\" in general block", qcc_token); + } + + hunkused = 0; + resethunk=true; + + pr_progstate = PRHunkAlloc(progfuncs, sizeof(progstate_t) * maxprogs); + pr_typecurrent=0; + + sv_num_edicts = 1; //set up a safty buffer so things won't go horribly wrong too often + sv_edicts=(struct edict_s *)tempedicts; + + sv_num_edicts = numents; //should be fine + +// PR_Configure(crc, NULL, memsize, maxedicts, maxprogs); + } + else if (!strcmp(qcc_token, "{")) + { + if (isloadgame) + { + if (numents == -1) //globals + { + while (1) + { + file = QCC_COM_Parse(file); + if (qcc_token[0] == '}') + break; + else if (!qcc_token[0] || !file) + Sys_Error("EOF when parsing global values"); + + switch(current_progstate->intsize) + { + case 16: + case 24: + if (!(d16 = ED_FindGlobal16(progfuncs, qcc_token))) + { + file = QCC_COM_Parse(file); + printf("global value %s not found", qcc_token); + } + else + { + file = QCC_COM_Parse(file); + ED_ParseEpair(progfuncs, pr_globals, (ddefXX_t*)d16, qcc_token, 16); + } + break; + case 32: + if (!(d32 = ED_FindGlobal32(progfuncs, qcc_token))) + { + file = QCC_COM_Parse(file); + printf("global value %s not found", qcc_token); + } + else + { + file = QCC_COM_Parse(file); + ED_ParseEpair(progfuncs, pr_globals, (ddefXX_t*)d32, qcc_token, 32); + } + break; + default: + Sys_Error("Bad intsize in LoadEnts"); + } + } + } + else + { + ed = (edictrun_t *)EDICT_NUM(progfuncs, numents); +#ifdef DYNAMIC_ENTS + if (!ed) + { + prinst->edicttable[numents] = (struct edict_s *)ed = (void*)memalloc(pr_edict_size); + ed->entnum = numents; + ed->isfree = true; + } +#endif + sv_num_edicts = numents; + ed->isfree = false; + file = ED_ParseEdict (progfuncs, file, ed); + } + numents++; + continue; + } + + if (entsize == 0 && resethunk) //edicts have not yet been initialized, and this is a compleate load (memsize has been set) + { + entsize = PR_InitEnts(progfuncs, maxedicts); +// sv_num_edicts = numents; + + for (num = 0; num < numents; num++) + { + ed = (edictrun_t *)EDICT_NUM(progfuncs, num); +#ifdef DYNAMIC_ENTS + if (!ed) + { + prinst->edicttable[num] = (struct edict_s *)ed = (void*)memalloc(pr_edict_size); + ed->entnum = num; + ed->isfree = true; + } +#endif + } + } + + if (!ed) //first entity + ed = (edictrun_t *)EDICT_NUM(progfuncs, 0); + else + ed = (edictrun_t *)ED_Alloc(progfuncs); + ed->isfree = false; + file = ED_ParseEdict(progfuncs, file, ed); + + if (killonspawnflags) + { + var = GetEdictFieldValue (progfuncs, (struct edict_s *)ed, "spawnflags", &spawnflagscache); + if (var) + { + if ((int)var->_float & (int)killonspawnflags) + { + ed->isfree = true; + ed->freetime = 0; + continue; + } + } + } + + if (!resethunk) + { + func_t f; + if (!CheckSpawn) + CheckSpawn = PR_FindFunc(progfuncs, "CheckSpawn", -2); + + var = GetEdictFieldValue (progfuncs, (struct edict_s *)ed, "classname", NULL); + if (!var || !var->string || !*(var->string+progfuncs->stringtable)) + { + printf("No classname\n"); + ED_Free(progfuncs, (struct edict_s *)ed); + } + else + { + //added by request of Mercury. + if (fulldata) //this is a vital part of HL map support!!! + { //essentually, it passes the ent's spawn info to the ent. + char *nl; //otherwise it sees only the named fields of + char *spawndata;//a standard quake ent. + spawndata = PRHunkAlloc(progfuncs, file - datastart +1); + strncpy(spawndata, datastart, file - datastart); + spawndata[file - datastart] = '\0'; + for (nl = spawndata; *nl; nl++) + if (*nl == '\n') + *nl = '\t'; + fulldata->string = spawndata - progfuncs->stringtable; + } + + f = PR_FindFunc(progfuncs, var->string+progfuncs->stringtable, -2); + if (f) + { + eval_t *selfvar; + selfvar = (eval_t *)((int *)pr_globals + ED_FindGlobalOfs(progfuncs, "self")); + selfvar->edict = EDICT_TO_PROG(ed); + if (CheckSpawn) + { + G_INT(OFS_PARM0) = f; + PR_ExecuteProgram(progfuncs, CheckSpawn); + //call the spawn func or remove. + } + else + PR_ExecuteProgram(progfuncs, f); + } + else if (CheckSpawn) + { + G_INT(OFS_PARM0) = 0; + PR_ExecuteProgram(progfuncs, CheckSpawn); + //the mod is responsible for freeing unrecognised ents. + } + else + { + printf("Couldn't find spawn function %s\n", var->string+progfuncs->stringtable); + ED_Free(progfuncs, (struct edict_s *)ed); + } + } + } + } + else + Sys_Error("Command %s not recognised", qcc_token); + } + if (resethunk) + { + header_crc = crc; + if (externs->loadcompleate) + externs->loadcompleate(entsize); + + sv_num_edicts = numents; + } + + if (resethunk) + { + return entsize; + } + else + return pr_edict_size; +} + +#define AddS(str) strcpy(s, str);s+=strlen(str); + +char *SaveEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed) +{ + fdef_t *d; + int *v; + unsigned int i;int j; + char *name; + int type; + + char *s = buf; + +// if (ed->free) +// continue; + + AddS ("{\n"); + + + for (i=0 ; is_name; + if (name[strlen(name)-2] == '_') + continue; // skip _x, _y, _z vars + + v = (int *)(((char *)ed + externs->edictsize) + d->ofs*4); + + // if the value is still all 0, skip the field + type = d->type & ~DEF_SAVEGLOBAL; + for (j=0 ; jtype, (eval_t *)v))); + } + + AddS ("}\n"); + + *size = s - buf; + + return buf; +} +struct edict_s *RestoreEnt (progfuncs_t *progfuncs, char *buf, int *size, struct edict_s *ed) +{ + edictrun_t *ent; + char *start = buf; + + buf = QCC_COM_Parse(buf); //read the key + if (strcmp(qcc_token, "{")) + Sys_Error("Restore Ent with no opening brace"); + + if (!ed) + ent = (edictrun_t *)ED_Alloc(progfuncs); + else + ent = (edictrun_t *)ed; + ent->isfree = false; + + ED_ClearEdict(progfuncs, ent); + + buf = ED_ParseEdict(progfuncs, buf, ent); + + *size = buf - start; + + return (struct edict_s *)ent; +} + +#define Host_Error Sys_Error + +//return true if pr_progs needs recompiling (source files have changed) +pbool PR_TestRecompile(progfuncs_t *progfuncs) +{ + int newsize; + int num, found=0, lost=0, changed=0; + includeddatafile_t *s; + if (!pr_progs->ofsfiles) + return false; + + num = *(int*)((char *)pr_progs + pr_progs->ofsfiles); + s = (includeddatafile_t *)((char *)pr_progs + pr_progs->ofsfiles+4); + while(num>0) + { + newsize = externs->FileSize(s->filename); + if (newsize == -1) //ignore now missing files. - the referencer must have changed... + lost++; + else if (s->size != newsize) //file + changed++; + else + found++; + + s++; + num--; + } + if (lost > found+changed) + return false; + if (changed) + return true; + return false; +} + +char *decode(int complen, int len, int method, char *info, char *buffer); +/* +=============== +PR_LoadProgs +=============== +*/ +int PR_ReallyLoadProgs (progfuncs_t *progfuncs, char *filename, int headercrc, progstate_t *progstate, pbool complain) +{ + unsigned int i, type; + extensionbuiltin_t *eb; +// float fl; + int len; +// int num; +// dfunction_t *f, *f2; + ddef16_t *d16; + ddef32_t *d32; + int *d2; + eval_t *eval; + char *s; + int progstype; + int trysleft = 2; +// bool qfhack = false; + pbool isfriked = false; //all imediate values were stripped, which causes problems with strings. + pbool hexencalling = false; //hexen style calling convention. The opcodes themselves are used as part of passing the arguments; + ddef16_t *gd16, *fld16; + float *glob; + dfunction_t *fnc; + dstatement16_t *st16; + + int hmark=0xffffffff; + + int reorg = prinst->reorganisefields; + + current_progstate = progstate; + + strcpy(current_progstate->filename, filename); + + +// flush the non-C variable lookup cache +// for (i=0 ; iautocompile == PR_COMPILEALWAYS) //always compile before loading + { + printf("Forcing compile of progs %s\n", filename); + if (!CompileFile(progfuncs, filename)) + return false; + } + +// CRC_Init (&pr_crc); + +retry: + if ((len=externs->FileSize(filename))<=0) + { + if (externs->autocompile == PR_COMPILENEXIST || externs->autocompile == PR_COMPILECHANGED) //compile if file is not found (if 2, we have already tried, so don't bother) + { + if (hmark==0xffffffff) //first try + { + printf("couldn't open progs %s. Attempting to compile.\n", filename); + CompileFile(progfuncs, filename); + } + if ((len=externs->FileSize(filename))<0) + { + printf("Couldn't find or compile file %s\n", filename); + return false; + } + } + else if (externs->autocompile == PR_COMPILEIGNORE) + return false; + else + { + printf("Couldn't find file %s\n", filename); + return false; + } + } + + hmark = PRHunkMark(progfuncs); + pr_progs = PRHunkAlloc(progfuncs, len+1); + if (!externs->ReadFile(filename, pr_progs, len+1)) + { + if (!complain) + return false; + printf("Failed to open %s", filename); + return false; + } + +// for (i=0 ; iversion == PROG_VERSION) + { +// printf("Opening standard progs file \"%s\"\n", filename); + current_progstate->intsize = 16; + } + else if (pr_progs->version == PROG_DEBUGVERSION) + { + if (pr_progs->secondaryversion == PROG_SECONDARYVERSION16) + { +// printf("Opening 16bit fte progs file \"%s\"\n", filename); + current_progstate->intsize = 16; + } + else if (pr_progs->secondaryversion == PROG_SECONDARYVERSION32) + { +// printf("Opening 32bit fte progs file \"%s\"\n", filename); + current_progstate->intsize = 32; + } + else + { +// printf("Opening KK7 progs file \"%s\"\n", filename); + current_progstate->intsize = 24; //KK progs. Yuck. Disabling saving would be a VERY good idea. + pr_progs->version = PROG_VERSION; //not fte. + } +/* else + { + printf ("Progs extensions are not compatable\nTry recompiling with the FTE compiler\n"); + HunkFree(hmark); + pr_progs=NULL; + return false; + } +*/ } + else + { + printf ("%s has wrong version number (%i should be %i)\n", filename, pr_progs->version, PROG_VERSION); + PRHunkFree(progfuncs, hmark); + pr_progs=NULL; + return false; + } + +//progs contains enough info for use to recompile it. + if (trysleft && externs->autocompile == PR_COMPILECHANGED && pr_progs->version == PROG_DEBUGVERSION) + { + if (PR_TestRecompile(progfuncs)) + { + printf("Source file has changed\nRecompiling.\n"); + if (CompileFile(progfuncs, filename)) + { + PRHunkFree(progfuncs, hmark); + pr_progs=NULL; + + trysleft--; + goto retry; + } + } + } + if (!trysleft) //the progs exists, let's just be happy about it. + printf("Progs is out of date and uncompilable\n"); + + if (headercrc != -1 && headercrc != 0) + if (pr_progs->crc != headercrc && pr_progs->crc != 0) //This shouldn't affect us. However, it does adjust expectations and usage of builtins. + { +// printf ("%s system vars have been modified, progdefs.h is out of date\n", filename); + PRHunkFree(progfuncs, hmark); + pr_progs=NULL; + return false; + } + + fnc = pr_functions = (dfunction_t *)((qbyte *)pr_progs + pr_progs->ofs_functions); + pr_strings = ((char *)pr_progs + pr_progs->ofs_strings); + gd16 = pr_globaldefs16 = (ddef16_t *)((qbyte *)pr_progs + pr_progs->ofs_globaldefs); + fld16 = pr_fielddefs16 = (ddef16_t *)((qbyte *)pr_progs + pr_progs->ofs_fielddefs); + pr_statements16 = (dstatement16_t *)((qbyte *)pr_progs + pr_progs->ofs_statements); + + glob = pr_globals = (void *)((qbyte *)pr_progs + pr_progs->ofs_globals); + + pr_linenums=NULL; + pr_types=NULL; + if (pr_progs->version == PROG_DEBUGVERSION) + { + if (pr_progs->ofslinenums) + pr_linenums = (int *)((qbyte *)pr_progs + pr_progs->ofslinenums); + if (pr_progs->ofs_types) + pr_types = (typeinfo_t *)((qbyte *)pr_progs + pr_progs->ofs_types); + + //start decompressing stuff... + if (pr_progs->blockscompressed & 1) //statements + { + switch(current_progstate->intsize) + { + case 16: + len=sizeof(dstatement16_t)*pr_progs->numstatements; + break; + case 32: + len=sizeof(dstatement32_t)*pr_progs->numstatements; + break; + default: + Sys_Error("Bad intsize"); + } + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, LittleLong(*(int *)pr_statements16), len, 2, (char *)(((int *)pr_statements16)+1), s); + + pr_statements16 = (dstatement16_t *)s; + } + if (pr_progs->blockscompressed & 2) //global defs + { + switch(current_progstate->intsize) + { + case 16: + len=sizeof(ddef16_t)*pr_progs->numglobaldefs; + break; + case 32: + len=sizeof(ddef32_t)*pr_progs->numglobaldefs; + break; + default: + Sys_Error("Bad intsize"); + } + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, LittleLong(*(int *)pr_globaldefs16), len, 2, (char *)(((int *)pr_globaldefs16)+1), s); + + gd16 = pr_globaldefs16 = (ddef16_t *)s; + } + if (pr_progs->blockscompressed & 4) //fields + { + switch(current_progstate->intsize) + { + case 16: + len=sizeof(ddef16_t)*pr_progs->numglobaldefs; + break; + case 32: + len=sizeof(ddef32_t)*pr_progs->numglobaldefs; + break; + default: + Sys_Error("Bad intsize"); + } + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, LittleLong(*(int *)pr_fielddefs16), len, 2, (char *)(((int *)pr_fielddefs16)+1), s); + + pr_fielddefs16 = (ddef16_t *)s; + } + if (pr_progs->blockscompressed & 8) //functions + { + len=sizeof(dfunction_t)*pr_progs->numfunctions; + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, LittleLong(*(int *)pr_functions), len, 2, (char *)(((int *)pr_functions)+1), s); + + fnc = pr_functions = (dfunction_t *)s; + } + if (pr_progs->blockscompressed & 16) //string table + { + len=sizeof(char)*pr_progs->numstrings; + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, LittleLong(*(int *)pr_strings), len, 2, (char *)(((int *)pr_strings)+1), s); + + pr_strings = (char *)s; + } + if (pr_progs->blockscompressed & 32) //globals + { + len=sizeof(float)*pr_progs->numglobals; + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, LittleLong(*(int *)pr_globals), len, 2, (char *)(((int *)pr_globals)+1), s); + + glob = pr_globals = (float *)s; + } + if (pr_progs->ofslinenums && pr_progs->blockscompressed & 64) //line numbers + { + len=sizeof(int)*pr_progs->numstatements; + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, LittleLong(*(int *)pr_linenums), len, 2, (char *)(((int *)pr_linenums)+1), s); + + pr_linenums = (int *)s; + } + if (pr_types && pr_progs->blockscompressed & 128) //types + { + len=sizeof(typeinfo_t)*pr_progs->numtypes; + s = PRHunkAlloc(progfuncs, len); + QC_decode(progfuncs, LittleLong(*(int *)pr_types), len, 2, (char *)(((int *)pr_types)+1), s); + + pr_types = (typeinfo_t *)s; + } + } + + pr_functions = fnc; +// pr_strings = ((char *)pr_progs + pr_progs->ofs_strings); + gd16 = pr_globaldefs16 = (ddef16_t *)((qbyte *)pr_progs + pr_progs->ofs_globaldefs); + fld16 = (ddef16_t *)((qbyte *)pr_progs + pr_progs->ofs_fielddefs); +// pr_statements16 = (dstatement16_t *)((qbyte *)pr_progs + pr_progs->ofs_statements); + pr_globals = glob; + st16 = pr_statements16; +#undef pr_globals +#undef pr_globaldefs16 +#undef pr_functions +#undef pr_statements16 +#undef pr_fielddefs16 + + + current_progstate->edict_size = pr_progs->entityfields * 4 + externs->edictsize; + +// byte swap the lumps + for (i=0 ; inumfunctions; i++) + { +#ifndef NOENDIAN + fnc[i].first_statement = LittleLong (fnc[i].first_statement); + fnc[i].parm_start = LittleLong (fnc[i].parm_start); + fnc[i].s_name = (string_t)LittleLong ((long)fnc[i].s_name); + fnc[i].s_file = (string_t)LittleLong ((long)fnc[i].s_file); + fnc[i].numparms = LittleLong (fnc[i].numparms); + fnc[i].locals = LittleLong (fnc[i].locals); +#endif + fnc[i].s_name += (int)pr_strings; + fnc[i].s_file += (int)pr_strings; + + if (!strncmp(fnc[i].s_name, "ext_", 4)) + { + for (eb = extensionbuiltin; eb; eb = eb->prev) + { + if (*eb->name == '_') + { + if (!strncmp(fnc[i].s_name+4, eb->name+1, strlen(eb->name+1))) + { + fnc[i].first_statement = -0x7fffffff; + (void*)fnc[i].profile = (void*)eb->func; + break; + } + } + else if (!strcmp(fnc[i].s_name+4, eb->name)) + { + fnc[i].first_statement = -0x7fffffff; + (void*)fnc[i].profile = (void*)eb->func; + break; + } + } + } + } + + //actual global values +#ifndef NOENDIAN + for (i=0 ; inumglobals ; i++) + ((int *)glob)[i] = LittleLong (((int *)glob)[i]); +#endif + + if (pr_progs->version == PROG_DEBUGVERSION) + { + if (pr_types) + { + for (i=0 ; inumtypes ; i++) + { +#ifndef NOENDIAN + pr_types[i].type = LittleLong(current_progstate->types[i].type); + pr_types[i].next = LittleLong(current_progstate->types[i].next); + pr_types[i].aux_type = LittleLong(current_progstate->types[i].aux_type); + pr_types[i].num_parms = LittleLong(current_progstate->types[i].num_parms); + pr_types[i].ofs = LittleLong(current_progstate->types[i].ofs); + pr_types[i].size = LittleLong(current_progstate->types[i].size); + pr_types[i].name = (string_t)LittleLong((long)current_progstate->types[i].name); +#endif + pr_types[i].name += (int)pr_strings; + } + } + } + + if (reorg) + reorg = (headercrc != -1); + + switch(current_progstate->intsize) + { + case 24: + case 16: + for (i=0 ; inumglobaldefs ; i++) + { +#ifndef NOENDIAN + gd16[i].type = LittleShort (gd16[i].type); + gd16[i].ofs = LittleShort (gd16[i].ofs); + gd16[i].s_name = (string_t)LittleLong ((long)gd16[i].s_name); +#endif + gd16[i].s_name += (int)pr_strings; + } + + for (i=0 ; inumfielddefs ; i++) + { +#ifndef NOENDIAN + fld16[i].type = LittleShort (fld16[i].type); + fld16[i].ofs = LittleShort (fld16[i].ofs); + fld16[i].s_name = (string_t)LittleLong ((long)fld16[i].s_name); +#endif + fld16[i].s_name += (int)pr_strings; + + if (reorg) + { + if (pr_types) + type = pr_types[fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + + if (progfuncs->fieldadjust) //we need to make sure all fields appear in thier origional place. + QC_RegisterFieldVar(progfuncs, type, fld16[i].s_name, 4*(fld16[i].ofs+progfuncs->fieldadjust), -1); + else if (type == ev_vector) //emit vector vars early, so thier fields cannot be alocated before the vector itself. (useful against scramblers) + { + QC_RegisterFieldVar(progfuncs, type, fld16[i].s_name, -1, -1); + } + } + } + break; + case 32: + for (i=0 ; inumglobaldefs ; i++) + { +#ifndef NOENDIAN + pr_globaldefs32[i].type = LittleLong (pr_globaldefs32[i].type); + pr_globaldefs32[i].ofs = LittleLong (pr_globaldefs32[i].ofs); + pr_globaldefs32[i].s_name = (string_t)LittleLong ((long)pr_globaldefs32[i].s_name); +#endif + pr_globaldefs32[i].s_name += (int)pr_strings; + } + + for (i=0 ; inumfielddefs ; i++) + { + #ifndef NOENDIAN + pr_fielddefs32[i].type = LittleLong (pr_fielddefs32[i].type); + pr_fielddefs32[i].ofs = LittleLong (pr_fielddefs32[i].ofs); + pr_fielddefs32[i].s_name = (string_t)LittleLong ((long)pr_fielddefs32[i].s_name); + #endif + pr_fielddefs32[i].s_name += (int)pr_strings; + + if (reorg) + { + if (pr_types) + type = pr_types[pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + if (type == ev_vector) + QC_RegisterFieldVar(progfuncs, type, pr_fielddefs32[i].s_name, -1, -1); + } + } + break; + default: + Sys_Error("Bad int size"); + } + +//this is a little over complicated and slow. +//we need to search through the statements, looking for if statements. +//if the if statement works on a string, we need to adjust the opcode a little. +//This way, if (string) works properly, rather than always being true. +//if only compilers expanded to if (string!=string_null) + switch(current_progstate->intsize) + { + case 16: + for (i=0 ; inumstatements ; i++) + { + ddef16_t *gd; +#ifndef NOENDIAN + st16[i].op = LittleShort(st16[i].op); + st16[i].a = LittleShort(st16[i].a); + st16[i].b = LittleShort(st16[i].b); + st16[i].c = LittleShort(st16[i].c); +#endif + if (st16[i].op == OP_IF || st16[i].op == OP_IFNOT) //strings are dodgy. if("") can evaluate to true + { + gd = ED_GlobalAtOfs16(progfuncs, st16[i].a); + if (!gd) + continue; + if (pr_types) + type = pr_types[gd->type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = gd->type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + + if (type == ev_string) + { //fix it. +// printf("Fixing if\n"); + + if (st16[i].op == OP_IF) + st16[i].op = OP_IFS; + else if (st16[i].op == OP_IFNOT) + st16[i].op = OP_IFNOTS; + } + } + else if (st16[i].op >= OP_CALL1 && st16[i].op <= OP_CALL8) + { + if (st16[i].b) + hexencalling = true; + + } + } + if (hexencalling) + { + printf("Detected Hexen2 calling convention\n"); + for (i=0 ; inumstatements ; i++) + { + if (st16[i].op >= OP_CALL1 && st16[i].op <= OP_CALL8) + st16[i].op += OP_CALL1H - OP_CALL1; + } + } + break; + + case 24: //24 sucks. Guess why. + for (i=0 ; inumstatements ; i++) + { + ddef16_t *gd; +#ifndef NOENDIAN + pr_statements32[i].op = LittleLong(pr_statements32[i].op); + pr_statements32[i].a = LittleLong(pr_statements32[i].a); + pr_statements32[i].b = LittleLong(pr_statements32[i].b); + pr_statements32[i].c = LittleLong(pr_statements32[i].c); +#endif + + if (pr_statements32[i].op == OP_IF || pr_statements32[i].op == OP_IFNOT) //strings are dodgy. if("") can evaluate to true + { + gd = ED_GlobalAtOfs16(progfuncs, pr_statements32[i].a); + if (!gd) + continue; + if (pr_types) + type = pr_types[gd->type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = gd->type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + + if (type == ev_string) + { //fix it. +// printf("Fixing if\n"); + + pr_statements32[i].op = pr_statements32[i].op - OP_IF + OP_IFS; + } + } + else if (pr_statements32[i].op >= OP_CALL1 && pr_statements32[i].op <= OP_CALL8) + { + if (pr_statements32[i].b) + hexencalling = true; + + } + } + if (hexencalling) + { + for (i=0 ; inumstatements ; i++) + { + if (pr_statements32[i].op >= OP_CALL1 && pr_statements32[i].op <= OP_CALL8) + pr_statements32[i].op += OP_CALL1H - OP_CALL1; + } + } + break; + break; + case 32: +#ifndef NOENDIAN + for (i=0 ; inumstatements ; i++) + { + pr_statements32[i].op = LittleLong(pr_statements32[i].op); + pr_statements32[i].a = LittleLong(pr_statements32[i].a); + pr_statements32[i].b = LittleLong(pr_statements32[i].b); + pr_statements32[i].c = LittleLong(pr_statements32[i].c); + + //don't bother fixing if(string). The FTE compiler fixes it with if(string != string_null) + } +#endif + break; + } + + + if (headercrc == -1) + { + isfriked = true; + if (current_progstate->intsize != 16) + Sys_Error("Decompiling a bigprogs"); + return true; + } + + progstype = current_progstate-pr_progstate; + +// QC_StartShares(progfuncs); + + isfriked = true; +// isfriked = -1; + +// len = 0; + switch(current_progstate->intsize) + { + case 24: + case 16: + for (i=0 ; inumglobaldefs ; i++) + { + if (pr_types) + type = pr_types[gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + + if (gd16[i].type & DEF_SHARED) + { + gd16[i].type &= ~DEF_SHARED; + if (pr_types) + QC_AddSharedVar(progfuncs, gd16[i].ofs, pr_types[gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].size); + else + QC_AddSharedVar(progfuncs, gd16[i].ofs, type_size[type]); + } + switch(type) + { + case ev_field: + QC_AddSharedFieldVar(progfuncs, i); + break; + case ev_string: + if (((unsigned int *)glob)[gd16[i].ofs]>=progstate->progs->numstrings) + printf("Insane value\n"); + else if (isfriked != -1) + { + if (pr_strings[((int *)glob)[gd16[i].ofs]]) //quakec uses string tables. 0 must remain null, or 'if (s)' can break. + { + ((int *)glob)[gd16[i].ofs] += (int)pr_strings-progfuncs->stringtable; + isfriked = false; + } + else + (char *)((int *)glob)[gd16[i].ofs] = NULL; + } + break; + case ev_function: + if (((int *)glob)[gd16[i].ofs]) //don't change null funcs + { +// if (fnc[((int *)glob)[gd16[i].ofs]].first_statement>=0) //this is a hack. Make all builtins switch to the main progs first. Allows builtin funcs to cache vars from just the main progs. + ((int *)glob)[gd16[i].ofs] |= progstype << 24; + } + break; + } + } + + if (pr_progs->version == PROG_DEBUGVERSION && pr_progs->numbodylessfuncs) + { + s = &((char *)pr_progs)[pr_progs->ofsbodylessfuncs]; + for (i = 0; i < pr_progs->numbodylessfuncs; i++) + { + d16 = ED_FindGlobal16(progfuncs, s); + d2 = ED_FindGlobalOfsFromProgs(progfuncs, s, 0, ev_function); + if (!d2) + Sys_Error("Runtime-linked function %s was not found in existing progs", s); + if (!d16) + Sys_Error("Couldn't find def for \"%s\"", s); + ((int *)glob)[d16->ofs] = (*(func_t *)&pr_progstate[0].globals[*d2]); + + s+=strlen(s)+1; + } + } + + eval = PR_FindGlobal(progfuncs, "thisprogs", progstype); + if (eval) + eval->prog = progstype; + + break; + case 32: + for (i=0 ; inumglobaldefs ; i++) + { + if (pr_types) + type = pr_types[pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + + if (pr_globaldefs32[i].type & DEF_SHARED) + { + pr_globaldefs32[i].type &= ~DEF_SHARED; + if (pr_types) + QC_AddSharedVar(progfuncs, pr_globaldefs32[i].ofs, pr_types[pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].size); + else + QC_AddSharedVar(progfuncs, pr_globaldefs32[i].ofs, type_size[type]); + } + switch(type) + { + case ev_field: + QC_AddSharedFieldVar(progfuncs, i); + break; + case ev_string: + if (pr_strings[((int *)glob)[pr_globaldefs32[i].ofs]]) //quakec uses string tables. 0 must remain null, or 'if (s)' can break. + { + ((int *)glob)[pr_globaldefs32[i].ofs] += (int)pr_strings-progfuncs->stringtable; + isfriked = false; + } + else if (isfriked != -1) + (char *)((int *)glob)[pr_globaldefs32[i].ofs] = NULL; + + break; + case ev_function: + if (((int *)glob)[pr_globaldefs32[i].ofs]) //don't change null funcs + ((int *)glob)[pr_globaldefs32[i].ofs] |= progstype << 24; + break; + } + } + + if (pr_progs->version == PROG_DEBUGVERSION && pr_progs->numbodylessfuncs) + { + s = &((char *)pr_progs)[pr_progs->ofsbodylessfuncs]; + for (i = 0; i < pr_progs->numbodylessfuncs; i++) + { + d32 = ED_FindGlobal32(progfuncs, s); + d2 = ED_FindGlobalOfsFromProgs(progfuncs, s, 0, ev_function); + if (!d2) + Sys_Error("Runtime-linked function %s was not found in existing progs", s); + if (!d32) + Sys_Error("Couldn't find def for \"%s\"", s); + ((int *)glob)[d32->ofs] = (*(func_t *)&pr_progstate[0].globals[*d2]); + + s+=strlen(s)+1; + } + } + + eval = PR_FindGlobal(progfuncs, "thisprogs", progstype); + if (eval) + eval->prog = progstype; + + break; + default: + Sys_Error("Bad int size"); + } + if (!isfriked) + pr_strings=NULL; + else + printf("String-Stripped support enabled.\n"); + + if ((isfriked && pr_typecurrent)) //friked progs only allow one file. + { + printf("You are trying to load a string-stripped progs as an addon.\nThis behaviour is not supported. Try removing some optimizations."); + PRHunkFree(progfuncs, hmark); + pr_progs=NULL; + return false; + } + if (!progfuncs->stringtable) + progfuncs->stringtable = (int)pr_strings; + + return true; +} + + + +struct edict_s *EDICT_NUM(progfuncs_t *progfuncs, int n) +{ + if (n < 0 || n >= maxedicts) + Sys_Error ("QCLIB: EDICT_NUM: bad number %i", n); +#ifdef DYNAMIC_ENTS + return prinst->edicttable[n]; +#else + return (struct edict_s *)((qbyte *)sv_edicts+ (n)*pr_edict_size); +#endif +} + +int NUM_FOR_EDICT(progfuncs_t *progfuncs, struct edict_s *e) +{ +#ifdef DYNAMIC_ENTS + edictrun_t *er = (edictrun_t*)e; + if (er->entnum < 0 || er->entnum >= maxedicts) + Sys_Error ("QCLIB: NUM_FOR_EDICT: bad pointer (%i)", e); + return er->entnum; +#else + int b; + b = (qbyte *)e - (qbyte *)sv_edicts; + b = b / pr_edict_size; + + if (b < 0 || b >= sv_num_edicts) + Sys_Error ("QCLIB: NUM_FOR_EDICT: bad pointer (%i)", b); + return b; +#endif +} diff --git a/engine/qclib/pr_multi.c b/engine/qclib/pr_multi.c new file mode 100644 index 000000000..795414875 --- /dev/null +++ b/engine/qclib/pr_multi.c @@ -0,0 +1,528 @@ +#define PROGSUSED +#include "progsint.h" + +#define HunkAlloc BADGDFG sdfhhsf FHS + +void PR_SetBuiltins(int type); +/* +progstate_t *pr_progstate; +progsnum_t pr_typecurrent; +int maxprogs; + +progstate_t *current_progstate; +int numshares; + +sharedvar_t *shares; //shared globals, not including parms +int maxshares; +*/ + +pbool PR_SwitchProgs(progfuncs_t *progfuncs, progsnum_t type) +{ + if (type >= maxprogs || type < 0) + PR_RunError(progfuncs, "QCLIB: Bad prog type - %i", type); +// Sys_Error("Bad prog type - %i", type); + + if (pr_progstate[(int)type].progs == NULL) //we havn't loaded it yet, for some reason + return false; + + current_progstate = &pr_progstate[(int)type]; + + pr_typecurrent = type; + + return true; +} + +void PR_MoveParms(progfuncs_t *progfuncs, progsnum_t progs1, progsnum_t progs2) //from 2 to 1 +{ + unsigned int a; + progstate_t *p1; + progstate_t *p2; + + if (progs1 == progs2) + return; //don't bother coping variables to themselves... + + p1 = &pr_progstate[(int)progs1]; + p2 = &pr_progstate[(int)progs2]; + + if (progs1 >= maxprogs || progs1 < 0 || !p1->globals) + Sys_Error("QCLIB: Bad prog type - %i", progs1); + if (progs2 >= maxprogs || progs2 < 0 || !p2->globals) + Sys_Error("QCLIB: Bad prog type - %i", progs2); + + //copy parms. + for (a = 0; a < MAX_PARMS;a++) + { + *(int *)&p1->globals[OFS_PARM0+3*a ] = *(int *)&p2->globals[OFS_PARM0+3*a ]; + *(int *)&p1->globals[OFS_PARM0+3*a+1] = *(int *)&p2->globals[OFS_PARM0+3*a+1]; + *(int *)&p1->globals[OFS_PARM0+3*a+2] = *(int *)&p2->globals[OFS_PARM0+3*a+2]; + } + p1->globals[OFS_RETURN] = p2->globals[OFS_RETURN]; + p1->globals[OFS_RETURN+1] = p2->globals[OFS_RETURN+1]; + p1->globals[OFS_RETURN+2] = p2->globals[OFS_RETURN+2]; + + //move the vars defined as shared. + for (a = 0; a < numshares; a++)//fixme: make offset per progs + { + memmove(&((int *)p1->globals)[shares[a].varofs], &((int *)p2->globals)[shares[a].varofs], shares[a].size*4); +/* ((int *)p1->globals)[shares[a].varofs] = ((int *)p2->globals)[shares[a].varofs]; + if (shares[a].size > 1) + { + ((int *)p1->globals)[shares[a].varofs+1] = ((int *)p2->globals)[shares[a].varofs+1]; + if (shares[a].size > 2) + ((int *)p1->globals)[shares[a].varofs+2] = ((int *)p2->globals)[shares[a].varofs+2]; + } +*/ + } +} + +progsnum_t PR_LoadProgs(progfuncs_t *progfuncs, char *s, int headercrc, builtin_t *builtins, int numbuiltins) +{ + progsnum_t a; + progsnum_t oldtype; + oldtype = pr_typecurrent; + for (a = 0; a < maxprogs; a++) + { + if (pr_progstate[(int)a].progs == NULL) + { + pr_typecurrent = a; + current_progstate = &pr_progstate[(int)a]; + if (PR_ReallyLoadProgs(progfuncs, s, headercrc, &pr_progstate[a], false)) //try and load it + { + current_progstate->builtins = builtins; + current_progstate->numbuiltins = numbuiltins; + if (oldtype>=0) + PR_SwitchProgs(progfuncs, oldtype); + return a; //we could load it. Yay! + } + if (oldtype!=-1) + PR_SwitchProgs(progfuncs, oldtype); + return -1; // loading failed. + } + } + PR_SwitchProgs(progfuncs, oldtype); + return -1; +} + +void PR_ShiftParms(progfuncs_t *progfuncs, int amount) +{ + int a; + for (a = 0; a < MAX_PARMS - amount;a++) + *(int *)&pr_globals[OFS_PARM0+3*a] = *(int *)&pr_globals[OFS_PARM0+3*(amount+a)]; +} + +//forget a progs +void PR_Clear(progfuncs_t *progfuncs) +{ + int a; + for (a = 0; a < maxprogs; a++) + { + pr_progstate[a].progs = NULL; + } +} + + + +void QC_StartShares(progfuncs_t *progfuncs) +{ + numshares = 0; + maxshares = 32; + if (shares) + memfree(shares); + shares = memalloc(sizeof(sharedvar_t)*maxshares); +} +void QC_AddSharedVar(progfuncs_t *progfuncs, int start, int size) //fixme: make offset per progs and optional +{ + int ofs; + unsigned int a; + + if (numshares >= maxshares) + { + void *buf; + buf = shares; + maxshares += 16; + shares = memalloc(sizeof(sharedvar_t)*maxshares); + + memcpy(shares, buf, sizeof(sharedvar_t)*numshares); + + memfree(buf); + } + ofs = start; + for (a = 0; a < numshares; a++) + { + if (shares[a].varofs+shares[a].size == ofs) + { + shares[a].size += size; //expand size. + return; + } + if (shares[a].varofs == start) + return; + } + + + shares[numshares].varofs = start; + shares[numshares].size = size; + numshares++; +} + + +//void ShowWatch(void); + +void QC_InitShares(progfuncs_t *progfuncs) +{ +// ShowWatch(); + if (!field) //don't make it so we will just need to remalloc everything + { + maxfields = 64; + field = memalloc(sizeof(fdef_t) * maxfields); + } + + numfields = 0; + progfuncs->fieldadjust = 0; +} + + +//called if a global is defined as a field +//returns offset. + +//vectors must be added before any of thier corresponding _x/y/z vars +//in this way, even screwed up progs work. +int QC_RegisterFieldVar(progfuncs_t *progfuncs, unsigned int type, char *name, int requestedpos, int origionalofs) +{ +// progstate_t *p; +// int pnum; + unsigned int i; + int namelen; + int ofs; + + int fnum; + + if (!name) + { + progfuncs->fieldadjust = pr_edict_size/4; + return 0; + } + + + prinst->reorganisefields = true; + + //look for an existing match + for (i = 0; i < numfields; i++) + { + if (!strcmp(name, field[i].s_name)) + { + if (field[i].type != type) + { + printf("Field type mismatch on %s\n", name); + continue; + } + if (!progfuncs->fieldadjust && requestedpos>=0) + if ((unsigned)requestedpos != field[i].ofs) + Sys_Error("Field %s at wrong offset", name); + + if (field[i].requestedofs == -1) + field[i].requestedofs = origionalofs; + return field[i].ofs; //got a match + } + } + + if (numfields+1>maxfields) + { + fdef_t *nf; + i = maxfields; + maxfields += 32; + nf = memalloc(sizeof(fdef_t) * maxfields); + memcpy(nf, field, sizeof(fdef_t) * i); + memfree(field); + field = nf; + } + + //try to add a new one + fnum = numfields; + numfields++; + field[fnum].s_name = name; + if (type == ev_vector) //resize with the following floats (this is where I think I went wrong) + { + char *n; + namelen = strlen(name)+5; + + n=PRHunkAlloc(progfuncs, namelen); + sprintf(n, "%s_x", name); + field[fnum].ofs = ofs = QC_RegisterFieldVar(progfuncs, ev_float, n, requestedpos, -1); + + n=PRHunkAlloc(progfuncs, namelen); + sprintf(n, "%s_y", name); + QC_RegisterFieldVar(progfuncs, ev_float, n, (requestedpos==-1)?-1:(requestedpos+4), -1); + + n=PRHunkAlloc(progfuncs, namelen); + sprintf(n, "%s_z", name); + QC_RegisterFieldVar(progfuncs, ev_float, n, (requestedpos==-1)?-1:(requestedpos+8), -1); + } + else if (requestedpos >= 0) + { + for (i = 0; i < numfields-1; i++) + { + if (field[i].ofs == (unsigned)requestedpos) + { + if (type == ev_float && field[i].type == ev_vector) //check names + { + if (strncmp(field[i].s_name, name, strlen(field[i].s_name))) + Sys_Error("Duplicated offset"); + } + else + Sys_Error("Duplicated offset"); + } + } + if (requestedpos&3) + Sys_Error("field %s is %i&3", name, requestedpos); + field[fnum].ofs = ofs = requestedpos/4; + } + else + field[fnum].ofs = ofs = pr_edict_size/4; +// if (type != ev_vector) + if (pr_edict_size < (ofs+type_size[type])*4) + pr_edict_size = (ofs+type_size[type])*4; + + if (pr_max_edict_size && pr_edict_size > pr_max_edict_size) + Sys_Error("Allocated too many additional fields after ents were inited."); + field[fnum].type = type; + + field[fnum].requestedofs = origionalofs; + + //we've finished setting the structure + return ofs; +} + + +//called if a global is defined as a field +void QC_AddSharedFieldVar(progfuncs_t *progfuncs, int num) +{ +// progstate_t *p; +// int pnum; + unsigned int i, o; + + char *s; + + //look for an existing match not needed, cos we look a little later too. + /* + for (i = 0; i < numfields; i++) + { + if (!strcmp(pr_globaldefs[num].s_name, field[i].s_name)) + { + //really we should look for a field def + + *(int *)&pr_globals[pr_globaldefs[num].ofs] = field[i].ofs; //got a match + + return; + } + } + */ + + switch(current_progstate->intsize) + { + case 24: + case 16: + for (i=1 ; inumfielddefs; i++) + { + if (!strcmp(pr_fielddefs16[i].s_name, pr_globaldefs16[num].s_name)) + { + *(int *)&pr_globals[pr_globaldefs16[num].ofs] = QC_RegisterFieldVar(progfuncs, pr_fielddefs16[i].type, pr_globaldefs16[num].s_name, -1, *(int *)&pr_globals[pr_globaldefs16[num].ofs])-progfuncs->fieldadjust; + return; + } + } + + s = pr_globaldefs16[num].s_name; + + for (i = 0; i < numfields; i++) + { + o = field[i].requestedofs; + if (o == *(unsigned int *)&pr_globals[pr_globaldefs16[num].ofs]) + { + *(int *)&pr_globals[pr_globaldefs16[num].ofs] = field[i].ofs-progfuncs->fieldadjust; + return; + } + } + + //oh well, must be a parameter. + if (*(int *)&pr_globals[pr_globaldefs16[num].ofs]) + Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", pr_globaldefs16[num].s_name, *(int *)&pr_globals[pr_globaldefs16[num].ofs]); + return; + case 32: + for (i=1 ; inumfielddefs; i++) + { + if (!strcmp(pr_fielddefs32[i].s_name, pr_globaldefs32[num].s_name)) + { + *(int *)&pr_globals[pr_globaldefs32[num].ofs] = QC_RegisterFieldVar(progfuncs, pr_fielddefs32[i].type, pr_globaldefs32[num].s_name, -1, *(int *)&pr_globals[pr_globaldefs32[num].ofs])-progfuncs->fieldadjust; + return; + } + } + + s = pr_globaldefs32[num].s_name; + + for (i = 0; i < numfields; i++) + { + o = field[i].requestedofs; + if (o == *(unsigned int *)&pr_globals[pr_globaldefs32[num].ofs]) + { + *(int *)&pr_globals[pr_globaldefs32[num].ofs] = field[i].ofs-progfuncs->fieldadjust; + return; + } + } + + //oh well, must be a parameter. + if (*(int *)&pr_globals[pr_globaldefs32[num].ofs]) + Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", pr_globaldefs32[num].s_name, *(int *)&pr_globals[pr_globaldefs32[num].ofs]); + return; + default: + Sys_Error("Bad bits"); + break; + } + Sys_Error("Should be unreachable"); +} + + + + + + + +/* +//Just a bit of code that makes a window appear with lots of variables listed. +//A little useless really. + + + +static void WatchDraw(window_t *wnd); +static void WatchDead(window_t *wnd); +static bool WatchKeyDown(window_t *wnd, int k); + +typedef struct { + int progs; + int globofs; +} watchinfo_t; + +static window_t watchtemplate = { + sizeof(window_t), //int size; //for possible later expansion + "Watch", //char *title; + BORDER_RESIZE, //void (*DrawBorder) (struct window_s *wnd); //the border drawing func (use a borde type) + WatchDraw, //void (*DrawWindow) (struct window_s *); //the drawing func + NULL, //void (*Draw3dWindow) (struct window_s *); //the function to draw 3d stuff + WatchDead, //void (*CloseWindow) (struct window_s *); //when it is closed + WatchKeyDown, //bool (*KeyDown) (struct window_s *, int key); //return true to stop the main game from recieving the call + NULL, //void (*KeyUp) (struct window_s *, int key); //sent to all + NULL, //void (*Think) (struct window_s *); + NULL, //void (*ReloadTex) (struct window_s *); + + {320, 0, 640, 240},//float viewarea[4]; //l, t, r, b + {1, 10, 1, 1},//float bordersize[4]; //l,t,r,b + {0, 0, 0},//float vieworigin[3]; //3d view origin + {0, 0, 0},//float viewangles[3]; //3d angles + + TRUE,//bool clear; //should it be cleared first (for 3d rendering and default border routine) + + NULL,//void *data; //use this to get unique windows of the same type + 0,//int classid; //a randomly chosen number that is the same for each of this window's type + 0,//int subclass; //a number if an app needs to identify between windows of the same class + + NULL//void *(*comunicate) (int type, void *info, void *moreinfo); //later development for chatting between windows (like the 'SendMessage' function in the OS) + + //for multiple windows + //struct window_s *next; + //struct window_s *prev; +}; + +void ShowWatch(void) +{ + watchinfo_t *inf; + window_t *wnd; + wnd = memalloc(sizeof(window_t)+sizeof(watchinfo_t), "watch window"); + memcpy(wnd, &watchtemplate, sizeof(window_t)); + wnd->data = inf = (watchinfo_t *)(wnd+1); + inf->globofs = 1; + inf->progs = 0; + AddWindow(wnd); +} + +static void WatchDead(window_t *wnd) +{ + RemoveWindow(wnd); + memfree(wnd); +} + +static bool WatchKeyDown(window_t *wnd, int k) +{ + watchinfo_t *inf = wnd->data; + int progs = inf->progs; + if (progs < 0) + progs = pr_typecurrent; + + switch(k) + { + case K_MOUSEWUP: + inf->globofs-=8; + if (inf->globofs < 1) + inf->globofs = 1; + break; + case K_MOUSEWDOWN: + inf->globofs+=8; + if (inf->globofs > pr_progstate[progs].progs->numglobaldefs-1) + inf->globofs = pr_progstate[progs].progs->numglobaldefs-1; + break; + case K_ESCAPE: + RemoveWindow(wnd); + break; + } + return true; +} + +char *PR_ValueString (etype_t type, eval_t *val); +static void WatchDraw(window_t *wnd) +{ + float yofs; + int def=0; + int progs = ((watchinfo_t *)wnd->data)->progs; + + if (progs < 0) + progs = pr_typecurrent; + + if (!pr_progstate[progs].progs) + { + Draw_String(wnd->viewarea[0], wnd->viewarea[1]+8+def*8, "Progs not loaded", 1, 0); + return; + } + +// if (sv_edicts==NULL) +// return; + + yofs=wnd->viewarea[1]; + Draw_String(wnd->viewarea[0], yofs, pr_progstate[progs].filename, 1, 0);yofs+=8; + + Draw_String(wnd->viewarea[0], yofs, Sva("%3i %16s %s", OFS_RETURN, "RETURN", PR_ValueString(ev_vector, (eval_t *)&pr_progstate[progs].globals[OFS_RETURN])), 1, 0);yofs+=8; + Draw_String(wnd->viewarea[0], yofs, Sva("%3i %16s %s", OFS_PARM0, "PARM0", PR_ValueString(ev_vector, (eval_t *)&pr_progstate[progs].globals[OFS_PARM0])), 1, 0);yofs+=8; + Draw_String(wnd->viewarea[0], yofs, Sva("%3i %16s %s", OFS_PARM1, "PARM1", PR_ValueString(ev_vector, (eval_t *)&pr_progstate[progs].globals[OFS_PARM1])), 1, 0);yofs+=8; + Draw_String(wnd->viewarea[0], yofs, Sva("%3i %16s %s", OFS_PARM2, "PARM2", PR_ValueString(ev_vector, (eval_t *)&pr_progstate[progs].globals[OFS_PARM2])), 1, 0);yofs+=8; + Draw_String(wnd->viewarea[0], yofs, Sva("%3i %16s %s", OFS_PARM3, "PARM3", PR_ValueString(ev_vector, (eval_t *)&pr_progstate[progs].globals[OFS_PARM3])), 1, 0);yofs+=8; + Draw_String(wnd->viewarea[0], yofs, Sva("%3i %16s %s", OFS_PARM4, "PARM4", PR_ValueString(ev_vector, (eval_t *)&pr_progstate[progs].globals[OFS_PARM4])), 1, 0);yofs+=8; + Draw_String(wnd->viewarea[0], yofs, Sva("%3i %16s %s", OFS_PARM5, "PARM5", PR_ValueString(ev_vector, (eval_t *)&pr_progstate[progs].globals[OFS_PARM5])), 1, 0);yofs+=8; + Draw_String(wnd->viewarea[0], yofs, Sva("%3i %16s %s", OFS_PARM6, "PARM6", PR_ValueString(ev_vector, (eval_t *)&pr_progstate[progs].globals[OFS_PARM6])), 1, 0);yofs+=8; + Draw_String(wnd->viewarea[0], yofs, Sva("%3i %16s %s", OFS_PARM7, "PARM7", PR_ValueString(ev_vector, (eval_t *)&pr_progstate[progs].globals[OFS_PARM7])), 1, 0);yofs+=8; + for (def = ((watchinfo_t *)wnd->data)->globofs; def < pr_progstate[progs].progs->numglobaldefs; def++) + { + if ((pr_progstate[progs].globaldefs[def].type &~DEF_SAVEGLOBAL)== ev_entity && sv_edicts==NULL) + { + grColor4f(0.5, 0.5, 0.5, 1); + Draw_String(wnd->viewarea[0], yofs, Sva("%3i %16s %s", pr_progstate[progs].globaldefs[def].ofs, pr_progstate[progs].globaldefs[def].s_name, "Entities not initialized"), 1, 0); + } + else + { + if (pr_progstate[progs].globaldefs[def].type == ev_void || pr_progstate[progs].globaldefs[def].type == ev_field || pr_progstate[progs].globaldefs[def].type == ev_function || !(pr_progstate[progs].globaldefs[def].type & DEF_SAVEGLOBAL)) + grColor4f(0.5, 0.5, 0.5, 1); + else + grColor4f(1, 1, 1, 1); + Draw_String(wnd->viewarea[0], yofs, Sva("%3i %16s %s", pr_progstate[progs].globaldefs[def].ofs, pr_progstate[progs].globaldefs[def].s_name, PR_ValueString(pr_progstate[progs].globaldefs[def].type, (eval_t *)&pr_progstate[progs].globals[pr_progstate[progs].globaldefs[def].ofs])), 1, 0); + } + yofs+=8; + } +} + +*/ + diff --git a/engine/qclib/progtype.h b/engine/qclib/progtype.h new file mode 100644 index 000000000..3c978210d --- /dev/null +++ b/engine/qclib/progtype.h @@ -0,0 +1,17 @@ +#ifndef DLL_PROG + +#else +typedef float vec3_t[3]; +#endif + +#ifndef t_bool +#define t_bool +typedef int pbool; + +#else +#define t_bool +#endif +typedef int progsnum_t; +typedef int func_t; +typedef char *string_t; + diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h new file mode 100644 index 000000000..7804a93cf --- /dev/null +++ b/engine/qclib/qcc.h @@ -0,0 +1,827 @@ +#define COMPILER +#define PROGSUSED + +//#define COMMONINLINES +//#define inline _inline + +#include "cmdlib.h" +#include +/* +#include +#include + + +#include "pr_comp.h" +*/ + +//this is for testing +#define WRITEASM + +#ifdef __MINGW32_VERSION +#define MINGW +#endif + +#define progfuncs qccprogfuncs +extern progfuncs_t *qccprogfuncs; + +#if _WIN32 && !defined(MINGW) +#define inline _inline +#endif + +void *qccHunkAlloc(size_t mem); + +extern short (*BigShort) (short l); +extern short (*LittleShort) (short l); +extern long (*BigLong) (long l); +extern long (*LittleLong) (long l); +extern float (*BigFloat) (float l); +extern float (*LittleFloat) (float l); + + +#define MAX_ERRORS 10 + +#define MAX_NAME 64 // chars long + +extern unsigned int MAX_REGS; + +extern int MAX_STRINGS; +extern int MAX_GLOBALS; +extern int MAX_FIELDS; +extern int MAX_STATEMENTS; +extern int MAX_FUNCTIONS; + +#define MAX_SOUNDS 1024 //convert to int? +#define MAX_TEXTURES 1024 //convert to int? +#define MAX_MODELS 1024 //convert to int? +#define MAX_FILES 1024 //convert to int? +#define MAX_DATA_PATH 64 + +extern int MAX_CONSTANTS; +#define MAXCONSTANTLENGTH 32 +#define MAXCONSTANTVALUELENGTH 256 +#define MAXCONSTANTPARAMLENGTH 32 +#define MAXCONSTANTPARAMS 4 + +typedef enum {QCF_STANDARD, QCF_HEXEN2, QCF_FTE, QCF_FTE32, QCF_FTEDEBUG, QCF_FTEDEBUG32, QCF_KK7} qcc_targetformat_t; +extern qcc_targetformat_t qcc_targetformat; + + +/* + +TODO: + +"stopped at 10 errors" + +other pointer types for models and clients? + +compact string heap? + +allways initialize all variables to something safe + +the def->type->type arrangement is really silly. + +return type checking + +parm count type checking + +immediate overflow checking + +pass the first two parms in call->b and call->c + +*/ + +/* + +comments +-------- +// comments discard text until the end of line +/ * * / comments discard all enclosed text (spaced out on this line because this documentation is in a regular C comment block, and typing them in normally causes a parse error) + +code structure +-------------- +A definition is: + [ = ] {, [ = ] }; + + +types +----- +simple types: void, float, vector, string, or entity + float width, height; + string name; + entity self, other; + +vector types: + vector org; // also creates org_x, org_y, and org_z float defs + + +A function type is specified as: simpletype ( type name {,type name} ) +The names are ignored except when the function is initialized. + void() think; + entity() FindTarget; + void(vector destination, float speed, void() callback) SUB_CalcMove; + void(...) dprint; // variable argument builtin + +A field type is specified as: .type + .vector origin; + .string netname; + .void() think, touch, use; + + +names +----- +Names are a maximum of 64 characters, must begin with A-Z,a-z, or _, and can continue with those characters or 0-9. + +There are two levels of scoping: global, and function. The parameter list of a function and any vars declared inside a function with the "local" statement are only visible within that function, + + +immediates +---------- +Float immediates must begin with 0-9 or minus sign. .5 is illegal. + +A parsing ambiguity is present with negative constants. "a-5" will be parsed as "a", then "-5", causing an error. Seperate the - from the digits with a space "a - 5" to get the proper behavior. + 12 + 1.6 + 0.5 + -100 + +Vector immediates are three float immediates enclosed in single quotes. + '0 0 0' + '20.5 -10 0.00001' + +String immediates are characters enclosed in double quotes. The string cannot contain explicit newlines, but the escape character \n can embed one. The \" escape can be used to include a quote in the string. + "maps/jrwiz1.bsp" + "sound/nin/pain.wav" + "ouch!\n" + +Code immediates are statements enclosed in {} braces. +statement: + { } + ; + local [ = ] {, [ = ] }; + return ; + if ( ) [ else ]; + while ( ) ; + do while ( ); + ( ); + +expression: + combiations of names and these operators with standard C precedence: + "&&", "||", "<=", ">=","==", "!=", "!", "*", "/", "-", "+", "=", ".", "<", ">", "&", "|" + Parenthesis can be used to alter order of operation. + The & and | operations perform integral bit ops on floats + +A built in function immediate is a number sign followed by an integer. + #1 + #12 + + +compilation +----------- +Source files are processed sequentially without dumping any state, so if a defs file is the first one processed, the definitions will be available to all other files. + +The language is strongly typed and there are no casts. + +Anything that is initialized is assumed to be constant, and will have immediates folded into it. If you change the value, your program will malfunction. All uninitialized globals will be saved to savegame files. + +Functions cannot have more than eight parameters. + +Error recovery during compilation is minimal. It will skip to the next global definition, so you will never see more than one error at a time in a given function. All compilation aborts after ten error messages. + +Names can be defined multiple times until they are defined with an initialization, allowing functions to be prototyped before their definition. + +void() MyFunction; // the prototype + +void() MyFunction = // the initialization +{ + dprint ("we're here\n"); +}; + + +entities and fields +------------------- + + +execution +--------- +Code execution is initiated by C code in quake from two main places: the timed think routines for periodic control, and the touch function when two objects impact each other. + +There are three global variables that are set before beginning code execution: + entity world; // the server's world object, which holds all global + // state for the server, like the deathmatch flags + // and the body ques. + entity self; // the entity the function is executing for + entity other; // the other object in an impact, not used for thinks + float time; // the current game time. Note that because the + // entities in the world are simulated sequentially, + // time is NOT strictly increasing. An impact late + // in one entity's time slice may set time higher + // than the think function of the next entity. + // The difference is limited to 0.1 seconds. +Execution is also caused by a few uncommon events, like the addition of a new client to an existing server. + +There is a runnaway counter that stops a program if 100000 statements are executed, assuming it is in an infinite loop. + +It is acceptable to change the system set global variables. This is usually done to pose as another entity by changing self and calling a function. + +The interpretation is fairly efficient, but it is still over an order of magnitude slower than compiled C code. All time consuming operations should be made into built in functions. + +A profile counter is kept for each function, and incremented for each interpreted instruction inside that function. The "profile" console command in Quake will dump out the top 10 functions, then clear all the counters. The "profile all" command will dump sorted stats for every function that has been executed. + + +afunc ( 4, bfunc(1,2,3)); +will fail because there is a shared parameter marshaling area, which will cause the 1 from bfunc to overwrite the 4 allready placed in parm0. When a function is called, it copies the parms from the globals into it's privately scoped variables, so there is no collision when calling another function. + +total = factorial(3) + factorial(4); +Will fail because the return value from functions is held in a single global area. If this really gets on your nerves, tell me and I can work around it at a slight performance and space penalty by allocating a new register for the function call and copying it out. + + +built in functions +------------------ +void(string text) dprint; +Prints the string to the server console. + +void(entity client, string text) cprint; +Prints a message to a specific client. + +void(string text) bprint; +Broadcast prints a message to all clients on the current server. + +entity() spawn; +Returns a totally empty entity. You can manually set everything up, or just set the origin and call one of the existing entity setup functions. + +entity(entity start, .string field, string match) find; +Searches the server entity list beginning at start, looking for an entity that has entity.field = match. To start at the beginning of the list, pass world. World is returned when the end of the list is reached. + + + + +gotchas +------- + +The && and || operators DO NOT EARLY OUT like C! + +Don't confuse single quoted vectors with double quoted strings + +The function declaration syntax takes a little getting used to. + +Don't forget the ; after the trailing brace of a function initialization. + +Don't forget the "local" before defining local variables. + +There are no ++ / -- operators, or operate/assign operators. + +*/ + + +#if 1 +#include "hash.h" +extern hashtable_t compconstantstable; +extern hashtable_t globalstable, localstable; +#endif + +#ifdef WRITEASM +FILE *asmfile; +#endif +//============================================================================= + +// offsets are allways multiplied by 4 before using +typedef unsigned int gofs_t; // offset in global data block +typedef struct QCC_function_s QCC_function_t; + +#define MAX_PARMS 8 + +typedef struct QCC_type_s +{ + etype_t type; + + struct QCC_type_s *parentclass; //type_entity... + struct QCC_type_s *next; +// function types are more complex + struct QCC_type_s *aux_type; // return type or field type + struct QCC_type_s *param; + int num_parms; // -1 = variable args +// struct QCC_type_s *parm_types[MAX_PARMS]; // only [num_parms] allocated + + unsigned int ofs; //inside a structure. + unsigned int size; + char *name; + +} QCC_type_t; +int typecmp(QCC_type_t *a, QCC_type_t *b); + +typedef struct temp_s { + gofs_t ofs; + struct QCC_def_s *scope; +#ifdef WRITEASM + struct QCC_def_s *lastfunc; +#endif + struct temp_s *next; + pbool used; + unsigned int size; +} temp_t; + +//not written +typedef struct QCC_def_s +{ + QCC_type_t *type; + char *name; + struct QCC_def_s *next; + struct QCC_def_s *nextlocal; //provides a chain of local variables for the opt_locals_marshalling optimisation. + gofs_t ofs; + struct QCC_def_s *scope; // function the var was defined in, or NULL + int initialized; // 1 when a declaration included "= immediate" + int constant; // 1 says we can use the value over and over again + + int references; + int timescalled; //part of the opt_stripfunctions optimisation. + + int s_file; + int s_line; + + int arraysize; + pbool shared; + pbool saved; + + temp_t *temp; +} QCC_def_t; + +//============================================================================ + +// pr_loc.h -- program local defs + + +//============================================================================= +extern char QCC_copyright[1024]; +extern char QCC_Packname[5][128]; +extern int QCC_packid; + +typedef union QCC_eval_s +{ + QCC_string_t string; + float _float; + float vector[3]; + func_t function; + int _int; + union QCC_eval_s *ptr; +} QCC_eval_t; + +const extern int type_size[9]; +//extern QCC_def_t *def_for_type[9]; + +extern QCC_type_t *type_void, *type_string, *type_float, *type_vector, *type_entity, *type_field, *type_function, *type_pointer, *type_integer, *type_floatfield; + +struct QCC_function_s +{ + int builtin; // if non 0, call an internal function + int code; // first statement + char *file; // source file with definition + int file_line; + struct QCC_def_s *def; + unsigned int parm_ofs[MAX_PARMS]; // allways contiguous, right? +}; + + +// +// output generated by prog parsing +// +typedef struct +{ + char *memory; + int max_memory; + int current_memory; + QCC_type_t *types; + + QCC_def_t def_head; // unused head of linked list + QCC_def_t *def_tail; // add new defs after this and move it + QCC_def_t *localvars; // chain of variables which need to be pushed and stuff. + + int size_fields; +} QCC_pr_info_t; + +extern QCC_pr_info_t pr; + + +typedef struct +{ + char name[MAXCONSTANTLENGTH]; + char value[MAXCONSTANTVALUELENGTH]; + char params[MAXCONSTANTPARAMS][MAXCONSTANTPARAMLENGTH]; + int numparams; + pbool used; + + int namelen; +} CompilerConstant_t; +extern CompilerConstant_t *CompilerConstant; + +//============================================================================ + +extern pbool pr_dumpasm; + +//extern QCC_def_t **pr_global_defs; // to find def for a global variable + +typedef enum { +tt_eof, // end of file reached +tt_name, // an alphanumeric name token +tt_punct, // code punctuation +tt_immediate, // string, float, vector +} token_type_t; + +extern char pr_token[8192]; +extern token_type_t pr_token_type; +extern QCC_type_t *pr_immediate_type; +extern QCC_eval_t pr_immediate; + +extern pbool keyword_var; +extern pbool keyword_thinktime; +extern pbool keyword_switch; +extern pbool keyword_for; +extern pbool keyword_case; +extern pbool keyword_default; +extern pbool keyword_do; +extern pbool keyword_asm; +extern pbool keyword_goto; +extern pbool keyword_break; +extern pbool keyword_continue; +extern pbool keyword_state; +extern pbool keyword_string; +extern pbool keyword_float; +extern pbool keyword_entity; +extern pbool keyword_vector; +extern pbool keyword_integer; +extern pbool keyword_int; +extern pbool keyword_const; +extern pbool keyword_class; + +extern pbool keywords_coexist; +extern pbool output_parms; +extern pbool autoprototype; + +extern pbool opt_overlaptemps; +extern pbool opt_shortenifnots; +extern pbool opt_noduplicatestrings; +extern pbool opt_constantarithmatic; +extern pbool opt_nonvec_parms; +extern pbool opt_constant_names; +extern pbool opt_precache_file; +extern pbool opt_filenames; +extern pbool opt_assignments; +extern pbool opt_unreferenced; +extern pbool opt_function_names; +extern pbool opt_locals; +extern pbool opt_dupconstdefs; +extern pbool opt_constant_names_strings; +extern pbool opt_return_only; +extern pbool opt_compound_jumps; +//extern pbool opt_comexprremoval; +extern pbool opt_stripfunctions; +extern pbool opt_locals_marshalling; +extern pbool opt_logicops; + +extern int optres_shortenifnots; +extern int optres_overlaptemps; +extern int optres_noduplicatestrings; +extern int optres_constantarithmatic; +extern int optres_nonvec_parms; +extern int optres_constant_names; +extern int optres_precache_file; +extern int optres_filenames; +extern int optres_assignments; +extern int optres_unreferenced; +extern int optres_function_names; +extern int optres_locals; +extern int optres_dupconstdefs; +extern int optres_constant_names_strings; +extern int optres_return_only; +extern int optres_compound_jumps; +//extern int optres_comexprremoval; +extern int optres_stripfunctions; +extern int optres_locals_marshalling; +extern int optres_logicops; + +pbool CompileParams(progfuncs_t *progfuncs, int doall, int nump, char **parms); + +void QCC_PR_PrintStatement (QCC_dstatement_t *s); + +void QCC_PR_Lex (void); +// reads the next token into pr_token and classifies its type + +QCC_type_t *QCC_PR_ParseType (int newtype); +QCC_type_t *QCC_TypeForName(char *name); +QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype); +char *QCC_PR_ParseName (void); +CompilerConstant_t *QCC_PR_DefineName(char *name); + +void QCC_RemapOffsets(unsigned int firststatement, unsigned int laststatement, unsigned int min, unsigned int max, unsigned int newmin); + +#ifndef COMMONINLINES +pbool QCC_PR_Check (char *string); +void QCC_PR_Expect (char *string); +#endif +void VARGS QCC_PR_ParseError (int errortype, char *error, ...); +void VARGS QCC_PR_ParseWarning (int warningtype, char *error, ...); +void VARGS QCC_PR_Warning (int type, char *file, int line, char *error, ...); +void QCC_PR_ParsePrintDef (int warningtype, QCC_def_t *def); +void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, char *error, ...); + +//QccMain.c must be changed if this is changed. +enum { + WARN_DEBUGGING, + WARN_ERROR, + WARN_NOTREFERENCED, + WARN_NOTREFERENCEDCONST, + WARN_CONFLICTINGRETURNS, + WARN_TOOFEWPARAMS, + WARN_TOOMANYPARAMS, + WARN_UNEXPECTEDPUNCT, + WARN_ASSIGNMENTTOCONSTANT, + WARN_MISSINGRETURNVALUE, + WARN_WRONGRETURNTYPE, + WARN_POINTLESSSTATEMENT, + WARN_MISSINGRETURN, + WARN_DUPLICATEDEFINITION, + WARN_PRECOMPILERMESSAGE, + WARN_STRINGTOOLONG, + WARN_BADTARGET, + WARN_BADPRAGMA, + WARN_HANGINGSLASHR, + WARN_NOTDEFINED, + WARN_SWITCHTYPEMISMATCH, + WARN_CONFLICTINGUNIONMEMBER, + WARN_KEYWORDDISABLED, + WARN_ENUMFLAGS_NOTINTEGER, + WARN_ENUMFLAGS_NOTBINARY, + WARN_CASEINSENSATIVEFRAMEMACRO, + WARN_DUPLICATELABEL, + WARN_ASSIGNMENTINCONDITIONAL, + WARN_MACROINSTRING, + WARN_BADPARAMS, + WARN_IMPLICITCONVERSION, + WARN_FIXEDRETURNVALUECONFLICT, + WARN_EXTRAPRECACHE, + WARN_NOTPRECACHED, + WARN_DEADCODE, + + ERR_PARSEERRORS, //caused by qcc_pr_parseerror beung called. + + //these are definatly my fault... + ERR_INTERNAL, + ERR_TOOCOMPLEX, + ERR_BADOPCODE, + ERR_TOOMANYSTATEMENTS, + ERR_TOOMANYSTRINGS, + ERR_BADTARGETSWITCH, + ERR_TOOMANYTYPES, + ERR_TOOMANYPAKFILES, + ERR_PRECOMPILERCONSTANTTOOLONG, + ERR_MACROTOOMANYPARMS, + ERR_CONSTANTTOOLONG, + ERR_TOOMANYFRAMEMACROS, + + //limitations, some are imposed by compiler, some arn't. + ERR_TOOMANYGLOBALS, + ERR_TOOMANYGOTOS, + ERR_TOOMANYBREAKS, + ERR_TOOMANYCONTINUES, + ERR_TOOMANYCASES, + ERR_TOOMANYLABELS, + ERR_TOOMANYOPENFILES, + ERR_TOOMANYPARAMETERSVARARGS, + ERR_TOOMANYPARAMETERSFORFUNC, + ERR_TOOMANYTOTALPARAMETERS, + + //these are probably yours, or qcc being fussy. + ERR_BADEXTENSION, + ERR_BADIMMEDIATETYPE, + ERR_NOOUTPUT, + ERR_NOTAFUNCTION, + ERR_BADHEX, + ERR_UNKNOWNPUCTUATION, + ERR_EXPECTED, + ERR_NOTANAME, + ERR_NAMETOOLONG, + ERR_NOFUNC, + ERR_COULDNTOPENFILE, + ERR_NOTFUNCTIONTYPE, + ERR_TOOFEWPARAMS, + ERR_TOOMANYPARAMS, + ERR_CONSTANTNOTDEFINED, + ERR_BADFRAMEMACRO, + ERR_TYPEMISMATCH, + ERR_TYPEMISMATCHREDEC, + ERR_TYPEMISMATCHPARM, + ERR_TYPEMISMATCHARRAYSIZE, + ERR_UNEXPECTEDPUNCTUATION, + ERR_NOTACONSTANT, + ERR_REDECLARATION, + ERR_INITIALISEDLOCALFUNCTION, + ERR_NOTDEFINED, + ERR_ARRAYNEEDSSIZE, + ERR_ARRAYNEEDSBRACES, + ERR_TOOMANYINITIALISERS, + ERR_TYPEINVALIDINSTRUCT, + ERR_NOSHAREDLOCALS, + ERR_TYPEWITHNONAME, + ERR_BADARRAYSIZE, + ERR_NONAME, + ERR_SHAREDINITIALISED, + ERR_UNKNOWNVALUE, + ERR_BADARRAYINDEXTYPE, + ERR_NOVALIDOPCODES, + ERR_MEMBERNOTVALID, + ERR_BADPLUSPLUSOPERATOR, + ERR_BADNOTTYPE, + ERR_BADTYPECAST, + ERR_MULTIPLEDEFAULTS, + ERR_CASENOTIMMEDIATE, + ERR_BADSWITCHTYPE, + ERR_BADLABELNAME, + ERR_NOLABEL, + ERR_THINKTIMETYPEMISMATCH, + ERR_STATETYPEMISMATCH, + ERR_BADBUILTINIMMEDIATE, + ERR_PARAMWITHNONAME, + ERR_BADPARAMORDER, + ERR_ILLEGALCONTINUES, + ERR_ILLEGALBREAKS, + ERR_ILLEGALCASES, + ERR_NOTANUMBER, + ERR_WRONGSUBTYPE, + ERR_EOF, + ERR_NOPRECOMPILERIF, + ERR_HASHERROR, + ERR_NOTATYPE, + ERR_TOOMANYPACKFILES, + ERR_INVALIDVECTORIMMEDIATE, + ERR_INVALIDSTRINGIMMEDIATE, + ERR_BADCHARACTURECODE, + ERR_BADPARMS, + + WARN_MAX +}; + + +typedef struct { + pbool *enabled; + char *abbrev; + int optimisationlevel; + int flags; //1: kills debuggers. 2: applied as default. + char *fullname; + void *guiinfo; +} optimisations_t; +extern optimisations_t optimisations[]; + +extern pbool qccwarningdisabled[WARN_MAX]; + +extern jmp_buf pr_parse_abort; // longjump with this on parse error +extern int pr_source_line; +extern char *pr_file_p; + +void *QCC_PR_Malloc (int size); + + +#define OFS_NULL 0 +#define OFS_RETURN 1 +#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors +#define OFS_PARM1 7 +#define OFS_PARM2 10 +#define OFS_PARM3 13 +#define OFS_PARM4 16 +#define RESERVED_OFS 28 + + +extern QCC_def_t *pr_scope; +extern int pr_error_count; + +void QCC_PR_NewLine (pbool incomment); +QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool allocate, int arraysize); + +void QCC_PR_PrintDefs (void); + +void QCC_PR_SkipToSemicolon (void); + +#define MAX_EXTRA_PARMS 128 +#ifdef MAX_EXTRA_PARMS +extern char pr_parm_names[MAX_PARMS+MAX_EXTRA_PARMS][MAX_NAME]; +extern QCC_def_t *extra_parms[MAX_EXTRA_PARMS]; +#else +extern char pr_parm_names[MAX_PARMS][MAX_NAME]; +#endif +extern pbool pr_trace; + +#define G_FLOAT(o) (qcc_pr_globals[o]) +#define G_INT(o) (*(int *)&qcc_pr_globals[o]) +#define G_VECTOR(o) (&qcc_pr_globals[o]) +#define G_STRING(o) (strings + *(QCC_string_t *)&qcc_pr_globals[o]) +#define G_FUNCTION(o) (*(func_t *)&qcc_pr_globals[o]) + +char *QCC_PR_ValueString (etype_t type, void *val); + +void QCC_PR_ClearGrabMacros (void); + +pbool QCC_PR_CompileFile (char *string, char *filename); + +extern pbool pr_dumpasm; + +extern QCC_string_t s_file; // filename for function definition + +extern QCC_def_t def_ret, def_parms[MAX_PARMS]; + +func_t QCC_PR_EmitArrayGetFunction(char *arrayname); +func_t QCC_PR_EmitArraySetFunction(char *arrayname); +int QCC_PR_EmitClassFromFunction(QCC_def_t *scope, char *tname); + +//============================================================================= + +extern char pr_immediate_string[8192]; + +extern float *qcc_pr_globals; +extern unsigned int numpr_globals; + +extern char *strings; +extern int strofs; + +extern QCC_dstatement_t *statements; +extern int numstatements; +extern int *statement_linenums; + +extern QCC_dfunction_t *functions; +extern int numfunctions; + +extern QCC_ddef_t *qcc_globals; +extern int numglobaldefs; + +extern QCC_def_t *activetemps; + +extern QCC_ddef_t *fields; +extern int numfielddefs; + +extern QCC_type_t *qcc_typeinfo; +extern int numtypeinfos; +extern int maxtypeinfos; + +extern int *qcc_tempofs; +extern int max_temps; +//extern int qcc_functioncalled; //unuse temps if this is true - don't want to reuse the same space. + +extern int tempsstart; +extern int numtemps; + +typedef char PATHSTRING[MAX_DATA_PATH]; + +PATHSTRING *precache_sounds; +int *precache_sounds_block; +int *precache_sounds_used; +int numsounds; + +PATHSTRING *precache_textures; +int *precache_textures_block; +int numtextures; + +PATHSTRING *precache_models; +int *precache_models_block; +int *precache_models_used; +int nummodels; + +PATHSTRING *precache_files; +int *precache_files_block; +int numfiles; + +int QCC_CopyString (char *str); + + + + +typedef struct qcc_cachedsourcefile_s { + char filename[128]; + int size; + char *file; + enum{FT_CODE, FT_DATA} type; //quakec source file or not. + struct qcc_cachedsourcefile_s *next; +} qcc_cachedsourcefile_t; +extern qcc_cachedsourcefile_t *qcc_sourcefile; + + + + + +#ifdef COMMONINLINES +bool inline QCC_PR_Check (char *string) +{ + if (strcmp (string, pr_token)) + return false; + + QCC_PR_Lex (); + return true; +} + +void inline QCC_PR_Expect (char *string) +{ + if (strcmp (string, pr_token)) + QCC_PR_ParseError ("expected %s, found %s",string, pr_token); + QCC_PR_Lex (); +} +#endif + +void editbadfile(char *fname, int line); +char *TypeName(QCC_type_t *type); +void QCC_PR_IncludeChunk (char *data, pbool duplicate, char *filename); diff --git a/engine/qclib/qcc_cmdlib.c b/engine/qclib/qcc_cmdlib.c new file mode 100644 index 000000000..c1fa9a97a --- /dev/null +++ b/engine/qclib/qcc_cmdlib.c @@ -0,0 +1,804 @@ +// cmdlib.c + +#include "qcc.h" +//#include + +#define PATHSEPERATOR '/' + +#ifndef QCC +extern jmp_buf qcccompileerror; +#endif + +// set these before calling CheckParm +int myargc; +char **myargv; + +char qcc_token[1024]; +int qcc_eof; + +#ifndef MINIMAL +/* +================ +I_FloatTime +================ +*/ +/* +double I_FloatTime (void) +{ + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +} + + */ + + +#ifdef QCC +int QC_strncasecmp (const char *s1, const char *s2, int n) +{ + int c1, c2; + + while (1) + { + c1 = *s1++; + c2 = *s2++; + + if (!n--) + return 0; // strings are equal until end point + + if (c1 != c2) + { + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return -1; // strings not equal + } + if (!c1) + return 0; // strings are equal +// s1++; +// s2++; + } + + return -1; +} + +int QC_strcasecmp (const char *s1, const char *s2) +{ + return QC_strncasecmp(s1, s2, 0x7fffffff); +} + +#else +int QC_strncasecmp(const char *s1, const char *s2, int n); +int QC_strcasecmp (const char *s1, const char *s2) +{ + return QC_strncasecmp(s1, s2, 0x7fffffff); +} + +#endif + + + +#endif //minimal +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *QCC_COM_Parse (char *data) +{ + int c; + int len; + + len = 0; + qcc_token[0] = 0; + + if (!data) + return NULL; + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + { + qcc_eof = true; + return NULL; // end of file; + } + data++; + } + +// skip // comments + if (c=='/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + do + { + c = *data++; + if (c=='\"'||c=='\0') + { + qcc_token[len] = 0; + return data; + } + qcc_token[len] = c; + len++; + } while (1); + } + +// parse single characters + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + { + qcc_token[len] = c; + len++; + qcc_token[len] = 0; + return data+1; + } + +// parse a regular word + do + { + qcc_token[len] = c; + data++; + len++; + c = *data; + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':' || c=='\"') + break; + } while (c>32); + + qcc_token[len] = 0; + return data; +} + +char *VARGS qcva (char *text, ...) +{ + va_list argptr; + static char msg[2048]; + + va_start (argptr,text); + QC_vsnprintf (msg,sizeof(msg)-1, text,argptr); + va_end (argptr); + + return msg; +} + + +#ifndef MINIMAL + +char *QC_strupr (char *start) +{ + char *in; + in = start; + while (*in) + { + *in = toupper(*in); + in++; + } + return start; +} + +char *QC_strlower (char *start) +{ + char *in; + in = start; + while (*in) + { + *in = tolower(*in); + in++; + } + return start; +} + + +/* +============================================================================= + + MISC FUNCTIONS + +============================================================================= +*/ + +/* +================= +Error + +For abnormal program terminations +================= +*/ +void VARGS QCC_Error (int errortype, const char *error, ...) +{ + va_list argptr; + char msg[2048]; + + va_start (argptr,error); + QC_vsnprintf (msg,sizeof(msg)-1, error,argptr); + va_end (argptr); + + printf ("\n************ ERROR ************\n%s\n", msg); + + + editbadfile(strings+s_file, pr_source_line); + +#ifndef QCC + longjmp(qcccompileerror, 1); +#else + print ("Press any key\n"); + getch(); +#endif + exit (1); +} + + +/* +================= +CheckParm + +Checks for the given parameter in the program's command line arguments +Returns the argument number (1 to argc-1) or 0 if not present +================= +*/ +int QCC_CheckParm (char *check) +{ + int i; + + for (i = 1;i 0 && path[length] != PATHSEPERATOR) + length--; + path[length] = 0; +} + +void StripExtension (char *path) +{ + int length; + + length = strlen(path)-1; + while (length > 0 && path[length] != '.') + { + length--; + if (path[length] == '/') + return; // no extension + } + if (length) + path[length] = 0; +} + + +/* +==================== +Extract file parts +==================== +*/ +void ExtractFilePath (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != PATHSEPERATOR) + src--; + + memcpy (dest, path, src-path); + dest[src-path] = 0; +} + +void ExtractFileBase (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a \ or the start +// + while (src != path && *(src-1) != PATHSEPERATOR) + src--; + + while (*src && *src != '.') + { + *dest++ = *src++; + } + *dest = 0; +} + +void ExtractFileExtension (char *path, char *dest) +{ + char *src; + + src = path + strlen(path) - 1; + +// +// back up until a . or the start +// + while (src != path && *(src-1) != '.') + src--; + if (src == path) + { + *dest = 0; // no extension + return; + } + + strcpy (dest,src); +} + + +/* +============== +ParseNum / ParseHex +============== +*/ +long ParseHex (char *hex) +{ + char *str; + long num; + + num = 0; + str = hex; + + while (*str) + { + num <<= 4; + if (*str >= '0' && *str <= '9') + num += *str-'0'; + else if (*str >= 'a' && *str <= 'f') + num += 10 + *str-'a'; + else if (*str >= 'A' && *str <= 'F') + num += 10 + *str-'A'; + else + QCC_Error (ERR_BADHEX, "Bad hex number: %s",hex); + str++; + } + + return num; +} + + +long ParseNum (char *str) +{ + if (str[0] == '$') + return ParseHex (str+1); + if (str[0] == '0' && str[1] == 'x') + return ParseHex (str+2); + return atol (str); +} + + + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +#ifdef __BIG_ENDIAN__ + +short QCC_LittleShort (short l) +{ + qbyte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short QCC_BigShort (short l) +{ + return l; +} + + +long QCC_LittleLong (long l) +{ + qbyte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((long)b1<<24) + ((long)b2<<16) + ((long)b3<<8) + b4; +} + +long QCC_BigLong (long l) +{ + return l; +} + + +float QCC_LittleFloat (float l) +{ + union {qbyte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float QCC_BigFloat (float l) +{ + return l; +} + + +#else + + +short QCC_BigShort (short l) +{ + qbyte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short QCC_LittleShort (short l) +{ + return l; +} + + +long QCC_BigLong (long l) +{ + qbyte b1,b2,b3,b4; + + b1 = (qbyte)(l&255); + b2 = (qbyte)((l>>8)&255); + b3 = (qbyte)((l>>16)&255); + b4 = (qbyte)((l>>24)&255); + + return ((long)b1<<24) + ((long)b2<<16) + ((long)b3<<8) + b4; +} + +long QCC_LittleLong (long l) +{ + return l; +} + +float QCC_BigFloat (float l) +{ + union {qbyte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float QCC_LittleFloat (float l) +{ + return l; +} + +#endif + +void SetEndian(void) +{ + if (!BigShort) + { + BigShort = QCC_BigShort; + LittleShort = QCC_LittleShort; + BigLong = QCC_BigLong; + LittleLong = QCC_LittleLong; + BigFloat = QCC_BigFloat; + LittleFloat = QCC_LittleFloat; + } +} + + + + + +//buffer size and max size are different. buffer is bigger. + +#define MAXQCCFILES 3 +struct { + char name[64]; + char *buff; +// int buffismalloc; + int buffsize; + int ofs; + int maxofs; +} qccfile[MAXQCCFILES]; +int SafeOpenWrite (char *filename, int maxsize) +{ + int i; + for (i = 0; i < MAXQCCFILES; i++) + { + if (!qccfile[i].buff) + { + strcpy(qccfile[i].name, filename); + qccfile[i].buffsize = maxsize; + qccfile[i].maxofs = 0; + qccfile[i].ofs = 0; +// if (maxsize > 8192) +// qccfile[i].buffismalloc = 1; +// else +// qccfile[i].buffismalloc = 0; +// if (qccfile[i].buffismalloc) + qccfile[i].buff = malloc(qccfile[i].buffsize); +// else +// qccfile[i].buff = memalloc(qccfile[i].buffsize); + return i; + } + } + QCC_Error(ERR_TOOMANYOPENFILES, "Too many open files on file %s", filename); + return -1; +} + +void ResizeBuf(int hand, int newsize) +{ +// int wasmal = qccfile[hand].buffismalloc; + char *nb; + + if (qccfile[hand].buffsize >= newsize) + return; //already big enough + +// if (newsize > 8192) +// { +// qccfile[hand].buffismalloc = true; + nb = malloc(newsize); +// } +// else +// { +// qccfile[hand].buffismalloc = false; +// nb = memalloc(newsize); +// } + + memcpy(nb, qccfile[hand].buff, qccfile[hand].maxofs); +// if (wasmal) + free(qccfile[hand].buff); +// else +// memfree(qccfile[hand].buff); + qccfile[hand].buff = nb; + qccfile[hand].buffsize = newsize; +} +void SafeWrite(int hand, void *buf, long count) +{ + if (qccfile[hand].ofs +count >= qccfile[hand].buffsize) + ResizeBuf(hand, qccfile[hand].ofs + count+(64*1024)); + + memcpy(&qccfile[hand].buff[qccfile[hand].ofs], buf, count); + qccfile[hand].ofs+=count; + if (qccfile[hand].ofs > qccfile[hand].maxofs) + qccfile[hand].maxofs = qccfile[hand].ofs; +} +int SafeSeek(int hand, int ofs, int mode) +{ + if (mode == SEEK_CUR) + return qccfile[hand].ofs; + else + { + ResizeBuf(hand, ofs+1024); + qccfile[hand].ofs = ofs; + if (qccfile[hand].ofs > qccfile[hand].maxofs) + qccfile[hand].maxofs = qccfile[hand].ofs; + return 0; + } +} +void SafeClose(int hand) +{ + externs->WriteFile(qccfile[hand].name, qccfile[hand].buff, qccfile[hand].maxofs); +// if (qccfile[hand].buffismalloc) + free(qccfile[hand].buff); +// else +// memfree(qccfile[hand].buff); + qccfile[hand].buff = NULL; +} + +qcc_cachedsourcefile_t *qcc_sourcefile; +long QCC_LoadFile (char *filename, void **bufferptr) +{ + char *mem; + int len; + len = externs->FileSize(filename); + if (len < 0) + { + QCC_Error(ERR_COULDNTOPENFILE, "Couldn't open file %s", filename); +// if (!Abort) + return -1; +// Abort("failed to find file %s", filename); + } + mem = qccHunkAlloc(sizeof(qcc_cachedsourcefile_t) + len+2); + + ((qcc_cachedsourcefile_t*)mem)->next = qcc_sourcefile; + qcc_sourcefile = (qcc_cachedsourcefile_t*)mem; + qcc_sourcefile->size = len; + mem += sizeof(qcc_cachedsourcefile_t); + strcpy(qcc_sourcefile->filename, filename); + qcc_sourcefile->file = mem; + qcc_sourcefile->type = FT_CODE; + + externs->ReadFile(filename, mem, len+2); + mem[len] = '\n'; + mem[len+1] = '\0'; + *bufferptr=mem; + + return len; +} +void QCC_AddFile (char *filename) +{ + char *mem; + int len; + len = externs->FileSize(filename); + if (len < 0) + Abort("failed to find file %s", filename); + mem = qccHunkAlloc(sizeof(qcc_cachedsourcefile_t) + len+1); + + ((qcc_cachedsourcefile_t*)mem)->next = qcc_sourcefile; + qcc_sourcefile = (qcc_cachedsourcefile_t*)mem; + qcc_sourcefile->size = len; + mem += sizeof(qcc_cachedsourcefile_t); + strcpy(qcc_sourcefile->filename, filename); + qcc_sourcefile->file = mem; + qcc_sourcefile->type = FT_DATA; + + externs->ReadFile(filename, mem, len+1); + mem[len] = '\0'; + + outputversion = PROG_DEBUGVERSION; +} +void *FS_ReadToMem(char *filename, void *mem, int *len) +{ + if (!mem) + { + *len = externs->FileSize(filename); + mem = memalloc(*len); + } + return externs->ReadFile(filename, mem, *len); +} + +void FS_CloseFromMem(void *mem) +{ + memfree(mem); +} + + +#endif diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c new file mode 100644 index 000000000..ee944f550 --- /dev/null +++ b/engine/qclib/qcc_pr_comp.c @@ -0,0 +1,6905 @@ +#ifndef MINIMAL + +#include "qcc.h" +void QCC_PR_ParseAsm(void); + +#define MEMBERFIELDNAME "__m%s" + +#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc +#define STRNCMP(s1,s2,l) (((*s1)!=(*s2)) || strncmp(s1+1,s2+1,l)) //pathetic saving here. + +extern char *compilingfile; + +pbool conditional; + +pbool keyword_var; +pbool keyword_thinktime; +pbool keyword_switch; +pbool keyword_for; +pbool keyword_case; +pbool keyword_default; +pbool keyword_do; +pbool keyword_asm; +pbool keyword_goto; +pbool keyword_break; +pbool keyword_continue; +pbool keyword_state; +pbool keyword_string; +pbool keyword_float; +pbool keyword_entity; +pbool keyword_vector; +pbool keyword_integer; +pbool keyword_int; +pbool keyword_const; +pbool keyword_class; + +pbool keywords_coexist; //don't disable a keyword simply because a var was made with the same name. +pbool output_parms; //emit some PARMX fields. confuses decompilers. +pbool autoprototype; //take two passes over the source code. First time round doesn't enter and functions or initialise variables. +pbool pr_subscopedlocals; //causes locals to be valid ONLY within thier statement block. (they simply can't be referenced by name outside of it) + +pbool opt_overlaptemps; //reduce numpr_globals by reuse of temps. When they are not needed they are freed for reuse. The way this is implemented is better than frikqcc's. (This is the single most important optimisation) +pbool opt_assignments; //STORE_F isn't used if an operation wrote to a temp. +pbool opt_shortenifnots; //if(!var) is made an IF rather than NOT IFNOT +pbool opt_noduplicatestrings; //brute force string check. time consuming but more effective than the equivelent in frikqcc. +pbool opt_constantarithmatic; //3*5 appears as 15 instead of the extra statement. +pbool opt_nonvec_parms; //store_f instead of store_v on function calls, where possible. +pbool opt_constant_names; //take out the defs and name strings of constants. +pbool opt_constant_names_strings;//removes the defs of strings too. plays havok with multiprogs. +pbool opt_precache_file; //remove the call, the parameters, everything. +pbool opt_filenames; //strip filenames. hinders older decompilers. +pbool opt_unreferenced; //strip defs that are not referenced. +pbool opt_function_names; //strip out the names of builtin functions. +pbool opt_locals; //strip out the names of locals and immediates. +pbool opt_dupconstdefs; //float X = 5; and float Y = 5; occupy the same global with this. +pbool opt_return_only; //RETURN; DONE; at the end of a function strips out the done statement if there is no way to get to it. +pbool opt_compound_jumps; //jumps to jump statements jump to the final point. +pbool opt_stripfunctions; //if a functions is only ever called directly or by exe, don't emit the def. +pbool opt_locals_marshalling; //make the local vars of all functions occupy the same globals. +pbool opt_logicops; //don't make conditions enter functions if the return value will be discarded due to a previous value. (C style if statements) +//bool opt_comexprremoval; + +//these are the results of the opt_. The values are printed out when compilation is compleate, showing effectivness. +int optres_shortenifnots; +int optres_assignments; +int optres_overlaptemps; +int optres_noduplicatestrings; +int optres_constantarithmatic; +int optres_nonvec_parms; +int optres_constant_names; +int optres_constant_names_strings; +int optres_precache_file; +int optres_filenames; +int optres_unreferenced; +int optres_function_names; +int optres_locals; +int optres_dupconstdefs; +int optres_return_only; +int optres_compound_jumps; +//int optres_comexprremoval; +int optres_stripfunctions; +int optres_locals_marshalling; +int optres_logicops; + +int optres_test1; +int optres_test2; + + +QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int ofs, int referable); +QCC_type_t *QCC_PR_NewType (char *name, int basictype); +QCC_type_t *QCC_PR_FindType (QCC_type_t *type); + +void QCC_PR_ParseState (void); +pbool simplestore; + +QCC_pr_info_t pr; +//QCC_def_t **pr_global_defs/*[MAX_REGS]*/; // to find def for a global variable + +//keeps track of how many funcs are called while parsing a statement +//int qcc_functioncalled; + +//======================================== + +QCC_def_t *pr_scope; // the function being parsed, or NULL +QCC_type_t *pr_classtype; +pbool pr_dumpasm; +QCC_string_t s_file, s_file2; // filename for function definition + +unsigned int locals_start; // for tracking local variables vs temps +unsigned int locals_end; // for tracking local variables vs temps + +jmp_buf pr_parse_abort; // longjump with this on parse error + +void QCC_PR_ParseDefs (char *classname); + +pbool qcc_usefulstatement; + +int max_breaks; +int max_continues; +int max_cases; +int num_continues; +int num_breaks; +int num_cases; +int *pr_breaks; +int *pr_continues; +int *pr_cases; +QCC_def_t **pr_casesdef; +QCC_def_t **pr_casesdef2; + +typedef struct { + int statementno; + int lineno; + char name[256]; +} gotooperator_t; + +int max_labels; +int max_gotos; +gotooperator_t *pr_labels; +gotooperator_t *pr_gotos; +int num_gotos; +int num_labels; + +QCC_def_t *extra_parms[MAX_EXTRA_PARMS]; + +#define ASSOC_RIGHT_RESULT ASSOC_RIGHT + +//======================================== + +//FIXME: modifiy list so most common GROUPS are first +//use look up table for value of first char and sort by first char and most common...? + +//if true, effectivly {b=a; return a;} +QCC_opcode_t pr_opcodes[] = +{ + {6, "", "DONE", -1, ASSOC_LEFT, &type_void, &type_void, &type_void}, + + {6, "*", "MUL_F", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "*", "MUL_V", 3, ASSOC_LEFT, &type_vector, &type_vector, &type_float}, + {6, "*", "MUL_FV", 3, ASSOC_LEFT, &type_float, &type_vector, &type_vector}, + {6, "*", "MUL_VF", 3, ASSOC_LEFT, &type_vector, &type_float, &type_vector}, + + {6, "/", "DIV_F", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, + + {6, "+", "ADD_F", 4, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "+", "ADD_V", 4, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, + + {6, "-", "SUB_F", 4, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "-", "SUB_V", 4, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, + + {6, "==", "EQ_F", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "==", "EQ_V", 5, ASSOC_LEFT, &type_vector, &type_vector, &type_float}, + {6, "==", "EQ_S", 5, ASSOC_LEFT, &type_string, &type_string, &type_float}, + {6, "==", "EQ_E", 5, ASSOC_LEFT, &type_entity, &type_entity, &type_float}, + {6, "==", "EQ_FNC", 5, ASSOC_LEFT, &type_function, &type_function, &type_float}, + + {6, "!=", "NE_F", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "!=", "NE_V", 5, ASSOC_LEFT, &type_vector, &type_vector, &type_float}, + {6, "!=", "NE_S", 5, ASSOC_LEFT, &type_string, &type_string, &type_float}, + {6, "!=", "NE_E", 5, ASSOC_LEFT, &type_entity, &type_entity, &type_float}, + {6, "!=", "NE_FNC", 5, ASSOC_LEFT, &type_function, &type_function, &type_float}, + + {6, "<=", "LE", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, ">=", "GE", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "<", "LT", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, ">", "GT", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, + + {6, ".", "INDIRECT_F", 1, ASSOC_LEFT, &type_entity, &type_field, &type_float}, + {6, ".", "INDIRECT_V", 1, ASSOC_LEFT, &type_entity, &type_field, &type_vector}, + {6, ".", "INDIRECT_S", 1, ASSOC_LEFT, &type_entity, &type_field, &type_string}, + {6, ".", "INDIRECT_E", 1, ASSOC_LEFT, &type_entity, &type_field, &type_entity}, + {6, ".", "INDIRECT_FI", 1, ASSOC_LEFT, &type_entity, &type_field, &type_field}, + {6, ".", "INDIRECT_FU", 1, ASSOC_LEFT, &type_entity, &type_field, &type_function}, + + {6, ".", "ADDRESS", 1, ASSOC_LEFT, &type_entity, &type_field, &type_pointer}, + + {6, "=", "STORE_F", 6, ASSOC_RIGHT, &type_float, &type_float, &type_float}, + {6, "=", "STORE_V", 6, ASSOC_RIGHT, &type_vector, &type_vector, &type_vector}, + {6, "=", "STORE_S", 6, ASSOC_RIGHT, &type_string, &type_string, &type_string}, + {6, "=", "STORE_ENT", 6, ASSOC_RIGHT, &type_entity, &type_entity, &type_entity}, + {6, "=", "STORE_FLD", 6, ASSOC_RIGHT, &type_field, &type_field, &type_field}, + {6, "=", "STORE_FNC", 6, ASSOC_RIGHT, &type_function, &type_function, &type_function}, + + {6, "=", "STOREP_F", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_float}, + {6, "=", "STOREP_V", 6, ASSOC_RIGHT, &type_pointer, &type_vector, &type_vector}, + {6, "=", "STOREP_S", 6, ASSOC_RIGHT, &type_pointer, &type_string, &type_string}, + {6, "=", "STOREP_ENT", 6, ASSOC_RIGHT, &type_pointer, &type_entity, &type_entity}, + {6, "=", "STOREP_FLD", 6, ASSOC_RIGHT, &type_pointer, &type_field, &type_field}, + {6, "=", "STOREP_FNC", 6, ASSOC_RIGHT, &type_pointer, &type_function, &type_function}, + + {6, "", "RETURN", -1, ASSOC_LEFT, &type_float, &type_void, &type_void}, + + {6, "!", "NOT_F", -1, ASSOC_LEFT, &type_float, &type_void, &type_float}, + {6, "!", "NOT_V", -1, ASSOC_LEFT, &type_vector, &type_void, &type_float}, + {6, "!", "NOT_S", -1, ASSOC_LEFT, &type_vector, &type_void, &type_float}, + {6, "!", "NOT_ENT", -1, ASSOC_LEFT, &type_entity, &type_void, &type_float}, + {6, "!", "NOT_FNC", -1, ASSOC_LEFT, &type_function, &type_void, &type_float}, + + {6, "", "IF", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, + {6, "", "IFNOT", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, + +// calls returns REG_RETURN + {6, "", "CALL0", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL1", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL2", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL3", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL4", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL5", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL6", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL7", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + {6, "", "CALL8", -1, ASSOC_LEFT, &type_function, &type_void, &type_void}, + + {6, "", "STATE", -1, ASSOC_LEFT, &type_float, &type_float, &type_void}, + + {6, "", "GOTO", -1, ASSOC_RIGHT, NULL, &type_void, &type_void}, + + {6, "&&", "AND", 7, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "||", "OR", 7, ASSOC_LEFT, &type_float, &type_float, &type_float}, + + {6, "&", "BITAND", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {6, "|", "BITOR", 3, ASSOC_LEFT, &type_float, &type_float, &type_float}, + + //version 6 are in normal progs. + + + +//these are hexen2 + {7, "*=", "MULSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, + {7, "*=", "MULSTORE_V", 6, ASSOC_RIGHT_RESULT, &type_vector, &type_float, &type_vector}, + {7, "*=", "MULSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, + {7, "*=", "MULSTOREP_V", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_vector}, + + {7, "/=", "DIVSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, + {7, "/=", "DIVSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, + + {7, "+=", "ADDSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, + {7, "+=", "ADDSTORE_V", 6, ASSOC_RIGHT_RESULT, &type_vector, &type_vector, &type_vector}, + {7, "+=", "ADDSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, + {7, "+=", "ADDSTOREP_V", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_vector, &type_vector}, + + {7, "-=", "SUBSTORE_F", 6, ASSOC_RIGHT_RESULT, &type_float, &type_float, &type_float}, + {7, "-=", "SUBSTORE_V", 6, ASSOC_RIGHT_RESULT, &type_vector, &type_vector, &type_vector}, + {7, "-=", "SUBSTOREP_F", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_float, &type_float}, + {7, "-=", "SUBSTOREP_V", 6, ASSOC_RIGHT_RESULT, &type_pointer, &type_vector, &type_vector}, + + {7, "", "FETCH_GBL_F", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {7, "", "FETCH_GBL_V", -1, ASSOC_LEFT, &type_vector, &type_float, &type_vector}, + {7, "", "FETCH_GBL_S", -1, ASSOC_LEFT, &type_string, &type_float, &type_string}, + {7, "", "FETCH_GBL_E", -1, ASSOC_LEFT, &type_entity, &type_float, &type_entity}, + {7, "", "FETCH_GBL_FNC", -1, ASSOC_LEFT, &type_function, &type_float, &type_function}, + + {7, "", "CSTATE", -1, ASSOC_LEFT, &type_float, &type_float, &type_void}, + + {7, "", "CWSTATE", -1, ASSOC_LEFT, &type_float, &type_float, &type_void}, + + {7, "", "THINKTIME", -1, ASSOC_LEFT, &type_entity, &type_float, &type_void}, + + {7, "(+)", "BITSET", 6, ASSOC_RIGHT, &type_float, &type_float, &type_float}, + {7, "(+)", "BITSETP", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_float}, + {7, "(-)", "BITCLR", 6, ASSOC_RIGHT, &type_float, &type_float, &type_float}, + {7, "(-)", "BITCLRP", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_float}, + + {7, "", "RAND0", -1, ASSOC_LEFT, &type_void, &type_void, &type_float}, + {7, "", "RAND1", -1, ASSOC_LEFT, &type_float, &type_void, &type_float}, + {7, "", "RAND2", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, + {7, "", "RANDV0", -1, ASSOC_LEFT, &type_void, &type_void, &type_vector}, + {7, "", "RANDV1", -1, ASSOC_LEFT, &type_vector, &type_void, &type_vector}, + {7, "", "RANDV2", -1, ASSOC_LEFT, &type_vector, &type_vector, &type_vector}, + + {7, "", "SWITCH_F", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_V", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_S", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_E", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + {7, "", "SWITCH_FNC", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + + {7, "", "CASE", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, + {7, "", "CASERANGE", -1, ASSOC_LEFT, &type_void, &type_void, NULL}, + + +//Later are additions by DMW. + + {7, "", "CALL1H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_void}, + {7, "", "CALL2H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL3H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL4H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL5H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL6H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL7H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + {7, "", "CALL8H", -1, ASSOC_LEFT, &type_function, &type_vector, &type_vector}, + + {7, "=", "STORE_I", 6, ASSOC_RIGHT, &type_integer, &type_integer, &type_integer}, + {7, "=", "STORE_IF", 6, ASSOC_RIGHT, &type_integer, &type_float, &type_integer}, + {7, "=", "STORE_FI", 6, ASSOC_RIGHT, &type_float, &type_integer, &type_float}, + + {7, "+", "ADD_I", 4, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "+", "ADD_FI", 4, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + {7, "+", "ADD_IF", 4, ASSOC_LEFT, &type_integer, &type_float, &type_float}, + + {7, "-", "SUB_I", 4, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "-", "SUB_FI", 4, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + {7, "-", "SUB_IF", 4, ASSOC_LEFT, &type_integer, &type_float, &type_float}, + + {7, "", "C_ITOF", -1, ASSOC_LEFT, &type_integer, &type_void, &type_float}, + {7, "", "C_FTOI", -1, ASSOC_LEFT, &type_float, &type_void, &type_integer}, + {7, "", "CP_ITOF", -1, ASSOC_LEFT, &type_pointer, &type_integer, &type_float}, + {7, "", "CP_FTOI", -1, ASSOC_LEFT, &type_pointer, &type_float, &type_integer}, + + {7, ".", "INDIRECT", 1, ASSOC_LEFT, &type_entity, &type_field, &type_integer}, + {7, "=", "STOREP_I", 6, ASSOC_RIGHT, &type_pointer, &type_integer, &type_integer}, + {7, "=", "STOREP_IF", 6, ASSOC_RIGHT, &type_pointer, &type_float, &type_integer}, + {7, "=", "STOREP_FI", 6, ASSOC_RIGHT, &type_pointer, &type_integer, &type_float}, + + {7, "&", "BITAND_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "|", "BITOR_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + + {7, "*", "MUL_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "/", "DIV_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "==", "EQ_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "!=", "NE_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + + {7, "", "IFNOTS", -1, ASSOC_RIGHT, &type_string, NULL, &type_void}, + {7, "", "IFS", -1, ASSOC_RIGHT, &type_string, NULL, &type_void}, + + {7, "!", "NOT_I", -1, ASSOC_LEFT, &type_integer, &type_void, &type_integer}, + + {7, "/", "DIV_VF", 3, ASSOC_LEFT, &type_vector, &type_float, &type_float}, + + {7, "^", "POWER_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, ">>", "RSHIFT_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "<<", "LSHIFT_I", 3, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + + //var, offset return + {7, "", "GET_POINTER", -1, ASSOC_LEFT, &type_void, &type_integer, &type_pointer}, + {7, "", "ARRAY_OFS", -1, ASSOC_LEFT, &type_pointer, &type_integer, &type_pointer}, + + {7, "=", "LOADA_F", 6, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + {7, "=", "LOADA_V", 6, ASSOC_LEFT, &type_vector, &type_integer, &type_vector}, + {7, "=", "LOADA_S", 6, ASSOC_LEFT, &type_string, &type_integer, &type_string}, + {7, "=", "LOADA_ENT", 6, ASSOC_LEFT, &type_entity, &type_integer, &type_entity}, + {7, "=", "LOADA_FLD", 6, ASSOC_LEFT, &type_field, &type_integer, &type_field}, + {7, "=", "LOADA_FNC", 6, ASSOC_LEFT, &type_function, &type_integer, &type_function}, + {7, "=", "LOADA_I", 6, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + + {7, "=", "STORE_P", 6, ASSOC_RIGHT, &type_pointer, &type_pointer, &type_pointer}, + {7, ".", "INDIRECT_P", 1, ASSOC_LEFT, &type_entity, &type_field, &type_pointer}, + + {7, "=", "LOADP_F", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_float}, + {7, "=", "LOADP_V", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_vector}, + {7, "=", "LOADP_S", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_string}, + {7, "=", "LOADP_ENT", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_entity}, + {7, "=", "LOADP_FLD", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_field}, + {7, "=", "LOADP_FNC", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_function}, + {7, "=", "LOADP_I", 6, ASSOC_LEFT, &type_pointer, &type_integer, &type_integer}, + + + {7, "<=", "LE_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, ">=", "GE_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, "<", "LT_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + {7, ">", "GT_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, + + {7, "<=", "LE_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, + {7, ">=", "GE_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, + {7, "<", "LT_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, + {7, ">", "GT_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, + + {7, "<=", "LE_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, + {7, ">=", "GE_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, + {7, "<", "LT_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, + {7, ">", "GT_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_integer}, + + {7, "==", "EQ_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, + {7, "==", "EQ_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + + //------------------------------------- + //string manipulation. + {7, "+", "ADD_SF", 4, ASSOC_LEFT, &type_string, &type_float, &type_string}, + {7, "-", "SUB_S", 4, ASSOC_LEFT, &type_string, &type_string, &type_float}, + {7, "", "STOREP_C", 1, ASSOC_RIGHT, &type_string, &type_float, &type_float}, + {7, "", "LOADP_C", 1, ASSOC_LEFT, &type_string, &type_void, &type_float}, + //------------------------------------- + + + +{7, "*", "MUL_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "*", "MUL_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, +{7, "*", "MUL_VI", 5, ASSOC_LEFT, &type_vector, &type_integer, &type_vector}, +{7, "*", "MUL_IV", 5, ASSOC_LEFT, &type_integer, &type_vector, &type_vector}, + +{7, "/", "DIV_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "/", "DIV_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + +{7, "&", "BITAND_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "|", "BITOR_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "&", "BITAND_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, +{7, "|", "BITOR_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, + +{7, "&&", "AND_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, +{7, "||", "OR_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, +{7, "&&", "AND_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "||", "OR_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "&&", "AND_FI", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "||", "OR_FI", 5, ASSOC_LEFT, &type_float, &type_float, &type_integer}, +{7, "!=", "NE_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "!=", "NE_FI", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, + + + + + + + {0, NULL} +}; + +#undef ASSOC_RIGHT_RESULT + +#define TOP_PRIORITY 7 +#define NOT_PRIORITY 5 +//conditional and/or +#define CONDITION_PRIORITY 7 + + +//this system cuts out 10/120 +//these evaluate as top first. +QCC_opcode_t *opcodeprioritized[TOP_PRIORITY+1][64] = +{ + { //don't use +/* &pr_opcodes[OP_DONE], + &pr_opcodes[OP_RETURN], + + &pr_opcodes[OP_NOT_F], + &pr_opcodes[OP_NOT_V], + &pr_opcodes[OP_NOT_S], + &pr_opcodes[OP_NOT_ENT], + &pr_opcodes[OP_NOT_FNC], + + &pr_opcodes[OP_IF], + &pr_opcodes[OP_IFNOT], + &pr_opcodes[OP_CALL0], + &pr_opcodes[OP_CALL1], + &pr_opcodes[OP_CALL2], + &pr_opcodes[OP_CALL3], + &pr_opcodes[OP_CALL4], + &pr_opcodes[OP_CALL5], + &pr_opcodes[OP_CALL6], + &pr_opcodes[OP_CALL7], + &pr_opcodes[OP_CALL8], + &pr_opcodes[OP_STATE], + &pr_opcodes[OP_GOTO], + + &pr_opcodes[OP_IFNOTS], + &pr_opcodes[OP_IFS], + + &pr_opcodes[OP_NOT_I], +*/ NULL + }, { //1 + + &pr_opcodes[OP_LOAD_F], + &pr_opcodes[OP_LOAD_V], + &pr_opcodes[OP_LOAD_S], + &pr_opcodes[OP_LOAD_ENT], + &pr_opcodes[OP_LOAD_FLD], + &pr_opcodes[OP_LOAD_FNC], + &pr_opcodes[OP_LOAD_I], + &pr_opcodes[OP_ADDRESS], + NULL + }, { //2 +/* //conversion. don't use + &pr_opcodes[OP_C_ITOF], + &pr_opcodes[OP_C_FTOI], + &pr_opcodes[OP_CP_ITOF], + &pr_opcodes[OP_CP_FTOI], +*/ NULL + }, { //3 + &pr_opcodes[OP_MUL_F], + &pr_opcodes[OP_MUL_V], + &pr_opcodes[OP_MUL_FV], + &pr_opcodes[OP_MUL_VF], + &pr_opcodes[OP_MUL_I], + + &pr_opcodes[OP_DIV_F], + &pr_opcodes[OP_DIV_I], + &pr_opcodes[OP_DIV_VF], + + &pr_opcodes[OP_BITAND], + &pr_opcodes[OP_BITAND_I], + + &pr_opcodes[OP_BITOR], + &pr_opcodes[OP_BITOR_I], + + &pr_opcodes[OP_POWER_I], + &pr_opcodes[OP_RSHIFT_I], + &pr_opcodes[OP_LSHIFT_I], + + NULL + }, { //4 + + &pr_opcodes[OP_ADD_F], + &pr_opcodes[OP_ADD_V], + &pr_opcodes[OP_ADD_I], + &pr_opcodes[OP_ADD_FI], + &pr_opcodes[OP_ADD_IF], + &pr_opcodes[OP_ADD_SF], + + &pr_opcodes[OP_SUB_F], + &pr_opcodes[OP_SUB_V], + &pr_opcodes[OP_SUB_I], + &pr_opcodes[OP_SUB_FI], + &pr_opcodes[OP_SUB_IF], + &pr_opcodes[OP_SUB_S], + NULL + }, { //5 + + &pr_opcodes[OP_EQ_F], + &pr_opcodes[OP_EQ_V], + &pr_opcodes[OP_EQ_S], + &pr_opcodes[OP_EQ_E], + &pr_opcodes[OP_EQ_FNC], + &pr_opcodes[OP_EQ_I], + &pr_opcodes[OP_EQ_IF], + &pr_opcodes[OP_EQ_FI], + + &pr_opcodes[OP_NE_F], + &pr_opcodes[OP_NE_V], + &pr_opcodes[OP_NE_S], + &pr_opcodes[OP_NE_E], + &pr_opcodes[OP_NE_FNC], + &pr_opcodes[OP_NE_I], + + &pr_opcodes[OP_LE], + &pr_opcodes[OP_LE_I], + &pr_opcodes[OP_LE_IF], + &pr_opcodes[OP_LE_FI], + &pr_opcodes[OP_GE], + &pr_opcodes[OP_GE_I], + &pr_opcodes[OP_GE_IF], + &pr_opcodes[OP_GE_FI], + &pr_opcodes[OP_LT], + &pr_opcodes[OP_LT_I], + &pr_opcodes[OP_LT_IF], + &pr_opcodes[OP_LT_FI], + &pr_opcodes[OP_GT], + &pr_opcodes[OP_GT_I], + &pr_opcodes[OP_GT_IF], + &pr_opcodes[OP_GT_FI], + + NULL + }, { //6 + &pr_opcodes[OP_STORE_F], + &pr_opcodes[OP_STORE_V], + &pr_opcodes[OP_STORE_S], + &pr_opcodes[OP_STORE_ENT], + &pr_opcodes[OP_STORE_FLD], + &pr_opcodes[OP_STORE_FNC], + &pr_opcodes[OP_STORE_I], + &pr_opcodes[OP_STORE_IF], + &pr_opcodes[OP_STORE_FI], + + &pr_opcodes[OP_STOREP_F], + &pr_opcodes[OP_STOREP_V], + &pr_opcodes[OP_STOREP_S], + &pr_opcodes[OP_STOREP_ENT], + &pr_opcodes[OP_STOREP_FLD], + &pr_opcodes[OP_STOREP_FNC], + &pr_opcodes[OP_STOREP_I], + &pr_opcodes[OP_STOREP_IF], + &pr_opcodes[OP_STOREP_FI], + + &pr_opcodes[OP_DIVSTORE_F], + &pr_opcodes[OP_DIVSTOREP_F], + &pr_opcodes[OP_MULSTORE_F], + &pr_opcodes[OP_MULSTORE_V], + &pr_opcodes[OP_MULSTOREP_F], + &pr_opcodes[OP_MULSTOREP_V], + &pr_opcodes[OP_ADDSTORE_F], + &pr_opcodes[OP_ADDSTORE_V], + &pr_opcodes[OP_ADDSTOREP_F], + &pr_opcodes[OP_ADDSTOREP_V], + &pr_opcodes[OP_SUBSTORE_F], + &pr_opcodes[OP_SUBSTORE_V], + &pr_opcodes[OP_SUBSTOREP_F], + &pr_opcodes[OP_SUBSTOREP_V], + + &pr_opcodes[OP_BITSET], + &pr_opcodes[OP_BITSETP], + &pr_opcodes[OP_BITCLR], + &pr_opcodes[OP_BITCLRP], + NULL + }, { //7 + &pr_opcodes[OP_AND], + &pr_opcodes[OP_OR], + NULL + } +}; + +pbool QCC_OPCodeValid(QCC_opcode_t *op) +{ + int num; + num = op - pr_opcodes; + switch(qcc_targetformat) + { + case QCF_STANDARD: + case QCF_KK7: + if (num < OP_MULSTORE_F) + return true; + return false; + case QCF_HEXEN2: + if (num >= OP_SWITCH_V && num <= OP_SWITCH_FNC) //these were assigned numbers but were never actually implemtented in standard h2. + return false; +// if (num >= OP_MULSTORE_F && num <= OP_SUBSTOREP_V) +// return false; + if (num <= OP_CALL8H) //CALLXH are fixed up. This is to provide more dynamic switching...?? + return true; + return false; + case QCF_FTE: + case QCF_FTE32: + case QCF_FTEDEBUG: + case QCF_FTEDEBUG32: + return true; + } + return false; +} + +QCC_def_t *QCC_PR_Expression (int priority); + +QCC_def_t junkdef; + +temp_t *functemps; //floats/strings/funcs/ents... + +//=========================================================================== + + +/* +============ +PR_Statement + +Emits a primitive statement, returning the var it places it's value in +============ +*/ +QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_dstatement_t **outstatement); +int inline QCC_ShouldConvert(QCC_def_t *var, etype_t wanted) +{ + if (var->type->type == ev_integer && wanted == ev_function) + return 0; + if (var->type->type == ev_pointer && var->type->aux_type) + { + if (var->type->aux_type->type == ev_float && wanted == ev_integer) + return OP_CP_FTOI; + + if (var->type->aux_type->type == ev_integer && wanted == ev_float) + return OP_CP_ITOF; + } + else + { + if (var->type->type == ev_float && wanted == ev_integer) + return OP_CONV_FTOI; + + if (var->type->type == ev_integer && wanted == ev_float) + return OP_CONV_ITOF; + } + + return -1; +} +QCC_def_t *QCC_SupplyConversion(QCC_def_t *var, etype_t wanted) +{ + int o; + + if (pr_classtype && var->type->type == ev_field && wanted != ev_field) + { + if (pr_classtype) + { //load self.var into a temp + QCC_def_t *self; + self = QCC_PR_GetDef(type_entity, "self", NULL, true, 1); + switch(wanted) + { + case ev_float: + return QCC_PR_Statement(pr_opcodes+OP_LOAD_F, self, var, NULL); + case ev_string: + return QCC_PR_Statement(pr_opcodes+OP_LOAD_S, self, var, NULL); + case ev_function: + return QCC_PR_Statement(pr_opcodes+OP_LOAD_FNC, self, var, NULL); + case ev_vector: + return QCC_PR_Statement(pr_opcodes+OP_LOAD_V, self, var, NULL); + case ev_entity: + return QCC_PR_Statement(pr_opcodes+OP_LOAD_ENT, self, var, NULL); + default: + QCC_Error(ERR_INTERNAL, "Inexplicit field load failed, try explicit"); + } + } + } + + o = QCC_ShouldConvert(var, wanted); + + if (o <= 0) //no conversion + return var; + + + return QCC_PR_Statement(&pr_opcodes[o], var, NULL, NULL); //conversion return value +} +QCC_def_t *QCC_MakeStringDef(char *value); +QCC_def_t *QCC_MakeFloatDef(float value); +QCC_def_t *QCC_MakeIntDef(int value); + +typedef struct freeoffset_s { + struct freeoffset_s *next; + gofs_t ofs; + unsigned int size; +} freeoffset_t; + +freeoffset_t *freeofs; + +//assistant functions. This can safly be bipassed with the old method for more complex things. +gofs_t QCC_GetFreeOffsetSpace(unsigned int size) +{ + int ofs; + if (opt_locals_marshalling) + { + freeoffset_t *fofs, *prev; + for (fofs = freeofs, prev = NULL; fofs; fofs=fofs->next) + { + if (fofs->size == size) + { + if (prev) + prev->next = fofs->next; + else + freeofs = fofs->next; + + return fofs->ofs; + } + prev = fofs; + } + for (fofs = freeofs, prev = NULL; fofs; fofs=fofs->next) + { + if (fofs->size > size) + { + fofs->size -= size; + fofs->ofs += size; + + return fofs->ofs-size; + } + prev = fofs; + } + } + + ofs = numpr_globals; + numpr_globals+=size; + + if (numpr_globals >= MAX_REGS) + { + if (!opt_overlaptemps || !opt_locals_marshalling) + QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS - you'll need to use more optimisations"); + else + QCC_Error(ERR_TOOMANYGLOBALS, "numpr_globals exceeded MAX_REGS"); + } + + return ofs; +} + +void QCC_FreeOffset(gofs_t ofs, unsigned int size) +{ + freeoffset_t *fofs; + if (ofs+size == numpr_globals) + { + numpr_globals -= size; + return; + } + + for (fofs = freeofs; fofs; fofs=fofs->next) + { + if (fofs->ofs == ofs + size) + { + fofs->ofs -= size; + fofs->size += size; + return; + } + if (fofs->ofs+fofs->size == ofs) + { + fofs->size += size; + return; + } + } + + fofs = qccHunkAlloc(sizeof(freeoffset_t)); + fofs->next = freeofs; + fofs->ofs = ofs; + fofs->size = size; + + freeofs = fofs; + return; + + QCC_Error(ERR_INTERNAL, "QCC_FreeOffset: not called with end of globals"); + +} + +static QCC_def_t *QCC_GetTemp(QCC_type_t *type) +{ +//#define CRAZYTEMPOPTS //not worth it. saves 2 temps with hexen2 (without even touching numpr_globals) + QCC_def_t *var_c; + temp_t *t; +#ifdef CRAZYTEMPOPTS + temp_t *best = NULL; +#endif + + var_c = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (var_c, 0, sizeof(QCC_def_t)); + var_c->type = type; + var_c->name = "temp"; + + if (opt_overlaptemps) //don't exceed. This lets us allocate a huge block, and still be able to compile smegging big funcs. + { + for (t = functemps; t; t = t->next) + { + if (!t->used && t->size == type->size) + { +#ifdef CRAZYTEMPOPTS + best = t; + if (t->scope == pr_scope) +#endif + break; + } + } +#ifdef CRAZYTEMPOPTS + t = best; +#endif + if (t && t->scope && t->scope != pr_scope) + QCC_Error(ERR_INTERNAL, "Internal error temp has scope not equal to current scope"); + + if (!t) + { + //allocate a new one + t = qccHunkAlloc(sizeof(temp_t)); + t->size = type->size; + t->next = functemps; + functemps = t; + + t->ofs = QCC_GetFreeOffsetSpace(t->size); + + numtemps+=t->size; + } + else + optres_overlaptemps+=t->size; + //use a previous one. + var_c->ofs = t->ofs; + var_c->temp = t; + t->lastfunc = pr_scope; + } + else + { + //allocate a new one + t = qccHunkAlloc(sizeof(temp_t)); + t->size = type->size; + + t->next = functemps; + functemps = t; + + t->ofs = QCC_GetFreeOffsetSpace(t->size); + + numtemps+=t->size; + + var_c->ofs = t->ofs; + var_c->temp = t; + t->lastfunc = pr_scope; + } + + var_c->s_file = s_file; + var_c->s_line = pr_source_line; + + if (var_c->temp) + var_c->temp->used = true; + + return var_c; +} + +//nothing else references this temp. +static void QCC_FreeTemp(QCC_def_t *t) +{ + if (t && t->temp) + t->temp->used = false; +} + +static void QCC_UnFreeTemp(QCC_def_t *t) +{ + if (t->temp) + t->temp->used = true; +} + +//We've just parsed a statement. +//We can gaurentee that any used temps are now not used. +static void QCC_FreeTemps(void) +{ + temp_t *t; + + t = functemps; + while(t) + { + if (t->used) + t->used = false; + t = t->next; + } +} + +//temps that are still in use over a function call can be considered dodgy. +//we need to remap these to locally defined temps, on return from the function so we know we got them all. +static void QCC_LockActiveTemps(void) +{ + temp_t *t; + + t = functemps; + while(t) + { + if (t->used) + t->scope = pr_scope; + t = t->next; + } + +} + +static void QCC_RemapLockedTemp(temp_t *t, int firststatement, int laststatement) +{ +#ifdef WRITEASM + char buffer[128]; + int locks=0; +#endif + + QCC_def_t *def; + int newofs; + QCC_dstatement_t *st; + int i; + + newofs = 0; + for (i = firststatement, st = &statements[i]; i < laststatement; i++, st++) + { + if (pr_opcodes[st->op].type_a && st->a == t->ofs) + { + if (!newofs) + { + newofs = QCC_GetFreeOffsetSpace(t->size); + numtemps+=t->size; + + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false); + def->nextlocal = pr.localvars; + def->constant = false; +#ifdef WRITEASM + sprintf(buffer, "locked_%i", ++locks); + def->name = qccHunkAlloc(strlen(buffer)+1); + strcpy(def->name, buffer); +#endif + pr.localvars = def; + } + st->a = newofs; + } + if (pr_opcodes[st->op].type_b && st->b == t->ofs) + { + if (!newofs) + { + newofs = QCC_GetFreeOffsetSpace(t->size); + numtemps+=t->size; + + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false); + def->nextlocal = pr.localvars; + def->constant = false; +#ifdef WRITEASM + sprintf(buffer, "locked_%i", ++locks); + def->name = qccHunkAlloc(strlen(buffer)+1); + strcpy(def->name, buffer); +#endif + pr.localvars = def; + } + st->b = newofs; + } + if (pr_opcodes[st->op].type_c && st->c == t->ofs) + { + if (!newofs) + { + newofs = QCC_GetFreeOffsetSpace(t->size); + numtemps+=t->size; + + def = QCC_PR_DummyDef(type_float, NULL, pr_scope, t->size, newofs, false); + def->nextlocal = pr.localvars; + def->constant = false; +#ifdef WRITEASM + sprintf(buffer, "locked_%i", ++locks); + def->name = qccHunkAlloc(strlen(buffer)+1); + strcpy(def->name, buffer); +#endif + pr.localvars = def; + } + st->c = newofs; + } + } +} + +static void QCC_RemapLockedTemps(int firststatement, int laststatement) +{ + temp_t *t; + + t = functemps; + while(t) + { + if (t->scope) + { + QCC_RemapLockedTemp(t, firststatement, laststatement); + t->scope = NULL; + t->lastfunc = NULL; + } + t = t->next; + } +} + +static void QCC_fprintfLocals(FILE *f, gofs_t paramstart, gofs_t paramend) +{ + QCC_def_t *var; + temp_t *t; + int i; + + for (var = pr.localvars; var; var = var->nextlocal) + { + if (var->ofs >= paramstart && var->ofs < paramend) + continue; + fprintf(f, "local %s %s;\n", TypeName(var->type), var->name); + } + + for (t = functemps, i = 0; t; t = t->next, i++) + { + if (t->lastfunc == pr_scope) + { + fprintf(f, "local %s temp_%i;\n", (t->size == 1)?"float":"vector", i); + } + } +} + +#ifdef WRITEASM +void QCC_WriteAsmFunction(QCC_def_t *sc, unsigned int firststatement, gofs_t firstparm); +static const char *QCC_VarAtOffset(unsigned int ofs, unsigned int size) +{ + static char message[1024]; + QCC_def_t *var; + //check the temps + temp_t *t; + int i; + + for (t = functemps, i = 0; t; t = t->next, i++) + { + if (ofs >= t->ofs && ofs < t->ofs + t->size) + { + if (size < t->size) + sprintf(message, "temp_%i_%c", i, 'x' + (ofs-t->ofs)%3); + else + sprintf(message, "temp_%i", i); + return message; + } + } + + for (var = pr.localvars; var; var = var->nextlocal) + { + if (var->scope && var->scope != pr_scope) + continue; //this should be an error + if (ofs >= var->ofs && ofs < var->ofs + var->type->size) + { + if (*var->name) + { + if (!STRCMP(var->name, "IMMEDIATE")) //continue, don't get bogged down by multiple bits of code + continue; + if (size < var->type->size) + sprintf(message, "%s_%c", var->name, 'x' + (ofs-var->ofs)%3); + else + sprintf(message, "%s", var->name); + return message; + } + } + } + + for (var = pr.def_head.next; var; var = var->next) + { + if (var->scope && var->scope != pr_scope) + continue; + + if (ofs >= var->ofs && ofs < var->ofs + var->type->size) + { + if (*var->name) + { + if (!STRCMP(var->name, "IMMEDIATE")) + { + switch(var->type->type) + { + case ev_string: + sprintf(message, "\"%.1020s\"", &strings[((int *)qcc_pr_globals)[var->ofs]]); + return message; + case ev_integer: + sprintf(message, "%i", ((int *)qcc_pr_globals)[var->ofs]); + return message; + case ev_float: + sprintf(message, "%f", qcc_pr_globals[var->ofs]); + return message; + case ev_vector: + sprintf(message, "'%f %f %f'", qcc_pr_globals[var->ofs], qcc_pr_globals[var->ofs+1], qcc_pr_globals[var->ofs+2]); + return message; + default: + sprintf(message, "IMMEDIATE"); + return message; + } + } + if (size < var->type->size) + sprintf(message, "%s_%c", var->name, 'x' + (ofs-var->ofs)%3); + else + sprintf(message, "%s", var->name); + return message; + } + } + } + + if (size >= 3) + { + if (ofs >= OFS_RETURN && ofs < OFS_PARM0) + sprintf(message, "return"); + else if (ofs >= OFS_PARM0 && ofs < RESERVED_OFS) + sprintf(message, "parm%i", (ofs-OFS_PARM0)/3); + else + sprintf(message, "offset_%i", ofs); + } + else + { + if (ofs >= OFS_RETURN && ofs < OFS_PARM0) + sprintf(message, "return_%c", 'x' + ofs-OFS_RETURN); + else if (ofs >= OFS_PARM0 && ofs < RESERVED_OFS) + sprintf(message, "parm%i_%c", (ofs-OFS_PARM0)/3, 'x' + (ofs-OFS_PARM0)%3); + else + sprintf(message, "offset_%i", ofs); + } + return message; +} +#endif + +QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_dstatement_t **outstatement) +{ + QCC_dstatement_t *statement; + QCC_def_t *var_c=NULL, *temp; + + if (var_a) + { + var_a->references++; + QCC_FreeTemp(var_a); + } + if (var_b) + { + var_b->references++; + QCC_FreeTemp(var_b); + } + + if (outstatement == (QCC_dstatement_t **)0xffffffff) + outstatement = NULL; + else if (op->priority != -1) + { + if (op->associative!=ASSOC_LEFT) + { + if (op->type_a == &type_pointer) + var_b = QCC_SupplyConversion(var_b, (*op->type_b)->type); + else + var_b = QCC_SupplyConversion(var_b, (*op->type_a)->type); + } + else + { + if (var_a) + var_a = QCC_SupplyConversion(var_a, (*op->type_a)->type); + if (var_b) + var_b = QCC_SupplyConversion(var_b, (*op->type_b)->type); +// if (op->type_a == &def_pointer) +// var_a = QCC_SupplyConversion(var_a, (*op->type_b)->type); +// else +// var_a = QCC_SupplyConversion(var_a, (*op->type_a)->type); +// } +// //can't convert the left componant of an assignment operation +// if (var_b && var_b->type && var_b->type != op->type_b->type) +// var_b = QCC_SupplyConversion(var_b, op->type_b->type->type); + } + } + + if (keyword_class && var_a && var_b) + { + if (var_a->type->type == ev_entity && var_b->type->type == ev_entity) + if (var_a->type != var_b->type) + if (strcmp(var_a->type->name, var_b->type->name)) + QCC_PR_ParseWarning(0, "Inexplict cast"); + } + + //maths operators + if (opt_constantarithmatic && (var_a && var_a->constant) && (var_b && var_b->constant)) + { + switch (op - pr_opcodes) //improve some of the maths. + { + case OP_BITOR: + optres_constantarithmatic++; + return QCC_MakeFloatDef((float)((int)G_FLOAT(var_a->ofs) | (int)G_FLOAT(var_b->ofs))); + case OP_BITAND: + optres_constantarithmatic++; + return QCC_MakeFloatDef((float)((int)G_FLOAT(var_a->ofs) & (int)G_FLOAT(var_b->ofs))); + case OP_MUL_F: + optres_constantarithmatic++; + return QCC_MakeFloatDef(G_FLOAT(var_a->ofs) * G_FLOAT(var_b->ofs)); + case OP_DIV_F: + optres_constantarithmatic++; + return QCC_MakeFloatDef(G_FLOAT(var_a->ofs) / G_FLOAT(var_b->ofs)); + case OP_ADD_F: + optres_constantarithmatic++; + return QCC_MakeFloatDef(G_FLOAT(var_a->ofs) + G_FLOAT(var_b->ofs)); + case OP_SUB_F: + optres_constantarithmatic++; + return QCC_MakeFloatDef(G_FLOAT(var_a->ofs) - G_FLOAT(var_b->ofs)); + + case OP_BITOR_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) | G_INT(var_b->ofs)); + case OP_BITAND_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) & G_INT(var_b->ofs)); + case OP_MUL_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) * G_INT(var_b->ofs)); + case OP_DIV_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) / G_INT(var_b->ofs)); + case OP_ADD_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) + G_INT(var_b->ofs)); + case OP_SUB_I: + optres_constantarithmatic++; + return QCC_MakeIntDef(G_INT(var_a->ofs) - G_INT(var_b->ofs)); + } + } + + switch (op - pr_opcodes) + { + case OP_AND: + if (var_a->ofs == var_b->ofs) + QCC_PR_ParseWarning(0, "Parameters for && are the same"); + if (var_a->constant || var_b->constant) + QCC_PR_ParseWarning(0, "Result of comparison is constant"); + break; + case OP_OR: + if (var_a->ofs == var_b->ofs) + QCC_PR_ParseWarning(0, "Parameters for || are the same"); + if (var_a->constant || var_b->constant) + QCC_PR_ParseWarning(0, "Result of comparison is constant"); + break; + 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_LE: + case OP_GE: + case OP_LT: + case OP_GT: + if ((var_a->constant && var_b->constant && !var_a->temp && !var_b->temp) || var_a->ofs == var_b->ofs) + QCC_PR_ParseWarning(0, "Result of comparison is constant"); + break; + case OP_IF: + case OP_IFNOT: + case OP_IFS: + case OP_IFNOTS: + if (var_a->type->type == ev_function && !var_a->temp) + QCC_PR_ParseWarning(0, "Result of comparison is constant"); + // if (var_a->constant && !var_a->temp) + // QCC_PR_ParseWarning(0, "Result of comparison is constant"); + break; + default: + break; + } + + if (numstatements) + { //optimise based on last statement. + if (op - pr_opcodes == OP_IFNOT) + { + if (opt_shortenifnots && var_a && (statements[numstatements-1].op == OP_NOT_F || statements[numstatements-1].op == OP_NOT_FNC || statements[numstatements-1].op == OP_NOT_ENT)) + { + if (statements[numstatements-1].c == var_a->ofs) + { + static QCC_def_t nvara; + op = &pr_opcodes[OP_IF]; + numstatements--; + QCC_FreeTemp(var_a); + memcpy(&nvara, var_a, sizeof(nvara)); + nvara.ofs = statements[numstatements].a; + var_a = &nvara; + + optres_shortenifnots++; + } + } + } + else if (op - pr_opcodes == OP_IFNOTS) + { + if (opt_shortenifnots && var_a && statements[numstatements-1].op == OP_NOT_S) + { + if (statements[numstatements-1].c == var_a->ofs) + { + static QCC_def_t nvara; + op = &pr_opcodes[OP_IFS]; + numstatements--; + QCC_FreeTemp(var_a); + memcpy(&nvara, var_a, sizeof(nvara)); + nvara.ofs = statements[numstatements].a; + var_a = &nvara; + + optres_shortenifnots++; + } + } + } + else if (((unsigned) ((op - pr_opcodes) - OP_STORE_F) < 6)) + { + if (opt_assignments && var_a && var_a->ofs == statements[numstatements-1].c)// && var_a->ofs >RESERVED_OFS) + { + if (var_a->type->type == var_b->type->type) + { + if (var_a->temp) + { + statement = &statements[numstatements-1]; + statement->c = var_b->ofs; + + if (var_a->type->type != var_b->type->type) + QCC_PR_ParseWarning(0, "store type mismatch"); + var_b->references++; + var_a->references--; + QCC_FreeTemp(var_a); + optres_assignments++; + + simplestore=true; + + QCC_UnFreeTemp(var_b); + return var_b; + } + } + } + } + } + simplestore=false; + + statement = &statements[numstatements]; + numstatements++; + + if (!QCC_OPCodeValid(op)) + { + switch(op - pr_opcodes) + { + case OP_IFS: + var_c = QCC_PR_GetDef(type_string, "string_null", NULL, true, 1); + numstatements--; + var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_S], var_a, var_b, NULL); + statement = &statements[numstatements]; + numstatements++; + + QCC_FreeTemp(var_a); + op = &pr_opcodes[OP_IF]; + break; + + case OP_IFNOTS: + var_c = QCC_PR_GetDef(type_string, "string_null", NULL, true, 1); + numstatements--; + var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_S], var_a, var_c, NULL); + statement = &statements[numstatements]; + numstatements++; + + QCC_FreeTemp(var_a); + op = &pr_opcodes[OP_IFNOT]; + break; + + case OP_ADDSTORE_F: + op = &pr_opcodes[OP_ADD_F]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_SUBSTORE_F: + op = &pr_opcodes[OP_SUB_F]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_DIVSTORE_F: + op = &pr_opcodes[OP_DIV_F]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_MULSTORE_F: + op = &pr_opcodes[OP_MUL_F]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_ADDSTORE_V: + op = &pr_opcodes[OP_ADD_V]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_SUBSTORE_V: + op = &pr_opcodes[OP_SUB_V]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_MULSTORE_V: + op = &pr_opcodes[OP_MUL_V]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_BITSET: + op = &pr_opcodes[OP_BITOR]; + var_c = var_b; + var_b = var_a; + var_a = var_c; + var_c = var_a; + break; + + case OP_BITCLR: + //b = var, a = bit field. + + QCC_UnFreeTemp(var_a); + QCC_UnFreeTemp(var_b); + + numstatements--; + var_c = QCC_PR_Statement(&pr_opcodes[OP_BITAND], var_b, var_a, NULL); + QCC_FreeTemp(var_c); + statement = &statements[numstatements]; + numstatements++; + + QCC_FreeTemp(var_a); + QCC_FreeTemp(var_b); + + op = &pr_opcodes[OP_SUB_F]; + var_a = var_b; + var_b = var_c; + var_c = var_a; + break; + + case OP_SUBSTOREP_F: + case OP_ADDSTOREP_F: + case OP_MULSTOREP_F: + case OP_DIVSTOREP_F: + case OP_BITSETP: + case OP_BITCLRP: +// QCC_PR_ParseWarning(0, "XSTOREP_F emulation is still experimental"); + QCC_UnFreeTemp(var_a); + QCC_UnFreeTemp(var_b); + //don't chain these... this expansion is not the same. + { + int st; + + for (st = numstatements-2; st>=0; st--) + { + if (statements[st].op == OP_ADDRESS) + if (statements[st].c == var_b->ofs) + break; + } + if (st < 0) + QCC_PR_ParseError(ERR_INTERNAL, "XSTOREP_F couldn't find pointer generation"); + var_c = QCC_GetTemp(*op->type_c); + + statement_linenums[statement-statements] = pr_source_line; + statement->op = OP_LOAD_F; + statement->a = statements[st].a; + statement->b = statements[st].b; + statement->c = var_c->ofs; + } + + statement = &statements[numstatements]; + numstatements++; + + statement_linenums[statement-statements] = pr_source_line; + switch(op - pr_opcodes) + { + case OP_SUBSTOREP_F: + statement->op = OP_SUB_F; + break; + case OP_ADDSTOREP_F: + statement->op = OP_ADD_F; + break; + case OP_MULSTOREP_F: + statement->op = OP_MUL_F; + break; + case OP_DIVSTOREP_F: + statement->op = OP_DIV_F; + break; + case OP_BITSETP: + statement->op = OP_BITOR; + break; + case OP_BITCLRP: + //float pointer float + temp = QCC_GetTemp(type_float); + statement->op = OP_BITAND; + statement->a = var_c ? var_c->ofs : 0; + statement->b = var_a ? var_a->ofs : 0; + statement->c = temp->ofs; + + statement = &statements[numstatements]; + numstatements++; + + statement_linenums[statement-statements] = pr_source_line; + statement->op = OP_SUB_F; + + //t = c & i + //c = c - t + break; + default: //no way will this be hit... + QCC_PR_ParseError(ERR_INTERNAL, "opcode invalid 3 times %i", op - pr_opcodes); + } + if (op - pr_opcodes == OP_BITCLRP) + { + statement->a = var_c ? var_c->ofs : 0; + statement->b = temp ? temp->ofs : 0; + statement->c = var_c->ofs; + QCC_FreeTemp(temp); + var_b = var_b; //this is the ptr. + var_a = var_c; //this is the value. + } + else + { + statement->a = var_c ? var_c->ofs : 0; + statement->b = var_a ? var_a->ofs : 0; + statement->c = var_c->ofs; + var_b = var_b; //this is the ptr. + var_a = var_c; //this is the value. + } + + op = &pr_opcodes[OP_STOREP_F]; + QCC_FreeTemp(var_c); + var_c = NULL; + QCC_FreeTemp(var_a); + QCC_FreeTemp(var_b); + + statement = &statements[numstatements]; + numstatements++; + break; + + case OP_MULSTOREP_V: + case OP_SUBSTOREP_V: + case OP_ADDSTOREP_V: +// QCC_PR_ParseWarning(0, "XSTOREP_V emulation is still experimental"); + QCC_UnFreeTemp(var_a); + QCC_UnFreeTemp(var_b); + //don't chain these... this expansion is not the same. + { + int st; + for (st = numstatements-2; st>=0; st--) + { + if (statements[st].op == OP_ADDRESS) + if (statements[st].c == var_b->ofs) + break; + } + if (st < 0) + QCC_PR_ParseError(ERR_INTERNAL, "XSTOREP_V couldn't find pointer generation"); + var_c = QCC_GetTemp(*op->type_c); + + statement_linenums[statement-statements] = pr_source_line; + statement->op = OP_LOAD_V; + statement->a = statements[st].a; + statement->b = statements[st].b; + statement->c = var_c ? var_c->ofs : 0; + } + + statement = &statements[numstatements]; + numstatements++; + + statement_linenums[statement-statements] = pr_source_line; + switch(op - pr_opcodes) + { + case OP_SUBSTOREP_V: + statement->op = OP_SUB_V; + break; + case OP_ADDSTOREP_V: + statement->op = OP_ADD_V; + break; + case OP_MULSTOREP_V: + statement->op = OP_MUL_V; + break; + default: //no way will this be hit... + QCC_PR_ParseError(ERR_INTERNAL, "opcode invalid 3 times %i", op - pr_opcodes); + } + statement->a = var_a ? var_a->ofs : 0; + statement->b = var_c ? var_c->ofs : 0; + var_c = QCC_GetTemp(*op->type_c); + statement->c = var_c ? var_c->ofs : 0; + + var_b = var_b; //this is the ptr. + var_a = var_c; //this is the value. + op = &pr_opcodes[OP_STOREP_V]; + + + + + QCC_FreeTemp(var_c); + var_c = NULL; + QCC_FreeTemp(var_a); + QCC_FreeTemp(var_b); + + statement = &statements[numstatements]; + numstatements++; + break; + default: + QCC_PR_ParseError(ERR_BADEXTENSION, "Opcode \"%s|%s\" not valid for target\n", op->name, op->opname); + break; + } + } + + if (outstatement) + *outstatement = statement; + + statement_linenums[statement-statements] = pr_source_line; + statement->op = op - pr_opcodes; + statement->a = var_a ? var_a->ofs : 0; + statement->b = var_b ? var_b->ofs : 0; + if (var_c != NULL) + { + statement->c = var_c->ofs; + } + else if (op->type_c == &type_void || op->associative==ASSOC_RIGHT || op->type_c == NULL) + { + var_c = NULL; + statement->c = 0; // ifs, gotos, and assignments + // don't need vars allocated + } + else + { // allocate result space + var_c = QCC_GetTemp(*op->type_c); + statement->c = var_c->ofs; + if (op->type_b == &type_field) + { + var_c->name = var_b->name; + var_c->s_file = var_b->s_file; + var_c->s_line = var_b->s_line; + } + } + + if (!var_c) + { + if (var_a) + QCC_UnFreeTemp(var_a); + return var_a; + } + return var_c; +} + +/* +============ +QCC_PR_SimpleStatement + +Emits a primitive statement, returning the var it places it's value in +============ +*/ +QCC_dstatement_t *QCC_PR_SimpleStatement( int op, int var_a, int var_b, int var_c) +{ + QCC_dstatement_t *statement; + + if (!QCC_OPCodeValid(pr_opcodes+op)) + { +// outputversion = op->extension; +// if (noextensions) + QCC_PR_ParseError(ERR_BADEXTENSION, "Opcode \"%s|%s\" not valid for target\n", pr_opcodes[op].name, pr_opcodes[op].opname); + } + + statement_linenums[numstatements] = pr_source_line; + statement = &statements[numstatements]; + + numstatements++; + statement->op = op; + statement->a = var_a; + statement->b = var_b; + statement->c = var_c; + return statement; +} + +void QCC_PR_Statement3 ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var_b, QCC_def_t *var_c) +{ + QCC_dstatement_t *statement; + + if (!QCC_OPCodeValid(op)) + { +// outputversion = op->extension; +// if (noextensions) + QCC_PR_ParseError(ERR_BADEXTENSION, "Opcode \"%s|%s\" not valid for target\n", op->name, op->opname); + } + + statement = &statements[numstatements]; + numstatements++; + + statement_linenums[statement-statements] = pr_source_line; + statement->op = op - pr_opcodes; + statement->a = var_a ? var_a->ofs : 0; + statement->b = var_b ? var_b->ofs : 0; + statement->c = var_c ? var_c->ofs : 0; +} + +/* +============ +PR_ParseImmediate + +Looks for a preexisting constant +============ +*/ +QCC_def_t *QCC_PR_ParseImmediate (void) +{ + QCC_def_t *cn; + + if (pr_immediate_type == type_float) + { + cn = QCC_MakeFloatDef(pr_immediate._float); + QCC_PR_Lex (); + return cn; + } + if (pr_immediate_type == type_integer) + { + cn = QCC_MakeIntDef(pr_immediate._int); + QCC_PR_Lex (); + return cn; + } + + if (pr_immediate_type == type_string) + { + cn = QCC_MakeStringDef(pr_immediate_string); + QCC_PR_Lex (); + return cn; + } + +// check for a constant with the same value + for (cn=pr.def_head.next ; cn ; cn=cn->next) //FIXME - hashtable. + { + if (!cn->initialized) + continue; + if (!cn->constant) + continue; + if (cn->type != pr_immediate_type) + continue; + if (pr_immediate_type == type_string) + { + if (!STRCMP(G_STRING(cn->ofs), pr_immediate_string) ) + { + QCC_PR_Lex (); + return cn; + } + } + else if (pr_immediate_type == type_float) + { + if ( G_FLOAT(cn->ofs) == pr_immediate._float ) + { + QCC_PR_Lex (); + return cn; + } + } + else if (pr_immediate_type == type_integer) + { + if ( G_INT(cn->ofs) == pr_immediate._int ) + { + QCC_PR_Lex (); + return cn; + } + } + else if (pr_immediate_type == type_vector) + { + if ( ( G_FLOAT(cn->ofs) == pr_immediate.vector[0] ) + && ( G_FLOAT(cn->ofs+1) == pr_immediate.vector[1] ) + && ( G_FLOAT(cn->ofs+2) == pr_immediate.vector[2] ) ) + { + QCC_PR_Lex (); + return cn; + } + } + else + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "weird immediate type"); + } + +// allocate a new one + cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + cn->next = NULL; + pr.def_tail->next = cn; + pr.def_tail = cn; + + cn->type = pr_immediate_type; + cn->name = "IMMEDIATE"; + cn->constant = true; + cn->initialized = 1; + cn->scope = NULL; // always share immediates + +// copy the immediate to the global area + cn->ofs = QCC_GetFreeOffsetSpace(type_size[pr_immediate_type->type]); + + if (pr_immediate_type == type_string) + pr_immediate.string = QCC_CopyString (pr_immediate_string); + + memcpy (qcc_pr_globals + cn->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); + + QCC_PR_Lex (); + + return cn; +} + + +void QCC_PrecacheSound (QCC_def_t *e, int ch) +{ + char *n; + int i; + + if (e->type->type != ev_string) + return; + + if (!e->ofs || e->temp || !e->constant) + return; + n = G_STRING(e->ofs); + if (!*n) + return; + for (i=0 ; i= '1' && ch <= '9') + precache_sounds_block[i] = ch - '0'; + else + precache_sounds_block[i] = 1; + numsounds++; +} + +void QCC_PrecacheModel (QCC_def_t *e, int ch) +{ + char *n; + int i; + + if (e->type->type != ev_string) + return; + + if (!e->ofs || e->temp || !e->constant) + return; + n = G_STRING(e->ofs); + if (!*n) + return; + for (i=0 ; i= '1' && ch <= '9') + precache_models_block[i] = ch - '0'; + else + precache_models_block[i] = 1; + } + return; + } + if (nummodels == MAX_MODELS) + return; +// QCC_Error ("PrecacheModels: nummodels == MAX_MODELS"); + strcpy (precache_models[i], n); + if (ch >= '1' && ch <= '9') + precache_models_block[i] = ch - '0'; + else + precache_models_block[i] = 1; + nummodels++; +} + +void QCC_SetModel (QCC_def_t *e) +{ + char *n; + int i; + + if (e->type->type != ev_string) + return; + + if (!e->ofs || e->temp || !e->constant) + return; + n = G_STRING(e->ofs); + if (!*n) + return; + for (i=0 ; itype->type != ev_string) + return; + + if (!e->ofs || e->temp || !e->constant) + return; + n = G_STRING(e->ofs); + if (!*n) + return; + for (i=0 ; i= '1' && ch <= '9') + precache_textures_block[i] = ch - '0'; + else + precache_textures_block[i] = 1; + numtextures++; +} + +void QCC_PrecacheFile (QCC_def_t *e, int ch) +{ + char *n; + int i; + + if (e->type->type != ev_string) + return; + + if (!e->ofs || e->temp || !e->constant) + return; + n = G_STRING(e->ofs); + if (!*n) + return; + for (i=0 ; i= '1' && ch <= '9') + precache_files_block[i] = ch - '0'; + else + precache_files_block[i] = 1; + numfiles++; +} + +void QCC_PrecacheFileOptimised (char *n, int ch) +{ + int i; + + for (i=0 ; i= '1' && ch <= '9') + precache_files_block[i] = ch - '0'; + else + precache_files_block[i] = 1; + numfiles++; +} + +/* +============ +PR_ParseFunctionCall +============ +*/ +QCC_def_t *QCC_PR_ParseFunctionCall (QCC_def_t *func) //warning, the func could have no name set if it's a field call. +{ + QCC_def_t *e, *d, *old, *oself; + int arg, i; + QCC_type_t *t, *p; + int extraparms=false; + int np; + int laststatement = numstatements; + + int callconvention; + QCC_dstatement_t *st; + + QCC_def_t *param[MAX_PARMS+MAX_EXTRA_PARMS]; + + func->timescalled++; + + if (QCC_OPCodeValid(&pr_opcodes[OP_CALL1H])) + callconvention = OP_CALL1H; //FTE extended + else + callconvention = OP_CALL1; //FTE extended + + t = func->type; + + if (t->type != ev_function) + { + QCC_PR_ParseErrorPrintDef (ERR_NOTAFUNCTION, func, "not a function"); + } + + if (!t->num_parms) //intrinsics. These base functions have variable arguments. I would check for (...) args too, but that might be used for extended builtin functionality. (this code wouldn't compile otherwise) + { + if (!strcmp(func->name, "random")) + { + func->references++; + if (!QCC_PR_Check(")")) + { + e = QCC_PR_Expression (TOP_PRIORITY); + if (e->type->type != ev_float) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 1); + if (!QCC_PR_Check(")")) + { + QCC_PR_Expect(","); + d = QCC_PR_Expression (TOP_PRIORITY); + if (d->type->type != ev_float) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 2); + QCC_PR_Expect(")"); + } + else + d = NULL; + } + else + { + e = NULL; + d = NULL; + } + + + if (def_ret.temp->used) + { + old = QCC_GetTemp(def_ret.type); + if (def_ret.type->size == 3) + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_V], &def_ret, old, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], &def_ret, old, NULL)); + QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); + } + else + old = NULL; + + if (QCC_OPCodeValid(&pr_opcodes[OP_RAND0])) + { + if (e) + { + if (d) + QCC_PR_SimpleStatement(OP_RAND2, e->ofs, d->ofs, OFS_RETURN); + else + QCC_PR_SimpleStatement(OP_RAND1, e->ofs, 0, OFS_RETURN); + } + else + QCC_PR_SimpleStatement(OP_RAND0, 0, 0, OFS_RETURN); + } + else + { + if (e) + { + if (d) + { + QCC_dstatement_t *st; + QCC_def_t *t; + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + + if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) + { + t = QCC_PR_Statement(&pr_opcodes[OP_GT], d, e, NULL); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_IFNOT], t, 0, &st)); + st->b = 3; + + t = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], d, e, NULL); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs, OFS_RETURN); + + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_GOTO], 0, 0, &st)); + st->a = 3; + } + + t = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], e, d, NULL); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs, OFS_RETURN); + } + else + { + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN); + } + } + else + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + } + + if (e) + { + QCC_FreeTemp(e); + e->references++; + } + if (d) + { + d->references++; + QCC_FreeTemp(d); + } + + if (old) + { + d = QCC_GetTemp(type_float); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, &def_ret, d, NULL)); + if (def_ret.type->size == 3) + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, old, &def_ret, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, old, &def_ret, NULL)); + QCC_FreeTemp(old); + + return d; + } + + if (def_ret.temp->used) + QCC_PR_ParseWarning(0, "Return value conflict - output is likly to be invalid"); + def_ret.temp->used = true; + def_ret.type = type_float; + return &def_ret; + + } + if (!strcmp(func->name, "randomv")) + { + func->references++; + if (!QCC_PR_Check(")")) + { + e = QCC_PR_Expression (TOP_PRIORITY); + if (e->type->type != ev_vector) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 1); + if (!QCC_PR_Check(")")) + { + QCC_PR_Expect(","); + d = QCC_PR_Expression (TOP_PRIORITY); + if (d->type->type != ev_vector) + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i", 2); + QCC_PR_Expect(")"); + } + else + d = NULL; + } + else + { + e = NULL; + d = NULL; + } + + + if (def_ret.temp->used) + { + old = QCC_GetTemp(def_ret.type); + if (def_ret.type->size == 3) + QCC_PR_Statement(&pr_opcodes[OP_STORE_V], &def_ret, old, NULL); + else + QCC_PR_Statement(&pr_opcodes[OP_STORE_F], &def_ret, old, NULL); + QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); + } + else + old = NULL; + + if (QCC_OPCodeValid(&pr_opcodes[OP_RANDV0])) + { + if (e) + { + if (d) + QCC_PR_SimpleStatement(OP_RANDV2, e->ofs, d->ofs, OFS_RETURN); + else + QCC_PR_SimpleStatement(OP_RANDV1, e->ofs, 0, OFS_RETURN); + } + else + QCC_PR_SimpleStatement(OP_RANDV0, 0, 0, OFS_RETURN); + } + else + { + if (e) + { + if (d) + { + QCC_def_t *t; + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + + if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) + { + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_GT, d->ofs+2, e->ofs+2, t->ofs); + QCC_PR_SimpleStatement(OP_IFNOT, t->ofs, 3, 0); + + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+2, e->ofs+2, t->ofs); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+2); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs+2, OFS_RETURN+2); + + QCC_PR_SimpleStatement(OP_GOTO, 3, 0, 0); + } + + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+2, e->ofs+2, t->ofs); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+2); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs+2, OFS_RETURN+2); + + + + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + + if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) + { + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_GT, d->ofs+1, e->ofs+1, t->ofs); + QCC_PR_SimpleStatement(OP_IFNOT, t->ofs, 3, 0); + + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+1, e->ofs+1, t->ofs); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+1); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs+1, OFS_RETURN+1); + + QCC_PR_SimpleStatement(OP_GOTO, 3, 0, 0); + } + + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs+1, e->ofs+1, t->ofs); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN+1); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs+1, OFS_RETURN+1); + + + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + + if ((!d->constant || !e->constant) && G_FLOAT(d->ofs) >= G_FLOAT(d->ofs)) + { + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_GT, d->ofs, e->ofs, t->ofs); + QCC_PR_SimpleStatement(OP_IFNOT, t->ofs, 3, 0); + + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs, e->ofs, t->ofs); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, e->ofs, OFS_RETURN); + + QCC_PR_SimpleStatement(OP_GOTO, 3, 0, 0); + } + + t = QCC_GetTemp(type_float); + QCC_PR_SimpleStatement(OP_SUB_F, d->ofs, e->ofs, t->ofs); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, t->ofs, OFS_RETURN); + QCC_FreeTemp(t); + QCC_PR_SimpleStatement(OP_ADD_F, OFS_RETURN, d->ofs, OFS_RETURN); + } + else + { + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN+2); + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN+1); + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + QCC_PR_SimpleStatement(OP_MUL_F, OFS_RETURN, e->ofs, OFS_RETURN); + } + } + else + { + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + QCC_PR_SimpleStatement(OP_STORE_F, OFS_RETURN, OFS_RETURN+2, 0); + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + QCC_PR_SimpleStatement(OP_STORE_F, OFS_RETURN, OFS_RETURN+1, 0); + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + } + } + + + if (e) + { + QCC_FreeTemp(e); + e->references++; + } + if (d) + { + d->references++; + QCC_FreeTemp(d); + } + + if (old) + { + d = QCC_GetTemp(type_float); + QCC_PR_Statement(pr_opcodes+OP_STORE_V, &def_ret, d, NULL); + if (def_ret.type->size == 3) + QCC_PR_Statement(pr_opcodes+OP_STORE_V, old, &def_ret, NULL); + else + QCC_PR_Statement(pr_opcodes+OP_STORE_F, old, &def_ret, NULL); + QCC_FreeTemp(old); + + return d; + } + + if (def_ret.temp->used) + QCC_PR_ParseWarning(0, "Return value conflict - output is likly to be invalid"); + def_ret.temp->used = true; + def_ret.type = type_vector; + return &def_ret; + } + else if (!strcmp(func->name, "spawn")) + { + QCC_type_t *rettype; + if (QCC_PR_Check(")")) + { + rettype = type_entity; + } + else + { + rettype = QCC_TypeForName(QCC_PR_ParseName()); + if (!rettype || rettype->type != ev_entity) + QCC_PR_ParseError(ERR_NOTANAME, "Spawn operator with undefined class"); + + QCC_PR_Expect(")"); + } + + if (def_ret.temp->used) + QCC_PR_ParseWarning(0, "Return value conflict - output is likly to be invalid"); + def_ret.temp->used = true; + + if (rettype != type_entity) + { + char genfunc[2048]; + sprintf(genfunc, "Class*%s", rettype->name); + func = QCC_PR_GetDef(type_function, genfunc, NULL, true, 1); + func->references++; + } + QCC_PR_SimpleStatement(OP_CALL0, func->ofs, 0, 0); + def_ret.type = rettype; + return &def_ret; + } + } + + if (opt_precache_file) + { + if (!strncmp(func->name,"precache_file", 13)) + { + if (pr_token_type == tt_immediate && pr_immediate_type->type == ev_string) + { + optres_precache_file += strlen(pr_immediate_string); + QCC_PR_Lex(); + QCC_PR_Expect(")"); + QCC_PrecacheFileOptimised (pr_immediate_string, func->name[13]); + def_ret.type = type_void; + return &def_ret; + } + } + } + + QCC_LockActiveTemps(); //any temps before are likly to be used with the return value. + + //any temps referenced to build the parameters don't need to be locked. + +// copy the arguments to the global parameter variables + arg = 0; + if (t->num_parms < 0) + { + extraparms = true; + np = (t->num_parms * -1) - 1; + } + else + np = t->num_parms; + + + if (!QCC_PR_Check(")")) + { + p = t->param; + do + { + if (extraparms && arg >= MAX_PARMS) + QCC_PR_ParseErrorPrintDef (ERR_TOOMANYPARAMETERSVARARGS, func, "More than %i parameters on varargs function", MAX_PARMS); + else if (arg >= MAX_PARMS+MAX_EXTRA_PARMS) + QCC_PR_ParseErrorPrintDef (ERR_TOOMANYTOTALPARAMETERS, func, "More than %i parameters", MAX_PARMS+MAX_EXTRA_PARMS); + if (!extraparms && arg >= t->num_parms) + QCC_PR_ParseErrorPrintDef (ERR_TOOMANYPARAMETERSFORFUNC, func, "too many parameters"); + + e = QCC_PR_Expression (TOP_PRIORITY); + + if (arg == 0 && func->name) + { + // save information for model and sound caching + if (!strncmp(func->name,"precache_", 9)) + { + if (!strncmp(func->name+9,"sound", 5)) + QCC_PrecacheSound (e, func->name[14]); + else if (!strncmp(func->name+9,"model", 5)) + QCC_PrecacheModel (e, func->name[14]); + else if (!strncmp(func->name+9,"texture", 7)) + QCC_PrecacheTexture (e, func->name[16]); + else if (!strncmp(func->name+9,"file", 4)) + QCC_PrecacheFile (e, func->name[13]); + } + } + + if (arg>=MAX_PARMS) + { + if (!extra_parms[arg - MAX_PARMS]) + { + d = (QCC_def_t *) qccHunkAlloc (sizeof(QCC_def_t)); + d->name = "extra parm"; + d->ofs = QCC_GetFreeOffsetSpace (3); + extra_parms[arg - MAX_PARMS] = d; + } + d = extra_parms[arg - MAX_PARMS]; + } + else + d = &def_parms[arg]; + + if (pr_classtype && e->type->type == ev_field && p->type != ev_field) + { //convert. + oself = QCC_PR_GetDef(type_entity, "self", NULL, true, 1); + switch(e->type->aux_type->type) + { + case ev_string: + e = QCC_PR_Statement(pr_opcodes+OP_LOAD_S, oself, e, NULL); + break; + case ev_float: + e = QCC_PR_Statement(pr_opcodes+OP_LOAD_F, oself, e, NULL); + break; + case ev_function: + e = QCC_PR_Statement(pr_opcodes+OP_LOAD_FNC, oself, e, NULL); + break; + case ev_vector: + e = QCC_PR_Statement(pr_opcodes+OP_LOAD_V, oself, e, NULL); + break; + case ev_entity: + e = QCC_PR_Statement(pr_opcodes+OP_LOAD_ENT, oself, e, NULL); + break; + default: + QCC_Error(ERR_INTERNAL, "Bad member type. Try forced expansion"); + } + } + + if (p) + { + if (typecmp(e->type, p)) + /*if (e->type->type != ev_integer && p->type != ev_function) + if (e->type->type != ev_function && p->type != ev_integer) + if ( e->type->type != p->type )*/ + { + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHPARM, func, "type mismatch on parm %i - (%s should be %s)", arg+1, TypeName(e->type), TypeName(p)); + } + + d->type = p; + + p=p->next; + } + // a vector copy will copy everything + else + d->type = type_void; + + if (arg == 1 && !STRCMP(func->name, "setmodel")) + { + QCC_SetModel(e); + } + + param[arg] = e; +/* if (e->type->size>1) + QCC_PR_Statement (&pr_opcodes[OP_STORE_V], e, d, (QCC_dstatement_t **)0xffffffff); + else + QCC_PR_Statement (&pr_opcodes[OP_STORE_F], e, d, (QCC_dstatement_t **)0xffffffff); + */ + arg++; + } while (QCC_PR_Check (",")); + + if (t->num_parms != -1 && arg < np) + QCC_PR_ParseWarning (WARN_TOOFEWPARAMS, "too few parameters on call to %s", func->name); + QCC_PR_Expect (")"); + } + else if (np) + { + QCC_PR_ParseWarning (WARN_TOOFEWPARAMS, "%s: Too few parameters", func->name); + QCC_PR_ParsePrintDef (WARN_TOOFEWPARAMS, func); + } + +// qcc_functioncalled++; + for (i = 0; i < arg; i++) + { + if (i>=MAX_PARMS) + d = extra_parms[i - MAX_PARMS]; + else + d = &def_parms[i]; + + if (callconvention == OP_CALL1H) + if (i < 2) + { + param[i]->references++; + d->references++; + QCC_FreeTemp(param[i]); + continue; + } + + if (param[i]->type->size>1 || !opt_nonvec_parms) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_V], param[i], d, (QCC_dstatement_t **)0xffffffff)); + else + { + d->type = param[i]->type; + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], param[i], d, (QCC_dstatement_t **)0xffffffff)); + optres_nonvec_parms++; + } + } + + if (def_ret.temp->used) + { + old = QCC_GetTemp(def_ret.type); + if (def_ret.type->size == 3) + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_V], &def_ret, old, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], &def_ret, old, NULL)); + QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); + } + else + old = NULL; + + if (strchr(func->name, ':') && laststatement && statements[laststatement-1].op == OP_LOAD_FNC && statements[laststatement-1].c == func->ofs) + { //we're entering C++ code with a different self. + + //FIXME: problems could occur with hexen2 calling conventions when parm0/1 is 'self' + //thiscall. copy the right ent into 'self' (if it's not the same offset) + d = QCC_PR_GetDef(type_entity, "self", NULL, true, 1); + if (statements[laststatement-1].a != d->ofs) + { + oself = QCC_GetTemp(type_entity); + QCC_PR_SimpleStatement(OP_STORE_ENT, d->ofs, oself->ofs, 0); + QCC_PR_SimpleStatement(OP_STORE_ENT, statements[laststatement-1].a, d->ofs, 0); + + if (callconvention == OP_CALL1H) //other.function(self) + //hexenc calling convention would mean that the + //passed parameter is essentually (self=other), + //so pass oself instead which won't be affected + { + QCC_def_t *temp; + if (arg>=1 && param[0]->ofs == d->ofs) + { + temp = QCC_GetTemp(type_entity); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_ENT, oself, temp, NULL)); + QCC_UnFreeTemp(temp); + param[0] = temp; + } + if (arg>=2 && param[1]->ofs == d->ofs) + { + temp = QCC_GetTemp(type_entity); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_ENT, oself, temp, NULL)); + QCC_UnFreeTemp(temp); + param[1] = temp; + } + } + } + else + oself = NULL; + } + else + oself = NULL; + + if (arg>MAX_PARMS) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[callconvention-1+MAX_PARMS], func, 0, (QCC_dstatement_t **)&st)); + else if (arg) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[callconvention-1+arg], func, 0, (QCC_dstatement_t **)&st)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CALL0], func, 0, (QCC_dstatement_t **)&st)); + + if (callconvention == OP_CALL1H) + { + if (arg) + { + st->b = param[0]->ofs; +// QCC_FreeTemp(param[0]); + if (arg>1) + { + st->c = param[1]->ofs; +// QCC_FreeTemp(param[1]); + } + } + } + if (oself) + QCC_PR_SimpleStatement(OP_STORE_ENT, oself->ofs, d->ofs, 0); + + for(; arg; arg--) + { + QCC_FreeTemp(param[arg-1]); + } + + if (old) + { + d = QCC_GetTemp(t->aux_type); + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, &def_ret, d, NULL)); + if (def_ret.type->size == 3) + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_V, old, &def_ret, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_STORE_F, old, &def_ret, NULL)); + QCC_FreeTemp(old); + + return d; + } + + def_ret.type = t->aux_type; + if (def_ret.temp->used) + QCC_PR_ParseWarning(WARN_FIXEDRETURNVALUECONFLICT, "Return value conflict - output is inefficient"); + def_ret.temp->used = true; + + return &def_ret; +} + +int constchecks; +int varchecks; +int typechecks; +QCC_def_t *QCC_MakeIntDef(int value) +{ + QCC_def_t *cn; + +// check for a constant with the same value + for (cn=pr.def_head.next ; cn ; cn=cn->next) + { + varchecks++; + if (!cn->initialized) + continue; + if (!cn->constant) + continue; + constchecks++; + if (cn->type != type_integer) + continue; + typechecks++; + + if ( G_INT(cn->ofs) == value ) + { + return cn; + } + } + +// allocate a new one + cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + cn->next = NULL; + pr.def_tail->next = cn; + pr.def_tail = cn; + + cn->type = type_integer; + cn->name = "IMMEDIATE"; + cn->constant = true; + cn->initialized = 1; + cn->scope = NULL; // always share immediates + cn->arraysize = 1; + +// copy the immediate to the global area + cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); + + G_INT(cn->ofs) = value; + + + return cn; +} + +hashtable_t floatconstdefstable; +QCC_def_t *QCC_MakeFloatDef(float value) +{ + QCC_def_t *cn; + + cn = Hash_GetKey(&floatconstdefstable, *(int*)&value); + if (cn) + return cn; + +// allocate a new one + cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + cn->next = NULL; + pr.def_tail->next = cn; + pr.def_tail = cn; + + cn->type = type_float; + cn->name = "IMMEDIATE"; + cn->constant = true; + cn->initialized = 1; + cn->scope = NULL; // always share immediates + cn->arraysize = 1; + +// copy the immediate to the global area + cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); + + Hash_AddKey(&floatconstdefstable, *(int *)&value, cn); + + G_FLOAT(cn->ofs) = value; + + + return cn; +} + +hashtable_t stringconstdefstable; +QCC_def_t *QCC_MakeStringDef(char *value) +{ + QCC_def_t *cn; + int string; + + cn = Hash_Get(&stringconstdefstable, value); + if (cn) + return cn; + +// allocate a new one + cn = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + cn->next = NULL; + pr.def_tail->next = cn; + pr.def_tail = cn; + + cn->type = type_string; + cn->name = "IMMEDIATE"; + cn->constant = true; + cn->initialized = 1; + cn->scope = NULL; // always share immediates + cn->arraysize = 1; + +// copy the immediate to the global area + cn->ofs = QCC_GetFreeOffsetSpace (type_size[type_integer->type]); + + string = QCC_CopyString (value); + + Hash_Add(&stringconstdefstable, strings+string, cn); + + G_INT(cn->ofs) = string; + + + return cn; +} + +QCC_type_t *QCC_PR_NewType (char *name, int basictype); +QCC_type_t *QCC_PointerTypeTo(QCC_type_t *type) +{ + QCC_type_t *newtype; + newtype = QCC_PR_NewType("POINTER TYPE", ev_pointer); + newtype->aux_type = type; + return newtype; +} + +int basictypefield[ev_union+1]; +char *basictypenames[] = { + "void", + "string", + "float", + "vector", + "entity", + "field", + "function", + "pointer", + "integer", + "struct", + "union" +}; + +QCC_def_t *QCC_MemberInParentClass(char *name, QCC_type_t *clas) +{ //if a member exists, return the member field (rather than mapped-to field) + QCC_type_t *mt; + QCC_def_t *def; + int p, np; + char membername[2048]; + + if (!clas) + { + def = QCC_PR_GetDef(NULL, name, NULL, 0, 0); + if (def && def->type->type == ev_field) //the member existed as a normal entity field. + return def; + return NULL; + } + + np = clas->num_parms; + for (p = 0, mt = clas->param; p < np; p++, mt = mt->next) + { + if (strcmp(mt->name, name)) + continue; + + //the parent has it. + + sprintf(membername, "%s::"MEMBERFIELDNAME, clas->name, mt->name); + def = QCC_PR_GetDef(NULL, membername, NULL, false, 0); + return def; + } + + return QCC_MemberInParentClass(name, clas->parentclass); +} + +//create fields for the types, instanciate the members to the fields. +//we retouch the parents each time to guarentee polymorphism works. +//FIXME: virtual methods will not work properly. Need to trace down to see if a parent already defined it +void QCC_PR_EmitFieldsForMembers(QCC_type_t *clas) +{ + char membername[2048]; + int p, np, a; + unsigned int o; + QCC_type_t *mt, *ft; + QCC_def_t *f, *m; + if (clas->parentclass != type_entity) //parents MUST have all thier fields set or inheritance would go crazy. + QCC_PR_EmitFieldsForMembers(clas->parentclass); + + np = clas->num_parms; + mt = clas->param; + for (p = 0; p < np; p++, mt = mt->next) + { + sprintf(membername, "%s::"MEMBERFIELDNAME, clas->name, mt->name); + m = QCC_PR_GetDef(NULL, membername, NULL, false, 0); + + f = QCC_MemberInParentClass(mt->name, clas->parentclass); + if (f) + { + if (m->arraysize>1) + QCC_Error(ERR_INTERNAL, "FTEQCC does not support overloaded arrays of members"); + a=0; + for (o = 0; o < m->type->size; o++) + ((int *)qcc_pr_globals)[o+a*mt->size+m->ofs] = ((int *)qcc_pr_globals)[o+f->ofs]; + continue; + } + + for (a = 0; a < m->arraysize; a++) + { + //we need the type in here so saved games can still work without saving ints as floats. (would be evil) + ft = QCC_PR_NewType(basictypenames[mt->type], ev_field); + ft->aux_type = QCC_PR_NewType(basictypenames[mt->type], mt->type); + ft->aux_type->aux_type = type_void; + ft = QCC_PR_FindType(ft); + sprintf(membername, "__f_%s_%i", ft->name, ++basictypefield[mt->type]); + f = QCC_PR_GetDef(ft, membername, NULL, true, 1); + + for (o = 0; o < m->type->size; o++) + ((int *)qcc_pr_globals)[o+a*mt->size+m->ofs] = ((int *)qcc_pr_globals)[o+f->ofs]; + + f->references++; + } + } +} + +void QCC_PR_EmitClassFunctionTable(QCC_type_t *clas, QCC_type_t *childclas, QCC_def_t *ed) +{ //go through clas, do the virtual thing only if the child class does not override. + + char membername[2048]; + QCC_type_t *type; + QCC_type_t *oc; + int p; + + QCC_def_t *point, *member; + QCC_def_t *virt; + + if (clas->parentclass) + QCC_PR_EmitClassFunctionTable(clas->parentclass, childclas, ed); + + type = clas->param; + for (p = 0; p < clas->num_parms; p++, type = type->next) + { + for (oc = childclas; oc != clas; oc = oc->parentclass) + { + sprintf(membername, "%s::"MEMBERFIELDNAME, oc->name, type->name); + if (QCC_PR_GetDef(NULL, membername, NULL, false, 0)) + break; //a child class overrides. + } + if (oc != clas) + continue; + + if (type->type == ev_function) //FIXME: inheritance will not install all the member functions. + { + sprintf(membername, "%s::"MEMBERFIELDNAME, clas->name, type->name); + member = QCC_PR_GetDef(NULL, membername, NULL, false, 1); + if (!member) + { + QCC_PR_Warning(0, NULL, 0, "Member function %s was not defined", membername); + continue; + } + /* if (!strcmp(type->name, clas->name)) + { + constructor = member; + }*/ + point = QCC_PR_Statement(&pr_opcodes[OP_ADDRESS], ed, member, NULL); + sprintf(membername, "%s::%s", clas->name, type->name); + virt = QCC_PR_GetDef(type, membername, NULL, false, 1); + QCC_PR_Statement(&pr_opcodes[OP_STOREP_FNC], virt, point, NULL); + } + } +} + +//take all functions in the type, and parent types, and make sure the links all work properly. +int QCC_PR_EmitClassFromFunction(QCC_def_t *scope, char *tname) +{ + QCC_type_t *basetype; + + QCC_dfunction_t *df; + + QCC_def_t *virt; + QCC_def_t *ed, *oself; + QCC_def_t *constructor = NULL; + +// int func; + + basetype = QCC_TypeForName(tname); + if (!basetype) + QCC_PR_ParseError(ERR_INTERNAL, "Type %s was not defined...", tname); + + pr_scope = scope; + + df = &functions[numfunctions]; + numfunctions++; + + df->s_file = 0; + df->s_name = 0; + df->first_statement = numstatements; + df->parm_size[0] = 1; + df->numparms = 1; + df->parm_start = numpr_globals; + + //locals here... + ed = QCC_PR_GetDef(type_entity, "self", NULL, true, 1); //the one parameter + df->parm_start = ed->ofs; + + oself = QCC_PR_GetDef(type_entity, "oself", scope, true, 1); //the one parameter + locals_end = oself->ofs + basetype->size; + + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], ed, oself, NULL)); + virt = QCC_PR_GetDef(type_function, "spawn", NULL, false, 0); + if (!virt) + QCC_Error(ERR_INTERNAL, "spawn function was not defined\n"); + QCC_PR_SimpleStatement(OP_CALL0, virt->ofs, 0, 0); //calling convention doesn't come into it. + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], &def_ret, ed, NULL)); + + ed->references = 1; //there may be no functions. + + df->locals = locals_end - df->parm_start; + + + QCC_PR_EmitClassFunctionTable(basetype, basetype, ed); + + if (constructor) + QCC_PR_SimpleStatement(OP_CALL1, constructor->ofs, 0, 0); + + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], oself, ed, NULL)); //return to our old self. boom boom. + + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_DONE], NULL, NULL, NULL)); + + + pr_scope = NULL; + memset(basictypefield, 0, sizeof(basictypefield)); + QCC_PR_EmitFieldsForMembers(basetype); + pr_scope = scope; + QCC_WriteAsmFunction(scope, df->first_statement, df->parm_start); + pr.localvars = NULL; + +/* + fixme. + + go through building correct stuff... + + also fix field offsets according to the offset. +*/ + //basetype + return df - functions; +} +/* +============ +PR_ParseValue + +Returns the global ofs for the current token +============ +*/ +QCC_def_t *QCC_PR_ParseValue (QCC_type_t *assumeclass) +{ + QCC_def_t *ao=NULL; //arrayoffset + QCC_def_t *d, *nd, *od; + char *name; + QCC_dstatement_t *st; + int i; + + char membername[2048]; + +// if the token is an immediate, allocate a constant for it + if (pr_token_type == tt_immediate) + return QCC_PR_ParseImmediate (); + + name = QCC_PR_ParseName (); + + if (assumeclass && assumeclass->parentclass) // 'testvar' becomes 'self::testvar' + { //try getting a member. + QCC_type_t *type; + type = assumeclass; + d = NULL; + while(type != type_entity && type) + { + sprintf(membername, "%s::"MEMBERFIELDNAME, type->name, name); + od = d = QCC_PR_GetDef (NULL, membername, pr_scope, false, 0); + if (d) + break; + + type = type->parentclass; + } + if (!d) + od = d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0); + } + else + +// look through the defs + od = d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0); + + if (!d) + { + if ( (!strcmp(name, "random" )) || + (!strcmp(name, "randomv")) ) //intrinsics, any old function with no args will do. + od = d = QCC_PR_GetDef (type_function, name, NULL, true, 1); + else if (keyword_class && !strcmp(name, "this")) + { + if (!pr_classtype) + QCC_PR_ParseError(ERR_NOTANAME, "Cannot use 'this' outside of an OO function\n"); + od = QCC_PR_GetDef(NULL, "self", NULL, true, 1); + od = d = QCC_PR_DummyDef(pr_classtype, "this", pr_scope, 1, od->ofs, true); + } + else if (keyword_class && !strcmp(name, "super")) + { + if (!pr_classtype) + QCC_PR_ParseError(ERR_NOTANAME, "Cannot use 'super' outside of an OO function\n"); + od = QCC_PR_GetDef(NULL, "self", NULL, true, 1); + od = d = QCC_PR_DummyDef(pr_classtype, "super", pr_scope, 1, od->ofs, true); + } + else + { + od = d = QCC_PR_GetDef (type_float, name, pr_scope, true, 1); + if (!d) + QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s\"", name); + else + { + QCC_PR_ParseWarning (ERR_UNKNOWNVALUE, "Unknown value \"%s\", assuming float.", name); + pr_error_count++; + } + } + } + +reloop: + + +//FIXME: Make this work with double arrays/2nd level structures. +//Should they just jump back to here? + if (QCC_PR_Check("[")) + { + QCC_type_t *newtype; + if (ao) + { + numstatements--; //remove the last statement + + nd = QCC_PR_Expression (TOP_PRIORITY); + + if (d->type->size != 1) //we need to multiply it to find the offset. + { + if (ao->type->type == ev_integer) + nd = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], nd, QCC_MakeIntDef(d->type->size), NULL); //get add part + else if (ao->type->type == ev_float) + nd = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], nd, QCC_MakeFloatDef((float)d->type->size), NULL); //get add part + else + QCC_PR_ParseError(ERR_BADARRAYINDEXTYPE, "Array offset is not of integer or float type"); + } + + if (nd->type->type == ao->type->type) + { + if (ao->type->type == ev_integer) + ao = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], ao, nd, NULL); //get add part + else if (ao->type->type == ev_float) + ao = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], ao, nd, NULL); //get add part + else + QCC_PR_ParseError(ERR_BADARRAYINDEXTYPE, "Array offset is not of integer or float type"); + } + else + { + if (nd->type->type == ev_float) + nd = QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], nd, 0, NULL); + ao = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], ao, nd, NULL); //get add part + } + + newtype = d->type; + d = od; + } + else + { + ao = QCC_PR_Expression (TOP_PRIORITY); + + if (QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F]) && d->type->size != 1) //we need to multiply it to find the offset. + { + if (ao->type->type == ev_integer) + ao = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], ao, QCC_MakeIntDef(d->type->size), NULL); //get add part + else if (ao->type->type == ev_float) + ao = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], ao, QCC_MakeFloatDef((float)d->type->size), NULL); //get add part + else + QCC_PR_ParseError(ERR_BADARRAYINDEXTYPE, "Array offset is not of integer or float type"); + } + + newtype = d->type; + } + if (ao->type->type == ev_integer) + { + switch(newtype->type) + { + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_F], d, ao, NULL); //get pointer to precise def. + break; + case ev_string: + if (d->arraysize <= 1) + { + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_C], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_ITOF], ao, 0, NULL), NULL); //get pointer to precise def. + newtype = nd->type;//don't be fooled + } + else + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_S], d, ao, NULL); //get pointer to precise def. + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_V], d, ao, NULL); //get pointer to precise def. + break; + case ev_entity: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_ENT], d, ao, NULL); //get pointer to precise def. + break; + case ev_field: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FLD], d, ao, NULL); //get pointer to precise def. + break; + case ev_function: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FNC], d, ao, NULL); //get pointer to precise def. + nd->type = d->type; + break; + case ev_integer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. + break; + + case ev_struct: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. + nd->type = d->type; + break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + break; + } + d=nd; + } + else if (ao->type->type == ev_float) + { + if (qcc_targetformat == QCF_HEXEN2) + { //hexen2 style retrieval, mixed with q1 style assignments... + QCC_PR_Expect("]"); + if (QCC_PR_Check("=")) + { + QCC_dstatement_t *st; + QCC_def_t *funcretr; + if (d->scope) + QCC_PR_ParseError(0, "Scoped array without specific engine support"); + if (def_ret.temp->used && ao != &def_ret) + QCC_PR_ParseWarning(0, "RETURN VALUE ALREADY IN USE"); + + funcretr = QCC_PR_GetDef(type_function, qcva("ArraySet*%s", d->name), NULL, true, 1); + nd = QCC_PR_Expression(TOP_PRIORITY); + if (nd->type->type != d->type->type) + QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, d, "Type Mismatch on array assignment"); + + QCC_PR_Statement (&pr_opcodes[OP_CALL2H], funcretr, 0, &st); + st->a = d->ofs; + st->b = nd->ofs; + qcc_usefulstatement = true; + + nd = &def_ret; + d=nd; + d->type = newtype; + return d; + } + + switch(newtype->type) + { + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_F], d, ao, &st); //get pointer to precise def. + st->a = d->ofs; + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_V], d, ao, &st); //get pointer to precise def. + st->a = d->ofs; + break; + case ev_string: + nd = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_S], d, ao, &st); //get pointer to precise def. + st->a = d->ofs; + break; + case ev_entity: + nd = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_E], d, ao, &st); //get pointer to precise def. + st->a = d->ofs; + break; + case ev_function: + nd = QCC_PR_Statement(&pr_opcodes[OP_FETCH_GBL_FNC], d, ao, &st); //get pointer to precise def. + st->a = d->ofs; + break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + break; + } + + d=nd; + d->type = newtype; + return d; + } + else + { + if (!QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F])) //q1 compatable. + { //you didn't see this, okay? + QCC_def_t *funcretr; + if (d->scope) + QCC_PR_ParseError(0, "Scoped array without specific engine support"); + if (def_ret.temp->used && ao != &def_ret) + QCC_PR_ParseWarning(0, "RETURN VALUE ALREADY IN USE"); + + QCC_PR_Statement (&pr_opcodes[OP_STORE_F], ao, &def_parms[0], NULL); + + QCC_PR_Expect("]"); + if (QCC_PR_Check("=")) + { + funcretr = QCC_PR_GetDef(type_function, qcva("ArraySet*%s", d->name), NULL, true, 1); + nd = QCC_PR_Expression(TOP_PRIORITY); + if (nd->type->type != d->type->type) + QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCH, d, "Type Mismatch on array assignment"); + + QCC_PR_Statement (&pr_opcodes[OP_STORE_V], nd, &def_parms[1], NULL); + QCC_PR_Statement (&pr_opcodes[OP_CALL2], funcretr, 0, NULL); + qcc_usefulstatement = true; + } + else + { + funcretr = QCC_PR_GetDef(type_function, qcva("ArrayGet*%s", d->name), NULL, true, 1); + QCC_PR_Statement (&pr_opcodes[OP_CALL1], funcretr, 0, NULL); + } + + nd = &def_ret; + d=nd; + d->type = newtype; + return d; + } + else + { + switch(newtype->type) + { + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_F], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + case ev_string: + if (d->arraysize <= 1) + { + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_C], d, ao, NULL); //get pointer to precise def. + newtype = nd->type;//don't be fooled + } + else + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_S], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_V], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + case ev_entity: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_ENT], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + case ev_field: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FLD], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + case ev_function: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FNC], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + nd->type = d->type; + break; + case ev_integer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + break; + + case ev_struct: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + nd->type = d->type; + break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + break; + } + } + } + d=nd; + } + else + QCC_PR_ParseError(ERR_BADARRAYINDEXTYPE, "Array offset is not of integer or float type"); + + QCC_PR_Expect("]"); + d->type = newtype; + goto reloop; + } + + + i = d->type->type; + if (i == ev_pointer) + { + int j; + QCC_type_t *type; + if (QCC_PR_Check(".") || QCC_PR_Check("->")) + { + for (i = d->type->num_parms, type = d->type+1; i; i--, type++) + { + if (QCC_PR_Check(type->name)) + { + //give result + if (ao) + { + numstatements--; //remove the last statement + d = od; + + nd = QCC_MakeIntDef(type->ofs); + ao = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], ao, nd, NULL); //get add part + + //so that we may offset it and readd it. + } + else + ao = QCC_MakeIntDef(type->ofs); + switch (type->type) + { + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_F], d, ao, NULL); //get pointer to precise def. + break; + case ev_string: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_S], d, ao, NULL); //get pointer to precise def. + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_V], d, ao, NULL); //get pointer to precise def. + break; + case ev_entity: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_ENT], d, ao, NULL); //get pointer to precise def. + break; + case ev_field: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_FLD], d, ao, NULL); //get pointer to precise def. + break; + case ev_function: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_FNC], d, ao, NULL); //get pointer to precise def. + nd->type = type; + break; + case ev_integer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, ao, NULL); //get pointer to precise def. + break; + +// case ev_struct: + //no suitable op. +// nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, ao, NULL); //get pointer to precise def. +// nd->type = type; +// break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + break; + } + + d=nd; + break; + } + if (type->num_parms) + { + for (j = type->num_parms; j;j--) + type++; + } + } + if (!i) + QCC_PR_ParseError (ERR_MEMBERNOTVALID, "\"%s\" is not a member of \"%s\"", pr_token, od->type->name); + + goto reloop; + } + } + else if (i == ev_struct || i == ev_union) + { + int j; + QCC_type_t *type; + if (QCC_PR_Check(".") || QCC_PR_Check("->")) + { + for (i = d->type->num_parms, type = d->type+1; i; i--, type++) + { + if (QCC_PR_Check(type->name)) + { + //give result + if (ao) + { + numstatements--; //remove the last statement + d = od; + + nd = QCC_MakeIntDef(type->ofs); + ao = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], ao, nd, NULL); //get add part + + //so that we may offset it and readd it. + } + else + ao = QCC_MakeIntDef(type->ofs); + switch (type->type) + { + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_F], d, ao, NULL); //get pointer to precise def. + break; + case ev_string: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_S], d, ao, NULL); //get pointer to precise def. + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_V], d, ao, NULL); //get pointer to precise def. + break; + case ev_entity: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_ENT], d, ao, NULL); //get pointer to precise def. + break; + case ev_field: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FLD], d, ao, NULL); //get pointer to precise def. + break; + case ev_function: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FNC], d, ao, NULL); //get pointer to precise def. + nd->type = type; + break; + case ev_integer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. + break; + +// case ev_struct: + //no suitable op. +// nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. +// nd->type = type; +// break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + break; + } + + d=nd; + break; + } + if (type->num_parms) + { + for (j = type->num_parms; j;j--) + type++; + } + } + if (!i) + QCC_PR_ParseError (ERR_MEMBERNOTVALID, "\"%s\" is not a member of \"%s\"", pr_token, od->type->name); + + goto reloop; + } + } + +/* if (d->type->type == ev_pointer) + { //expand now, not in function call/maths parsing + switch(d->type->aux_type->type) + { + case ev_string: + d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_S], d, NULL, NULL); + break; + case ev_float: + d = QCC_PR_Statement(&pr_opcodes[OP_LOADP_F], d, NULL, NULL); + break; + } + } +*/ + if (!keyword_class) + return d; + + if (d->type->parentclass||d->type->type == ev_entity) //class + { + if (QCC_PR_Check(".") || QCC_PR_Check("->")) + { + QCC_def_t *field; + field = QCC_PR_ParseValue(d->type); + if (field->type->type == ev_field) + { + switch(field->type->aux_type->type) + { + default: + QCC_PR_ParseError(ERR_INTERNAL, "Bad field type"); + return d; + case ev_float: + return QCC_PR_Statement(&pr_opcodes[OP_LOAD_F], d, field, NULL); + case ev_string: + return QCC_PR_Statement(&pr_opcodes[OP_LOAD_S], d, field, NULL); + case ev_vector: + return QCC_PR_Statement(&pr_opcodes[OP_LOAD_V], d, field, NULL); + case ev_function: + { //complicated for a typecast + d = QCC_PR_Statement(&pr_opcodes[OP_LOAD_FNC], d, field, NULL); + nd = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (nd, 0, sizeof(QCC_def_t)); + nd->type = field->type->aux_type; + nd->ofs = d->ofs; + nd->temp = d->temp; + nd->constant = false; + nd->name = d->name; + return nd; + + } + case ev_entity: + return QCC_PR_Statement(&pr_opcodes[OP_LOAD_ENT], d, field, NULL); + } + } + } + } + + return d; +} + + +/* +============ +PR_Term +============ +*/ +QCC_def_t *QCC_PR_Term (void) +{ + QCC_def_t *e, *e2; + etype_t t; + if (pr_token_type == tt_punct) //a little extra speed... + { + if (QCC_PR_Check("++")) //supposedly. I'm unsure weather it works properly. + { + qcc_usefulstatement=true; + e = QCC_PR_Expression (TOP_PRIORITY); + if (e->constant) + QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to constant %s", e->name); + switch (e->type->type) + { + case ev_integer: + QCC_PR_Statement3(&pr_opcodes[OP_ADD_I], e, QCC_MakeIntDef(1), e); + break; + case ev_float: + QCC_PR_Statement3(&pr_opcodes[OP_ADD_F], e, QCC_MakeFloatDef(1), e); + break; + default: + QCC_PR_ParseError(ERR_BADPLUSPLUSOPERATOR, "++ operator on unsupported type"); + break; + } + return e; + } + else if (QCC_PR_Check("--")) + { + qcc_usefulstatement=true; + e = QCC_PR_Expression (TOP_PRIORITY); + if (e->constant) + QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to constant %s", e->name); + switch (e->type->type) + { + case ev_integer: + QCC_PR_Statement3(&pr_opcodes[OP_SUB_I], e, QCC_MakeIntDef(1), e); + break; + case ev_float: + QCC_PR_Statement3(&pr_opcodes[OP_SUB_F], e, QCC_MakeFloatDef(1), e); + break; + default: + QCC_PR_ParseError(ERR_BADPLUSPLUSOPERATOR, "-- operator on unsupported type"); + break; + } + return e; + } + + if (QCC_PR_Check ("!")) + { + e = QCC_PR_Expression (NOT_PRIORITY); + t = e->type->type; + if (t == ev_float) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_F], e, 0, NULL); + else if (t == ev_string) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_S], e, 0, NULL); + else if (t == ev_entity) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_ENT], e, 0, NULL); + else if (t == ev_vector) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_V], e, 0, NULL); + else if (t == ev_function) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_FNC], e, 0, NULL); + else if (t == ev_integer) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_FNC], e, 0, NULL); //functions are integer values too. + else if (t == ev_pointer) + e2 = QCC_PR_Statement (&pr_opcodes[OP_NOT_FNC], e, 0, NULL); //Pointers are too. + else + { + e2 = NULL; // shut up compiler warning; + QCC_PR_ParseError (ERR_BADNOTTYPE, "type mismatch for !"); + } + return e2; + } + + else if (QCC_PR_Check ("&")) + { + e = QCC_PR_Expression (NOT_PRIORITY); + t = e->type->type; + + e2 = QCC_PR_Statement (&pr_opcodes[OP_GLOBALADDRESS], e, 0, NULL); + return e2; + } + + if (QCC_PR_Check ("(")) + { + if (keyword_float && QCC_PR_Check("float")) //check for type casts + { + QCC_PR_Expect (")"); + e = QCC_PR_Term(); + if (e->type->type == ev_float) + return e; + else if (e->type->type == ev_integer) + return QCC_PR_Statement (&pr_opcodes[OP_CONV_ITOF], e, 0, NULL); + else if (e->type->type == ev_function) + return e; + // else + // QCC_PR_ParseError ("invalid typecast"); + + QCC_PR_ParseWarning (0, "Not all vars make sence as floats"); + + e2 = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (e2, 0, sizeof(QCC_def_t)); + e2->type = type_float; + e2->ofs = e->ofs; + e2->constant = true; + e2->temp = e->temp; + return e2; + } + else if (keyword_class && QCC_PR_Check("class")) + { + QCC_type_t *classtype = QCC_TypeForName(QCC_PR_ParseName()); + if (!classtype) + QCC_PR_ParseError(ERR_NOTANAME, "Class not defined for cast"); + + QCC_PR_Expect (")"); + e = QCC_PR_Term(); + e2 = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (e2, 0, sizeof(QCC_def_t)); + e2->type = classtype; + e2->ofs = e->ofs; + e2->constant = true; + e2->temp = e->temp; + return e2; + } + else if (keyword_integer && QCC_PR_Check("integer")) //check for type casts + { + QCC_PR_Expect (")"); + e = QCC_PR_Term(); + if (e->type->type == ev_integer) + return e; + else if (e->type->type == ev_float) + return QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], e, 0, NULL); + else + QCC_PR_ParseError (ERR_BADTYPECAST, "invalid typecast"); + } + else if (keyword_int && QCC_PR_Check("int")) //check for type casts + { + QCC_PR_Expect (")"); + e = QCC_PR_Term(); + if (e->type->type == ev_integer) + return e; + else if (e->type->type == ev_float) + return QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], e, 0, NULL); + else + QCC_PR_ParseError (ERR_BADTYPECAST, "invalid typecast"); + } + else + { + e = QCC_PR_Expression (TOP_PRIORITY); + QCC_PR_Expect (")"); + } + return e; + } + } + return QCC_PR_ParseValue (pr_classtype); +} + + +int QCC_canConv(QCC_def_t *from, etype_t to) +{ + if (from->type->type == to) + return 0; + + if (from->type->type == ev_vector && to == ev_float) + return 4; + + if (pr_classtype) + { + if (from->type->type == ev_field) + { + if (from->type->aux_type->type == to) + return 1; + } + } + +/* if (from->type->type == ev_pointer && from->type->aux_type->type == to) + return 1; + + if (QCC_ShouldConvert(from, to)>=0) + return 1; +*/ + if (from->type->type == ev_integer && to == ev_function) + return 1; + + return -100; +} +/* +============== +PR_Expression +============== +*/ + +QCC_def_t *QCC_PR_Expression (int priority) +{ + QCC_dstatement32_t *st; + QCC_opcode_t *op, *oldop; + + QCC_opcode_t *bestop; + int numconversions, c; + + int opnum; + + QCC_def_t *e, *e2; + etype_t type_a, type_b, type_c; + + if (priority == 0) + return QCC_PR_Term (); + + e = QCC_PR_Expression (priority-1); + + while (1) + { + if (priority == 1 && QCC_PR_Check ("(") ) + { + qcc_usefulstatement=true; + return QCC_PR_ParseFunctionCall (e); + } + + opnum=0; + + if (pr_token_type == tt_immediate) + { + if (pr_immediate_type->type == ev_float) + if (pr_immediate._float < 0) //hehehe... was a minus all along... + { + QCC_PR_IncludeChunk(pr_token, true, NULL); + strcpy(pr_token, "+");//two negatives would make a positive. + pr_token_type = tt_punct; + } + } + + if (pr_token_type != tt_punct) + { + QCC_PR_ParseWarning(WARN_UNEXPECTEDPUNCT, "Expected punctuation"); + } + + //go straight for the correct priority. + for (op = opcodeprioritized[priority][opnum]; op; op = opcodeprioritized[priority][++opnum]) +// for (op=pr_opcodes ; op->name ; op++) + { +// if (op->priority != priority) +// continue; + if (!QCC_PR_Check (op->name)) + continue; + st = NULL; + if ( op->associative!=ASSOC_LEFT ) + { + // if last statement is an indirect, change it to an address of + if (!simplestore && ((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I) && statements[numstatements-1].c == e->ofs) + { + qcc_usefulstatement=true; + statements[numstatements-1].op = OP_ADDRESS; + type_pointer->aux_type->type = e->type->type; + e->type = type_pointer; + } + //if last statement retrieved a value, switch it to retrieve a usable pointer. + if ( !simplestore && (unsigned)(statements[numstatements-1].op - OP_LOADA_F) < 7)// || statements[numstatements-1].op == OP_LOADA_C) + { + statements[numstatements-1].op = OP_GLOBALADDRESS; + type_pointer->aux_type->type = e->type->type; + e->type = type_pointer; + } + if ( !simplestore && (unsigned)(statements[numstatements-1].op - OP_LOADP_F) < 7) + { + statements[numstatements-1].op = OP_ADD_I; + } + if ( !simplestore && statements[numstatements-1].op == OP_LOADP_C && e->ofs == statements[numstatements-1].c) + { + statements[numstatements-1].op = OP_ADD_SF; + e->type = type_string; + + //now we want to make sure that string = float can't work without it being a dereferenced pointer. (we don't want to allow storep_c without dereferece) + e2 = QCC_PR_Expression (priority); + if (e2->type->type == ev_float) + op = &pr_opcodes[OP_STOREP_C]; + } + else + e2 = QCC_PR_Expression (priority); + } + else + { + if (op->priority == 7 && opt_logicops) + { + optres_logicops++; + if (*op->name == '&') + QCC_PR_Statement(&pr_opcodes[OP_IFNOT], e, NULL, &st); + else + QCC_PR_Statement(&pr_opcodes[OP_IF], e, NULL, &st); + } + + e2 = QCC_PR_Expression (priority-1); + } + + // type check + type_a = e->type->type; + type_b = e2->type->type; + + if (op->name[0] == '.')// field access gets type from field + { + if (e2->type->aux_type) + type_c = e2->type->aux_type->type; + else + type_c = -1; // not a field + } + else + type_c = ev_void; + + oldop = op; + bestop = NULL; + numconversions = 32767; + while (op) + { + if (!(type_c != ev_void && type_c != (*op->type_c)->type)) + { + if (!STRCMP (op->name , oldop->name)) //matches + { + //return values are never converted - what to? + // if (type_c != ev_void && type_c != op->type_c->type->type) + // { + // op++; + // continue; + // } + + if (op->associative!=ASSOC_LEFT) + {//assignment + if (op->type_a == &type_pointer) //ent var + { + if (e->type->type != ev_pointer || e->type->aux_type->type != (*op->type_b)->type) //if e isn't a pointer to a type_b + c = -200; //don't let the conversion work + else + c = QCC_canConv(e2, (*op->type_c)->type); + } + else + { + c=QCC_canConv(e2, (*op->type_b)->type); + if (type_a != (*op->type_a)->type) //in this case, a is the final assigned value + c = -300; //don't use this op, as we must not change var b's type + } + } + else + { + if (op->type_a == &type_pointer) //ent var + { + if (e2->type->type != ev_pointer || e2->type->aux_type->type != (*op->type_b)->type) //if e isn't a pointer to a type_b + c = -200; //don't let the conversion work + else + c = 0; + } + else + { + c=QCC_canConv(e, (*op->type_a)->type); + c+=QCC_canConv(e2, (*op->type_b)->type); + } + } + + if (c>=0 && c < numconversions) + { + bestop = op; + numconversions=c; + if (c == 0)//can't get less conversions than 0... + break; + } + } + else + break; + } + op = opcodeprioritized[priority][++opnum]; + } + if (bestop == NULL) + { + if (oldop->priority == CONDITION_PRIORITY) + op = oldop; + else + { + if (e->type->type == ev_pointer) + QCC_PR_ParseError (ERR_TYPEMISMATCH, "type mismatch for %s (%s and %s)", oldop->name, e->type->name, e2->type->name); + else + QCC_PR_ParseError (ERR_TYPEMISMATCH, "type mismatch for %s (%s and %s)", oldop->name, e->type->name, e2->type->name); + } + } + else + { + if (numconversions>3) + QCC_PR_ParseWarning(WARN_IMPLICITCONVERSION, "Implicit conversion"); + op = bestop; + } + +// if (type_a == ev_pointer && type_b != e->type->aux_type->type) +// QCC_PR_ParseError ("type mismatch for %s", op->name); + + if (st) + st->b = &statements[numstatements] - st; + + + if (op->associative!=ASSOC_LEFT) + { + qcc_usefulstatement = true; + if (e->constant || e->ofs < OFS_PARM0) + { + QCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, "Assignment to constant %s", e->name); + QCC_PR_ParsePrintDef(WARN_ASSIGNMENTTOCONSTANT, e); +#ifndef QCC + editbadfile(strings+s_file, pr_source_line); +#endif + } + if (conditional) + QCC_PR_ParseWarning(WARN_ASSIGNMENTINCONDITIONAL, "Assignment in conditional"); + + e = QCC_PR_Statement (op, e2, e, NULL); + } + else + e = QCC_PR_Statement (op, e, e2, NULL); + + if (type_c != ev_void/* && type_c != ev_string*/) // field access gets type from field + e->type = e2->type->aux_type; + + break; + } + if (!op) + { + if (!STRCMP(pr_token, "++")) + { + QCC_PR_IncludeChunk("+=1", false, NULL); + QCC_PR_Lex(); + } + else if (!STRCMP(pr_token, "--")) + { + QCC_PR_IncludeChunk("-=1", false, NULL); + QCC_PR_Lex(); + } + break; // next token isn't at this priority level + } + } + if (e == NULL) + QCC_PR_ParseError(ERR_INTERNAL, "e == null"); + return e; +} + + +/* +============ +PR_ParseStatement + +============ +*/ +void QCC_PR_ParseStatement (void) +{ + int continues; + int breaks; + int cases; + int i; + QCC_def_t *e, *e2; + QCC_dstatement_t *patch1, *patch2, *patch3; + + if (QCC_PR_Check ("{")) + { + e = pr.localvars; + while (!QCC_PR_Check("}")) + QCC_PR_ParseStatement (); + + if (pr_subscopedlocals) + { + for (e2 = pr.localvars; e2 != e; e2 = e2->nextlocal) + { + Hash_RemoveData(&localstable, e2->name, e2); + } + } + return; + } + + if (QCC_PR_Check("return")) + { + /*if (pr_classtype) + { + e = QCC_PR_GetDef(NULL, "__oself", pr_scope, false, 0); + e2 = QCC_PR_GetDef(NULL, "self", NULL, false, 0); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], e, QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 1, e2->ofs, false), NULL)); + }*/ + + if (QCC_PR_Check (";")) + { + if (pr_scope->type->aux_type->type != ev_void) + QCC_PR_ParseWarning(WARN_MISSINGRETURNVALUE, "\'%s\' should return %s", pr_scope->name, pr_scope->type->aux_type->name); + if (opt_return_only) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_DONE], 0, 0, NULL)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], 0, 0, NULL)); + return; + } + e = QCC_PR_Expression (TOP_PRIORITY); + QCC_PR_Expect (";"); + if (pr_scope->type->aux_type->type != e->type->type) + QCC_PR_ParseWarning(WARN_WRONGRETURNTYPE, "\'%s\' returned %s, expected %s", pr_scope->name, e->type->name, pr_scope->type->aux_type->name); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], e, 0, NULL)); + return; + } + + if (QCC_PR_Check("while")) + { + continues = num_continues; + breaks = num_breaks; + + QCC_PR_Expect ("("); + patch2 = &statements[numstatements]; + conditional = true; + e = QCC_PR_Expression (TOP_PRIORITY); + conditional = false; + if (((e->constant && !e->temp) || !STRCMP(e->name, "IMMEDIATE")) && opt_compound_jumps) + { + optres_compound_jumps++; + if (!G_INT(e->ofs)) + { + QCC_PR_ParseWarning(0, "while(0)?"); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch1)); + } + else + { + patch1 = NULL; + } + } + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT], e, 0, &patch1)); + QCC_PR_Expect (")"); //after the line number is noted.. + QCC_PR_ParseStatement (); + junkdef.ofs = patch2 - &statements[numstatements]; + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], &junkdef, 0, (QCC_dstatement_t **)0xffffffff)); + if (patch1) + patch1->b = &statements[numstatements] - patch1; + + if (breaks != num_breaks) + { + for(i = breaks; i < num_breaks; i++) + { + patch2 = &statements[pr_breaks[i]]; + statements[pr_breaks[i]].a = &statements[numstatements] - patch2; + } + num_breaks = breaks; + } + if (continues != num_continues) + { + for(i = continues; i < num_continues; i++) + { + patch2 = &statements[pr_continues[i]]; + statements[pr_continues[i]].a = patch1 - patch2; + } + num_continues = continues; + } + return; + } + if (keyword_for && QCC_PR_Check("for")) + { + int old_numstatements; + int numtemp, i; + + QCC_dstatement_t temp[32]; + int linenum[32]; + + continues = num_continues; + breaks = num_breaks; + + QCC_PR_Expect("("); + if (!QCC_PR_Check(";")) + { + QCC_FreeTemp(QCC_PR_Expression(TOP_PRIORITY)); + QCC_PR_Expect(";"); + } + + patch2 = &statements[numstatements]; + conditional = true; + e = QCC_PR_Expression(TOP_PRIORITY); + conditional = false; + QCC_PR_Expect(";"); + + old_numstatements = numstatements; + QCC_FreeTemp(QCC_PR_Expression(TOP_PRIORITY)); + numtemp = numstatements - old_numstatements; + if (numtemp > 32) + QCC_PR_ParseError(ERR_TOOCOMPLEX, "Update expression too large"); + numstatements = old_numstatements; + for (i = 0 ; i < numtemp ; i++) + { + linenum[i] = statement_linenums[numstatements + i]; + temp[i] = statements[numstatements + i]; + } + + QCC_PR_Expect(")"); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_IFNOT], e, 0, NULL)); + patch1 = &statements[numstatements-1]; + QCC_PR_ParseStatement(); + patch3 = &statements[numstatements]; + for (i = 0 ; i < numtemp ; i++) + { + statement_linenums[numstatements] = linenum[i]; + statements[numstatements++] = temp[i]; + } + QCC_PR_SimpleStatement(OP_GOTO, patch2 - &statements[numstatements], 0, 0); + patch1->b = &statements[numstatements] - patch1; + + if (breaks != num_breaks) + { + for(i = breaks; i < num_breaks; i++) + { + patch1 = &statements[pr_breaks[i]]; + statements[pr_breaks[i]].a = &statements[numstatements] - patch1; + } + num_breaks = breaks; + } + if (continues != num_continues) + { + for(i = continues; i < num_continues; i++) + { + patch1 = &statements[pr_continues[i]]; + statements[pr_continues[i]].a = patch3 - patch1; + } + num_continues = continues; + } + + return; + } + if (keyword_do && QCC_PR_Check("do")) + { + continues = num_continues; + breaks = num_breaks; + + patch1 = &statements[numstatements]; + QCC_PR_ParseStatement (); + QCC_PR_Expect ("while"); + QCC_PR_Expect ("("); + conditional = true; + e = QCC_PR_Expression (TOP_PRIORITY); + conditional = false; + junkdef.ofs = patch1 - &statements[numstatements]; + if (e->type == type_string) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFS], e, &junkdef, (QCC_dstatement_t **)0xffffffff)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF], e, &junkdef, (QCC_dstatement_t **)0xffffffff)); + + QCC_PR_Expect (")"); + QCC_PR_Expect (";"); + + if (breaks != num_breaks) + { + for(i = breaks; i < num_breaks; i++) + { + patch2 = &statements[pr_breaks[i]]; + statements[pr_breaks[i]].a = &statements[numstatements] - patch2; + } + num_breaks = breaks; + } + if (continues != num_continues) + { + for(i = continues; i < num_continues; i++) + { + patch2 = &statements[pr_continues[i]]; + statements[pr_continues[i]].a = patch1 - patch2; + } + num_continues = continues; + } + + return; + } + + if (QCC_PR_Check("local")) + { +// if (locals_end != numpr_globals) //is this breaking because of locals? +// QCC_PR_ParseWarning("local vars after temp vars\n"); + QCC_PR_ParseDefs (NULL); + locals_end = numpr_globals; + return; + } + + if (pr_token_type == tt_name) + if ((keyword_var && !STRCMP ("var", pr_token)) || + (keyword_string && !STRCMP ("string", pr_token)) || + (keyword_float && !STRCMP ("float", pr_token)) || + (keyword_entity && !STRCMP ("entity", pr_token)) || + (keyword_vector && !STRCMP ("vector", pr_token)) || + (keyword_integer && !STRCMP ("integer", pr_token)) || + (keyword_int && !STRCMP ("int", pr_token)) || + (keyword_const && !STRCMP ("const", pr_token))) + { +// if (locals_end != numpr_globals) //is this breaking because of locals? +// QCC_PR_ParseWarning("local vars after temp vars\n"); + QCC_PR_ParseDefs (NULL); + locals_end = numpr_globals; + return; + } + + if (keyword_state && QCC_PR_Check("state")) + { + QCC_PR_Expect("["); + QCC_PR_ParseState(); + QCC_PR_Expect(";"); + return; + } + + if (QCC_PR_Check("if")) + { + if (QCC_PR_Check("not")) + { + QCC_PR_Expect ("("); + conditional = true; + e = QCC_PR_Expression (TOP_PRIORITY); + conditional = false; + + if (e->type == type_string) //special case, as strings are now pointers, not offsets from string table + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFS], e, 0, &patch1)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF], e, 0, &patch1)); + } + else + { + QCC_PR_Expect ("("); + conditional = true; + e = QCC_PR_Expression (TOP_PRIORITY); + conditional = false; + + if (e->type == type_string) //special case, as strings are now pointers, not offsets from string table + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOTS], e, 0, &patch1)); + else + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT], e, 0, &patch1)); + } + + QCC_PR_Expect (")"); //close bracket is after we save the statement to mem (so debugger does not show the if statement as being on the line after + + QCC_PR_ParseStatement (); + + if (QCC_PR_Check ("else")) + { + int lastwasreturn; + lastwasreturn = 0;//statements[numstatements-1].op == OP_RETURN || statements[numstatements-1].op == OP_DONE; + + if (lastwasreturn && opt_compound_jumps) + { + optres_compound_jumps++; + QCC_PR_ParseStatement (); + } + else + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch2)); + patch1->b = &statements[numstatements] - patch1; + QCC_PR_ParseStatement (); + patch2->a = &statements[numstatements] - patch2; + } + } + else + patch1->b = &statements[numstatements] - patch1; + + return; + } + if (keyword_switch && QCC_PR_Check("switch")) + { + int hcstyle; + int defaultcase = -1; + int oldallowtemps; + breaks = num_breaks; + cases = num_cases; + + QCC_PR_Expect ("("); + + oldallowtemps = opt_overlaptemps; //disable temp reuse for now. + opt_overlaptemps = 0; + conditional = true; + e = QCC_PR_Expression (TOP_PRIORITY); + conditional = false; + opt_overlaptemps = oldallowtemps; + + hcstyle = QCC_OPCodeValid(&pr_opcodes[OP_SWITCH_F]); + + //expands + + //switch (CONDITION) + //{ + //case 1: + // break; + //case 2: + //default: + // break; + //} + + //to + + // x = CONDITION, goto start + // l1: + // goto end + // l2: + // def: + // goto end + // goto end P1 + // start: + // if (x == 1) goto l1; + // if (x == 2) goto l2; + // goto def + // end: + + //x is emitted in an opcode, stored as a register that we cannot access later. + //it should be possible to nest these. + + if (hcstyle) + { + switch(e->type->type) + { + case ev_float: + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_SWITCH_F], e, 0, &patch1)); + break; + case ev_entity: //whu??? + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_SWITCH_E], e, 0, &patch1)); + break; + case ev_vector: + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_SWITCH_V], e, 0, &patch1)); + break; + case ev_string: + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_SWITCH_S], e, 0, &patch1)); + break; + case ev_function: + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_SWITCH_FNC], e, 0, &patch1)); + break; + default: + hcstyle = false; + break; + } + } + if (!hcstyle) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], e, 0, &patch1)); + + QCC_PR_Expect (")"); //close bracket is after we save the statement to mem (so debugger does not show the if statement as being on the line after + + QCC_PR_ParseStatement (); + + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch2)); //the P1 statement/the theyforgotthebreak statement. + if (hcstyle) + patch1->b = &statements[numstatements] - patch1; //the goto start part + else + patch1->a = &statements[numstatements] - patch1; //the goto start part + + for (i = cases; i < num_cases; i++) + { + if (!pr_casesdef[i]) + { + if (defaultcase >= 0) + QCC_PR_ParseError(ERR_MULTIPLEDEFAULTS, "Duplicated default case"); + defaultcase = i; + } + else + { + if (pr_casesdef[i]->type->type != e->type->type) + QCC_PR_ParseWarning(WARN_SWITCHTYPEMISMATCH, "switch case type mismatch"); + if (pr_casesdef2[i]) + { + if (pr_casesdef2[i]->type->type != e->type->type) + QCC_PR_ParseWarning(WARN_SWITCHTYPEMISMATCH, "switch caserange type mismatch"); + + if (hcstyle) + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASERANGE], pr_casesdef[i], pr_casesdef2[i], &patch3)); + patch3->c = &statements[pr_cases[i]] - patch3; + } + else + { + QCC_def_t *e3; + e2 = QCC_PR_Statement (&pr_opcodes[OP_GE], e, pr_casesdef[i], NULL); + e3 = QCC_PR_Statement (&pr_opcodes[OP_LE], e, pr_casesdef2[i], NULL); + e2 = QCC_PR_Statement (&pr_opcodes[OP_AND], e2, e3, NULL); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF], e2, 0, &patch3)); + patch3->b = &statements[pr_cases[i]] - patch3; + } + } + else + { + if (hcstyle) + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASE], pr_casesdef[i], 0, &patch3)); + patch3->b = &statements[pr_cases[i]] - patch3; + } + else + { + if (!pr_casesdef[i]->constant || G_INT(pr_casesdef[i]->ofs)) + { + switch(e->type->type) + { + case ev_float: + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_F], e, pr_casesdef[i], NULL); + break; + case ev_entity: //whu??? + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_E], e, pr_casesdef[i], &patch1); + break; + case ev_vector: + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_V], e, pr_casesdef[i], &patch1); + break; + case ev_string: + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_S], e, pr_casesdef[i], &patch1); + break; + case ev_function: + e2 = QCC_PR_Statement (&pr_opcodes[OP_EQ_FNC], e, pr_casesdef[i], &patch1); + break; + default: + QCC_PR_ParseError(ERR_BADSWITCHTYPE, "Bad switch type"); + break; + } + QCC_PR_Statement (&pr_opcodes[OP_IF], e2, 0, &patch3); + } + else + { + if (e->type->type == ev_string) + QCC_PR_Statement (&pr_opcodes[OP_IFNOTS], e, 0, &patch3); + else + QCC_PR_Statement (&pr_opcodes[OP_IFNOT], e, 0, &patch3); + } + patch3->b = &statements[pr_cases[i]] - patch3; + } + } + } + } + if (defaultcase>=0) + { + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch3)); + patch3->a = &statements[pr_cases[defaultcase]] - patch3; + } + + num_cases = cases; + + + patch3 = &statements[numstatements]; + patch2->a = patch3 - patch2; //set P1 jump + + if (breaks != num_breaks) + { + for(i = breaks; i < num_breaks; i++) + { + patch2 = &statements[pr_breaks[i]]; + patch2->a = patch3 - patch2; + } + num_breaks = breaks; + } + + return; + } + + if (keyword_asm && QCC_PR_Check("asm")) + { + QCC_PR_Expect ("{"); + + while (!QCC_PR_Check("}")) + QCC_PR_ParseAsm (); + return; + } + + if (QCC_PR_Check(":")) + { + if (pr_token_type != tt_name) + { + QCC_PR_ParseError(ERR_BADLABELNAME, "invalid label name \"%s\"", pr_token); + return; + } + + for (i = 0; i < num_labels; i++) + if (!STRNCMP(pr_labels[i].name, pr_token, sizeof(pr_labels[num_labels].name) -1)) + { + QCC_PR_ParseWarning(WARN_DUPLICATELABEL, "Duplicate label %s", pr_token); + QCC_PR_Lex(); + return; + } + + if (num_labels >= max_labels) + { + max_labels += 8; + pr_labels = realloc(pr_labels, sizeof(*pr_labels)*max_labels); + } + + strncpy(pr_labels[num_labels].name, pr_token, sizeof(pr_labels[num_labels].name) -1); + pr_labels[num_labels].lineno = pr_source_line; + pr_labels[num_labels].statementno = numstatements; + + num_labels++; + +// QCC_PR_ParseWarning("Gotos are evil"); + QCC_PR_Lex(); + return; + } + if (keyword_goto && QCC_PR_Check("goto")) + { + if (pr_token_type != tt_name) + { + QCC_PR_ParseError(ERR_NOLABEL, "invalid label name \"%s\"", pr_token); + return; + } + + if (num_gotos >= max_gotos) + { + max_gotos += 8; + pr_gotos = realloc(pr_gotos, sizeof(*pr_gotos)*max_gotos); + } + + strncpy(pr_gotos[num_gotos].name, pr_token, sizeof(pr_gotos[num_gotos].name) -1); + pr_gotos[num_gotos].lineno = pr_source_line; + QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, &patch2); + pr_gotos[num_gotos].statementno = patch2 - statements; + + + num_gotos++; + +// QCC_PR_ParseWarning("Gotos are evil"); + QCC_PR_Lex(); + QCC_PR_Expect(";"); + return; + } + + if (keyword_break && QCC_PR_Check("break")) + { + if (!STRCMP ("(", pr_token)) + { //make sure it wasn't a call to the break function. + QCC_PR_IncludeChunk("break(", true, NULL); + QCC_PR_Lex(); //so it sees the break. + } + else + { + if (num_breaks >= max_breaks) + { + max_breaks += 8; + pr_breaks = realloc(pr_breaks, sizeof(*pr_breaks)*max_breaks); + } + pr_breaks[num_breaks] = numstatements; + QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, NULL); + num_breaks++; + QCC_PR_Expect(";"); + return; + } + } + if (keyword_continue && QCC_PR_Check("continue")) + { + if (num_continues >= max_continues) + { + max_continues += 8; + pr_continues = realloc(pr_continues, sizeof(*pr_continues)*max_continues); + } + pr_continues[num_continues] = numstatements; + QCC_PR_Statement (&pr_opcodes[OP_GOTO], 0, 0, NULL); + num_continues++; + QCC_PR_Expect(";"); + return; + } + if (keyword_case && QCC_PR_Check("case")) + { + if (num_cases >= max_cases) + { + max_cases += 8; + pr_cases = realloc(pr_cases, sizeof(*pr_cases)*max_cases); + pr_casesdef = realloc(pr_casesdef, sizeof(*pr_casesdef)*max_cases); + pr_casesdef2 = realloc(pr_casesdef2, sizeof(*pr_casesdef2)*max_cases); + } + pr_cases[num_cases] = numstatements; + pr_casesdef[num_cases] = QCC_PR_Expression (TOP_PRIORITY); + if (QCC_PR_Check("..")) + { + pr_casesdef2[num_cases] = QCC_PR_Expression (TOP_PRIORITY); + if (pr_casesdef[num_cases]->constant && pr_casesdef2[num_cases]->constant && + !pr_casesdef[num_cases]->temp && !pr_casesdef2[num_cases]->temp) + if (G_FLOAT(pr_casesdef[num_cases]->ofs) >= G_FLOAT(pr_casesdef2[num_cases]->ofs)) + QCC_PR_ParseError(ERR_CASENOTIMMEDIATE, "Caserange statement uses backwards range\n"); + } + else + pr_casesdef2[num_cases] = NULL; + + if (numstatements != pr_cases[num_cases]) + QCC_PR_ParseError(ERR_CASENOTIMMEDIATE, "Case statements may not use formulas\n"); + num_cases++; + QCC_PR_Expect(":"); + return; + } + if (keyword_default && QCC_PR_Check("default")) + { + if (num_cases >= max_cases) + { + max_cases += 8; + pr_cases = realloc(pr_cases, sizeof(*pr_cases)*max_cases); + pr_casesdef = realloc(pr_casesdef, sizeof(*pr_casesdef)*max_cases); + pr_casesdef2 = realloc(pr_casesdef2, sizeof(*pr_casesdef2)*max_cases); + } + pr_cases[num_cases] = numstatements; + pr_casesdef[num_cases] = NULL; + pr_casesdef2[num_cases] = NULL; + num_cases++; + QCC_PR_Expect(":"); + return; + } + + if (keyword_thinktime && QCC_PR_Check("thinktime")) + { + QCC_def_t *nextthink; + QCC_def_t *time; + e = QCC_PR_Expression (TOP_PRIORITY); + QCC_PR_Expect(":"); + e2 = QCC_PR_Expression (TOP_PRIORITY); + if (e->type->type != ev_entity || e2->type->type != ev_float) + QCC_PR_ParseError(ERR_THINKTIMETYPEMISMATCH, "thinktime type mismatch"); + + if (QCC_OPCodeValid(&pr_opcodes[OP_THINKTIME])) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_THINKTIME], e, e2, NULL)); + else + { + nextthink = QCC_PR_GetDef(NULL, "nextthink", NULL, false, 0); + if (!nextthink) + QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s\"", "nextthink"); + time = QCC_PR_GetDef(type_float, "time", NULL, false, 0); + if (!time) + QCC_PR_ParseError (ERR_UNKNOWNVALUE, "Unknown value \"%s\"", "time"); + nextthink = QCC_PR_Statement(&pr_opcodes[OP_ADDRESS], e, nextthink, NULL); + time = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], time, e2, NULL); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STOREP_F], time, nextthink, NULL)); + } + QCC_PR_Expect(";"); + return; + } + if (QCC_PR_Check(";")) + { + QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "Effectless statement"); + return; + } + +// qcc_functioncalled=0; + + qcc_usefulstatement = false; + e = QCC_PR_Expression (TOP_PRIORITY); + QCC_PR_Expect (";"); + + if (e->type->type != ev_void && !qcc_usefulstatement) + QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "Effectless statement"); + + QCC_FreeTemp(e); + +// qcc_functioncalled=false; + + QCC_FreeTemps(); +} + + +/* +============== +PR_ParseState + +States are special functions made for convenience. They automatically +set frame, nextthink (implicitly), and think (allowing forward definitions). + +// void() name = [framenum, nextthink] {code} +// expands to: +// function void name () +// { +// self.frame=framenum; +// self.nextthink = time + 0.1; +// self.think = nextthink +// +// }; +============== +*/ +void QCC_PR_ParseState (void) +{ + char *name; + QCC_def_t *s1, *def, *sc = pr_scope; + char f; + + f = *pr_token; + if (QCC_PR_Check("++") || QCC_PR_Check("--")) + { + s1 = QCC_PR_ParseImmediate (); + QCC_PR_Expect(".."); + def = QCC_PR_ParseImmediate (); + QCC_PR_Expect ("]"); + + if (s1->type->type != ev_float || def->type->type != ev_float) + QCC_PR_ParseError(ERR_STATETYPEMISMATCH, "state type mismatch"); + + + if (QCC_OPCodeValid(&pr_opcodes[OP_CSTATE])) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CSTATE], s1, def, NULL)); + else + { + QCC_def_t *t1, *t2; + QCC_def_t *framef, *frame; + QCC_def_t *self; + QCC_def_t *cycle_wrapped; + self = QCC_PR_GetDef(type_entity, "self", NULL, false, 0); + framef = QCC_PR_GetDef(NULL, "frame", NULL, false, 0); + cycle_wrapped = QCC_PR_GetDef(type_float, "cycle_wrapped", NULL, false, 0); + + frame = QCC_PR_Statement(&pr_opcodes[OP_LOAD_F], self, framef, NULL); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatDef(0), cycle_wrapped, NULL)); + + //make sure the frame is within the bounds given. + t1 = QCC_PR_Statement(&pr_opcodes[OP_LT], frame, s1, NULL); + QCC_UnFreeTemp(frame); + t2 = QCC_PR_Statement(&pr_opcodes[OP_GT], frame, def, NULL); + QCC_UnFreeTemp(frame); + t1 = QCC_PR_Statement(&pr_opcodes[OP_OR], t1, t2, NULL); + QCC_PR_SimpleStatement(OP_IFNOT, t1->ofs, 2, 0); + QCC_FreeTemp(t1); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], s1, frame, NULL)); + QCC_PR_SimpleStatement(OP_GOTO, t1->ofs, 13, 0); + + t1 = QCC_PR_Statement(&pr_opcodes[OP_GE], def, s1, NULL); + QCC_PR_SimpleStatement(OP_IFNOT, t1->ofs, 7, 0); + QCC_FreeTemp(t1); //this block is the 'it's in a forwards direction' + QCC_PR_SimpleStatement(OP_ADD_F, frame->ofs, QCC_MakeFloatDef(1)->ofs, frame->ofs); + t1 = QCC_PR_Statement(&pr_opcodes[OP_GT], frame, def, NULL); + QCC_PR_SimpleStatement(OP_IFNOT, t1->ofs,2, 0); + QCC_FreeTemp(t1); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], s1, frame, NULL)); + QCC_UnFreeTemp(frame); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatDef(1), cycle_wrapped, NULL)); + + QCC_PR_SimpleStatement(OP_GOTO, 6, 0, 0); + //reverse animation. + QCC_PR_SimpleStatement(OP_SUB_F, frame->ofs, QCC_MakeFloatDef(1)->ofs, frame->ofs); + t1 = QCC_PR_Statement(&pr_opcodes[OP_LT], frame, s1, NULL); + QCC_PR_SimpleStatement(OP_IFNOT, t1->ofs,2, 0); + QCC_FreeTemp(t1); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], def, frame, NULL)); + QCC_UnFreeTemp(frame); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatDef(1), cycle_wrapped, NULL)); + + //self.frame = frame happens with the normal state opcode. + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], frame, pr_scope, NULL)); + QCC_FreeTemp(frame); + } + return; + } + + if (pr_token_type != tt_immediate || pr_immediate_type != type_float) + QCC_PR_ParseError (ERR_STATETYPEMISMATCH, "state frame must be a number"); + s1 = QCC_PR_ParseImmediate (); + + QCC_PR_Check (","); + + name = QCC_PR_ParseName (); + pr_scope = NULL; + def = QCC_PR_GetDef (type_function, name, NULL, true, 1); + pr_scope = sc; + + QCC_PR_Expect ("]"); + + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], s1, def, NULL)); +} + +void QCC_PR_ParseAsm(void) +{ +QCC_dstatement_t *patch1; + int op; + QCC_def_t *a, *b, *c; + + if (QCC_PR_Check("local")) + { + QCC_PR_ParseDefs (NULL); + locals_end = numpr_globals; + return; + } + + for (op = 0; op < OP_NUMOPS; op++) + { + if (!STRCMP(pr_token, pr_opcodes[op].opname)) + { + QCC_PR_Lex(); + if (pr_opcodes[op].priority==-1 && pr_opcodes[op].associative!=ASSOC_LEFT) + { + if (pr_opcodes[op].type_a==NULL) + { + (short)(int)a = (short)pr_immediate._float; + QCC_PR_Lex(); + patch1 = &statements[numstatements]; + QCC_PR_Statement3(&pr_opcodes[op], NULL, NULL, NULL); + patch1->a = (short)(int)a; + } + else if (pr_opcodes[op].type_b==NULL) + { + a = QCC_PR_ParseValue(pr_classtype); + (short)(int)b = (short)pr_immediate._float; + QCC_PR_Lex(); + + patch1 = &statements[numstatements]; + QCC_PR_Statement3(&pr_opcodes[op], a, NULL, NULL); + + patch1->b = (short)(int)b; + } + else + { + a = QCC_PR_ParseValue(pr_classtype); + b = QCC_PR_ParseValue(pr_classtype); + (short)(int)c = (short)pr_immediate._float; + QCC_PR_Lex(); + + patch1 = &statements[numstatements]; + QCC_PR_Statement3(&pr_opcodes[op], a, b, NULL); + + patch1->c = (short)(int)c; + } + } + else + { + if (pr_opcodes[op].type_a != &type_void) + a = QCC_PR_ParseValue(pr_classtype); + else + a=NULL; + if (pr_opcodes[op].type_b != &type_void) + b = QCC_PR_ParseValue(pr_classtype); + else + b=NULL; + if (pr_opcodes[op].associative==ASSOC_LEFT && pr_opcodes[op].type_c != &type_void) + c = QCC_PR_ParseValue(pr_classtype); + else + c=NULL; + + QCC_PR_Statement3(&pr_opcodes[op], a, b, c); + } + + QCC_PR_Expect(";"); + return; + } + } + QCC_PR_ParseError(ERR_BADOPCODE, "Bad op code name %s", pr_token); +} + +pbool QCC_FuncJumpsTo(int first, int last, int statement) +{ + int st; + for (st = first; st < last; st++) + { + if (pr_opcodes[statements[st].op].type_a == NULL) + { + if (st + (signed)statements[st].a == statement) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + if (pr_opcodes[statements[st].op].type_b == NULL) + { + if (st + (signed)statements[st].b == statement) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + if (pr_opcodes[statements[st].op].type_c == NULL) + { + if (st + (signed)statements[st].c == statement) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + } + return false; +} + +pbool QCC_FuncJumpsToRange(int first, int last, int firstr, int lastr) +{ + int st; + for (st = first; st < last; st++) + { + if (pr_opcodes[statements[st].op].type_a == NULL) + { + if (st + (signed)statements[st].a >= firstr && st + (signed)statements[st].a <= lastr) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + if (pr_opcodes[statements[st].op].type_b == NULL) + { + if (st + (signed)statements[st].b >= firstr && st + (signed)statements[st].b <= lastr) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + if (pr_opcodes[statements[st].op].type_c == NULL) + { + if (st + (signed)statements[st].c >= firstr && st + (signed)statements[st].c <= lastr) + { + if (st != first) + { + if (statements[st-1].op == OP_RETURN) + continue; + if (statements[st-1].op == OP_DONE) + continue; + return true; + } + } + } + } + return false; +} + +void QCC_CompoundJumps(int first, int last) +{ + //jumps to jumps are reordered so they become jumps to the final target. + int statement; + int st; + for (st = first; st < last; st++) + { + if (pr_opcodes[statements[st].op].type_a == NULL) + { + statement = st + (signed)statements[st].a; + if (statements[statement].op == OP_RETURN || statements[statement].op == OP_DONE) + { //goto leads to return. Copy the command out to remove the goto. + statements[st].op = statements[statement].op; + statements[st].a = statements[statement].a; + statements[st].b = statements[statement].b; + statements[st].c = statements[statement].c; + optres_compound_jumps++; + } + while (statements[statement].op == OP_GOTO) + { + (signed)statements[st].a = statement+statements[statement].a - st; + statement = st + (signed)statements[st].a; + optres_compound_jumps++; + } + } + if (pr_opcodes[statements[st].op].type_b == NULL) + { + statement = st + (signed)statements[st].b; + while (statements[statement].op == OP_GOTO) + { + (signed)statements[st].b = statement+statements[statement].a - st; + statement = st + (signed)statements[st].b; + optres_compound_jumps++; + } + } + if (pr_opcodes[statements[st].op].type_c == NULL) + { + statement = st + (signed)statements[st].c; + while (statements[statement].op == OP_GOTO) + { + (signed)statements[st].c = statement+statements[statement].a - st; + statement = st + (signed)statements[st].c; + optres_compound_jumps++; + } + } + } +} +/* +//goes through statements, if it sees a matching statement earlier, it'll strim out the current. +void QCC_CommonSubExpressionRemoval(int first, int last) +{ + int cur; //the current + int prev; //the earlier statement + for (cur = last-1; cur >= first; cur--) + { + if (pr_opcodes[statements[cur].op].priority == -1) + continue; + for (prev = cur-1; prev >= first; prev--) + { + if (statements[prev].op >= OP_CALL0 && statements[prev].op <= OP_CALL8) + { + optres_test1++; + break; + } + if (statements[prev].op >= OP_CALL1H && statements[prev].op <= OP_CALL8H) + { + optres_test1++; + break; + } + if (pr_opcodes[statements[prev].op].right_associative) + { //make sure no changes to var_a occur. + if (statements[prev].b == statements[cur].a) + { + optres_test2++; + break; + } + if (statements[prev].b == statements[cur].b && !pr_opcodes[statements[cur].op].right_associative) + { + optres_test2++; + break; + } + } + else + { + if (statements[prev].c == statements[cur].a) + { + optres_test2++; + break; + } + if (statements[prev].c == statements[cur].b && !pr_opcodes[statements[cur].op].right_associative) + { + optres_test2++; + break; + } + } + + if (statements[prev].op == statements[cur].op) + if (statements[prev].a == statements[cur].a) + if (statements[prev].b == statements[cur].b) + if (statements[prev].c == statements[cur].c) + { + if (!QCC_FuncJumpsToRange(first, last, prev, cur)) + { + statements[cur].op = OP_STORE_F; + statements[cur].a = 28; + statements[cur].b = 28; + optres_comexprremoval++; + } + else + optres_test1++; + break; + } + } + } +} +*/ + +void QCC_RemapOffsets(unsigned int firststatement, unsigned int laststatement, unsigned int min, unsigned int max, unsigned int newmin) +{ + QCC_dstatement_t *st; + unsigned int i; + + for (i = firststatement, st = &statements[i]; i < laststatement; i++, st++) + { + if (pr_opcodes[st->op].type_a && st->a >= min && st->a < max) + st->a = st->a - min + newmin; + if (pr_opcodes[st->op].type_b && st->b >= min && st->b < max) + st->b = st->b - min + newmin; + if (pr_opcodes[st->op].type_c && st->c >= min && st->c < max) + st->c = st->c - min + newmin; + } +} + +void QCC_Marshal_Locals(int first, int laststatement) +{ + QCC_def_t *local; + unsigned int newofs; + + if (!pr.localvars) //nothing to marshal + { + locals_start = numpr_globals; + locals_end = numpr_globals; + return; + } + + if (!opt_locals_marshalling) + { + pr.localvars = NULL; + return; + } + + //initial backwards bounds. + locals_start = MAX_REGS; + locals_end = 0; + + newofs = MAX_REGS; //this is a handy place to put it. :) + + //the params need to be in the order that they were allocated + //so we allocate in a backwards order. + for (local = pr.localvars; local; local = local->nextlocal) + { + if (local->constant) + continue; + + newofs += local->type->size*local->arraysize; + if (local->arraysize>1) + newofs++; + } + + locals_start = MAX_REGS; + locals_end = newofs; + + + optres_locals_marshalling+=newofs-MAX_REGS; + + for (local = pr.localvars; local; local = local->nextlocal) + { + if (local->constant) + continue; + + newofs -= local->type->size*local->arraysize; + if (local->arraysize>1) + newofs--; + + QCC_RemapOffsets(first, laststatement, local->ofs, local->ofs+local->type->size*local->arraysize, newofs); + QCC_FreeOffset(local->ofs, local->type->size*local->arraysize); + + local->ofs = newofs; + } + + + pr.localvars = NULL; +} + +#ifdef WRITEASM +void QCC_WriteAsmFunction(QCC_def_t *sc, unsigned int firststatement, gofs_t firstparm) +{ + unsigned int i; + unsigned int p; + gofs_t o; + QCC_type_t *type; + QCC_def_t *param; + + if (!asmfile) + return; + + type = sc->type; + fprintf(asmfile, "%s(", TypeName(type->aux_type)); + p = type->num_parms; + for (o = firstparm, i = 0, type = type->param; i < p; i++, type = type->next) + { + if (i) + fprintf(asmfile, ", "); + + for (param = pr.localvars; param; param = param->nextlocal) + { + if (param->ofs == o) + break; + } + if (param) + fprintf(asmfile, "%s %s", TypeName(type), param->name); + else + fprintf(asmfile, "%s", TypeName(type)); + + o += type->size; + } + + fprintf(asmfile, ") %s = asm\n{\n", sc->name); + + QCC_fprintfLocals(asmfile, firstparm, o); + + for (i = firststatement; i < (unsigned int)numstatements; i++) + { + fprintf(asmfile, "\t%s", pr_opcodes[statements[i].op].opname); + if (pr_opcodes[statements[i].op].type_a != &type_void) + { + if (strlen(pr_opcodes[statements[i].op].opname)<6) + fprintf(asmfile, "\t"); + if (pr_opcodes[statements[i].op].type_a) + fprintf(asmfile, "\t%s", QCC_VarAtOffset(statements[i].a, (*pr_opcodes[statements[i].op].type_a)->size)); + else + fprintf(asmfile, "\t%i", statements[i].a); + if (pr_opcodes[statements[i].op].type_b != &type_void) + { + if (pr_opcodes[statements[i].op].type_b) + fprintf(asmfile, ",\t%s", QCC_VarAtOffset(statements[i].b, (*pr_opcodes[statements[i].op].type_b)->size)); + else + fprintf(asmfile, ",\t%i", statements[i].b); + if (pr_opcodes[statements[i].op].type_c != &type_void && pr_opcodes[statements[i].op].associative==ASSOC_LEFT) + { + if (pr_opcodes[statements[i].op].type_c) + fprintf(asmfile, ",\t%s", QCC_VarAtOffset(statements[i].c, (*pr_opcodes[statements[i].op].type_c)->size)); + else + fprintf(asmfile, ",\t%i", statements[i].c); + } + } + } + fprintf(asmfile, ";\n"); + } + + fprintf(asmfile, "}\n\n"); +} +#endif + +/* +============ +PR_ParseImmediateStatements + +Parse a function body +============ +*/ +QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) +{ + int i; + QCC_function_t *f; + QCC_def_t *defs[MAX_PARMS+MAX_EXTRA_PARMS], *e2; + + QCC_type_t *parm; + pbool needsdone=false; + freeoffset_t *oldfofs; + + conditional = false; + + + f = (void *)qccHunkAlloc (sizeof(QCC_function_t)); + +// +// check for builtin function definition #1, #2, etc +// +// hexenC has void name() : 2; + if (QCC_PR_Check ("#") || QCC_PR_Check (":")) + { + if (pr_token_type != tt_immediate + || pr_immediate_type != type_float + || pr_immediate._float != (int)pr_immediate._float) + QCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, "Bad builtin immediate"); + f->builtin = (int)pr_immediate._float; + QCC_PR_Lex (); + + locals_start = locals_end = OFS_PARM0; //hmm... + return f; + } + + f->builtin = 0; +// +// define the parms +// + + locals_start = locals_end = numpr_globals; + + oldfofs = freeofs; + freeofs = NULL; + + parm = type->param; + for (i=0 ; inum_parms ; i++) + { + if (!*pr_parm_names[i]) + QCC_PR_ParseError(ERR_PARAMWITHNONAME, "Parameter is not named"); + defs[i] = QCC_PR_GetDef (parm, pr_parm_names[i], pr_scope, true, 1); + + defs[i]->references++; + if (i < MAX_PARMS) + { + f->parm_ofs[i] = defs[i]->ofs; + if (i > 0 && f->parm_ofs[i] < f->parm_ofs[i-1]) + QCC_Error (ERR_BADPARAMORDER, "bad parm order"); + if (i > 0 && f->parm_ofs[i] != f->parm_ofs[i-1]+defs[i-1]->type->size) + QCC_Error (ERR_BADPARAMORDER, "parms not packed"); + } + parm = parm->next; + } + + if (type->num_parms) + locals_start = locals_end = defs[0]->ofs; + + freeofs = oldfofs; + + f->code = numstatements; + + if (type->num_parms > MAX_PARMS) + { + for (i = MAX_PARMS; i < type->num_parms; i++) + { + if (!extra_parms[i - MAX_PARMS]) + { + e2 = (QCC_def_t *) qccHunkAlloc (sizeof(QCC_def_t)); + e2->name = "extra parm"; + e2->ofs = QCC_GetFreeOffsetSpace(3); + extra_parms[i - MAX_PARMS] = e2; + } + if (defs[i]->type->type != ev_vector) + QCC_PR_Statement (&pr_opcodes[OP_STORE_F], extra_parms[i - MAX_PARMS], defs[i], NULL); + else + QCC_PR_Statement (&pr_opcodes[OP_STORE_V], extra_parms[i - MAX_PARMS], defs[i], NULL); + } + } + + QCC_RemapLockedTemps(-1, -1); + + /*if (pr_classtype) + { + QCC_def_t *e, *e2; + e = QCC_PR_GetDef(pr_classtype, "__oself", pr_scope, true, 1); + e2 = QCC_PR_GetDef(type_entity, "self", NULL, true, 1); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 1, e2->ofs, false), e, NULL)); + }*/ + +// +// check for a state opcode +// + if (QCC_PR_Check ("[")) + QCC_PR_ParseState (); + + if (QCC_PR_Check ("asm")) + { + QCC_PR_Expect ("{"); + + while (!QCC_PR_Check("}")) + QCC_PR_ParseAsm (); + } + else + { +// +// parse regular statements +// + QCC_PR_Expect ("{"); + + while (!QCC_PR_Check("}")) + QCC_PR_ParseStatement (); + } + + // this is cheap + if (type->aux_type->type) + if (statements[numstatements - 1].op != OP_RETURN) + QCC_PR_ParseWarning(WARN_MISSINGRETURN, "%s: not all control paths return a value", pr_scope->name ); + + if (f->code == numstatements) + needsdone = true; + else if (statements[numstatements - 1].op != OP_RETURN && statements[numstatements - 1].op != OP_DONE) + needsdone = true; + + if (num_gotos) + { + int j; + for (i = 0; i < num_gotos; i++) + { + for (j = 0; j < num_labels; j++) + { + if (!strcmp(pr_gotos[i].name, pr_labels[j].name)) + { + (signed int)statements[pr_gotos[i].statementno].a += pr_labels[j].statementno - pr_gotos[i].statementno; + break; + } + } + if (j == num_labels) + { + num_gotos = 0; + QCC_PR_ParseError(ERR_NOLABEL, "Goto statement with no matching label \"%s\"", pr_gotos[i].name); + } + } + num_gotos = 0; + } + + if (opt_return_only && !needsdone) + needsdone = QCC_FuncJumpsTo(f->code, numstatements, numstatements); + + // emit an end of statements opcode + if (!opt_return_only || needsdone) + { + /*if (pr_classtype) + { + QCC_def_t *e, *e2; + e = QCC_PR_GetDef(NULL, "__oself", pr_scope, false, 0); + e2 = QCC_PR_GetDef(NULL, "self", NULL, false, 0); + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], e, QCC_PR_DummyDef(pr_classtype, "self", pr_scope, 1, e2->ofs, false), NULL)); + }*/ + + QCC_PR_Statement (pr_opcodes, 0,0, NULL); + } + else + optres_return_only++; + + if (opt_compound_jumps) + QCC_CompoundJumps(f->code, numstatements); +// if (opt_comexprremoval) +// QCC_CommonSubExpressionRemoval(f->code, numstatements); + + + QCC_RemapLockedTemps(f->code, numstatements); + locals_end = numpr_globals; + + QCC_WriteAsmFunction(pr_scope, f->code, locals_start); + + QCC_Marshal_Locals(f->code, numstatements); + + if (num_labels) + num_labels = 0; + + + if (num_continues) + { + num_continues=0; + QCC_PR_ParseError(ERR_ILLEGALCONTINUES, "%s: function contains illegal continues\n", pr_scope->name); + } + if (num_breaks) + { + num_breaks=0; + QCC_PR_ParseError(ERR_ILLEGALBREAKS, "%s: function contains illegal breaks\n", pr_scope->name); + } + if (num_cases) + { + num_cases = 0; + QCC_PR_ParseError(ERR_ILLEGALCASES, "%s: function contains illegal cases\n", pr_scope->name); + } + + return f; +} + +void QCC_PR_ArrayRecurseDivideRegular(QCC_def_t *array, QCC_def_t *index, int min, int max) +{ + QCC_dstatement_t *st; + QCC_def_t *eq; + if (min == max || min+1 == max) + { + eq = QCC_PR_Statement(pr_opcodes+OP_EQ_F, index, QCC_MakeFloatDef((float)min), NULL); + QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + st->a = array->ofs + min*array->type->size; + } + else + { + int mid = min + (max-min)/2; + + if (max-min>4) + { + eq = QCC_PR_Statement(pr_opcodes+OP_LT, index, QCC_MakeFloatDef((float)mid), NULL); + QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st); + } + else + st = NULL; + QCC_PR_ArrayRecurseDivideRegular(array, index, min, mid); + if (st) + st->b = numstatements - (st-statements); + QCC_PR_ArrayRecurseDivideRegular(array, index, mid, max); + } +} + +//the idea here is that we return a vector, the caller then figures out the extra 3rd. +//This is useful when we have a load of indexes. +void QCC_PR_ArrayRecurseDivideUsingVectors(QCC_def_t *array, QCC_def_t *index, int min, int max) +{ + QCC_dstatement_t *st; + QCC_def_t *eq; + if (min == max || min+1 == max) + { + eq = QCC_PR_Statement(pr_opcodes+OP_EQ_F, index, QCC_MakeFloatDef((float)min), NULL); + QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + st->a = array->ofs + min*3; + } + else + { + int mid = min + (max-min)/2; + + if (max-min>4) + { + eq = QCC_PR_Statement(pr_opcodes+OP_GE, index, QCC_MakeFloatDef((float)mid), NULL); + QCC_PR_Statement(pr_opcodes+OP_IF, eq, 0, &st); + } + else + st = NULL; + QCC_PR_ArrayRecurseDivideUsingVectors(array, index, min, mid); + if (st) + st->b = numstatements - (st-statements); + QCC_PR_ArrayRecurseDivideUsingVectors(array, index, mid, max); + } +} + +//returns a vector overlapping the result needed. +QCC_def_t *QCC_PR_EmitArrayGetVector(QCC_def_t *array) +{ + QCC_dfunction_t *df; + QCC_def_t *temp, *index, *func; + + func = QCC_PR_GetDef(type_function, qcva("ArrayGetVec*%s", array->name), NULL, true, 1); + + df = &functions[numfunctions]; + numfunctions++; + + df->s_file = 0; + df->s_name = QCC_CopyString(func->name); + df->first_statement = numstatements; + df->parm_size[0] = 1; + df->numparms = 1; + df->parm_start = numpr_globals; + index = QCC_PR_GetDef(type_float, "index___", func, true, 1); + locals_end = numpr_globals; + df->locals = locals_end - df->parm_start; + temp = QCC_GetTemp(type_float); + QCC_PR_Statement3(pr_opcodes+OP_DIV_F, index, QCC_MakeFloatDef(3), temp); + QCC_PR_Statement3(pr_opcodes+OP_BITAND, temp, temp, temp); + + QCC_PR_ArrayRecurseDivideUsingVectors(array, index, 0, (array->arraysize+2)/3); //round up + + QCC_PR_Statement(pr_opcodes+OP_DONE, QCC_MakeFloatDef(0), 0, NULL); + + G_FUNCTION(func->ofs) = df - functions; + func->initialized = 1; + return func; +} + +func_t QCC_PR_EmitArrayGetFunction(char *arrayname) +{ + QCC_def_t *vectortrick; + QCC_dfunction_t *df; + QCC_def_t *def, *index; + + QCC_dstatement_t *st; + QCC_def_t *eq; + + def = QCC_PR_GetDef(NULL, arrayname, NULL, false, 0); + + if (def->arraysize >= 15 && def->type->size == 1) + { + vectortrick = QCC_PR_EmitArrayGetVector(def); + } + else + vectortrick = NULL; + + df = &functions[numfunctions]; + numfunctions++; + + df->s_file = 0; + df->s_name = 0; + df->first_statement = numstatements; + df->parm_size[0] = 1; + df->numparms = 1; + df->parm_start = numpr_globals; + index = QCC_PR_GetDef(type_float, "indexg___", def, true, 1); + locals_end = numpr_globals; + df->locals = locals_end - df->parm_start; + + if (vectortrick) + { + QCC_def_t *div3, *ret; + def->type = type_vector; + + eq = QCC_PR_Statement(pr_opcodes+OP_GE, index, QCC_MakeFloatDef((float)def->arraysize), NULL); + QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, QCC_MakeFloatDef(0), 0, &st); + + QCC_PR_Statement3(pr_opcodes+OP_BITAND, index, index, index); + div3 = QCC_PR_Statement(pr_opcodes+OP_DIV_F, index, QCC_MakeFloatDef(3), NULL); + QCC_PR_Statement3(pr_opcodes+OP_BITAND, div3, div3, div3); + + QCC_PR_Statement3(pr_opcodes+OP_STORE_F, div3, &def_parms[0], NULL); + QCC_PR_Statement3(pr_opcodes+OP_CALL1, vectortrick, NULL, NULL); + vectortrick->references++; + ret = QCC_GetTemp(type_vector); + QCC_PR_Statement3(pr_opcodes+OP_STORE_V, &def_ret, ret, NULL); + + div3 = QCC_PR_Statement(pr_opcodes+OP_MUL_F, div3, QCC_MakeFloatDef(3), NULL); + QCC_PR_Statement3(pr_opcodes+OP_SUB_F, index, div3, index); + + eq = QCC_PR_Statement(pr_opcodes+OP_EQ_F, index, QCC_MakeFloatDef(0), NULL); + QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + st->a = ret->ofs + 0; + + eq = QCC_PR_Statement(pr_opcodes+OP_EQ_F, index, QCC_MakeFloatDef(1), NULL); + QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + st->a = ret->ofs + 1; + + eq = QCC_PR_Statement(pr_opcodes+OP_EQ_F, index, QCC_MakeFloatDef(2), NULL); + QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st); + st->b = 2; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + st->a = ret->ofs + 2; + } + else + { + QCC_PR_Statement3(pr_opcodes+OP_BITAND, index, index, index); + QCC_PR_ArrayRecurseDivideRegular(def, index, 0, def->arraysize); + } + + QCC_PR_Statement(pr_opcodes+OP_RETURN, QCC_MakeFloatDef(0), 0, NULL); + + + + return df - functions; +} + +void QCC_PR_ArraySetRecurseDivide(QCC_def_t *array, QCC_def_t *index, QCC_def_t *value, int min, int max) +{ + QCC_dstatement_t *st; + QCC_def_t *eq; + if (min == max || min+1 == max) + { + eq = QCC_PR_Statement(pr_opcodes+OP_EQ_F, index, QCC_MakeFloatDef((float)min), NULL); + QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st); + st->b = 3; + if (array->type->size == 3) + QCC_PR_Statement(pr_opcodes+OP_STORE_V, value, array, &st); + else + QCC_PR_Statement(pr_opcodes+OP_STORE_F, value, array, &st); + st->b = array->ofs + min*array->type->size; + QCC_PR_Statement(pr_opcodes+OP_RETURN, 0, 0, &st); + } + else + { + int mid = min + (max-min)/2; + + if (max-min>4) + { + eq = QCC_PR_Statement(pr_opcodes+OP_LT, index, QCC_MakeFloatDef((float)mid), NULL); + QCC_PR_Statement(pr_opcodes+OP_IFNOT, eq, 0, &st); + } + else + st = NULL; + QCC_PR_ArraySetRecurseDivide(array, index, value, min, mid); + if (st) + st->b = numstatements - (st-statements); + QCC_PR_ArraySetRecurseDivide(array, index, value, mid, max); + } +} + +func_t QCC_PR_EmitArraySetFunction(char *arrayname) +{ + QCC_dfunction_t *df; + QCC_def_t *def, *index, *value; + + def = QCC_PR_GetDef(NULL, arrayname, NULL, false, 0); + + df = &functions[numfunctions]; + numfunctions++; + + df->s_file = 0; + df->s_name = 0; + df->first_statement = numstatements; + df->parm_size[0] = 1; + df->parm_size[1] = def->type->size; + df->numparms = 2; + df->parm_start = numpr_globals; + index = QCC_PR_GetDef(type_float, "indexs___", def, true, 1); + value = QCC_PR_GetDef(def->type, "value___", def, true, 1); + locals_end = numpr_globals; + df->locals = locals_end - df->parm_start; + + QCC_PR_Statement3(pr_opcodes+OP_BITAND, index, index, index); + QCC_PR_ArraySetRecurseDivide(def, index, value, 0, def->arraysize); + + QCC_PR_Statement(pr_opcodes+OP_DONE, 0, 0, NULL); + + + + return df - functions; +} + +//register a def, and all of it's sub parts. +//only the main def is of use to the compiler. +//the subparts are emitted to the compiler and allow correct saving/loading +//be careful with fields, this doesn't allocated space, so will it allocate fields. It only creates defs at specified offsets. +QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int ofs, int referable) +{ + char array[64]; + char newname[256]; + int a, i; + QCC_def_t *def, *first=NULL; + +#define KEYWORD(x) if (!STRCMP(name, #x) && keyword_##x) {if (keyword_##x)QCC_PR_ParseWarning(WARN_KEYWORDDISABLED, "\""#x"\" keyword used as variable name%s", keywords_coexist?" - coexisting":" - disabling");keyword_##x=keywords_coexist;} + if (name) + { + KEYWORD(var); + KEYWORD(thinktime); + KEYWORD(for); + KEYWORD(switch); + KEYWORD(case); + KEYWORD(default); + KEYWORD(do); + KEYWORD(goto); + if (type->type != ev_function) + KEYWORD(break); + KEYWORD(continue); + KEYWORD(state); + KEYWORD(string); + if (qcc_targetformat != QCF_HEXEN2) + KEYWORD(float); //hmm... hexen2 requires this... + KEYWORD(entity); + KEYWORD(vector); + KEYWORD(const); + KEYWORD(asm); + } + + for (a = 0; a < arraysize; a++) + { + if (a == 0) + *array = '\0'; + else + sprintf(array, "[%i]", a); + + if (name) + sprintf(newname, "%s%s", name, array); + else + *newname = *""; + + // allocate a new def + def = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (def, 0, sizeof(*def)); + def->next = NULL; + def->arraysize = arraysize; + if (name) + { + pr.def_tail->next = def; + pr.def_tail = def; + } + + if (a > 0) + def->references++; + + def->s_line = pr_source_line; + def->s_file = s_file; + + def->name = (void *)qccHunkAlloc (strlen(newname)+1); + strcpy (def->name, newname); + def->type = type; + + def->scope = scope; + + // if (arraysize>1) + def->constant = true; + + if (ofs + type->size*a >= MAX_REGS) + QCC_Error(ERR_TOOMANYGLOBALS, "MAX_REGS is too small"); + def->ofs = ofs + type->size*a; + if (!first) + first = def; + +// printf("Emited %s\n", newname); + + if (type->type == ev_struct) + { + int partnum; + QCC_type_t *parttype; + parttype = type->param; + for (partnum = 0; partnum < type->num_parms; partnum++) + { + switch (parttype->type) + { + case ev_vector: + sprintf(newname, "%s%s.%s", name, array, parttype->name); + QCC_PR_DummyDef(parttype, newname, scope, 1, ofs + type->size*a + parttype->ofs, false); + + sprintf(newname, "%s%s.%s_x", name, array, parttype->name); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a + parttype->ofs, false); + sprintf(newname, "%s%s.%s_y", name, array, parttype->name); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a + parttype->ofs+1, false); + sprintf(newname, "%s%s.%s_z", name, array, parttype->name); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a + parttype->ofs+2, false); + break; + + case ev_float: + case ev_string: + case ev_entity: + case ev_field: + case ev_pointer: + case ev_integer: + case ev_struct: + case ev_union: + sprintf(newname, "%s%s.%s", name, array, parttype->name); + QCC_PR_DummyDef(parttype, newname, scope, 1, ofs + type->size*a + parttype->ofs, false); + break; + + case ev_function: + sprintf(newname, "%s%s.%s", name, array, parttype->name); + QCC_PR_DummyDef(parttype, newname, scope, 1, ofs + type->size*a +parttype->ofs, false)->initialized = true; + for (i = parttype->num_parms; i>0; i--) + parttype=parttype->next; + break; + case ev_void: + break; + } + parttype=parttype->next; + } + } + else if (type->type == ev_vector) + { //do the vector thing. + sprintf(newname, "%s%s_x", name, array); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a+0, referable); + sprintf(newname, "%s%s_y", name, array); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a+1, referable); + sprintf(newname, "%s%s_z", name, array); + QCC_PR_DummyDef(type_float, newname, scope, 1, ofs + type->size*a+2, referable); + } + else if (type->type == ev_field) + { + if (type->aux_type->type == ev_vector) + { + //do the vector thing. + sprintf(newname, "%s%s_x", name, array); + QCC_PR_DummyDef(type_floatfield, newname, scope, 1, ofs + type->size*a+0, referable); + sprintf(newname, "%s%s_y", name, array); + QCC_PR_DummyDef(type_floatfield, newname, scope, 1, ofs + type->size*a+1, referable); + sprintf(newname, "%s%s_z", name, array); + QCC_PR_DummyDef(type_floatfield, newname, scope, 1, ofs + type->size*a+2, referable); + } + } + } + + if (referable) + { + if (!Hash_Get(&globalstable, "end_sys_fields")) + first->references++; //anything above needs to be left in, and so warning about not using it is just going to pee people off. + if (arraysize <= 1) + first->constant = false; + if (pr_scope) + Hash_Add(&localstable, first->name, first); + else + Hash_Add(&globalstable, first->name, first); + + if (!pr_scope && asmfile) + fprintf(asmfile, "%s %s;\n", TypeName(first->type), first->name); + } + + return first; +} + +/* +============ +PR_GetDef + +If type is NULL, it will match any type +If allocate is true, a new def will be allocated if it can't be found +============ +*/ + +QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, QCC_def_t *scope, pbool allocate, int arraysize) +{ + int ofs; + QCC_def_t *def; +// char element[MAX_NAME]; + unsigned int i; + + if (scope) + { + def = Hash_Get(&localstable, name); + + while(def) + { + if ( def->scope && def->scope != scope) + { + def = Hash_GetNext(&localstable, name, def); + continue; // in a different function + } + + if (type && typecmp(def->type, type)) + QCC_PR_ParseError (ERR_TYPEMISMATCHREDEC, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type), TypeName(def->type)); + if (def->arraysize != arraysize && arraysize) + QCC_PR_ParseError (ERR_TYPEMISMATCHARRAYSIZE, "Array sizes for redecleration of %s do not match",name); + if (allocate && scope) + { + QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); + QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); +// if (!scope) +// QCC_PR_ParsePrintDef(def); + } + return def; + } + } + + + def = Hash_Get(&globalstable, name); + + while(def) + { + if ( def->scope && def->scope != scope) + { + def = Hash_GetNext(&globalstable, name, def); + continue; // in a different function + } + + if (type && typecmp(def->type, type)) + { + if (!pr_scope) + QCC_PR_ParseError (ERR_TYPEMISMATCHREDEC, "Type mismatch on redeclaration of %s. %s, should be %s",name, TypeName(type), TypeName(def->type)); + } + if (def->arraysize != arraysize && arraysize) + QCC_PR_ParseError (ERR_TYPEMISMATCHARRAYSIZE, "Array sizes for redecleration of %s do not match",name); + if (allocate && scope) + { + if (pr_scope) + { //warn? or would that be pointless? + def = Hash_GetNext(&globalstable, name, def); + continue; // in a different function + } + + QCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, "%s duplicate definition ignored", name); + QCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def); +// if (!scope) +// QCC_PR_ParsePrintDef(def); + } + return def; + } + + if (!allocate) + return NULL; + if (arraysize < 1) + { + QCC_PR_ParseError (ERR_ARRAYNEEDSSIZE, "First declaration of array %s with no size",name); + } + + ofs = numpr_globals; + if (arraysize > 1) + { //write the array size + ofs = QCC_GetFreeOffsetSpace(1 + (type->size * arraysize)); + + ((int *)qcc_pr_globals)[ofs] = arraysize-1; //An array needs the size written first. This is a hexen2 opcode thing. + ofs++; + } + else + ofs = QCC_GetFreeOffsetSpace(type->size * arraysize); + + def = QCC_PR_DummyDef(type, name, scope, arraysize, ofs, true); + + //fix up fields. + if (type->type == ev_field && allocate != 2) + { + for (i = 0; i < type->size*arraysize; i++) //make arrays of fields work. + *(int *)&qcc_pr_globals[def->ofs+i] = pr.size_fields+i; + + pr.size_fields += i; + } + + if (scope) + { + def->nextlocal = pr.localvars; + pr.localvars = def; + } + + return def; +} + +QCC_def_t *QCC_PR_DummyFieldDef(QCC_type_t *type, char *name, QCC_def_t *scope, int arraysize, unsigned int *fieldofs) +{ + char array[64]; + char newname[256]; + int a, i, parms; + QCC_def_t *def, *first=NULL; + unsigned int maxfield, startfield; + QCC_type_t *ftype; + pbool isunion; + startfield = *fieldofs; + maxfield = startfield; + + for (a = 0; a < arraysize; a++) + { + if (a == 0) + *array = '\0'; + else + sprintf(array, "[%i]", a); + + if (*name) + { + sprintf(newname, "%s%s", name, array); + + // allocate a new def + def = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + memset (def, 0, sizeof(*def)); + def->next = NULL; + def->arraysize = arraysize; + + pr.def_tail->next = def; + pr.def_tail = def; + + def->s_line = pr_source_line; + def->s_file = s_file; + + def->name = (void *)qccHunkAlloc (strlen(newname)+1); + strcpy (def->name, newname); + def->type = type; + + def->scope = scope; + + def->ofs = QCC_GetFreeOffsetSpace(1); + ((int *)qcc_pr_globals)[def->ofs] = *fieldofs; + *fieldofs++; + if (!first) + first = def; + } + else + { + def=NULL; + } + +// printf("Emited %s\n", newname); + + if ((type)->type == ev_struct||(type)->type == ev_union) + { + int partnum; + QCC_type_t *parttype; + if (def) + def->references++; + parttype = (type)->param; + isunion = ((type)->type == ev_union); + for (partnum = 0, parms = (type)->num_parms; partnum < parms; partnum++) + { + switch (parttype->type) + { + case ev_union: + case ev_struct: + if (*name) + sprintf(newname, "%s%s.%s", name, array, parttype->name); + else + sprintf(newname, "%s%s", parttype->name, array); + def = QCC_PR_DummyFieldDef(parttype, newname, scope, 1, fieldofs); + break; + case ev_float: + case ev_string: + case ev_vector: + case ev_entity: + case ev_field: + case ev_pointer: + case ev_integer: + if (*name) + sprintf(newname, "%s%s.%s", name, array, parttype->name); + else + sprintf(newname, "%s%s", parttype->name, array); + ftype = QCC_PR_NewType("FIELD TYPE", ev_field); + ftype->aux_type = parttype; + def = QCC_PR_GetDef(NULL, newname, scope, false, 1); + if (!def) + { + def = QCC_PR_GetDef(ftype, newname, scope, true, 1); + } + else + { + QCC_PR_ParseWarning(WARN_CONFLICTINGUNIONMEMBER, "conflicting offsets for union/struct expansion of %s. Ignoring new def.", newname); + QCC_PR_ParsePrintDef(WARN_CONFLICTINGUNIONMEMBER, def); + } + break; + + case ev_function: + if (*name) + sprintf(newname, "%s%s.%s", name, array, parttype->name); + else + sprintf(newname, "%s%s", parttype->name, array); + ftype = QCC_PR_NewType("FIELD TYPE", ev_field); + ftype->aux_type = parttype; + def = QCC_PR_GetDef(ftype, newname, scope, true, 1); + def->initialized = true; + ((int *)qcc_pr_globals)[def->ofs] = *fieldofs; + *fieldofs += parttype->size; + for (i = parttype->num_parms; i>0; i--) + parttype=parttype->next; + break; + case ev_void: + break; + } + if (*fieldofs > maxfield) + maxfield = *fieldofs; + if (isunion) + *fieldofs = startfield; + + type = parttype; + parttype=parttype->next; + } + } + } + + *fieldofs = maxfield; + return first; +} + + + +void QCC_PR_ExpandUnionToFields(QCC_type_t *type, int *fields) +{ + QCC_type_t *pass = type->aux_type; + QCC_PR_DummyFieldDef(pass, "", pr_scope, 1, fields); +} + +/* +================ +PR_ParseDefs + +Called at the outer layer and when a local statement is hit +================ +*/ +void QCC_PR_ParseDefs (char *classname) +{ + char *name; + QCC_type_t *type, *parm; + QCC_def_t *def, *d; + QCC_function_t *f; + QCC_dfunction_t *df; + int i; + pbool shared=false; + pbool externfnc=false; + pbool constant = true; + pbool noref = false; + pbool nosave = false; + pbool allocatenew = true; + gofs_t oldglobals; + int arraysize; + + if (QCC_PR_Check("enum")) + { + float v = 0; + QCC_PR_Expect("{"); + i = 0; + d = NULL; + while(1) + { + name = QCC_PR_ParseName(); + if (QCC_PR_Check("=")) + { + if (pr_token_type != tt_immediate && pr_immediate_type->type != ev_float) + QCC_PR_ParseError(ERR_NOTANUMBER, "enum - not a number"); + v = pr_immediate._float; + QCC_PR_Lex(); + } + def = QCC_PR_GetDef(type_float, name, pr_scope, true, 1); + G_FLOAT(def->ofs) = v; + v++; + + if (QCC_PR_Check("}")) + break; + QCC_PR_Expect(","); + } + QCC_PR_Expect(";"); + return; + } + + if (QCC_PR_Check("enumflags")) + { + float v = 1; + int bits; + QCC_PR_Expect("{"); + i = 0; + d = NULL; + while(1) + { + name = QCC_PR_ParseName(); + if (QCC_PR_Check("=")) + { + if (pr_token_type != tt_immediate && pr_immediate_type->type != ev_float) + QCC_PR_ParseError(ERR_NOTANUMBER, "enumflags - not a number"); + v = pr_immediate._float; + bits = 0; + i = (int)v; + if (i != v) + QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, "enumflags - %f not an integer", v); + else + { + while(i) + { + if (((i>>1)<<1) != i) + bits++; + i>>=1; + } + if (bits != 1) + QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTBINARY, "enumflags - %i not a binary value", (int)v); + } + QCC_PR_Lex(); + } + def = QCC_PR_GetDef(type_float, name, pr_scope, true, 1); + G_FLOAT(def->ofs) = v; + v*=2; + + if (QCC_PR_Check("}")) + break; + QCC_PR_Expect(","); + } + QCC_PR_Expect(";"); + return; + } + + if (QCC_PR_Check ("typedef")) + { + type = QCC_PR_ParseType(true); + if (!type) + { + QCC_PR_ParseError(ERR_NOTANAME, "typedef found unexpected tokens"); + } + type->name = QCC_CopyString(pr_token)+strings; + QCC_PR_Lex(); + QCC_PR_Expect(";"); + return; + } + + while(1) + { + if (QCC_PR_Check("extern")) + externfnc=true; + else if (QCC_PR_Check("shared")) + { + shared=true; + if (pr_scope) + QCC_PR_ParseError (ERR_NOSHAREDLOCALS, "Cannot have shared locals"); + } + else if (QCC_PR_Check("const")) + constant = true; + else if (QCC_PR_Check("var")) + constant = false; + else if (QCC_PR_Check("noref")) + noref=true; + else if (QCC_PR_Check("nosave")) + nosave = true; + else + break; + } + + type = QCC_PR_ParseType (false); + if (type == NULL) //ignore + return; + + if (externfnc && type->type != ev_function) + { + printf ("Only functions may be defined as external (yet)\n"); + externfnc=false; + } + + +// if (pr_scope && (type->type == ev_field) ) +// QCC_PR_ParseError ("Fields must be global"); + + do + { + if (QCC_PR_Check (";")) + { + if (type->type == ev_field && (type->aux_type->type == ev_union || type->aux_type->type == ev_struct)) + { + QCC_PR_ExpandUnionToFields(type, &pr.size_fields); + return; + } +// if (type->type == ev_union) +// { +// return; +// } + QCC_PR_ParseError (ERR_TYPEWITHNONAME, "type with no name"); + } + else + name = QCC_PR_ParseName (); + + if (QCC_PR_Check("::") && !classname) + { + classname = name; + name = QCC_PR_ParseName(); + } + +//check for an array + + if ( QCC_PR_Check ("[") ) + { + if (pr_immediate_type->type == ev_integer) + arraysize = pr_immediate._int; + else if (pr_immediate_type->type == ev_float && (float)(int)pr_immediate._float == pr_immediate._float) + arraysize = (int)pr_immediate._float; + else + QCC_PR_ParseError (ERR_BADARRAYSIZE, "Definition of array (%s) size is not of a numerical value", name); + QCC_PR_Lex(); + QCC_PR_Expect("]"); + } + else + arraysize = 1; + + if (QCC_PR_Check("(")) + type = QCC_PR_ParseFunctionType(false, type); + + if (classname) + { + char *membername = name; + name = qccHunkAlloc(strlen(classname) + strlen(name) + 3); + sprintf(name, "%s::"MEMBERFIELDNAME, classname, membername); + if (!QCC_PR_GetDef(NULL, name, NULL, false, 0)) + QCC_PR_ParseError(ERR_NOTANAME, "%s %s is not a member of class %s\n", TypeName(type), membername, classname); + sprintf(name, "%s::%s", classname, membername); + + pr_classtype = QCC_TypeForName(classname); + if (!pr_classtype || !pr_classtype->parentclass) + QCC_PR_ParseError(ERR_NOTANAME, "%s is not a class\n", classname); + } + else + pr_classtype = NULL; + + oldglobals = numpr_globals; + def = QCC_PR_GetDef (type, name, pr_scope, allocatenew, arraysize); + + if (!def) + QCC_PR_ParseError(ERR_NOTANAME, "%s is not part of class %s", name, classname); + + if (qcc_targetformat == QCF_HEXEN2 && arraysize>1) + def->constant = true; + + if (noref) + def->references++; + if (nosave) + def->saved = false; + else def->saved = true; + + if (!def->initialized && shared) //shared count as initiialised + { + def->shared = shared; + def->initialized = true; + } + if (externfnc) + def->initialized = 2; + +// check for an initialization + if (type->type == ev_function && (pr_scope || !constant)) + { + def->initialized = 1; //fake function + G_FUNCTION(def->ofs) = 0; + if ( QCC_PR_Check ("=") ) + QCC_PR_ParseError (ERR_INITIALISEDLOCALFUNCTION, "local functions may only be used as pointers"); + continue; + } + + if ( QCC_PR_Check ("=") || ((type->type == ev_function) && (pr_token[0] == '{' || pr_token[0] == '[' || pr_token[0] == ':'))) //this is an initialisation (or a function) + { + if (def->shared) + QCC_PR_ParseError (ERR_SHAREDINITIALISED, "shared values may not be assigned an initial value", name); + if (def->initialized == 1) + { + if (def->type->type == ev_function) + { + i = G_FUNCTION(def->ofs); + df = &functions[i]; + QCC_PR_ParseErrorPrintDef (ERR_REDECLARATION, def, "%s redeclared, prev instance is in %s", name, strings+df->s_file); + } + else + QCC_PR_ParseErrorPrintDef(ERR_REDECLARATION, def, "%s redeclared", name); + } + + if (autoprototype) + { //ignore the code and stuff + if (QCC_PR_Check("[")) + { + while (!QCC_PR_Check("]")) + { + if (pr_token_type == tt_eof) + break; + QCC_PR_Lex(); + } + } + if (QCC_PR_Check("{")) + { + int blev = 1; + //balance out the { and } + while(blev) + { + if (pr_token_type == tt_eof) + break; + if (QCC_PR_Check("{")) + blev++; + else if (QCC_PR_Check("}")) + blev--; + else + QCC_PR_Lex(); //ignore it. + } + } + else + { + QCC_PR_Check("#"); + QCC_PR_Lex(); + } + continue; + } + + if (type->type == ev_function) + { + def->constant = constant; + if (QCC_PR_Check("0")) + { + def->constant = 0; + def->initialized = 1; //fake function + G_FUNCTION(def->ofs) = 0; + continue; + } + + if (arraysize>1) + { + int i; + def->initialized = 1; //fake function + QCC_PR_Expect ("{"); + i = 0; + do + { + name = QCC_PR_ParseName (); + + d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0); + if (!d) + QCC_PR_ParseError(ERR_NOTDEFINED, "%s was not defined\n", name); + else + G_FUNCTION(def->ofs+i) = G_FUNCTION(d->ofs); + + i++; + } while(QCC_PR_Check(",")); + QCC_PR_Expect("}"); + if (i > arraysize) + QCC_PR_ParseError(ERR_TOOMANYINITIALISERS, "Too many initializers"); + continue; + } + + def->references++; + pr_scope = def; + f = QCC_PR_ParseImmediateStatements (type); + pr_scope = NULL; + def->initialized = 1; + G_FUNCTION(def->ofs) = numfunctions; + f->def = def; +// if (pr_dumpasm) +// PR_PrintFunction (def); + + // fill in the dfunction + df = &functions[numfunctions]; + numfunctions++; + if (f->builtin) + df->first_statement = -f->builtin; + else + df->first_statement = f->code; + + if (f->builtin && opt_function_names) + optres_function_names += strlen(f->def->name); + else + df->s_name = QCC_CopyString (f->def->name); + df->s_file = s_file2; + df->numparms = f->def->type->num_parms; + df->locals = locals_end - locals_start; + df->parm_start = locals_start; + for (i=0,parm = type->param ; inumparms ; i++, parm = parm->next) + { + df->parm_size[i] = parm->size; + } + + continue; + } + + else if (type->type == ev_struct) + { + int arraypart, partnum; + QCC_type_t *parttype; + def->initialized = 1; + def->constant = constant; +// if (constant) +// QCC_PR_ParseError("const used on a struct isn't useful"); + + //FIXME: should do this recursivly + QCC_PR_Expect("{"); + for (arraypart = 0; arraypart < arraysize; arraypart++) + { + parttype = type->param; + QCC_PR_Expect("{"); + for (partnum = 0; partnum < type->num_parms; partnum++) + { + switch (parttype->type) + { + case ev_float: + case ev_integer: + case ev_vector: + if (pr_token_type == tt_punct) + { + if (QCC_PR_Check("{")) + { + QCC_PR_Expect("}"); + } + else + QCC_PR_ParseError(ERR_UNEXPECTEDPUNCTUATION, "Unexpected punctuation"); + + } + else if (pr_token_type == tt_immediate) + { + if (pr_immediate_type->type == ev_float && parttype->type == ev_integer) + G_INT(def->ofs + arraypart*type->size + parttype->ofs) = (int)pr_immediate._float; + else if (pr_immediate_type->type != parttype->type) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate subtype for %s.%s", def->name, parttype->name); + else + memcpy (qcc_pr_globals + def->ofs + arraypart*type->size + parttype->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); + } + else if (pr_token_type == tt_name) + { + d = QCC_PR_GetDef(NULL, pr_token, pr_scope, false, 0); + if (!d) + QCC_PR_ParseError(ERR_NOTDEFINED, "%s was not defined\n", pr_token); + else if (d->type->type != parttype->type) + QCC_PR_ParseError (ERR_WRONGSUBTYPE, "wrong subtype for %s.%s", def->name, parttype->name); + else if (!d->constant) + QCC_PR_ParseError(ERR_NOTACONSTANT, "%s isn't a constant\n", pr_token); + + memcpy (qcc_pr_globals + def->ofs + arraypart*type->size + parttype->ofs, qcc_pr_globals + d->ofs, 4*d->type->size); + } + else + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate subtype for %s.%s", def->name, parttype->name); + QCC_PR_Lex (); + + break; + case ev_string: + if (pr_token_type == tt_punct) + { + if (QCC_PR_Check("{")) + { + unsigned int i; + for (i = 0; i < parttype->size; i++) + { +/* //the executor defines strings as true c strings, but reads in index from string table. + //structures can hide these strings. + d = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + d->next = NULL; + pr.def_tail->next = d; + pr.def_tail = d; + + d->type = parttype; + d->name = "STRUCTIMMEDIATE"; + d->constant = constant; + d->initialized = 1; + d->scope = NULL; + + d->ofs = def->ofs+arraypart*type->size+parttype->ofs+i; +*/ + G_INT(def->ofs+arraypart*type->size+parttype->ofs+i) = QCC_CopyString(pr_immediate_string); + QCC_PR_Lex (); + + if (!QCC_PR_Check(",")) + { + i++; + break; + } + } + for (; i < parttype->size; i++) + { +/* //the executor defines strings as true c strings, but reads in index from string table. + //structures can hide these strings. + d = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + d->next = NULL; + pr.def_tail->next = d; + pr.def_tail = d; + + d->type = parttype; + d->name = "STRUCTIMMEDIATE"; + d->constant = constant; + d->initialized = 1; + d->scope = NULL; + + d->ofs = def->ofs+arraypart*type->size+parttype->ofs+i; +*/ + G_INT(def->ofs+arraypart*type->size+parttype->ofs+i) = 0; + } + QCC_PR_Expect("}"); + } + else + QCC_PR_ParseError(ERR_UNEXPECTEDPUNCTUATION, "Unexpected punctuation"); + } + else + { +/* //the executor defines strings as true c strings, but reads in index from string table. + //structures can hide these strings. + d = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + d->next = NULL; + pr.def_tail->next = d; + pr.def_tail = d; + + d->type = parttype; + d->name = "STRUCTIMMEDIATE"; + d->constant = constant; + d->initialized = 1; + d->scope = NULL; + + d->ofs = def->ofs+arraypart*type->size+parttype->ofs; +*/ + G_INT(def->ofs+arraypart*type->size+parttype->ofs) = QCC_CopyString(pr_immediate_string); + QCC_PR_Lex (); + } + break; + case ev_function: + if (pr_token_type == tt_immediate) + { + if (pr_immediate._int != 0) + QCC_PR_ParseError(ERR_NOTFUNCTIONTYPE, "Expected function name or NULL"); + G_FUNCTION(def->ofs+arraypart*type->size+parttype->ofs) = 0; + QCC_PR_Lex(); + } + else + { + name = QCC_PR_ParseName (); + + d = QCC_PR_GetDef (NULL, name, pr_scope, false, 0); + if (!d) + QCC_PR_ParseError(ERR_NOTDEFINED, "%s was not defined\n", name); + else + G_FUNCTION(def->ofs+arraypart*type->size+parttype->ofs) = G_FUNCTION(d->ofs); + } + break; + default: + QCC_PR_ParseError(ERR_TYPEINVALIDINSTRUCT, "type %i not valid in a struct", parttype->type); + QCC_PR_Lex(); + break; + } + if (!QCC_PR_Check(",")) + break; + + parttype=parttype->next; + } + QCC_PR_Expect("}"); + if (!QCC_PR_Check(",")) + break; + } + QCC_PR_Expect("}"); + continue; + } + + else if (type->type == ev_integer) //handle these differently, because they may need conversions + { + def->constant = constant; + def->initialized = 1; + memcpy (qcc_pr_globals + def->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); + QCC_PR_Lex (); + + if (pr_immediate_type->type == ev_float) + G_INT(def->ofs) = (int)pr_immediate._float; + else if (pr_immediate_type->type != ev_integer) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + + continue; + } + else if (type->type == ev_string) + { + if (arraysize>=1 && QCC_PR_Check("{")) + { + int i; + for (i = 0; i < arraysize; i++) + { + //the executor defines strings as true c strings, but reads in index from string table. + //structures can hide these strings. + if (i != 0) //not for the first entry - already a string def for that + { + d = (void *)qccHunkAlloc (sizeof(QCC_def_t)); + d->next = NULL; + pr.def_tail->next = d; + pr.def_tail = d; + + d->type = type_string; + d->name = "IMMEDIATE"; + d->constant = constant; + d->initialized = 1; + d->scope = NULL; + + d->ofs = def->ofs+i; + if (d->ofs >= MAX_REGS) + QCC_Error(ERR_TOOMANYGLOBALS, "MAX_REGS is too small"); + } + + (((int *)qcc_pr_globals)[def->ofs+i]) = QCC_CopyString(pr_immediate_string); + QCC_PR_Lex (); + + if (!QCC_PR_Check(",")) + break; + } + QCC_PR_Expect("}"); + + continue; + } + else if (arraysize<=1) + { + def->constant = constant; + def->initialized = 1; + (((int *)qcc_pr_globals)[def->ofs]) = QCC_CopyString(pr_immediate_string); + QCC_PR_Lex (); + + if (pr_immediate_type->type == ev_float) + G_INT(def->ofs) = (int)pr_immediate._float; + else if (pr_immediate_type->type != ev_string) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + + continue; + } + else + QCC_PR_ParseError(ERR_ARRAYNEEDSBRACES, "Array initialisation requires curly brasces"); + } + else if (type->type == ev_float) + { + if (arraysize>=1 && QCC_PR_Check("{")) + { + int i; + for (i = 0; i < arraysize; i++) + { + if (pr_immediate_type->type != ev_float) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + (((float *)qcc_pr_globals)[def->ofs+i]) = pr_immediate._float; + QCC_PR_Lex (); + + if (!QCC_PR_Check(",")) + break; + } + QCC_PR_Expect("}"); + + continue; + } + else if (arraysize<=1) + { + def->constant = constant; + def->initialized = 1; + + if (pr_immediate_type->type != ev_float) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + + if (constant && opt_dupconstdefs) + { + if (def->ofs == oldglobals) + { + if (Hash_GetKey(&floatconstdefstable, *(int*)&pr_immediate._float)) + optres_dupconstdefs++; + QCC_FreeOffset(def->ofs, def->type->size); + d = QCC_MakeFloatDef(pr_immediate._float); + d->references++; + def->ofs = d->ofs; + QCC_PR_Lex(); + continue; + } + } + + (((float *)qcc_pr_globals)[def->ofs]) = pr_immediate._float; + QCC_PR_Lex (); + + continue; + } + else + QCC_PR_ParseError(ERR_ARRAYNEEDSBRACES, "Array initialisation requires curly brasces"); + } + else if (type->type == ev_vector) + { + if (arraysize>=1 && QCC_PR_Check("{")) + { + int i; + for (i = 0; i < arraysize; i++) + { + if (pr_immediate_type->type != ev_vector) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + (((float *)qcc_pr_globals)[def->ofs+i*3+0]) = pr_immediate.vector[0]; + (((float *)qcc_pr_globals)[def->ofs+i*3+1]) = pr_immediate.vector[1]; + (((float *)qcc_pr_globals)[def->ofs+i*3+2]) = pr_immediate.vector[2]; + QCC_PR_Lex (); + + if (!QCC_PR_Check(",")) + break; + } + QCC_PR_Expect("}"); + + continue; + } + else if (arraysize<=1) + { + def->constant = constant; + def->initialized = 1; + (((float *)qcc_pr_globals)[def->ofs+0]) = pr_immediate.vector[0]; + (((float *)qcc_pr_globals)[def->ofs+1]) = pr_immediate.vector[1]; + (((float *)qcc_pr_globals)[def->ofs+2]) = pr_immediate.vector[2]; + QCC_PR_Lex (); + + if (pr_immediate_type->type != ev_vector) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); + + continue; + } + else + QCC_PR_ParseError(ERR_ARRAYNEEDSBRACES, "Array initialisation requires curly brasces"); + } + else if (pr_token_type == tt_name) + { +// if (pr_scope)//create a new instance, emit a copy op +// { +// QCC_PR_ParseError ("name defined for local : %s", name); +// } +// else + { + d = QCC_PR_GetDef (NULL, pr_token, pr_scope, false, 0); + if (!d) + QCC_PR_ParseError (ERR_NOTDEFINED, "initialisation name not defined : %s", pr_token); + memcpy (def, d, sizeof(*d)); + def->name = name; + def->initialized = true; + } + QCC_PR_Lex (); + continue; + } + else if (pr_token_type != tt_immediate) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "not an immediate for %s - %s", name, pr_token); + else if (pr_immediate_type->type != type->type) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s - %s", name, pr_token); + + def->constant = constant; + def->initialized = 1; + memcpy (qcc_pr_globals + def->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); + QCC_PR_Lex (); + } + + } while (QCC_PR_Check (",")); + + if (type->type == ev_function) + QCC_PR_Check (";"); + else + QCC_PR_Expect (";"); +} + +/* +============ +PR_CompileFile + +compiles the 0 terminated text, adding defintions to the pr structure +============ +*/ +pbool QCC_PR_CompileFile (char *string, char *filename) +{ + if (!pr.memory) + QCC_Error (ERR_INTERNAL, "PR_CompileFile: Didn't clear"); + + QCC_PR_ClearGrabMacros (); // clear the frame macros + + compilingfile = filename; + + if (opt_filenames) + { + optres_filenames += strlen(filename); + pr_file_p = qccHunkAlloc(strlen(filename)+1); + strcpy(pr_file_p, filename); + s_file = pr_file_p - strings; + s_file2 = 0; + } + else + { + s_file = s_file2 = QCC_CopyString (filename); + } + pr_file_p = string; + + pr_source_line = 0; + + QCC_PR_NewLine (false); + + QCC_PR_Lex (); // read first token + + while (pr_token_type != tt_eof) + { + if (setjmp(pr_parse_abort)) + { + if (++pr_error_count > MAX_ERRORS) + return false; + QCC_PR_SkipToSemicolon (); + if (pr_token_type == tt_eof) + return false; + } + + pr_scope = NULL; // outside all functions + + QCC_PR_ParseDefs (NULL); + } + + return (pr_error_count == 0); +} + +pbool QCC_Include(char *filename) +{ + char *newfile; + char fname[512]; + char *opr_file_p; + QCC_string_t os_file, os_file2; + int opr_source_line; + char *ocompilingfile; + struct qcc_includechunk_s *oldcurrentchunk; + extern struct qcc_includechunk_s *currentchunk; + + extern char qccmsourcedir[]; + + ocompilingfile = compilingfile; + os_file = s_file; + os_file2 = s_file2; + opr_source_line = pr_source_line; + opr_file_p = pr_file_p; + oldcurrentchunk = currentchunk; + + if (*filename == '/') + strcpy(fname, filename+1); + else + { + strcpy(fname, qccmsourcedir); + strcat(fname, filename); + } + QCC_LoadFile(fname, (void **)&newfile); + currentchunk = NULL; + pr_file_p = newfile; + QCC_PR_CompileFile(newfile, fname); + currentchunk = oldcurrentchunk; + + compilingfile = ocompilingfile; + s_file = os_file; + s_file2 = os_file2; + pr_source_line = opr_source_line; + pr_file_p = opr_file_p; + +// QCC_PR_IncludeChunk(newfile, false, fname); + + return true; +} + +#endif diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c new file mode 100644 index 000000000..0a5ea9912 --- /dev/null +++ b/engine/qclib/qcc_pr_lex.c @@ -0,0 +1,2660 @@ +#ifndef MINIMAL + +#include "qcc.h" +#ifdef QCC +#define print printf +#endif + +#define MEMBERFIELDNAME "__m%s" + +#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc + +void QCC_PR_ConditionCompilation(void); +pbool QCC_PR_UndefineName(char *name); +char *QCC_PR_CheakCompConstString(char *def); +CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def); +pbool QCC_Include(char *filename); +float QCC_PR_LexOctal (void); + +char *compilingfile; + +int pr_source_line; + +char *pr_file_p; +char *pr_line_start; // start of current source line + +int pr_bracelevel; + +char pr_token[8192]; +token_type_t pr_token_type; +QCC_type_t *pr_immediate_type; +QCC_eval_t pr_immediate; + +char pr_immediate_string[8192]; + +int pr_error_count; + + +CompilerConstant_t *CompilerConstant; +int numCompilerConstants; + + + +char *pr_punctuation[] = +// longer symbols must be before a shorter partial match +{"&&", "||", "<=", ">=","==", "!=", "/=", "*=", "+=", "-=", "(+)", "(-)", "++", "--", "::", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", "..", ".", "<<", "<", ">>", ">" , "#" , "@", "&" , "|", "^", ":", NULL}; + +// simple types. function types are dynamically allocated +QCC_type_t *type_void;// = {ev_void/*, &def_void*/}; +QCC_type_t *type_string;// = {ev_string/*, &def_string*/}; +QCC_type_t *type_float;// = {ev_float/*, &def_float*/}; +QCC_type_t *type_vector;// = {ev_vector/*, &def_vector*/}; +QCC_type_t *type_entity;// = {ev_entity/*, &def_entity*/}; +QCC_type_t *type_field;// = {ev_field/*, &def_field*/}; +QCC_type_t *type_function;// = {ev_function/*, &def_function*/,NULL,&type_void}; +// type_function is a void() function used for state defs +QCC_type_t *type_pointer;// = {ev_pointer/*, &def_pointer*/}; +QCC_type_t *type_integer;// = {ev_integer/*, &def_integer*/}; + +QCC_type_t *type_floatfield;// = {ev_field/*, &def_field*/, NULL, &type_float}; + +#ifdef QCCONLY +const int type_size[9] = {1,1,1,3,1,1,1,1,1}; +#endif + +/*QCC_def_t def_void = {type_void, "temp"}; +QCC_def_t def_string = {type_string, "temp"}; +QCC_def_t def_float = {type_float, "temp"}; +QCC_def_t def_vector = {type_vector, "temp"}; +QCC_def_t def_entity = {type_entity, "temp"}; +QCC_def_t def_field = {type_field, "temp"}; +QCC_def_t def_function = {type_function, "temp"}; +QCC_def_t def_pointer = {type_pointer, "temp"}; +QCC_def_t def_integer = {type_integer, "temp"}; +*/ +QCC_def_t def_ret, def_parms[MAX_PARMS]; + +//QCC_def_t *def_for_type[9] = {&def_void, &def_string, &def_float, &def_vector, &def_entity, &def_field, &def_function, &def_pointer, &def_integer}; + +void QCC_PR_LexWhitespace (void); + + + + +//for compiler constants and file includes. + +typedef struct qcc_includechunk_s { + struct qcc_includechunk_s *prev; + char *filename; + char *currentdatapoint; + int currentlinenumber; +} qcc_includechunk_t; +qcc_includechunk_t *currentchunk; +void QCC_PR_IncludeChunk (char *data, pbool duplicate, char *filename) +{ + qcc_includechunk_t *chunk = qccHunkAlloc(sizeof(qcc_includechunk_t)); + chunk->prev = currentchunk; + currentchunk = chunk; + + chunk->currentdatapoint = pr_file_p; + chunk->currentlinenumber = pr_source_line; + + if (duplicate) + { + pr_file_p = qccHunkAlloc(strlen(data)+1); + strcpy(pr_file_p, data); + } + else + pr_file_p = data; +} + +pbool QCC_PR_UnInclude(void) +{ + if (!currentchunk) + return false; + pr_file_p = currentchunk->currentdatapoint; + pr_source_line = currentchunk->currentlinenumber; + + currentchunk = currentchunk->prev; + + return true; +} + + +/* +============== +PR_PrintNextLine +============== +*/ +void QCC_PR_PrintNextLine (void) +{ + char *t; + + printf ("%3i:",pr_source_line); + for (t=pr_line_start ; *t && *t != '\n' ; t++) + printf ("%c",*t); + printf ("\n"); +} + +/* +============== +PR_NewLine + +Call at start of file and when *pr_file_p == '\n' +============== +*/ +int ForcedCRC; +int QCC_PR_LexInteger (void); +void QCC_AddFile (char *filename); +void QCC_PR_LexString (void); +pbool QCC_PR_SimpleGetToken (void); +void QCC_PR_NewLine (pbool incomment) +{ + char msg[1024]; + int ifmode; + int a; + pbool m; + static int ifs = 0; + int level; //#if level + pbool eval = false; + + if (*pr_file_p == '\n') + { + pr_file_p++; + m = true; + } + else + m = false; + + pr_source_line++; + pr_line_start = pr_file_p; + while(*pr_file_p==' ' || *pr_file_p == '\t') + pr_file_p++; + if (incomment) //no constants if in a comment. + { + } + else if (!strncmp(pr_file_p, "#define", 7)) + { + QCC_PR_ConditionCompilation(); + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + if (!m) + pr_file_p++; + } + else if (!strncmp(pr_file_p, "#undef", 6)) + { + pr_file_p+=6; + while(*pr_file_p <= ' ') + pr_file_p++; + + QCC_PR_SimpleGetToken (); + QCC_PR_UndefineName(pr_token); + +// QCC_PR_ConditionCompilation(); + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + if (!m) + pr_file_p++; + } + else if (!strncmp(pr_file_p, "#if", 3)) + { + pr_file_p+=3; + if (!strncmp(pr_file_p, "def ", 4)) + { + ifmode = 0; + pr_file_p+=4; + } + else if (!strncmp(pr_file_p, "ndef ", 5)) + { + ifmode = 1; + pr_file_p+=5; + } + else + { + ifmode = 2; + pr_file_p+=0; + //QCC_PR_ParseError("bad \"#if\" type"); + } + + QCC_PR_SimpleGetToken (); + level = 1; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } +// pr_file_p++; +// pr_source_line++; + + if (ifmode == 2) + { + if (atof(pr_token)) + eval = true; + } + else + { +// if (!STRCMP(pr_token, "COOP_MODE")) +// eval = false; + if (QCC_PR_CheckCompConstDefined(pr_token)) + eval = true; + + if (ifmode == 1) + eval = eval?false:true; + } + + if (eval) + ifs+=1; + else + { + while (1) + { + while(*pr_file_p==' ' || *pr_file_p == '\t') + pr_file_p++; + if (!strncmp(pr_file_p, "#endif", 6)) + level--; + if (!strncmp(pr_file_p, "#if", 3)) + level++; + if (!strncmp(pr_file_p, "#else", 5) && level == 1) + { + ifs+=1; + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + break; + } + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + if (level <= 0) + break; + pr_file_p++; //next line + pr_source_line++; + } + } + } + else if (!strncmp(pr_file_p, "#else", 5)) + { + ifs -= 1; + level = 1; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + while (1) + { + while(*pr_file_p==' ' || *pr_file_p == '\t') + pr_file_p++; + if (!strncmp(pr_file_p, "#endif", 6)) + level--; + if (!strncmp(pr_file_p, "#if", 3)) + level++; + if (!strncmp(pr_file_p, "#else", 5) && level == 1) + { + ifs+=1; + break; + } + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + if (level <= 0) + break; + pr_file_p++; //go off the end + pr_source_line++; + } + } + else if (!strncmp(pr_file_p, "#endif", 6)) + { + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + if (ifs <= 0) + QCC_PR_ParseError(ERR_NOPRECOMPILERIF, "unmatched #endif"); + else + ifs-=1; + } + else if (!strncmp(pr_file_p, "#eof", 4)) + { + pr_file_p = NULL; + return; + } + else if (!strncmp(pr_file_p, "#error", 6)) + { + pr_file_p += 6; + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line, yes, I KNOW we are going to register an error, and not properly leave this function tree, but... + { + pr_file_p++; + } + + QCC_PR_ParseError(ERR_HASHERROR, "#Error: %s", msg); + } + else if (!strncmp(pr_file_p, "#warning", 8)) + { + pr_file_p += 8; + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + + QCC_PR_ParseWarning(WARN_PRECOMPILERMESSAGE, "#warning: %s", msg); + } + else if (!strncmp(pr_file_p, "#message", 8)) + { + pr_file_p += 8; + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + + printf("#message: %s\n", msg); + } + else if (!strncmp(pr_file_p, "#copyright", 10)) + { + pr_file_p += 10; + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + + if (strlen(msg) >= sizeof(QCC_copyright)) + QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n"); + strncpy(QCC_copyright, msg, sizeof(QCC_copyright)-1); + } + else if (!strncmp(pr_file_p, "#pack", 5)) + { + ifmode = 0; + pr_file_p+=5; + if (!strncmp(pr_file_p, "id", 2)) + pr_file_p+=3; + else + { + ifmode = QCC_PR_LexInteger(); + if (ifmode == 0) + ifmode = 1; + pr_file_p++; + } + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + + if (ifmode == 0) + QCC_packid = atoi(msg); + else if (ifmode <= 5) + strcpy(QCC_Packname[ifmode-1], msg); + else + QCC_PR_ParseError(ERR_TOOMANYPACKFILES, "No more than 5 packs are allowed"); + } + else if (!strncmp(pr_file_p, "#forcecrc", 9)) + { + pr_file_p+=9; + + ForcedCRC = QCC_PR_LexInteger(); + + pr_file_p++; + + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(pr_file_p, "#includelist", 12)) + { + pr_file_p+=12; + + while(*pr_file_p <= ' ') + pr_file_p++; + + while(1) + { + QCC_PR_LexWhitespace(); + if (!QCC_PR_SimpleGetToken()) + { + if (!*pr_file_p) + QCC_Error(ERR_EOF, "eof in includelist"); + else + { + pr_file_p++; + pr_source_line++; + } + continue; + } + if (!strcmp(pr_token, "#endlist")) + break; + printf("Including: %s\n", pr_token); + QCC_Include(pr_token); + + if (*pr_file_p == '\r') + pr_file_p++; + + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(pr_file_p, "#include", 8)) + { + pr_file_p+=8; + + while(*pr_file_p <= ' ') + pr_file_p++; + + QCC_PR_LexString(); + printf("Including: %s\n", pr_token); + QCC_Include(pr_token); + + pr_file_p++; + + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(pr_file_p, "#datafile", 9)) + { + pr_file_p+=9; + + while(*pr_file_p <= ' ') + pr_file_p++; + + QCC_PR_LexString(); + printf("Including datafile: %s\n", pr_token); + QCC_AddFile(pr_token); + + pr_file_p++; + + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(pr_file_p, "#output", 7)) + { + extern char destfile[1024]; + if (pr_file_p[1] == 'p') + pr_file_p+=17; + else + pr_file_p+=7; + + while(*pr_file_p <= ' ') + pr_file_p++; + + QCC_PR_LexString(); + strcpy(destfile, pr_token); + printf("Outputfile: %s\n", destfile); + + pr_file_p++; + + for (a = 0; a < 1023 && pr_file_p[a] != '\n' && pr_file_p[a] != '\0'; a++) + msg[a] = pr_file_p[a]; + + msg[a-1] = '\0'; + + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + } + else if (!strncmp(pr_file_p, "#pragma", 7)) + { + pr_file_p+=7; + while(*pr_file_p <= ' ') + pr_file_p++; + + qcc_token[0] = '\0'; + for(a = 0; *pr_file_p != '\n' && *pr_file_p != '\0'; pr_file_p++) //read on until the end of the line + { + if ((*pr_file_p == ' ' || *pr_file_p == '\t') && !*qcc_token) + { + msg[a] = '\0'; + strcpy(qcc_token, msg); + a=0; + continue; + } + msg[a++] = *pr_file_p; + } + + msg[a] = '\0'; + { + char *end; + for (end = msg + a-1; end>=msg && *end <= ' '; end--) + *end = '\0'; + } + + if (!*qcc_token) + { + strcpy(qcc_token, msg); + msg[0] = '\0'; + } + + { + char *end; + for (end = msg + a-1; end>=msg && *end <= ' '; end--) + *end = '\0'; + } + + if (!QC_strcasecmp(qcc_token, "DONT_COMPILE_THIS_FILE")) + { + while (*pr_file_p) + { + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + pr_file_p++; + + if (*pr_file_p == '\n') + { + QCC_PR_NewLine(false); + pr_file_p++; + } + } + } + else if (!QC_strcasecmp(qcc_token, "COPYRIGHT")) + { + if (strlen(msg) >= sizeof(QCC_copyright)) + QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Copyright message is too long\n"); + strncpy(QCC_copyright, msg, sizeof(QCC_copyright)-1); + } + else if (!QC_strcasecmp(qcc_token, "TARGET")) + { + if (qcc_targetformat == QCF_HEXEN2 && numstatements) + QCC_PR_ParseWarning(WARN_BADTARGET, "Cannot switch from hexen2 target \'%s\'. Ignored.", msg); + else if (!QC_strcasecmp(msg, "H2")) + { + if (numstatements) + QCC_PR_ParseWarning(WARN_BADTARGET, "Cannot switch from hexen2 target \'%s\'. Ignored.", msg); + else + qcc_targetformat = QCF_HEXEN2; + } + else if (!QC_strcasecmp(msg, "KK7")) + qcc_targetformat = QCF_KK7; + else if (!QC_strcasecmp(msg, "FTEDEBUG")) + qcc_targetformat = QCF_FTEDEBUG; + else if (!QC_strcasecmp(msg, "FTE")) + qcc_targetformat = QCF_FTE; + else if (!QC_strcasecmp(msg, "FTEDEBUG32") || !QC_strcasecmp(msg, "FTE32DEBUG")) + qcc_targetformat = QCF_FTEDEBUG32; + else if (!QC_strcasecmp(msg, "FTE32")) + qcc_targetformat = QCF_FTE32; + else if (!QC_strcasecmp(msg, "STANDARD") || !QC_strcasecmp(msg, "ID")) + qcc_targetformat = QCF_STANDARD; + else if (!QC_strcasecmp(msg, "DEBUG")) + { + if (qcc_targetformat == QCF_FTE32) + qcc_targetformat = QCF_FTEDEBUG32; + else if (qcc_targetformat == QCF_FTE) + qcc_targetformat = QCF_FTEDEBUG; + else if (qcc_targetformat == QCF_STANDARD) + qcc_targetformat = QCF_FTEDEBUG; + } + else + QCC_PR_ParseWarning(WARN_BADTARGET, "Unknown target \'%s\'. Ignored.", msg); + } + else if (!QC_strcasecmp(qcc_token, "PROGS_SRC")) + { //doesn't make sence, but silenced if you are switching between using a certain precompiler app used with CuTF. + } + else if (!QC_strcasecmp(qcc_token, "PROGS_DAT")) + { //doesn't make sence, but silenced if you are switching between using a certain precompiler app used with CuTF. + extern char destfile[1024]; + QCC_COM_Parse(msg); + strcpy(destfile, qcc_token); + printf("Outputfile: %s\n", destfile); + } + else if (!QC_strcasecmp(qcc_token, "disable")) + { + qccwarningdisabled[atoi(msg)] = true; + } + else if (!QC_strcasecmp(qcc_token, "enable")) + { + qccwarningdisabled[atoi(msg)] = false; + } + else + QCC_PR_ParseWarning(WARN_BADPRAGMA, "Unknown pragma \'%s\'", qcc_token); + } + +// if (pr_dumpasm) +// PR_PrintNextLine (); + if (m) + pr_file_p--; +} + +/* +============== +PR_LexString + +Parses a quoted string +============== +*/ +#if 0 +void QCC_PR_LexString (void) +{ + int c; + int len; + char tmpbuf[2048]; + + char *text; + char *oldf; + int oldline; + + bool fromfile = true; + + len = 0; + + text = pr_file_p; + do + { + QCC_COM_Parse(text); +// print("Next token is \"%s\"\n", com_token); + if (*text == '\"') + { + text++; + if (fromfile) pr_file_p++; + } + do + { + c = *text++; + if (fromfile) pr_file_p++; + if (!c) + QCC_PR_ParseError ("EOF inside quote"); + if (c=='\n') + QCC_PR_ParseError ("newline inside quote"); + if (c=='\\') + { // escape char + c = *text++; + if (fromfile) pr_file_p++; + if (!c) + QCC_PR_ParseError ("EOF inside quote"); + if (c == 'n') + c = '\n'; + else if (c == '"') + c = '"'; + else if (c == '\\') + c = '\\'; + else + QCC_PR_ParseError ("Unknown escape char"); + } + else if (c=='\"') + { + if (fromfile) pr_file_p++; + break; + } + tmpbuf[len] = c; + len++; + } while (1); + tmpbuf[len] = 0; +// if (fromfile) pr_file_p++; + + pr_immediate_type=NULL; + oldline=pr_source_line; + oldf=pr_file_p; + QCC_PR_Lex(); + if (pr_immediate_type == &type_string) + { +// print("Appending \"%s\" to \"%s\"\n", pr_immediate_string, tmpbuf); + strcat(tmpbuf, pr_immediate_string); + len+=strlen(pr_immediate_string); + } + else + { + pr_source_line = oldline; + pr_file_p = oldf-1; + QCC_PR_LexWhitespace(); + if (*pr_file_p != '\"') //annother string + break; + } + + QCC_PR_LexWhitespace(); + text = pr_file_p; + + } while (1); + + strcpy(pr_token, tmpbuf); + pr_token_type = tt_immediate; + pr_immediate_type = &type_string; + strcpy (pr_immediate_string, pr_token); + +// print("Found \"%s\"\n", pr_immediate_string); +} +#else +void QCC_PR_LexString (void) +{ + int c; + int len; + char *end, *cnst; + + int texttype=0; + + len = 0; + pr_file_p++; + do + { + c = *pr_file_p++; + if (!c) + QCC_PR_ParseError (ERR_EOF, "EOF inside quote"); + if (c=='\n') + QCC_PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, "newline inside quote"); + if (c=='\\') + { // escape char + c = *pr_file_p++; + if (!c) + QCC_PR_ParseError (ERR_EOF, "EOF inside quote"); + if (c == 'n') + c = '\n'; + else if (c == 'r') + c = '\r'; + else if (c == '"') + c = '"'; + else if (c == 't') + c = '\t'; + else if (c == 's' || c == 'b') + { + texttype ^= 128; + continue; + } + else if (c == '[') + c = 16; + else if (c == ']') + c = 17; + else if (c == '{') + { + int d; + c = 0; + while ((d = *pr_file_p++) != '}') + { + c = c * 10 + d - '0'; + if (d < '0' || d > '9' || c > 255) + QCC_PR_ParseError(ERR_BADCHARACTURECODE, "Bad character code"); + } + } + else if (c == '<') + c = 29; + else if (c == '-') + c = 30; + else if (c == '>') + c = 31; + else if (c == '\\') + c = '\\'; + else if (c == '\'') + c = '\''; + else if (c >= '0' && c <= '9') + c = (int)QCC_PR_LexOctal(); + + else if (c == '\r') + { //sigh + c = *pr_file_p++; + if (c != '\n') + QCC_PR_ParseWarning(WARN_HANGINGSLASHR, "Hanging \\\\\r"); + pr_source_line++; + } + else if (c == '\n') + { //sigh + pr_source_line++; + } + else + QCC_PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, "Unknown escape char %c", c); + } + else if (c=='\"') + { + if (len >= sizeof(pr_immediate_string)-1) + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_immediate_string)-1); + pr_token[len] = 0; + pr_token_type = tt_immediate; + pr_immediate_type = type_string; + strcpy (pr_immediate_string, pr_token); + return; + } + else if (c == '#') + { + for (end = pr_file_p; ; end++) + { + if (*end <= ' ') + break; + + if (*end == ')' + || *end == '(' + || *end == '+' + || *end == '-' + || *end == '*' + || *end == '/' + || *end == '\\' + || *end == '|' + || *end == '&' + || *end == '=' + || *end == '^' + || *end == '~' + || *end == '[' + || *end == ']' + || *end == '\"' + || *end == '{' + || *end == '}' + || *end == ';' + || *end == ':' + || *end == ',' + || *end == '.' + || *end == '#') + break; + } + + c = *end; + *end = '\0'; + cnst = QCC_PR_CheakCompConstString(pr_file_p); + if (cnst==pr_file_p) + cnst=NULL; + *end = c; + c = '#'; //undo + if (cnst) + { + QCC_PR_ParseWarning(WARN_MACROINSTRING, "Macro expansion in string"); + + if (len+strlen(cnst) >= sizeof(pr_token)-1) + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); + + strcpy(pr_token+len, cnst); + len+=strlen(cnst); + pr_file_p = end; + continue; + } + } + + pr_token[len] = c|texttype; + len++; + if (len >= sizeof(pr_token)-1) + QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1); + } while (1); +} +#endif + +/* +============== +PR_LexNumber +============== +*/ +float QCC_PR_LexOctal (void) +{ + int c; + int len; + int result; + + char *s; + + len = 0; + c = *pr_file_p; + do + { + pr_token[len] = c; + len++; + pr_file_p++; + c = *pr_file_p; + if (len >= 3) + break; //max of 3 + } while ((c >= '0' && c<= '7')); + pr_token[len] = 0; + + result = 0; + for (s = pr_token, result = 0; *s; s++) + { + result*=8; + result+=*s-'0'; + } + return (float)result; +} + +int QCC_PR_LexInteger (void) +{ + int c; + int len; + + len = 0; + c = *pr_file_p; + if (pr_file_p[0] == '0' && pr_file_p[1] == 'x') + { + pr_token[0] = '0'; + pr_token[1] = 'x'; + len = 2; + c = *(pr_file_p+=2); + } + do + { + pr_token[len] = c; + len++; + pr_file_p++; + c = *pr_file_p; + } while ((c >= '0' && c<= '9') || c == '.' || (c>='a' && c <= 'f')); + pr_token[len] = 0; + return atoi (pr_token); +} + +void QCC_PR_LexNumber (void) +{ + int num=0; + int base=10; + int c; + int sign=1; + if (*pr_file_p == '-') + { + sign=-1; + pr_file_p++; + } + if (pr_file_p[1] == 'x') + { + pr_file_p+=2; + base = 16; + } + + while((c = *pr_file_p)) + { + if (c >= '0' && c <= '9') + { + num*=base; + num += c-'0'; + } + else if (c >= 'a' && c <= 'f') + { + num*=base; + num += c -'a'+10; + } + else if (c >= 'A' && c <= 'F') + { + num*=base; + num += c -'A'+10; + } + else if (c == '.') + { + pr_file_p++; + pr_immediate_type = type_float; + pr_immediate._float = (float)num; + num = 1; + while(1) + { + c = *pr_file_p; + if (c >= '0' && c <= '9') + { + num*=base; + pr_immediate._float += (c-'0')/(float)(num); + } + else + { + break; + } + pr_file_p++; + } + pr_immediate._float *= sign; + return; + } + else if (c == 'i') + { + pr_file_p++; + pr_immediate_type = type_integer; + pr_immediate._int = num*sign; + return; + } + else break; + pr_file_p++; + } + + pr_immediate_type = type_float; + pr_immediate._float = (float)(num*sign); +} + + +float QCC_PR_LexFloat (void) +{ + int c; + int len; + + len = 0; + c = *pr_file_p; + do + { + pr_token[len] = c; + len++; + pr_file_p++; + c = *pr_file_p; + } while ((c >= '0' && c<= '9') || c == '.'&&pr_file_p[1]!='.'); + pr_token[len] = 0; + return (float)atof (pr_token); +} + +/* +============== +PR_LexVector + +Parses a single quoted vector +============== +*/ +void QCC_PR_LexVector (void) +{ + int i; + + pr_file_p++; + + if (*pr_file_p == '\\') + {//extended characture constant + pr_token_type = tt_immediate; + pr_immediate_type = type_float; + pr_file_p++; + switch(*pr_file_p) + { + case 'n': + pr_immediate._float = '\n'; + break; + case 'r': + pr_immediate._float = '\r'; + break; + case 't': + pr_immediate._float = '\t'; + break; + case '\'': + pr_immediate._float = '\''; + break; + case '\"': + pr_immediate._float = '\"'; + break; + case '\\': + pr_immediate._float = '\\'; + break; + case '0': + case '1': + case '2': //assume string constant + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + pr_immediate._float = QCC_PR_LexOctal(); //what's the real point? Neatness? + break; + default: + QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad characture constant"); + } + if (*pr_file_p != '\'') + QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad characture constant"); + pr_file_p++; + return; + } + if (pr_file_p[1] == '\'') + {//character constant + pr_token_type = tt_immediate; + pr_immediate_type = type_float; + pr_immediate._float = pr_file_p[0]; + pr_file_p+=2; + return; + } + pr_token_type = tt_immediate; + pr_immediate_type = type_vector; + QCC_PR_LexWhitespace (); + for (i=0 ; i<3 ; i++) + { + pr_immediate.vector[i] = QCC_PR_LexFloat (); + QCC_PR_LexWhitespace (); + } + if (*pr_file_p != '\'') + QCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, "Bad vector"); + pr_file_p++; +} + +/* +============== +PR_LexName + +Parses an identifier +============== +*/ +void QCC_PR_LexName (void) +{ + int c; + int len; + + len = 0; + c = *pr_file_p; + do + { + pr_token[len] = c; + len++; + pr_file_p++; + c = *pr_file_p; + } while ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' + || (c >= '0' && c <= '9')); + + pr_token[len] = 0; + pr_token_type = tt_name; +} + +/* +============== +PR_LexPunctuation +============== +*/ +void QCC_PR_LexPunctuation (void) +{ + int i; + int len; + char *p; + + pr_token_type = tt_punct; + + for (i=0 ; (p = pr_punctuation[i]) != NULL ; i++) + { + len = strlen(p); + if (!strncmp(p, pr_file_p, len) ) + { + strcpy (pr_token, p); + if (p[0] == '{') + pr_bracelevel++; + else if (p[0] == '}') + pr_bracelevel--; + pr_file_p += len; + return; + } + } + + QCC_PR_ParseError (ERR_UNKNOWNPUCTUATION, "Unknown punctuation"); +} + + +/* +============== +PR_LexWhitespace +============== +*/ +void QCC_PR_LexWhitespace (void) +{ + int c; + + while (1) + { + // skip whitespace + while ( (c = *pr_file_p) <= ' ') + { + if (c=='\n') + { + QCC_PR_NewLine (false); + if (!pr_file_p) + return; + } + if (c == 0) + return; // end of file + pr_file_p++; + } + + // skip // comments + if (c=='/' && pr_file_p[1] == '/') + { + while (*pr_file_p && *pr_file_p != '\n') + pr_file_p++; + QCC_PR_NewLine(false); + pr_file_p++; + continue; + } + + // skip /* */ comments + if (c=='/' && pr_file_p[1] == '*') + { + do + { + pr_file_p++; + if (pr_file_p[0]=='\n') + QCC_PR_NewLine(true); + if (pr_file_p[1] == 0) + { + pr_file_p++; + return; + } + } while (pr_file_p[-1] != '*' || pr_file_p[0] != '/'); + pr_file_p++; + continue; + } + + break; // a real character has been found + } +} + +//============================================================================ + +#define MAX_FRAMES 8192 + +char pr_framemacros[MAX_FRAMES][16]; +int pr_framemacrovalue[MAX_FRAMES]; +int pr_nummacros; +int pr_macrovalue; +int pr_savedmacro; + +void QCC_PR_ClearGrabMacros (void) +{ + pr_nummacros = 0; + pr_macrovalue = 0; + pr_savedmacro = -1; +} + +void QCC_PR_FindMacro (void) +{ + int i; + + for (i=0 ; i ' ' && c != ',' && c != ';' && c != ')' && c != '(') + { + pr_token[i] = c; + i++; + pr_file_p++; + } + pr_token[i] = 0; + return true; +} + +void QCC_PR_ParseFrame (void) +{ + while (QCC_PR_SimpleGetToken ()) + { + strcpy (pr_framemacros[pr_nummacros], pr_token); + pr_framemacrovalue[pr_nummacros] = pr_macrovalue++; + pr_nummacros++; + if (pr_nummacros >= MAX_FRAMES) + QCC_PR_ParseError(ERR_TOOMANYFRAMEMACROS, "Too many frame macros defined"); + } +} + +/* +============== +PR_LexGrab + +Deals with counting sequence numbers and replacing frame macros +============== +*/ +void QCC_PR_LexGrab (void) +{ + pr_file_p++; // skip the $ +// if (!QCC_PR_SimpleGetToken ()) +// QCC_PR_ParseError ("hanging $"); + if (*pr_file_p <= ' ') + QCC_PR_ParseError (ERR_BADFRAMEMACRO, "hanging $"); + QCC_PR_LexName(); + if (!*pr_token) + QCC_PR_ParseError (ERR_BADFRAMEMACRO, "hanging $"); + +// check for $frame + if (!STRCMP (pr_token, "frame") || !STRCMP (pr_token, "framesave")) + { + QCC_PR_ParseFrame (); + QCC_PR_Lex (); + } +// ignore other known $commands - just for model/spritegen + else if (!STRCMP (pr_token, "cd") + || !STRCMP (pr_token, "origin") + || !STRCMP (pr_token, "base") + || !STRCMP (pr_token, "flags") + || !STRCMP (pr_token, "scale") + || !STRCMP (pr_token, "skin") ) + { // skip to end of line + while (QCC_PR_SimpleGetToken ()) + ; + QCC_PR_Lex (); + } + else if (!STRCMP (pr_token, "flush")) + { + QCC_PR_ClearGrabMacros(); + while (QCC_PR_SimpleGetToken ()) + ; + QCC_PR_Lex (); + } + else if (!STRCMP (pr_token, "framevalue")) + { + QCC_PR_SimpleGetToken (); + pr_macrovalue = atoi(pr_token); + + QCC_PR_Lex (); + } + else if (!STRCMP (pr_token, "framerestore")) + { + QCC_PR_SimpleGetToken (); + QCC_PR_FindMacro(); + pr_macrovalue = (int)pr_immediate._float; + + QCC_PR_Lex (); + } +// look for a frame name macro + else + QCC_PR_FindMacro (); +} + +//=========================== +//compiler constants - dmw + +pbool QCC_PR_UndefineName(char *name) +{ +// int a; + CompilerConstant_t *c; + c = Hash_Get(&compconstantstable, name); + if (!c) + return false; + + Hash_Remove(&compconstantstable, name); + return true; + /* + a = c-CompilerConstant; +// for (a = 0; a < numCompilerConstants; a++) + { +// if (!STRCMP(name, CompilerConstant[a].name)) + { + memmove(&CompilerConstant[a], &CompilerConstant[a+1], sizeof(CompilerConstant_t) * (numCompilerConstants-a)); + numCompilerConstants--; + + + + + if (!STRCMP(name, "OP_NODUP")) + qccop_noduplicatestrings = false; + + if (!STRCMP(name, "OP_COMP_ALL")) //group + { + QCC_PR_UndefineName("OP_COMP_STATEMENTS"); + QCC_PR_UndefineName("OP_COMP_DEFS"); + QCC_PR_UndefineName("OP_COMP_FIELDS"); + QCC_PR_UndefineName("OP_COMP_FUNCTIONS"); + QCC_PR_UndefineName("OP_COMP_STRINGS"); + QCC_PR_UndefineName("OP_COMP_GLOBALS"); + QCC_PR_UndefineName("OP_COMP_LINES"); + QCC_PR_UndefineName("OP_COMP_TYPES"); + } + + return true; + } + } +// return false; +*/ +} + +CompilerConstant_t *QCC_PR_DefineName(char *name) +{ + int i; + CompilerConstant_t *cnst; + +// if (numCompilerConstants >= MAX_CONSTANTS) +// QCC_PR_ParseError("Too many compiler constants - %i >= %i", numCompilerConstants, MAX_CONSTANTS); + + if (strlen(name) >= MAXCONSTANTLENGTH || !*name) + QCC_PR_ParseError(ERR_CONSTANTTOOLONG, "Compiler constant name length is too long or short"); + + QCC_PR_UndefineName(name); + + cnst = qccHunkAlloc(sizeof(CompilerConstant_t)); + + cnst->used = false; + cnst->numparams = 0; + strcpy(cnst->name, name); + cnst->namelen = strlen(name); + *cnst->value = '\0'; + for (i = 0; i < MAXCONSTANTPARAMS; i++) + cnst->params[i][0] = '\0'; + + Hash_Add(&compconstantstable, cnst->name, cnst); + + if (!STRCMP(name, "OP_NODUP")) + opt_noduplicatestrings = true; + + + if (!STRCMP(name, "OP_TIME")) //group - optimize for a fast compiler + { + QCC_PR_UndefineName("OP_SIZE"); + QCC_PR_UndefineName("OP_SPEED"); + + QCC_PR_UndefineName("OP_NODUP"); + QCC_PR_UndefineName("OP_COMP_ALL"); + } + + if (!STRCMP(name, "OP_SPEED")) //group - optimize run speed + { + QCC_PR_UndefineName("OP_SIZE"); + QCC_PR_UndefineName("OP_TIME"); + +// QCC_PR_UndefineName("OP_NODUP"); + QCC_PR_UndefineName("OP_COMP_ALL"); + } + + if (!STRCMP(name, "OP_SIZE")) //group - produce small output. + { + QCC_PR_UndefineName("OP_SPEED"); + QCC_PR_UndefineName("OP_TIME"); + + QCC_PR_DefineName("OP_NODUP"); + QCC_PR_DefineName("OP_COMP_ALL"); + } + + if (!STRCMP(name, "OP_COMP_ALL")) //group - compress the output + { + QCC_PR_DefineName("OP_COMP_STATEMENTS"); + QCC_PR_DefineName("OP_COMP_DEFS"); + QCC_PR_DefineName("OP_COMP_FIELDS"); + QCC_PR_DefineName("OP_COMP_FUNCTIONS"); + QCC_PR_DefineName("OP_COMP_STRINGS"); + QCC_PR_DefineName("OP_COMP_GLOBALS"); + QCC_PR_DefineName("OP_COMP_LINES"); + QCC_PR_DefineName("OP_COMP_TYPES"); + } + + + + return cnst; +} + +void QCC_PR_Undefine(void) +{ + QCC_PR_SimpleGetToken (); + + QCC_PR_UndefineName(pr_token); +// QCC_PR_ParseError("%s was not defined.", pr_token); +} + +void QCC_PR_ConditionCompilation(void) +{ + char *d; + char *s; + int quote=false; + CompilerConstant_t *cnst; + + QCC_PR_SimpleGetToken (); + + if (!QCC_PR_SimpleGetToken ()) + QCC_PR_ParseError(ERR_NONAME, "No name defined for compiler constant"); + + QCC_PR_UndefineName(pr_token); + + cnst = QCC_PR_DefineName(pr_token); + + if (*pr_file_p == '(') + { + s = pr_file_p+1; + while(*pr_file_p++) + { + if (*pr_file_p == ',') + { + strncpy(cnst->params[cnst->numparams], s, pr_file_p-s); + cnst->params[cnst->numparams][pr_file_p-s] = '\0'; + cnst->numparams++; + if (cnst->numparams > MAXCONSTANTPARAMS) + QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "May not have more than %i parameters to a macro", MAXCONSTANTPARAMS); + pr_file_p++; + s = pr_file_p; + } + if (*pr_file_p == ')') + { + strncpy(cnst->params[cnst->numparams], s, pr_file_p-s); + cnst->params[cnst->numparams][pr_file_p-s] = '\0'; + cnst->numparams++; + if (cnst->numparams > MAXCONSTANTPARAMS) + QCC_PR_ParseError(ERR_MACROTOOMANYPARMS, "May not have more than %i parameters to a macro", MAXCONSTANTPARAMS); + pr_file_p++; + break; + } + } + } + else cnst->numparams = -1; + + s = pr_file_p; + d = cnst->value; + while(*s == ' ' || *s == '\t') + s++; + while(1) + { + if (*s == '\r' || *s == '\n' || *s == '\0') + break; + if (!quote && s[0]=='/'&&(s[1]=='/'||s[1]=='*')) + break; + if (*s == '\"') + quote=!quote; + + *d = *s; + d++; + s++; + } + *d = '\0'; + d--; + while(*d<= ' ' && d >= cnst->value) + *d-- = '\0'; + if (strlen(cnst->value) >= sizeof(cnst->value)) //this is too late. + QCC_PR_ParseError(ERR_CONSTANTTOOLONG, "Macro %s too long (%i not %i)", cnst->name, strlen(cnst->value), sizeof(cnst->value)); +} + +char *daynames[] = +{ + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", + "Sun" +}; +char *monthnames[] = +{ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" +}; +int QCC_PR_CheakCompConst(void) +{ + char *oldpr_file_p = pr_file_p; + + CompilerConstant_t *c; + + char *end; + for (end = pr_file_p; ; end++) + { + if (*end <= ' ') + break; + + if (*end == ')' + || *end == '(' + || *end == '+' + || *end == '-' + || *end == '*' + || *end == '/' + || *end == '|' + || *end == '&' + || *end == '=' + || *end == '^' + || *end == '~' + || *end == '[' + || *end == ']' + || *end == '\"' + || *end == '{' + || *end == '}' + || *end == ';' + || *end == ':' + || *end == ',' + || *end == '.' + || *end == '#') + break; + } + strncpy(pr_token, pr_file_p, end-pr_file_p); + pr_token[end-pr_file_p]='\0'; +// printf("%s\n", pr_token); + c = Hash_Get(&compconstantstable, pr_token); + + if (c) + { + pr_file_p = oldpr_file_p+strlen(c->name); + QCC_PR_LexWhitespace(); + if (c->numparams>=0) + { + if (*pr_file_p == '(') + { + int p; + char *start; + char buffer[1024]; + char *paramoffset[MAXCONSTANTPARAMS+1]; + int param=0; + + pr_file_p++; + QCC_PR_LexWhitespace(); + start = pr_file_p; + while(1) + { + if (*pr_file_p == ',' || *pr_file_p == ')') + { + paramoffset[param++] = start; + start = pr_file_p+1; + if (*pr_file_p == ')') + { + *pr_file_p = '\0'; + pr_file_p++; + break; + } + *pr_file_p = '\0'; + pr_file_p++; + QCC_PR_LexWhitespace(); + if (param == MAXCONSTANTPARAMS) + QCC_PR_ParseError(ERR_TOOMANYPARAMS, "Too many parameters in macro call"); + } + if (!*pr_file_p) + QCC_PR_ParseError(ERR_EOF, "EOF on macro call"); + pr_file_p++; + } + if (param < c->numparams) + QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Not enough macro parameters"); + paramoffset[param] = start; + + *buffer = '\0'; + + oldpr_file_p = pr_file_p; + pr_file_p = c->value; + do + { + pr_file_p = QCC_COM_Parse(pr_file_p); + if (!pr_file_p) + break; + + for (p = 0; p < param; p++) + { + if (!STRCMP(qcc_token, c->params[p])) + { + strcat(buffer, paramoffset[p]); + break; + } + } + if (p == param) + strcat(buffer, qcc_token); + } while(1); + + for (p = 0; p < param-1; p++) + paramoffset[p][strlen(paramoffset[p])] = ','; + paramoffset[p][strlen(paramoffset[p])] = ')'; + + pr_file_p = oldpr_file_p; + QCC_PR_IncludeChunk(buffer, true, NULL); + } + else + QCC_PR_ParseError(ERR_TOOFEWPARAMS, "Macro without opening brace"); + } + else + QCC_PR_IncludeChunk(c->value, false, NULL); + + QCC_PR_Lex(); + return true; + } + + if (!strncmp(pr_file_p, "__TIME__", 8)) + { + static char retbuf[256]; +#ifdef WIN32 + SYSTEMTIME Systime; + GetSystemTime(&Systime); + sprintf(retbuf, "\"%i:%i\"", Systime.wHour, Systime.wMinute); +#else //linux + sprintf(retbuf, "\"unknown time\""); +#endif + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + + return true; + } + if (!strncmp(pr_file_p, "__DATE__", 8)) + { + static char retbuf[256]; +#ifdef WIN32 + SYSTEMTIME Systime; + GetSystemTime(&Systime); + //dayname, day, month, year + sprintf(retbuf, "\"%s %i %s %i\"", daynames[Systime.wDayOfWeek], Systime.wDay, monthnames[Systime.wMonth], Systime.wYear); +#else //linux + sprintf(retbuf, "\"unknown date\""); +#endif + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + + return true; + } + if (!strncmp(pr_file_p, "__FILE__", 8)) + { + static char retbuf[256]; + sprintf(retbuf, "\"%s\"", strings + s_file); + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + + return true; + } + if (!strncmp(pr_file_p, "__LINE__", 8)) + { + static char retbuf[256]; + sprintf(retbuf, "\"%i\"", pr_source_line); + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + return true; + } + if (!strncmp(pr_file_p, "__FUNC__", 8)) + { + static char retbuf[256]; + sprintf(retbuf, "\"%s\"",pr_scope->name); + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + return true; + } + if (!strncmp(pr_file_p, "__NULL__", 8)) + { + static char retbuf[256]; + sprintf(retbuf, "~0"); + pr_file_p = retbuf; + QCC_PR_Lex(); //translate the macro's value + pr_file_p = oldpr_file_p+8; + return true; + } + return false; +} + +char *QCC_PR_CheakCompConstString(char *def) +{ + char *s; + + CompilerConstant_t *c; + + c = Hash_Get(&compconstantstable, def); + + if (c) + { + s = QCC_PR_CheakCompConstString(c->value); + return s; + } + return def; +} + +CompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def) +{ + CompilerConstant_t *c = Hash_Get(&compconstantstable, def); + return c; + /*int a; + for (a = 0; a < numCompilerConstants; a++) + { + if (!strncmp(def, CompilerConstant[a].name, CompilerConstant[a].namelen+1)) + return &CompilerConstant[a]; + } + return NULL; + */ +} + +//============================================================================ + +/* +============== +PR_Lex + +Sets pr_token, pr_token_type, and possibly pr_immediate and pr_immediate_type +============== +*/ +void QCC_PR_Lex (void) +{ + int c; + + pr_token[0] = 0; + + if (!pr_file_p) + { + if (QCC_PR_UnInclude()) + { + QCC_PR_Lex(); + return; + } + pr_token_type = tt_eof; + return; + } + + QCC_PR_LexWhitespace (); + + if (!pr_file_p) + { + if (QCC_PR_UnInclude()) + { + QCC_PR_Lex(); + return; + } + pr_token_type = tt_eof; + return; + } + + c = *pr_file_p; + + if (!c) + { + if (QCC_PR_UnInclude()) + { + QCC_PR_Lex(); + return; + } + pr_token_type = tt_eof; + return; + } + +// handle quoted strings as a unit + if (c == '\"') + { + QCC_PR_LexString (); + return; + } + +// handle quoted vectors as a unit + if (c == '\'') + { + QCC_PR_LexVector (); + return; + } + +// if the first character is a valid identifier, parse until a non-id +// character is reached + if ( c == '~' ) + { + pr_file_p++; + pr_token_type = tt_immediate; + pr_immediate_type = type_integer; + pr_immediate._int = QCC_PR_LexInteger (); + return; + } + if ( c == '0' && pr_file_p[1] == 'x') + { + pr_token_type = tt_immediate; + QCC_PR_LexNumber(); + return; + } + if ( (c == '.'&&pr_file_p[1] >='0' && pr_file_p[1] <= '9') || (c >= '0' && c <= '9') || ( c=='-' && pr_file_p[1]>='0' && pr_file_p[1] <='9') ) + { + pr_token_type = tt_immediate; + pr_immediate_type = type_float; + pr_immediate._float = QCC_PR_LexFloat (); + +// pr_token_type = tt_immediate; +// QCC_PR_LexNumber (); + return; + } + + if (c == '#' && !(pr_file_p[1]=='-' || (pr_file_p[1]>='0' && pr_file_p[1] <='9'))) //hash and not number + { + pr_file_p++; + if (!QCC_PR_CheakCompConst()) + { + if (!QCC_PR_SimpleGetToken()) + strcpy(pr_token, "unknown"); + QCC_PR_ParseError(ERR_CONSTANTNOTDEFINED, "Explicit precompiler usage when not defined %s", pr_token); + } + else + if (pr_token_type == tt_eof) + QCC_PR_Lex(); + + return; + } + + if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' ) + { + if (!QCC_PR_CheakCompConst()) //look for a macro. + QCC_PR_LexName (); + else + if (pr_token_type == tt_eof) + { + if (QCC_PR_UnInclude()) + { + QCC_PR_Lex(); + return; + } + pr_token_type = tt_eof; + } + return; + } + + if (c == '$') + { + QCC_PR_LexGrab (); + return; + } + +// parse symbol strings until a non-symbol is found + QCC_PR_LexPunctuation (); +} + +//============================================================================= + + +void QCC_PR_ParsePrintDef (int type, QCC_def_t *def) +{ + if (qccwarningdisabled[type]) + return; + if (def->s_file) + printf ("%s:%i: %s is defined here\n", strings + def->s_file, def->s_line, def->name); +} + +/* +============ +PR_ParseError + +Aborts the current file load +============ +*/ +#ifndef QCC +void editbadfile(char *file, int line); +#endif +void VARGS QCC_PR_ParseError (int errortype, char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + QC_vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + +#ifndef QCC + editbadfile(strings+s_file, pr_source_line); +#endif + printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string); + + longjmp (pr_parse_abort, 1); +} +void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + QC_vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + +#ifndef QCC + editbadfile(strings+s_file, pr_source_line); +#endif + printf ("%s:%i: error: %s\n", strings + s_file, pr_source_line, string); + + QCC_PR_ParsePrintDef(WARN_ERROR, def); + + longjmp (pr_parse_abort, 1); +} +void VARGS QCC_PR_ParseWarning (int type, char *error, ...) +{ + va_list argptr; + char string[1024]; + + if (qccwarningdisabled[type]) + return; + + va_start (argptr,error); + QC_vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + + printf ("%s:%i: warning: %s\n", strings + s_file, pr_source_line, string); +} + +void VARGS QCC_PR_Warning (int type, char *file, int line, char *error, ...) +{ + va_list argptr; + char string[1024]; + + if (qccwarningdisabled[type]) + return; + + va_start (argptr,error); + QC_vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + + if (file) + printf ("%s:%i: warning: %s\n", file, line, string); + else + printf ("warning: %s\n", string); +} + + +/* +============= +PR_Expect + +Issues an error if the current token isn't equal to string +Gets the next token +============= +*/ +#ifndef COMMONINLINES +void QCC_PR_Expect (char *string) +{ + if (STRCMP (string, pr_token)) + QCC_PR_ParseError (ERR_EXPECTED, "expected %s, found %s",string, pr_token); + QCC_PR_Lex (); +} +#endif + + +/* +============= +PR_Check + +Returns true and gets the next token if the current token equals string +Returns false and does nothing otherwise +============= +*/ +#ifndef COMMONINLINES +pbool QCC_PR_Check (char *string) +{ + if (STRCMP (string, pr_token)) + return false; + + QCC_PR_Lex (); + return true; +} +#endif + +/* +============ +PR_ParseName + +Checks to see if the current token is a valid name +============ +*/ +char *QCC_PR_ParseName (void) +{ + static char ident[MAX_NAME]; + char *ret; + + if (pr_token_type != tt_name) + QCC_PR_ParseError (ERR_NOTANAME, "\"%s\" - not a name", pr_token); + if (strlen(pr_token) >= MAX_NAME-1) + QCC_PR_ParseError (ERR_NAMETOOLONG, "name too long"); + strcpy (ident, pr_token); + QCC_PR_Lex (); + + ret = qccHunkAlloc(strlen(ident)+1); + strcpy(ret, ident); + return ret; +// return ident; +} + +/* +============ +PR_FindType + +Returns a preexisting complex type that matches the parm, or allocates +a new one and copies it out. +============ +*/ + +//0 if same +QCC_type_t *QCC_PR_NewType (char *name, int basictype); +int typecmp(QCC_type_t *a, QCC_type_t *b) +{ + if (a == b) + return 0; + if (!a || !b) + return 1; //different (^ and not both null) + + if (a->type != b->type) + return 1; + if (a->num_parms != b->num_parms) + return 1; + + if (a->size != b->size) + return 1; + if (STRCMP(a->name, b->name)) + return 1; + + if (typecmp(a->aux_type, b->aux_type)) + return 1; + + if (a->param || b->param) + { + a = a->param; + b = b->param; + + while(a || b) + { + if (typecmp(a, b)) + return 1; + + a=a->next; + b=b->next; + } + } + + return 0; +} + +QCC_type_t *QCC_PR_DuplicateType(QCC_type_t *in) +{ + QCC_type_t *out, *op, *ip; + if (!in) + return NULL; + + out = QCC_PR_NewType(in->name, in->type); + out->aux_type = QCC_PR_DuplicateType(in->aux_type); + out->param = QCC_PR_DuplicateType(in->param); + ip = in->param; + op = NULL; + while(ip) + { + if (!op) + out->param = op = QCC_PR_DuplicateType(ip); + else + op = (op->next = QCC_PR_DuplicateType(ip)); + ip = ip->next; + } + out->size = in->size; + out->num_parms = in->num_parms; + out->ofs = in->ofs; + out->name = in->name; + out->parentclass = in->parentclass; + + return out; +} + +char *TypeName(QCC_type_t *type) +{ + static char buffer[2][512]; + static int op; + char *ret; + + + op++; + ret = buffer[op&1]; + if (type->type == ev_field) + { + type = type->aux_type; + *ret++ = '.'; + } + *ret = 0; + + if (type->type == ev_function) + { + strcat(ret, type->aux_type->name); + strcat(ret, " ("); + type = type->param; + while(type) + { + strcat(ret, type->name); + type = type->next; + + if (type) + strcat(ret, ", "); + } + strcat(ret, ")"); + } + else if (type->type == ev_entity && type->parentclass) + { + op++; + ret = buffer[op&1]; + *ret = 0; + strcat(ret, "class "); + strcat(ret, type->name); +/* strcat(ret, " {"); + type = type->param; + while(type) + { + strcat(ret, type->name); + type = type->next; + + if (type) + strcat(ret, ", "); + } + strcat(ret, "}"); +*/ + } + else + strcpy(ret, type->name); + + return buffer[op&1]; +} +//#define typecmp(a, b) (a && ((a)->type==(b)->type) && !STRCMP((a)->name, (b)->name)) + +QCC_type_t *QCC_PR_FindType (QCC_type_t *type) +{ + int t; + for (t = 0; t < numtypeinfos; t++) + { +// check = &qcc_typeinfo[t]; + if (typecmp(&qcc_typeinfo[t], type)) + continue; + + +// c2 = check->next; +// n2 = type->next; +// for (i=0 ; n2&&c2 ; i++) +// { +// if (!typecmp((c2), (n2))) +// break; +// c2=c2->next; +// n2=n2->next; +// } + +// if (n2==NULL&&c2==NULL) + { + return &qcc_typeinfo[t]; + } + } +QCC_Error(ERR_INTERNAL, "Error with type"); + + return type; +} +/* +QCC_type_t *QCC_PR_NextSubType(QCC_type_t *type, QCC_type_t *prev) +{ + int p; + if (!prev) + return type->next; + + for (p = prev->num_parms; p; p--) + prev = QCC_PR_NextSubType(prev, NULL); + if (prev->num_parms) + + switch(prev->type) + { + case ev_function: + + } + + return prev->next; +} +*/ + +QCC_type_t *QCC_TypeForName(char *name) +{ + int i; + + for (i = 0; i < numtypeinfos; i++) + { + if (!STRCMP(qcc_typeinfo[i].name, name)) + { + return &qcc_typeinfo[i]; + } + } + + return NULL; +} + +/* +============ +PR_SkipToSemicolon + +For error recovery, also pops out of nested braces +============ +*/ +void QCC_PR_SkipToSemicolon (void) +{ + do + { + if (!pr_bracelevel && QCC_PR_Check (";")) + return; + QCC_PR_Lex (); + } while (pr_token_type != tt_eof); +} + + +/* +============ +PR_ParseType + +Parses a variable type, including field and functions types +============ +*/ +#ifdef MAX_EXTRA_PARMS +char pr_parm_names[MAX_PARMS+MAX_EXTRA_PARMS][MAX_NAME]; +#else +char pr_parm_names[MAX_PARMS][MAX_NAME]; +#endif + +QCC_type_t *QCC_PR_NewType (char *name, int basictype); +//expects a ( to have already been parsed. +QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype) +{ + QCC_type_t *ftype, *ptype, *nptype; + char *name; + ftype = QCC_PR_NewType(type_function->name, ev_function); + + ftype->aux_type = returntype; // return type + ftype->num_parms = 0; + ptype = NULL; + + + if (!QCC_PR_Check (")")) + { + if (QCC_PR_Check ("...")) + ftype->num_parms = -1; // variable args + else + do + { + if (ftype->num_parms>=MAX_PARMS+MAX_EXTRA_PARMS) + QCC_PR_ParseError(ERR_TOOMANYPARAMETERSFORFUNC, "Too many parameters. Sorry. (limit is %i)\n", MAX_PARMS+MAX_EXTRA_PARMS); + + if (QCC_PR_Check ("...")) + { + ftype->num_parms = (ftype->num_parms * -1) - 1; + break; + } + + nptype = QCC_PR_ParseType(true); + + if (nptype->type == ev_void) + break; + if (!ptype) + { + ptype = nptype; + ftype->param = ptype; + } + else + { + ptype->next = nptype; + ptype = ptype->next; + } +// type->name = "FUNC PARAMETER"; + + + if (STRCMP(pr_token, ",") && STRCMP(pr_token, ")")) + { + name = QCC_PR_ParseName (); + strcpy (pr_parm_names[ftype->num_parms], name); + } + else + strcpy (pr_parm_names[ftype->num_parms], ""); + ftype->num_parms++; + } while (QCC_PR_Check (",")); + + QCC_PR_Expect (")"); + } + if (newtype) + return ftype; + return QCC_PR_FindType (ftype); +} +QCC_type_t *QCC_PR_ParseType (int newtype) +{ + QCC_type_t *newparm; + QCC_type_t *newt; + QCC_type_t *type; + char *name; + int i; + +// int ofs; + + if (QCC_PR_Check (".")) + { + newt = QCC_PR_NewType("FIELD TYPE", ev_field); + newt->aux_type = QCC_PR_ParseType (false); + + newt->size = newt->aux_type->size; + + if (newtype) + return newt; + return QCC_PR_FindType (newt); + } + + name = QCC_PR_CheakCompConstString(pr_token); + + if (QCC_PR_Check ("class")) + { +// int parms; + QCC_type_t *fieldtype; + char membername[2048]; + char *classname = QCC_PR_ParseName(); + newt = QCC_PR_NewType(classname, ev_entity); + newt->size=type_entity->size; + + type = NULL; + + if (QCC_PR_Check(":")) + { + char *parentname = QCC_PR_ParseName(); + newt->parentclass = QCC_TypeForName(parentname); + if (!newt->parentclass) + QCC_PR_ParseError(ERR_NOTANAME, "Parent class %s was not defined", parentname); + } + else + newt->parentclass = type_entity; + + + QCC_PR_Expect("{"); + if (QCC_PR_Check(",")) + QCC_PR_ParseError(ERR_NOTANAME, "member missing name"); + while (!QCC_PR_Check("}")) + { +// if (QCC_PR_Check(",")) +// type->next = QCC_PR_NewType(type->name, type->type); +// else + newparm = QCC_PR_ParseType(true); + + if (newparm->type == ev_struct || newparm->type == ev_union) //we wouldn't be able to handle it. + QCC_PR_ParseError(ERR_INTERNAL, "Struct or union in class %s", classname); + + if (!QCC_PR_Check(";")) + { + newparm->name = QCC_CopyString(pr_token)+strings; + QCC_PR_Lex(); + if (QCC_PR_Check("[")) + { + type->next->size*=atoi(pr_token); + QCC_PR_Lex(); + QCC_PR_Expect("]"); + } + QCC_PR_Check(";"); + } + else + newparm->name = QCC_CopyString("")+strings; + + sprintf(membername, "%s::"MEMBERFIELDNAME, classname, newparm->name); + fieldtype = QCC_PR_NewType(newparm->name, ev_field); + fieldtype->aux_type = newparm; + QCC_PR_GetDef(fieldtype, membername, pr_scope, 2, 1); + + + newparm->ofs = newt->size; + newt->num_parms++; + + if (type) + type->next = newparm; + else + newt->param = newparm; + + type = newparm; + } + + + QCC_PR_Expect(";"); + return NULL; + } + if (QCC_PR_Check ("struct")) + { + newt = QCC_PR_NewType("struct", ev_struct); + newt->size=0; + QCC_PR_Expect("{"); + + type = NULL; + if (QCC_PR_Check(",")) + QCC_PR_ParseError(ERR_NOTANAME, "element missing name"); + while (!QCC_PR_Check("}")) + { + if (QCC_PR_Check(",")) + { + if (!newparm) + QCC_PR_ParseError(ERR_NOTANAME, "element missing type"); + newparm = QCC_PR_NewType(newparm->name, newparm->type); + } + else + newparm = QCC_PR_ParseType(true); + + if (!QCC_PR_Check(";")) + { + newparm->name = QCC_CopyString(pr_token)+strings; + QCC_PR_Lex(); + if (QCC_PR_Check("[")) + { + newparm->size*=atoi(pr_token); + QCC_PR_Lex(); + QCC_PR_Expect("]"); + } + QCC_PR_Check(";"); + } + else + newparm->name = QCC_CopyString("")+strings; + newparm->ofs = newt->size; + newt->size += newparm->size; + newt->num_parms++; + + if (type) + type->next = newparm; + else + newt->param = newparm; + type = newparm; + } + return newt; + } + if (QCC_PR_Check ("union")) + { + newt = QCC_PR_NewType("union", ev_union); + newt->size=0; + QCC_PR_Expect("{"); + + type = NULL; + if (QCC_PR_Check(",")) + QCC_PR_ParseError(ERR_NOTANAME, "element missing name"); + while (!QCC_PR_Check("}")) + { + if (QCC_PR_Check(",")) + { + if (!newparm) + QCC_PR_ParseError(ERR_NOTANAME, "element missing type"); + newparm = QCC_PR_NewType(newparm->name, newparm->type); + } + else + newparm = QCC_PR_ParseType(true); + if (QCC_PR_Check(";")) + newparm->name = QCC_CopyString("")+strings; + else + { + newparm->name = QCC_CopyString(pr_token)+strings; + QCC_PR_Lex(); + QCC_PR_Expect(";"); + } + newparm->ofs = 0; + if (newparm->size > newt->size) + newt->size = newparm->size; + newt->num_parms++; + + if (type) + type->next = newparm; + else + newt->param = newparm; + type = newparm; + } + return newt; + } + type = NULL; + for (i = 0; i < numtypeinfos; i++) + { + if (!STRCMP(qcc_typeinfo[i].name, name)) + { + type = &qcc_typeinfo[i]; + break; + } + } + + if (i == numtypeinfos) + { + QCC_PR_ParseError (ERR_NOTATYPE, "\"%s\" is not a type", name); + type = type_float; // shut up compiler warning + } + QCC_PR_Lex (); + + if (QCC_PR_Check ("(")) //this is followed by parameters. Must be a function. + return QCC_PR_ParseFunctionType(newtype, type); + else + { + if (newtype) + { + type = QCC_PR_DuplicateType(type); + } + + return type; + } +} + + +#endif \ No newline at end of file diff --git a/engine/qclib/qcd.h b/engine/qclib/qcd.h new file mode 100644 index 000000000..71fdbe42c --- /dev/null +++ b/engine/qclib/qcd.h @@ -0,0 +1,6 @@ +char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, char *info, char *buffer); +int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle); + +char *filefromprogs(progfuncs_t *progfuncs, progsnum_t prnum, char *fname, int *size, char *buffer); +char *filefromnewprogs(progfuncs_t *progfuncs, char *prname, char *fname, int *size, char *buffer);//fixme - remove parm 1 + diff --git a/engine/qclib/qcd_main.c b/engine/qclib/qcd_main.c new file mode 100644 index 000000000..ac0f0f0ab --- /dev/null +++ b/engine/qclib/qcd_main.c @@ -0,0 +1,209 @@ +#include "progsint.h" +//#include "qcc.h" + +#ifdef AVAIL_ZLIB +#ifdef _WIN32 +#define ZEXPORT VARGS +#include "../libs/zlib.h" + +//# pragma comment (lib, "zip/zlib.lib") +#else +#include +#endif +#endif + +char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, char *info, char *buffer) +{ + int i; + if (method == 0) //copy + { + if (complen != len) Sys_Error("lengths do not match"); + memcpy(buffer, info, len); + } + else if (method == 1) //encryption + { + if (complen != len) Sys_Error("lengths do not match"); + for (i = 0; i < len; i++) + buffer[i] = info[i] ^ 0xA5; + } +#ifdef AVAIL_ZLIB + else if (method == 2) //compression (ZLIB) + { + z_stream strm = { + info, + complen, + 0, + + buffer, + len, + 0, + + NULL, + NULL, + + NULL, + NULL, + NULL, + + Z_BINARY, + 0, + 0 + }; + + inflateInit(&strm); + if (Z_STREAM_END != inflate(&strm, Z_FINISH)) //decompress it in one go. + Sys_Error("Failed block decompression\n"); + inflateEnd(&strm); + } +#endif + //add your decryption/decompression routine here. + else + Sys_Error("Bad file encryption routine\n"); + + + return buffer; +} + +#ifndef MINIMAL +void SafeWrite(int hand, void *buf, long count); +int SafeSeek(int hand, int ofs, int mode); +//we are allowed to trash our input here. +int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle) +{ + int i; + if (method == 0) //copy + { + SafeWrite(handle, in, len); + return len; + } + else if (method == 1) //encryption + { + for (i = 0; i < len; i++) + in[i] = in[i] ^ 0xA5; + SafeWrite(handle, in, len); + return len; + } +#ifdef AVAIL_ZLIB + else if (method == 2) //compression (ZLIB) + { + char out[8192]; + + z_stream strm = { + in, + len, + 0, + + out, + sizeof(out), + 0, + + NULL, + NULL, + + NULL, + NULL, + NULL, + + Z_BINARY, + 0, + 0 + }; + i=0; + + deflateInit(&strm, Z_BEST_COMPRESSION); + while(deflate(&strm, Z_FINISH) == Z_OK) + { + SafeWrite(handle, out, sizeof(out) - strm.avail_out); //compress in chunks of 8192. Saves having to allocate a huge-mega-big buffer + i+=sizeof(out) - strm.avail_out; + strm.next_out = out; + strm.avail_out = sizeof(out); + } + SafeWrite(handle, out, sizeof(out) - strm.avail_out); + i+=sizeof(out) - strm.avail_out; + deflateEnd(&strm); + return i; + } +#endif + //add your compression/decryption routine here. + else + { + Sys_Error("Wierd method"); + return 0; + } +} +#endif + +char *filefromprogs(progfuncs_t *progfuncs, progsnum_t prnum, char *fname, int *size, char *buffer) +{ + int num; + includeddatafile_t *s; + if (!pr_progstate[prnum].progs) + return NULL; + if (pr_progstate[prnum].progs->version < PROG_DEBUGVERSION) + return NULL; + if (!pr_progstate[prnum].progs->ofsfiles) + return NULL; + + num = *(int*)((char *)pr_progstate[prnum].progs + pr_progstate[prnum].progs->ofsfiles); + s = (includeddatafile_t *)((char *)pr_progstate[prnum].progs + pr_progstate[prnum].progs->ofsfiles+4); + while(num>0) + { + if (!strcmp(s->filename, fname)) + { + if (size) + *size = s->size; + if (!buffer) + return (char *)0xffffffff; + return QC_decode(progfuncs, s->compsize, s->size, s->compmethod, (char *)pr_progstate[prnum].progs+s->ofs, buffer); + } + + s++; + num--; + } + + if (size) + *size = 0; + return NULL; +} + +#ifndef QCCONLY +char *filefromnewprogs(progfuncs_t *progfuncs, char *prname, char *fname, int *size, char *buffer) +{ + int num; + includeddatafile_t *s; + progstate_t progs; + if (!PR_ReallyLoadProgs(progfuncs, prname, -1, &progs, false)) + { + if (size) + *size = 0; + return NULL; + } + + if (progs.progs->version < PROG_DEBUGVERSION) + return NULL; + if (!progs.progs->ofsfiles) + return NULL; + + num = *(int*)((char *)progs.progs + progs.progs->ofsfiles); + s = (includeddatafile_t *)((char *)progs.progs + progs.progs->ofsfiles+4); + while(num>0) + { + if (!strcmp(s->filename, fname)) + { + if (size) + *size = s->size; + if (!buffer) + return (char *)0xffffffff; + return QC_decode(progfuncs, s->compsize, s->size, s->compmethod, (char *)progs.progs+s->ofs, buffer); + } + + s++; + num--; + } + + if (size) + *size = 0; + return NULL; +} + +#endif diff --git a/engine/qclib/qcdecomp.c b/engine/qclib/qcdecomp.c new file mode 100644 index 000000000..35d310622 --- /dev/null +++ b/engine/qclib/qcdecomp.c @@ -0,0 +1,979 @@ +#ifndef MINIMAL + +#include "progsint.h" +#include "setjmp.h" + +#define MAX_PARMS 8 + +typedef struct QCC_type_s +{ + etype_t type; + + struct QCC_type_s *next; +// function types are more complex + struct QCC_type_s *aux_type; // return type or field type + int num_parms; // -1 = variable args +// struct QCC_type_s *parm_types[MAX_PARMS]; // only [num_parms] allocated + + int ofs; //inside a structure. + int size; + char *name; + +} QCC_type_t; + + +extern QCC_type_t *qcc_typeinfo; +extern int numtypeinfos; +extern int maxtypeinfos; +extern QCC_type_t *type_void;// = {ev_void/*, &def_void*/}; +extern QCC_type_t *type_string;// = {ev_string/*, &def_string*/}; +extern QCC_type_t *type_float;// = {ev_float/*, &def_float*/}; +extern QCC_type_t *type_vector;// = {ev_vector/*, &def_vector*/}; +extern QCC_type_t *type_entity;// = {ev_entity/*, &def_entity*/}; +extern QCC_type_t *type_field;// = {ev_field/*, &def_field*/}; +extern QCC_type_t *type_function;// = {ev_function/*, &def_function*/,NULL,&type_void}; +// type_function is a void() function used for state defs +extern QCC_type_t *type_pointer;// = {ev_pointer/*, &def_pointer*/}; +extern QCC_type_t *type_integer;// = {ev_integer/*, &def_integer*/}; + +extern QCC_type_t *type_floatfield;// = {ev_field/*, &def_field*/, NULL, &type_float}; +QCC_type_t *QCC_PR_NewType (char *name, int basictype); + + +jmp_buf decompilestatementfailure; + +#if 0 +bool Decompile(progfuncs_t *progfuncs, char *fname) +{ + return false; +} +#else + +QCC_type_t **ofstype; +qbyte *ofsflags; + +int SafeOpenWrite (char *filename, int maxsize); +void SafeWrite(int hand, void *buf, long count); +int SafeSeek(int hand, int ofs, int mode); +void SafeClose(int hand); +void VARGS writes(int hand, char *msg, ...) +{ + va_list va; + char buf[4192]; + + va_start(va, msg); + Q_vsnprintf (buf,sizeof(buf)-1, msg, va); + va_end(va); + + SafeWrite(hand, buf, strlen(buf)); +}; + +char *PR_UglyValueString (etype_t type, eval_t *val); +ddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs); +char *VarAtOfs(progfuncs_t *progfuncs, int ofs) +{ + static char buf [4192]; + ddef16_t *def; + int typen; + + if (ofsflags[ofs]&8) + def = ED_GlobalAtOfs16(progfuncs, ofs); + else + def = NULL; + if (!def) + { + if (ofsflags[ofs]&3) + { + if (ofstype[ofs]) + sprintf(buf, "_v_%s_%i", ofstype[ofs]->name, ofs); + else + sprintf(buf, "_v_%i", ofs); + } + else + { + if (ofstype[ofs]) + { + typen = ofstype[ofs]->type; + goto evaluateimmediate; + } + else + sprintf(buf, "_c_%i", ofs); + } + return buf; + } + if (!*def->s_name || !strcmp(def->s_name, "IMMEDIATE")) + { + if (current_progstate->types) + typen = current_progstate->types[def->type & ~DEF_SHARED].type; + else + typen = def->type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + +evaluateimmediate: +// return PR_UglyValueString(def->type, (eval_t *)¤t_progstate->globals[def->ofs]); + switch(typen) + { + case ev_float: + sprintf(buf, "%f", G_FLOAT(ofs)); + return buf; + case ev_vector: + sprintf(buf, "\'%f %f %f\'", G_FLOAT(ofs), G_FLOAT(ofs+1), G_FLOAT(ofs+2)); + return buf; + case ev_string: + { + char *s, *s2; + s = buf; + *s++ = '\"'; + s2 = pr_strings+G_INT(ofs); + + + if (s2) + while(*s2) + { + if (*s2 == '\n') + { + *s++ = '\\'; + *s++ = 'n'; + s2++; + } + else if (*s2 == '\"') + { + *s++ = '\\'; + *s++ = '\"'; + s2++; + } + else if (*s2 == '\t') + { + *s++ = '\\'; + *s++ = 't'; + s2++; + } + else + *s++=*s2++; + } + *s++ = '\"'; + *s++ = '\0'; + } + return buf; + case ev_pointer: + sprintf(buf, "_c_pointer_%i", ofs); + return buf; + default: + sprintf(buf, "_c_%i", ofs); + return buf; + } + } + return def->s_name; +} + + +int file; + +int ImmediateReadLater(progfuncs_t *progfuncs, progstate_t *progs, unsigned int ofs, int firstst) +{ + dstatement16_t *st; + if (ofsflags[ofs] & 8) + return false; //this is a global/local/pramater, not a temp + if (!(ofsflags[ofs] & 3)) + return false; //this is a constant. + for (st = &((dstatement16_t*)progs->statements)[firstst]; ; st++,firstst++) + { //if written, return false, if read, return true. + if (st->op >= OP_CALL0 && st->op <= OP_CALL8) + { + if (ofs == OFS_RETURN) + return false; + if (ofs < OFS_PARM0 + 3*((unsigned int)st->op - OP_CALL0)) + return true; + } + else if (pr_opcodes[st->op].associative == ASSOC_RIGHT) + { + if (ofs == st->b) + return false; + if (ofs == st->a) + return true; + } + else + { + if (st->a == ofs) + return true; + if (st->b == ofs) + return true; + if (st->c == ofs) + return false; + } + + if (st->op == OP_DONE || st->op == OP_RETURN) //we missed our chance. (return/done ends any code coherancy). + return false; + } + return false; +} +int ProductReadLater(progfuncs_t *progfuncs, progstate_t *progs, int stnum) +{ + dstatement16_t *st; + st = &((dstatement16_t*)progs->statements)[stnum]; + if (pr_opcodes[st->op].priority == -1) + { + if (st->op >= OP_CALL0 && st->op <= OP_CALL7) + return ImmediateReadLater(progfuncs, progs, OFS_RETURN, stnum+1); + return false;//these don't have products... + } + + if (pr_opcodes[st->op].associative == ASSOC_RIGHT) + return ImmediateReadLater(progfuncs, progs, st->b, stnum+1); + else + return ImmediateReadLater(progfuncs, progs, st->c, stnum+1); +} + +void WriteStatementProducingOfs(progfuncs_t *progfuncs, progstate_t *progs, int lastnum, int firstpossible, int ofs) //recursive, works backwards +{ + int i; + dstatement16_t *st; + ddef16_t *def; + if (ofs == 0) + longjmp(decompilestatementfailure, 1); + for (; lastnum >= firstpossible; lastnum--) + { + st = &((dstatement16_t*)progs->statements)[lastnum]; + if (st->op >= OP_CALL0 && st->op < OP_CALL7) + { + if (ofs != OFS_RETURN) + continue; + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); + writes(file, "("); + for (i = 0; i < st->op - OP_CALL0; i++) + { + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, OFS_PARM0 + i*3); + if (i != st->op - OP_CALL0-1) + writes(file, ", "); + } + writes(file, ")"); + return; + } + else if (pr_opcodes[st->op].associative == ASSOC_RIGHT) + { + if (st->b != ofs) + continue; + if (!ImmediateReadLater(progfuncs, progs, st->b, lastnum+1)) + { + writes(file, "("); + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->b); + writes(file, " "); + writes(file, pr_opcodes[st->op].name); + writes(file, " "); + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); + writes(file, ")"); + return; + } + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); + return; + } + else + { + if (st->c != ofs) + continue; + + if (!ImmediateReadLater(progfuncs, progs, st->c, lastnum+1)) + { + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->c); + writes(file, " = "); + } + writes(file, "("); + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a); + + if (!strcmp(pr_opcodes[st->op].name, ".")) + writes(file, pr_opcodes[st->op].name); //extra spaces around .s are ugly. + else + { + writes(file, " "); + writes(file, pr_opcodes[st->op].name); + writes(file, " "); + } + WriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->b); + writes(file, ")"); + return; + } + } + + def = ED_GlobalAtOfs16(progfuncs, ofs); + if (def) + { + if (!strcmp(def->s_name, "IMMEDIATE")) + writes(file, "%s", VarAtOfs(progfuncs, ofs)); + else + writes(file, "%s", def->s_name); + } + else + writes(file, "%s", VarAtOfs(progfuncs, ofs)); +// longjmp(decompilestatementfailure, 1); +} + +int WriteStatement(progfuncs_t *progfuncs, progstate_t *progs, int stnum, int firstpossible) +{ + int count, skip; + dstatement16_t *st; + st = &((dstatement16_t*)progs->statements)[stnum]; + switch(st->op) + { + case OP_IFNOT: + count = (signed short)st->b; + writes(file, "if ("); + WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->a); + writes(file, ")\r\n"); + writes(file, "{\r\n"); + firstpossible = stnum+1; + count--; + stnum++; + while(count) + { + if (ProductReadLater(progfuncs, progs, stnum)) + { + count--; + stnum++; + continue; + } + skip = WriteStatement(progfuncs, progs, stnum, firstpossible); + count-=skip; + stnum+=skip; + } + writes(file, "}\r\n"); + st = &((dstatement16_t*)progs->statements)[stnum]; + if (st->op == OP_GOTO) + { + count = (signed short)st->b; + count--; + stnum++; + + writes(file, "else\r\n"); + writes(file, "{\r\n"); + while(count) + { + if (ProductReadLater(progfuncs, progs, stnum)) + { + count--; + stnum++; + continue; + } + skip = WriteStatement(progfuncs, progs, stnum, firstpossible); + count-=skip; + stnum+=skip; + } + writes(file, "}\r\n"); + } + break; + case OP_IF: + longjmp(decompilestatementfailure, 1); + break; + case OP_GOTO: + longjmp(decompilestatementfailure, 1); + break; + case OP_RETURN: + case OP_DONE: + if (st->a) + WriteStatementProducingOfs(progfuncs, progs, stnum-1, firstpossible, st->a); + 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: + WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, OFS_RETURN); + writes(file, ";\r\n"); + break; + default: + if (pr_opcodes[st->op].associative == ASSOC_RIGHT) + WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->b); + else + WriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->c); + writes(file, ";\r\n"); + break; + } + + return 1; +} + +void WriteAsmStatements(progfuncs_t *progfuncs, progstate_t *progs, int num, int f, char *functionname) +{ + int stn = progs->functions[num].first_statement; + QCC_opcode_t *op; + dstatement16_t *st = NULL; + eval_t *v; + + ddef16_t *def; + int ofs,i; + + int fileofs; + + if (!functionname && stn<0) + { + //we wrote this one... + return; + } + + if (stn>=0) + { + for (stn = progs->functions[num].first_statement; stn < (signed int)pr_progs->numstatements; stn++) + { + st = &((dstatement16_t*)progs->statements)[stn]; + if (st->op == OP_DONE || st->op == OP_RETURN) + { + if (!st->a) + writes(f, "void("); + else if (ofstype[st->a]) + { + writes(f, "%s", ofstype[st->a]->name); + writes(f, "("); + } + else + writes(f, "function("); + break; + } + } + st=NULL; + stn = progs->functions[num].first_statement; + } + else + writes(f, "function("); + for (ofs = progs->functions[num].parm_start, i = 0; i < progs->functions[num].numparms; i++, ofs+=progs->functions[num].parm_size[i]) + { + ofsflags[ofs] |= 4; + + def = ED_GlobalAtOfs16(progfuncs, ofs); + if (def && stn>=0) + { + if (st) + writes(f, ", "); + st = (void *)0xffff; + + if (!*def->s_name) + { + char mem[64]; + sprintf(mem, "_p_%i", def->ofs); + def->s_name = malloc(strlen(mem)+1); + strcpy(def->s_name, mem); + } + + if (current_progstate->types) + writes(f, "%s %s", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name); + else + switch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) + { + case ev_string: + writes(f, "%s %s", "string", def->s_name); + break; + case ev_float: + writes(f, "%s %s", "float", def->s_name); + break; + case ev_entity: + writes(f, "%s %s", "entity", def->s_name); + break; + case ev_vector: + writes(f, "%s %s", "vector", def->s_name); + break; + default: + writes(f, "%s %s", "randomtype", def->s_name); + break; + } + } + } + for (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1) + ofsflags[ofs] |= 4; + + if (!*progs->functions[num].s_name) + { + char mem[64]; + if (!functionname) + { + sprintf(mem, "_bi_%i", num); + progs->functions[num].s_name = malloc(strlen(mem)+1); + strcpy(progs->functions[num].s_name, mem); + } + else + { + progs->functions[num].s_name = malloc(strlen(functionname)+1); + strcpy(progs->functions[num].s_name, functionname); + } + } + + writes(f, ") %s", progs->functions[num].s_name); + + if (stn < 0) + { + stn*=-1; + writes(f, " = #%i;\r\n", stn); +/* + for (ofs = progs->functions[num].parm_start, i = 0; i < progs->functions[num].numparms; i++, ofs+=progs->functions[num].parm_size[i]) + { + def = ED_GlobalAtOfs16(progfuncs, ofs); + if (def) + { + def->ofs = 0xffff; + + if (progs->types) + { + if (progs->types[def->type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type == ev_vector) + { + def = ED_GlobalAtOfs16(progfuncs, ofs); + def->ofs = 0xffff; + def = ED_GlobalAtOfs16(progfuncs, ofs+1); + def->ofs = 0xffff; + def = ED_GlobalAtOfs16(progfuncs, ofs+2); + def->ofs = 0xffff; + } + } + else if ((def->type & (~(DEF_SHARED|DEF_SAVEGLOBAL))) == ev_vector) + { + def = ED_GlobalAtOfs16(progfuncs, ofs); + def->ofs = 0xffff; + def = ED_GlobalAtOfs16(progfuncs, ofs+1); + def->ofs = 0xffff; + def = ED_GlobalAtOfs16(progfuncs, ofs+2); + def->ofs = 0xffff; + } + } + } + */ + return; + } + + if (functionname) //parsing defs + { + writes(f, ";\r\n"); + return; + } + + fileofs = SafeSeek(f, 0, SEEK_CUR); + if (setjmp(decompilestatementfailure)) + { + writes(f, "*/\r\n"); + // SafeSeek(f, fileofs, SEEK_SET); + writes(f, " = asm {\r\n"); + + stn = progs->functions[num].first_statement; + for (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1) + { + def = ED_GlobalAtOfs16(progfuncs, ofs); + if (def) + { + v = (eval_t *)&((int *)progs->globals)[def->ofs]; + if (current_progstate->types) + writes(f, "\tlocal %s %s;\r\n", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name); + else + { + if (!*def->s_name) + { + char mem[64]; + sprintf(mem, "_l_%i", def->ofs); + def->s_name = malloc(strlen(mem)+1); + strcpy(def->s_name, mem); + } + + switch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) + { + case ev_string: + writes(f, "\tlocal %s %s;\r\n", "string", def->s_name); + break; + case ev_float: + writes(f, "\tlocal %s %s;\r\n", "float", def->s_name); + break; + case ev_entity: + writes(f, "\tlocal %s %s;\r\n", "entity", def->s_name); + break; + case ev_vector: + if (v->vector[0] || v->vector[1] || v->vector[2]) + writes(f, "\tlocal vector %s = '%f %f %f';\r\n", def->s_name, v->vector[0], v->vector[1], v->vector[2]); + else + writes(f, "\tlocal %s %s;\r\n", "vector", def->s_name); + ofs+=2; //skip floats; + break; + default: + writes(f, "\tlocal %s %s;\r\n", "randomtype", def->s_name); + break; + } + } + } + } + + while(1) + { + st = &((dstatement16_t*)progs->statements)[stn]; + if (!st->op) //end of function statement! + break; + op = &pr_opcodes[st->op]; + writes(f, "\t%s", op->opname); + + if (op->priority==-1&&op->associative==ASSOC_RIGHT) //last param is a goto + { + if (op->type_b == &type_void) + { + if (st->a) + writes(f, " %i", (signed short)st->a); + } + else if (op->type_c == &type_void) + { + if (st->a) + writes(f, " %s", VarAtOfs(progfuncs, st->a)); + if (st->b) + writes(f, " %i", (signed short)st->b); + } + else + { + if (st->a) + writes(f, " %s", VarAtOfs(progfuncs, st->a)); + if (st->b) + writes(f, " %s", VarAtOfs(progfuncs, st->b)); + if (st->c) //rightness means it uses a as c + writes(f, " %i", (signed short)st->c); + } + } + else + { + if (st->a) + { + if (op->type_a == NULL) + writes(f, " %i", (signed short)st->a); + else + writes(f, " %s", VarAtOfs(progfuncs, st->a)); + } + if (st->b) + { + if (op->type_b == NULL) + writes(f, " %i", (signed short)st->b); + else + writes(f, " %s", VarAtOfs(progfuncs, st->b)); + } + if (st->c && op->associative != ASSOC_RIGHT) //rightness means it uses a as c + { + if (op->type_c == NULL) + writes(f, " %i", (signed short)st->c); + else + writes(f, " %s", VarAtOfs(progfuncs, st->c)); + } + } + + writes(f, ";\r\n"); + + stn++; + } + } + else + { + if (!strcmp(progs->functions[num].s_name, "SUB_Remove")) + file = 0; + file = f; + + writes(f, "/*\r\n"); + + writes(f, " =\r\n{\r\n"); + + for (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1) + { + def = ED_GlobalAtOfs16(progfuncs, ofs); + if (def) + { + v = (eval_t *)&((int *)progs->globals)[def->ofs]; + if (current_progstate->types) + writes(f, "\tlocal %s %s;\r\n", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name); + else + { + if (!*def->s_name) + { + char mem[64]; + sprintf(mem, "_l_%i", def->ofs); + def->s_name = malloc(strlen(mem)+1); + strcpy(def->s_name, mem); + } + + switch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) + { + case ev_string: + writes(f, "\tlocal %s %s;\r\n", "string", def->s_name); + break; + case ev_float: + writes(f, "\tlocal %s %s;\r\n", "float", def->s_name); + break; + case ev_entity: + writes(f, "\tlocal %s %s;\r\n", "entity", def->s_name); + break; + case ev_vector: + if (v->vector[0] || v->vector[1] || v->vector[2]) + writes(f, "\tlocal vector %s = '%f %f %f';\r\n", def->s_name, v->vector[0], v->vector[1], v->vector[2]); + else + writes(f, "\tlocal %s %s;\r\n", "vector", def->s_name); + ofs+=2; //skip floats; + break; + default: + writes(f, "\tlocal %s %s;\r\n", "randomtype", def->s_name); + break; + } + } + } + } + + + for (stn = progs->functions[num].first_statement; stn < (signed int)pr_progs->numstatements; stn++) + { + if (ProductReadLater(progfuncs, progs, stn)) + continue; + + st = &((dstatement16_t*)progs->statements)[stn]; + if (!st->op) + break; + WriteStatement(progfuncs, progs, stn, progs->functions[num].first_statement); + } + + longjmp(decompilestatementfailure, 1); + } + writes(f, "};\r\n"); +} + +void FigureOutTypes(progfuncs_t *progfuncs) +{ + ddef16_t *def; + QCC_opcode_t *op; + unsigned int i,p; + dstatement16_t *st; + + int parmofs[8]; + + ofstype = realloc(ofstype, sizeof(*ofstype)*65535); + ofsflags = realloc(ofsflags, sizeof(*ofsflags)*65535); + + maxtypeinfos=256; + qcc_typeinfo = (void *)realloc(qcc_typeinfo, sizeof(QCC_type_t)*maxtypeinfos); + numtypeinfos = 0; + + memset(ofstype, 0, sizeof(*ofstype)*65535); + memset(ofsflags, 0, sizeof(*ofsflags)*65535); + + type_void = QCC_PR_NewType("void", ev_void); + type_string = QCC_PR_NewType("string", ev_string); + type_float = QCC_PR_NewType("float", ev_float); + type_vector = QCC_PR_NewType("vector", ev_vector); + type_entity = QCC_PR_NewType("entity", ev_entity); + type_field = QCC_PR_NewType("field", ev_field); + type_function = QCC_PR_NewType("function", ev_function); + type_pointer = QCC_PR_NewType("pointer", ev_pointer); + type_integer = QCC_PR_NewType("integer", ev_integer); + + type_floatfield = QCC_PR_NewType("fieldfloat", ev_field); + type_floatfield->aux_type = type_float; + type_pointer->aux_type = QCC_PR_NewType("pointeraux", ev_float); + + type_function->aux_type = type_void; + + for (i = 0,st = pr_statements16; i < pr_progs->numstatements; i++,st++) + { + op = &pr_opcodes[st->op]; + if (st->op >= OP_CALL1 && st->op <= OP_CALL8) + { + for (p = 0; p < (unsigned int)st->op-OP_CALL0; p++) + { + ofstype[parmofs[p]] = ofstype[OFS_PARM0+p*3]; + } + } + else if (op->associative == ASSOC_RIGHT) + { //assignment + ofsflags[st->b] |= 1; + if (st->b >= OFS_PARM0 && st->b < RESERVED_OFS) + parmofs[(st->b-OFS_PARM0)/3] = st->a; + +// if (st->op != OP_STORE_F || st->b>RESERVED_OFS) //optimising compilers fix the OP_STORE_V, it's the storef that becomes meaningless (this is the only time that we need this sort of info anyway) + { + if (op->type_c && op->type_c != &type_void) + ofstype[st->a] = *op->type_c; + if (op->type_b && op->type_b != &type_void) + ofstype[st->b] = *op->type_b; + } + } + else if (op->type_c) + { + ofsflags[st->c] |= 2; + + if (st->c >= OFS_PARM0 && st->b < RESERVED_OFS) //too complicated + parmofs[(st->b-OFS_PARM0)/3] = 0; + +// if (st->op != OP_STORE_F || st->b>RESERVED_OFS) //optimising compilers fix the OP_STORE_V, it's the storef that becomes meaningless (this is the only time that we need this sort of info anyway) + { + if (op->type_a && op->type_a != &type_void) + ofstype[st->a] = *op->type_a; + if (op->type_b && op->type_b != &type_void) + ofstype[st->b] = *op->type_b; + if (op->type_c && op->type_c != &type_void) + ofstype[st->c] = *op->type_c; + } + } + } + + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs16[i]; + ofsflags[def->ofs] |= 8; + switch(def->type) + { + case ev_float: + ofstype[def->ofs] = type_float; + break; + case ev_string: + ofstype[def->ofs] = type_string; + break; + case ev_vector: + ofstype[def->ofs] = type_vector; + break; + default: + break; + } + } +} + +pbool Decompile(progfuncs_t *progfuncs, char *fname) +{ + extern progfuncs_t *qccprogfuncs; + unsigned int i; + unsigned int fld=0; + eval_t *v; +// char *filename; + int f, type; + + progstate_t progs, *op; + + qccprogfuncs = progfuncs; + op=current_progstate; + + if (!PR_ReallyLoadProgs(progfuncs, fname, -1, &progs, false)) + { + return false; + } + + f=SafeOpenWrite("qcdtest/defs.qc", 1024*512); + + writes(f, "//Decompiled code can contain little type info.\r\n#define NOWARNINGS\r\n"); + + FigureOutTypes(progfuncs); + + for (i = 1; i < progs.progs->numglobaldefs; i++) + { + if (!strcmp(pr_globaldefs16[i].s_name, "IMMEDIATE")) + continue; + + if (ofsflags[pr_globaldefs16[i].ofs] & 4) + continue; //this is a local. + + if (current_progstate->types) + type = progs.types[pr_globaldefs16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type; + else + type = pr_globaldefs16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL); + v = (eval_t *)&((int *)progs.globals)[pr_globaldefs16[i].ofs]; + + if (!*pr_globaldefs16[i].s_name) + { + char mem[64]; + if (ofsflags[pr_globaldefs16[i].ofs] & 3) + { + ofsflags[pr_globaldefs16[i].ofs] &= ~8; + continue; //this is a constant... + } + + sprintf(mem, "_g_%i", pr_globaldefs16[i].ofs); + pr_globaldefs16[i].s_name = malloc(strlen(mem)+1); + strcpy(pr_globaldefs16[i].s_name, mem); + } + + switch(type) + { + case ev_void: + writes(f, "void %s;\r\n", pr_globaldefs16[i].s_name); + break; + case ev_string: + if (v->string && *(pr_strings+v->_int)) + writes(f, "string %s = \"%s\";\r\n", pr_globaldefs16[i].s_name, v->string); + else + writes(f, "string %s;\r\n", pr_globaldefs16[i].s_name); + break; + case ev_float: + if (v->_float) + writes(f, "float %s = %f;\r\n", pr_globaldefs16[i].s_name, v->_float); + else + writes(f, "float %s;\r\n", pr_globaldefs16[i].s_name); + break; + case ev_vector: + if (v->vector[0] || v->vector[1] || v->vector[2]) + writes(f, "vector %s = '%f %f %f';\r\n", pr_globaldefs16[i].s_name, v->vector[0], v->vector[1], v->vector[2]); + else + writes(f, "vector %s;\r\n", pr_globaldefs16[i].s_name); + i+=3;//skip the floats + break; + case ev_entity: + writes(f, "entity %s;\r\n", pr_globaldefs16[i].s_name); + break; + case ev_field: +//wierd + fld++; + switch(pr_fielddefs16[fld].type) + { + case ev_string: + writes(f, ".string %s;\r\n", pr_globaldefs16[i].s_name); + break; + + case ev_float: + writes(f, ".float %s;\r\n", pr_globaldefs16[i].s_name); + break; + + case ev_vector: + writes(f, ".float %s;\r\n", pr_globaldefs16[i].s_name); + break; + + case ev_entity: + writes(f, ".float %s;\r\n", pr_globaldefs16[i].s_name); + break; + + case ev_function: + writes(f, ".void() %s;\r\n", pr_globaldefs16[i].s_name); + break; + + default: + writes(f, "field %s;\r\n", pr_globaldefs16[i].s_name); + break; + } + break; + + case ev_function: +//wierd + WriteAsmStatements(progfuncs, &progs, ((int *)progs.globals)[pr_globaldefs16[i].ofs], f, pr_globaldefs16[i].s_name); + break; + + case ev_pointer: + writes(f, "pointer %s;\r\n", pr_globaldefs16[i].s_name); + break; + case ev_integer: + writes(f, "integer %s;\r\n", pr_globaldefs16[i].s_name); + break; + + case ev_union: + writes(f, "union %s;\r\n", pr_globaldefs16[i].s_name); + break; + case ev_struct: + writes(f, "struct %s;\r\n", pr_globaldefs16[i].s_name); + break; + default: + break; + + } + } + + for (i = 0; i < progs.progs->numfunctions; i++) + { + WriteAsmStatements(progfuncs, &progs, i, f, NULL); + } + + SafeClose(f); + + current_progstate=op; + + return true; +} +#endif + +#endif \ No newline at end of file