From 819d541362ded90fc85b5de931a71093baf9832f Mon Sep 17 00:00:00 2001 From: Spoike Date: Thu, 16 Oct 2008 23:04:36 +0000 Subject: [PATCH] local float blob = someformula; works now. added support for -TDP (and #pragma TARGET DP). It is identical to -TFTE except for the instructions used. To be used with Blub's patch when he commits it for DP. It should allow more, this is a first-version. #if defined(A) && defined(B) will also work as expected. Careful with nesting however, as complex formulas will not. Please use only a single operator. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3047 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/qclib/pr_comp.h | 3 + engine/qclib/qcc.h | 5 +- engine/qclib/qcc_pr_comp.c | 249 ++++++++++++++++++++++++++++++++++++- engine/qclib/qcc_pr_lex.c | 116 ++++++++++++++--- engine/qclib/qccmain.c | 11 +- 5 files changed, 364 insertions(+), 20 deletions(-) diff --git a/engine/qclib/pr_comp.h b/engine/qclib/pr_comp.h index 415a355d8..09aad2929 100644 --- a/engine/qclib/pr_comp.h +++ b/engine/qclib/pr_comp.h @@ -308,6 +308,9 @@ enum { OP_PUSH, //push 4octets onto the local-stack (which is ALWAYS poped on function return). Returns a pointer. OP_POP, //pop those ones that were pushed (don't over do it). Needs assembler. + OP_SWITCH_I,//hmm. + OP_GLOAD_V, + OP_NUMOPS }; diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index a9c1454bf..f2a0c57a1 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -68,7 +68,7 @@ extern int MAX_CONSTANTS; #define MAXCONSTANTPARAMLENGTH 32 #define MAXCONSTANTPARAMS 8 -typedef enum {QCF_STANDARD, QCF_HEXEN2, QCF_FTE, QCF_FTEDEBUG, QCF_KK7} qcc_targetformat_t; +typedef enum {QCF_STANDARD, QCF_HEXEN2, QCF_DARKPLACES, QCF_FTE, QCF_FTEDEBUG, QCF_KK7} qcc_targetformat_t; extern qcc_targetformat_t qcc_targetformat; @@ -872,6 +872,9 @@ extern qcc_cachedsourcefile_t *qcc_sourcefile; #ifdef COMMONINLINES bool inline QCC_PR_CheckToken (char *string) { + if (pr_token_type != tt_punct) + return false; + if (STRCMP (string, pr_token)) return false; diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 5f50b7962..27bf1f90d 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -758,6 +758,219 @@ pbool QCC_OPCodeValid(QCC_opcode_t *op) case QCF_FTE: case QCF_FTEDEBUG: return true; + case QCF_DARKPLACES: + //all id opcodes. + if (num < OP_MULSTORE_F) + return true; + //extended opcodes. + //DPFIXME: this is a list of the extended opcodes. I was conservative regarding supported ones. + // at the time of writing, these are the ones that look like they'll work just fine in Blub\0's patch. + // the ones that looked too permissive with bounds checks, or would give false positives are disabled. + // if the DP guys want I can change them as desired. + switch(num) + { + //maths and conditionals (simple opcodes that read from specific globals and write to a global) + case OP_ADD_I: + case OP_ADD_IF: + case OP_ADD_FI: + case OP_SUB_I: + case OP_SUB_IF: + case OP_SUB_FI: + case OP_MUL_I: + case OP_MUL_IF: + case OP_MUL_FI: + case OP_MUL_VI: + case OP_DIV_VF: + case OP_DIV_I: + case OP_DIV_IF: + case OP_DIV_FI: + case OP_BITAND_I: + case OP_BITOR_I: + case OP_BITAND_IF: + case OP_BITOR_IF: + case OP_BITAND_FI: + case OP_BITOR_FI: + case OP_GE_I: + case OP_LE_I: + case OP_GT_I: + case OP_LT_I: + case OP_AND_I: + case OP_OR_I: + case OP_GE_IF: + case OP_LE_IF: + case OP_GT_IF: + case OP_LT_IF: + case OP_AND_IF: + case OP_OR_IF: + case OP_GE_FI: + case OP_LE_FI: + case OP_GT_FI: + case OP_LT_FI: + case OP_AND_FI: + case OP_OR_FI: + case OP_NOT_I: + case OP_EQ_I: + case OP_EQ_IF: + case OP_EQ_FI: + case OP_NE_I: + case OP_NE_IF: + case OP_NE_FI: + return true; + + //stores into a pointer (generated from 'ent.field=XXX') + case OP_STOREP_I: //no worse than the other OP_STOREP_X functions + //reads from an entity field + case OP_LOAD_I: //no worse than the other OP_LOAD_X functions. + return true; + + //stores into the globals array. + //they can change any global dynamically, but thats no security risk. + //fteqcc will not automatically generate these. + //fteqw does not support them either. + case OP_GSTOREP_I: + case OP_GSTOREP_F: + case OP_GSTOREP_ENT: + case OP_GSTOREP_FLD: + case OP_GSTOREP_S: + case OP_GSTOREP_FNC: + case OP_GSTOREP_V: + return true; + + //this opcode looks weird + case OP_GADDRESS://floatc = globals[inta + floatb] (fte does not support) + return true; + + //fteqcc will not automatically generate these + //fteqw does not support them either, for that matter. + case OP_GLOAD_I://c = globals[inta] + case OP_GLOAD_F://note: fte does not support these + case OP_GLOAD_FLD: + case OP_GLOAD_ENT: + case OP_GLOAD_S: + case OP_GLOAD_FNC: + return true; + case OP_GLOAD_V: + return false; //DPFIXME: this is commented out in the patch I was given a link to... because the opcode wasn't defined. + + //these are reportedly functional. + case OP_CALL8H: + case OP_CALL7H: + case OP_CALL6H: + case OP_CALL5H: + case OP_CALL4H: + case OP_CALL3H: + case OP_CALL2H: + case OP_CALL1H: + return true; + + case OP_RAND0: + case OP_RAND1: + case OP_RAND2: + case OP_RANDV0: + case OP_RANDV1: + case OP_RANDV2: + return true; + + case OP_BITSET: // b |= a + case OP_BITCLR: // b &= ~a + case OP_BITSETP: // *b |= a + case OP_BITCLRP: // *b &= ~a + return false; //FIXME: I do not fully follow the controversy over these. + + case OP_SWITCH_F: + case OP_SWITCH_V: + case OP_SWITCH_S: + case OP_SWITCH_E: + case OP_SWITCH_FNC: + case OP_CASE: + case OP_CASERANGE: + return true; + + //assuming the pointers here are fine, the return values are a little strange. + //but its fine + case OP_ADDSTORE_F: + case OP_ADDSTORE_V: + case OP_ADDSTOREP_F: // e.f += f + case OP_ADDSTOREP_V: // e.v += v + case OP_SUBSTORE_F: + case OP_SUBSTORE_V: + case OP_SUBSTOREP_F: // e.f += f + case OP_SUBSTOREP_V: // e.v += v + return true; + + case OP_LOADA_I: + case OP_LOADA_F: + case OP_LOADA_FLD: + case OP_LOADA_ENT: + case OP_LOADA_S: + case OP_LOADA_FNC: + case OP_LOADA_V: + return false; //DPFIXME: DP does not bounds check these properly. I won't generate them. + + case OP_CONV_ITOF: + case OP_CONV_FTOI: + return true; //these look fine. + + case OP_STOREP_C: // store a char in a string + return false; //DPFIXME: dp's bounds check may give false positives with expected uses. + + case OP_MULSTORE_F: + case OP_MULSTORE_V: + case OP_MULSTOREP_F: + case OP_MULSTOREP_V: // e.v *= f + case OP_DIVSTORE_F: + case OP_DIVSTOREP_F: + case OP_STORE_IF: + case OP_STORE_FI: + case OP_STOREP_IF: // store a value to a pointer + case OP_STOREP_FI: + case OP_IFNOTS: + case OP_IFS: + return true; + + case OP_CP_ITOF: + case OP_CP_FTOI: + return false; //DPFIXME: These are not bounds checked at all. + case OP_GLOBALADDRESS: + return false; //DPFIXME: DP will reject these pointers if they are ever used. + case OP_POINTER_ADD: + return true; //just maths. + + case OP_ADD_SF: //(char*)c = (char*)a + (float)b + case OP_SUB_S: //(float)c = (char*)a - (char*)b + return true; + case OP_LOADP_C: //load character from a string + return false; //DPFIXME: DP looks like it'll reject these or wrongly allow. + + case OP_LOADP_I: + case OP_LOADP_F: + case OP_LOADP_FLD: + case OP_LOADP_ENT: + case OP_LOADP_S: + case OP_LOADP_FNC: + case OP_LOADP_V: + return true; + + case OP_POWER_I: + case OP_RSHIFT_I: + case OP_LSHIFT_I: + return true; + + case OP_FETCH_GBL_F: + case OP_FETCH_GBL_S: + case OP_FETCH_GBL_E: + case OP_FETCH_GBL_FNC: + case OP_FETCH_GBL_V: + return false; //DPFIXME: DP will not bounds check this properly, it is too permissive. + case OP_CSTATE: + case OP_CWSTATE: + return false; //DP does not support this hexenc opcode. + case OP_THINKTIME: + return true; //but it does support this one. + + default: //anything I forgot to mention is new. + return false; + } } return false; } @@ -4220,7 +4433,7 @@ QCC_def_t *QCC_PR_Term (void) if (QCC_PR_CheckToken ("(")) { - if (keyword_float && QCC_PR_CheckToken("float")) //check for type casts + if (QCC_PR_CheckKeyword(keyword_float, "float")) //check for type casts { QCC_PR_Expect (")"); e = QCC_PR_Term(); @@ -8105,7 +8318,7 @@ void QCC_PR_ParseDefs (char *classname) continue; } - if (type->type == ev_field && QCC_PR_CheckToken ("alias")) + if (type->type == ev_field && QCC_PR_CheckName ("alias")) { QCC_PR_ParseError(ERR_INTERNAL, "FTEQCC does not support this variant of decompiled hexenc\nPlease obtain the original version released by Raven Software instead."); name = QCC_PR_ParseName(); @@ -8161,6 +8374,34 @@ void QCC_PR_ParseDefs (char *classname) continue; } +#pragma message("this is experimental") + if (pr_scope) + { + d = QCC_PR_Expression(TOP_PRIORITY, false); + if (d->constant) + { + for (i = 0; i < d->type->size; i++) + G_INT(def->ofs) = G_INT(d->ofs); + def->constant = !isvar; + def->initialized = 1; + continue; + } + else if (def->type->size >= 3) + { + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_V], d, def, NULL)); + def->constant = false; + def->initialized = false; + continue; + } + else + { + QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], d, def, NULL)); + def->constant = false; + def->initialized = false; + continue; + } + } + else if (pr_token_type == tt_name) { unsigned int i; @@ -8193,7 +8434,7 @@ void QCC_PR_ParseDefs (char *classname) def->constant = false; else def->constant = true; - if (QCC_PR_CheckToken("0")) + if (QCC_PR_CheckImmediate("0")) { def->constant = 0; def->initialized = 1; //fake function @@ -8222,7 +8463,7 @@ void QCC_PR_ParseDefs (char *classname) i = 0; do { - if (QCC_PR_CheckToken("0")) + if (QCC_PR_CheckImmediate("0")) G_FUNCTION(def->ofs+i) = 0; else { diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index a6f3c6a5d..170115140 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -222,6 +222,71 @@ void QCC_AddFile (char *filename); void QCC_PR_LexString (void); pbool QCC_PR_SimpleGetToken (void); +int ParsePrecompilerIf(void) +{ + CompilerConstant_t *c; + int eval; + char *start = pr_file_p; + if (!QCC_PR_SimpleGetToken()) + { + if (*pr_file_p == '(') + { + eval = ParsePrecompilerIf(); + while (*pr_file_p == ' ' || *pr_file_p == '\t') + pr_file_p++; + if (*pr_file_p != ')') + QCC_PR_ParseError(ERR_EXPECTED, "unclosed bracket condition\n"); + } + else + QCC_PR_ParseError(ERR_EXPECTED, "expected bracket or constant\n"); + } + else if (!strcmp(pr_token, "defined")) + { + while (*pr_file_p == ' ' || *pr_file_p == '\t') + pr_file_p++; + if (*pr_file_p != '(') + QCC_PR_ParseError(ERR_EXPECTED, "no opening bracket after defined\n"); + else + { + pr_file_p++; + + QCC_PR_SimpleGetToken(); + eval = !!QCC_PR_CheckCompConstDefined(pr_token); + + while (*pr_file_p == ' ' || *pr_file_p == '\t') + pr_file_p++; + if (*pr_file_p != ')') + QCC_PR_ParseError(ERR_EXPECTED, "unclosed defined condition\n"); + pr_file_p++; + } + } + else + { + c = QCC_PR_CheckCompConstDefined(pr_token); + if (!c) + eval = false; + else + eval = atoi(c->value); + } + + QCC_PR_SimpleGetToken(); + if (!strcmp(pr_token, "||")) + eval = ParsePrecompilerIf()||eval; + else if (!strcmp(pr_token, "&&")) + eval = ParsePrecompilerIf()&&eval; + else if (!strcmp(pr_token, "<=")) + eval = eval <= ParsePrecompilerIf(); + else if (!strcmp(pr_token, ">=")) + eval = eval >= ParsePrecompilerIf(); + else if (!strcmp(pr_token, "<")) + eval = eval < ParsePrecompilerIf(); + else if (!strcmp(pr_token, ">")) + eval = eval > ParsePrecompilerIf(); + else if (!strcmp(pr_token, "!=")) + eval = eval != ParsePrecompilerIf(); + + return eval; +} /* ============== QCC_PR_Precompiler @@ -293,23 +358,19 @@ pbool QCC_PR_Precompiler(void) //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; + eval = ParsePrecompilerIf(); + + if(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + QCC_PR_ParseError (ERR_NOENDIF, "junk on the end of #if line"); + } } else { + QCC_PR_SimpleGetToken (); + // if (!STRCMP(pr_token, "COOP_MODE")) // eval = false; if (QCC_PR_CheckCompConstDefined(pr_token)) @@ -319,6 +380,12 @@ pbool QCC_PR_Precompiler(void) eval = eval?false:true; } + while(*pr_file_p != '\n' && *pr_file_p != '\0') //read on until the end of the line + { + pr_file_p++; + } + level = 1; + if (eval) ifs+=1; else @@ -753,6 +820,8 @@ pbool QCC_PR_Precompiler(void) } else if (!QC_strcasecmp(msg, "KK7")) qcc_targetformat = QCF_KK7; + else if (!QC_strcasecmp(msg, "DP") || !QC_strcasecmp(msg, "DARKPLACES")) + qcc_targetformat = QCF_DARKPLACES; else if (!QC_strcasecmp(msg, "FTEDEBUG")) qcc_targetformat = QCF_FTEDEBUG; else if (!QC_strcasecmp(msg, "FTE")) @@ -1596,6 +1665,8 @@ pbool QCC_PR_SimpleGetToken (void) int c; int i; + pr_token[0] = 0; + // skip whitespace while ( (c = *pr_file_p) <= ' ') { @@ -2572,6 +2643,21 @@ Returns false and does nothing otherwise #ifndef COMMONINLINES pbool QCC_PR_CheckToken (char *string) { + if (pr_token_type != tt_punct) + return false; + + if (STRCMP (string, pr_token)) + return false; + + QCC_PR_Lex (); + return true; +} + +pbool QCC_PR_CheckImmediate (char *string) +{ + if (pr_token_type != tt_immediate) + return false; + if (STRCMP (string, pr_token)) return false; @@ -2581,6 +2667,8 @@ pbool QCC_PR_CheckToken (char *string) pbool QCC_PR_CheckName(char *string) { + if (pr_token_type != tt_name) + return false; if (flag_caseinsensative) { if (stricmp (string, pr_token)) @@ -2972,13 +3060,13 @@ QCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype) break; } - if (QCC_PR_CheckToken("arg")) + if (QCC_PR_CheckName("arg")) { sprintf(argname, "arg%i", ftype->num_parms); name = argname; nptype = QCC_PR_NewType("Variant", ev_variant); } - else if (QCC_PR_CheckToken("vect")) //this can only be of vector sizes, so... + else if (QCC_PR_CheckName("vect")) //this can only be of vector sizes, so... { sprintf(argname, "arg%i", ftype->num_parms); name = argname; diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index d5e54a0d3..919975b34 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -242,6 +242,8 @@ struct { {QCF_KK7, "version7"}, {QCF_KK7, "kkqwsv"}, {QCF_FTE, "fte"}, + {QCF_DARKPLACES,"darkplaces"}, + {QCF_DARKPLACES,"dp"}, {0, NULL} }; @@ -592,6 +594,7 @@ void QCC_WriteData (int crc) qcc_targetformat = QCF_FTE; case QCF_FTEDEBUG: case QCF_FTE: + case QCF_DARKPLACES: if (qcc_targetformat == QCF_FTEDEBUG) debugtarget = true; @@ -613,7 +616,10 @@ void QCC_WriteData (int crc) //include a type block? types = debugtarget;//!!QCC_PR_CheckCompConstDefined("TYPES"); //useful for debugging and saving (maybe, anyway...). - printf("An FTE executor will be required\n"); + if (qcc_targetformat == QCF_DARKPLACES) + printf("DarkPlaces or FTE will be required\n"); + else + printf("An FTE executor will be required\n"); break; case QCF_KK7: if (bodylessfuncs) @@ -1056,6 +1062,7 @@ strofs = (strofs+3)&~3; case QCF_HEXEN2: //urgh progs.version = PROG_VERSION; break; + case QCF_DARKPLACES: case QCF_FTE: case QCF_FTEDEBUG: progs.version = PROG_EXTENDEDVERSION; @@ -2491,6 +2498,8 @@ void QCC_SetDefaultProperties (void) qcc_targetformat = QCF_HEXEN2; else if (QCC_CheckParm ("-fte")) qcc_targetformat = QCF_FTE; + else if (QCC_CheckParm ("-dp")) + qcc_targetformat = QCF_DARKPLACES; else qcc_targetformat = QCF_STANDARD;