1
0
Fork 0
forked from fte/fteqw

Add 'auto' keyword, this works around ptr-to-local in a C compatible way. Unlike C, can also be used for globals where it serves to reduce bss in favour of loadtime allocs.

Fix some other C-related qcc bugs.
Add 'extern "QC"' and 'extern "C"', probably doesn't quite tweak enough compiler settings like type keywords, but should otherwise be fine for *extensions.qc.
Avoid using popen on weird systems (for git info).
Add emulation for pointers to char/short, so they can be used with older/limited opcode targets (eg DP - char/short ptr maths is still an issue there though).
This commit is contained in:
Shpoike 2025-01-22 04:33:23 +00:00
parent 64f4e2a10a
commit f38f27e388
10 changed files with 944 additions and 304 deletions

View file

@ -40,6 +40,8 @@
#define QCFAULT return (prinst.pr_xstatement=(st-pr_statements)-1),PR_HandleFault
#define EVAL_FLOATISTRUE(ev) ((ev)->_int & 0x7fffffff) //mask away sign bit. This avoids using denormalized floats.
#define A_RSHIFT_I(x,y) ((x < 0) ? ~(~(x) >> (y)) : ((x) >> (y))) //C leaves it undefined whether signed rshift is arithmatic or logical. gcc should be smart enough to fold this to the proper signed instruction at least on x86.
#ifdef __GNUC__
#define errorif(x) if(__builtin_expect(x,0))
#else
@ -875,7 +877,6 @@ reeval:
case OP_CALL1:
case OP_CALL0:
{
int callerprogs;
int newpr;
unsigned int fnum;
RUNAWAYCHECK();
@ -889,7 +890,7 @@ reeval:
glob = NULL; //try to derestrict it.
callerprogs=prinst.pr_typecurrent; //so we can revert to the right caller.
progfuncs->funcs.callprogs=prinst.pr_typecurrent; //so we can revert to the right caller.
newpr = (fnum & 0xff000000)>>24; //this is the progs index of the callee
fnum &= ~0xff000000; //the callee's function index.
@ -897,7 +898,7 @@ reeval:
errorif (!PR_SwitchProgsParms(progfuncs, newpr) || !fnum || fnum > pr_progs->numfunctions)
{
char *msg = fnum?"OP_CALL references invalid function in %s\n":"NULL function from qc (inside %s).\n";
PR_SwitchProgsParms(progfuncs, callerprogs);
PR_SwitchProgsParms(progfuncs, progfuncs->funcs.callprogs);
glob = pr_globals;
if (!progfuncs->funcs.debug_trace)
@ -937,12 +938,12 @@ reeval:
}
else
PR_RunError (&progfuncs->funcs, "Bad builtin call number - %i", -newf->first_statement);
PR_SwitchProgsParms(progfuncs, (progsnum_t)callerprogs);
PR_SwitchProgsParms(progfuncs, (progsnum_t)progfuncs->funcs.callprogs);
//decide weather non debugger wants to start debugging.
return prinst.pr_xstatement;
}
s = PR_EnterFunction (progfuncs, newf, callerprogs);
s = PR_EnterFunction (progfuncs, newf, progfuncs->funcs.callprogs);
st = &pr_statements[s];
}
@ -1033,6 +1034,8 @@ reeval:
case OP_DIV_I:
if (OPB->_int == 0) //no division by zero allowed...
OPC->_int = 0;
else if (OPB->_int == -1 && OPA->_int==(int)0x80000000)
OPC->_int = 0x7fffffff;
else
OPC->_int = OPA->_int / OPB->_int;
break;
@ -1263,7 +1266,7 @@ reeval:
OPC->_int = OPA->_int ^ OPB->_int;
break;
case OP_RSHIFT_I:
OPC->_int = OPA->_int >> OPB->_int;
OPC->_int = A_RSHIFT_I(OPA->_int, OPB->_int);
break;
case OP_RSHIFT_U:
OPC->_uint = OPA->_uint >> OPB->_uint;
@ -1703,7 +1706,7 @@ reeval:
case OP_BITOR_I64: OPC->i64 = OPA->i64 | OPB->i64; break;
case OP_BITXOR_I64: OPC->i64 = OPA->i64 ^ OPB->i64; break;
case OP_LSHIFT_I64I: OPC->i64 = OPA->i64 << OPB->_int; break;
case OP_RSHIFT_I64I: OPC->i64 = OPA->i64 >> OPB->_int; break;
case OP_RSHIFT_I64I: OPC->i64 = A_RSHIFT_I(OPA->i64, OPB->_int); break;
case OP_LT_I64: OPC->_int = OPA->i64 < OPB->i64; break;
case OP_LE_I64: OPC->_int = OPA->i64 <= OPB->i64; break;
case OP_EQ_I64: OPC->_int = OPA->i64 == OPB->i64; break;
@ -1724,6 +1727,8 @@ reeval:
case OP_CONV_DI64: OPC->i64 = OPA->_double; break;
case OP_CONV_U64D: OPC->_double = OPA->u64; break;
case OP_CONV_DU64: OPC->u64 = OPA->_double; break;
case OP_CONV_U64F: OPC->_float = OPA->u64; break;
case OP_CONV_FU64: OPC->u64 = OPA->_float; break;
case OP_ADD_D: OPC->_double = OPA->_double + OPB->_double; break;
case OP_SUB_D: OPC->_double = OPA->_double - OPB->_double; break;
case OP_MUL_D: OPC->_double = OPA->_double * OPB->_double; break;
@ -1733,9 +1738,9 @@ reeval:
case OP_EQ_D: OPC->_int = OPA->_double == OPB->_double; break;
case OP_NE_D: OPC->_int = OPA->_double != OPB->_double; break;
case OP_BITEXTEND_I: OPC->_int = ( signed int)(OPA->_int << (32-(OPB->_uint&0xff)-(OPB->_uint>>8))) >> (32-(OPB->_uint&0xff)); break; //shift it up and down. should sign extend.
case OP_BITEXTEND_I: OPC->_int = A_RSHIFT_I(( signed int)(OPA->_int << (32-(OPB->_uint&0xff)-(OPB->_uint>>8))), (signed)(32-(OPB->_uint&0xff))); break; //shift it up and down. should sign extend.
case OP_BITEXTEND_U: OPC->_uint = (unsigned int)(OPA->_uint << (32-(OPB->_uint&0xff)-(OPB->_uint>>8))) >> (32-(OPB->_uint&0xff)); break; //shift it up and down. should clear the bits.
case OP_BITCOPY_I: i=((1<<(OPB->_uint&0xff))-1);OPC->_int=(OPC->_int&~(i<<(OPB->_uint>>8)))|(((OPA->_int&i)<<(OPB->_uint>>8)));break; //replaces the specified bits (uses the same format bitextend uses to select its input to extend)
case OP_BITCOPY_I: i=((1<<(OPB->_uint&0xff))-1);OPC->_uint=(OPC->_uint&~(i<<(OPB->_uint>>8)))|(((OPA->_uint&i)<<(OPB->_uint>>8)));break; //replaces the specified bits (uses the same format bitextend uses to select its input to extend)
case OP_CONV_UF: OPC->_float = OPA->_uint; break;
case OP_CONV_FU: OPC->_uint = OPA->_float; break;

View file

@ -1219,7 +1219,7 @@ void *PR_PointerToNative_MoInvalidate(pubprogfuncs_t *inst, pint_t ptr, size_t o
else
{ //regular pointer
offset += ptr;
if (datasize > inst->stringtablesize || offset >= inst->stringtablesize-datasize || !offset)
if (datasize > inst->stringtablesize || offset >= inst->stringtablesize-datasize || (!offset && datasize>1))
return NULL; //can't autoresize these. just fail.
return inst->stringtable + ptr;
}
@ -1425,6 +1425,8 @@ static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str,
prinst.nexttempstring = i;
prinst.livetemps++;
len = ((len+3)&~3); //round up, primarily so its safe to use loadp_i to read the last few chars of the string
prinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len);
prinst.tempstrings[i]->size = len;
*str = prinst.tempstrings[i]->value;
@ -1717,6 +1719,7 @@ static pubprogfuncs_t deffuncs = {
QC_Decompile,
#endif
0, //callargc
0,
0, //string table(pointer base address)
0, //string table size

View file

@ -247,7 +247,7 @@ enum qcop_e {
OP_LOADA_FNC, //150
OP_LOADA_I,
OP_STORE_P, //152... erm.. wait...
OP_STORE_P,
OP_LOAD_P,
OP_LOADP_F,
@ -344,8 +344,8 @@ enum qcop_e {
//r5768+
//opcodes for 32bit uints
OP_LE_U, //aka GT
OP_LT_U, //aka GE
OP_LE_U, //aka GE
OP_LT_U, //aka GT
OP_DIV_U, //don't need mul+add+sub
OP_RSHIFT_U, //lshift is the same for signed+unsigned
@ -359,13 +359,13 @@ enum qcop_e {
OP_BITXOR_I64,
OP_LSHIFT_I64I,
OP_RSHIFT_I64I,
OP_LE_I64, //aka GT
OP_LT_I64, //aka GE
OP_LE_I64, //aka GE
OP_LT_I64, //aka GT
OP_EQ_I64,
OP_NE_I64,
//extra opcodes for 64bit uints
OP_LE_U64, //aka GT
OP_LT_U64, //aka GE
OP_LE_U64, //aka GE
OP_LT_U64, //aka GT
OP_DIV_U64,
OP_RSHIFT_U64I,
@ -409,6 +409,8 @@ enum qcop_e {
OP_CONV_FU, //OPC.i=(int)OPA.f
OP_CONV_U64D, //OPC.d=(double)OPA.u64 -- useful mostly so decompilers don't do weird stuff.
OP_CONV_DU64, //OPC.u64=(uint64_t)OPA.d
OP_CONV_U64F, //OPC.f=(float)OPA.u64 -- useful mostly so decompilers don't do weird stuff.
OP_CONV_FU64, //OPC.u64=(uint64_t)OPA.f
OP_NUMREALOPS,
@ -647,6 +649,7 @@ typedef struct qcc_statement_s
{
unsigned short op;
#define STF_LOGICOP (1u<<0) //do not bother following when looking for uninitialised variables.
#define STF_NOFOLD (1u<<1) //do not allow changing its var_c to fold the following store.
unsigned short flags;
QCC_sref_t a, b, c;
unsigned int linenum;

View file

@ -3552,12 +3552,20 @@ retry:
if (reorg && !basictypetable)
QC_AddSharedFieldVar(&progfuncs->funcs, i, pr_strings - stringadjust);
break;
case ev_pointer:
if (((int *)glob)[gd16[i].ofs] & 0x80000000)
{
((int *)glob)[gd16[i].ofs] &= ~0x80000000;
((int *)glob)[gd16[i].ofs] += pointeradjust;
break;
}
FALLTHROUGH
case ev_string:
if (((unsigned int *)glob)[gd16[i].ofs]>=progstate->progs->numstrings)
externs->Printf("PR_LoadProgs: invalid string value (%x >= %x) in '%s'\n", ((unsigned int *)glob)[gd16[i].ofs], progstate->progs->numstrings, gd16[i].s_name+pr_strings-stringadjust);
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.
if (((int *)glob)[gd16[i].ofs]) //quakec uses string tables. 0 must remain null, or 'if (s)' can break.
{
((int *)glob)[gd16[i].ofs] += stringadjust;
isfriked = false;
@ -3573,13 +3581,6 @@ retry:
((int *)glob)[gd16[i].ofs] |= progstype << 24;
}
break;
case ev_pointer:
if (((int *)glob)[gd16[i].ofs] & 0x80000000)
{
((int *)glob)[gd16[i].ofs] &= ~0x80000000;
((int *)glob)[gd16[i].ofs] += pointeradjust;
}
break;
}
}
break;
@ -3606,8 +3607,18 @@ retry:
case ev_field:
QC_AddSharedFieldVar(&progfuncs->funcs, i, pr_strings - stringadjust);
break;
case ev_pointer:
if (((int *)glob)[pr_globaldefs32[i].ofs] & 0x80000000)
{
((int *)glob)[pr_globaldefs32[i].ofs] &= ~0x80000000;
((int *)glob)[pr_globaldefs32[i].ofs] += pointeradjust;
break;
}
FALLTHROUGH
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.
if (((unsigned int *)glob)[pr_globaldefs32[i].ofs]>=progstate->progs->numstrings)
externs->Printf("PR_LoadProgs: invalid string value (%x >= %x) in '%s'\n", ((unsigned int *)glob)[pr_globaldefs32[i].ofs], progstate->progs->numstrings, pr_globaldefs32[i].s_name+pr_strings-stringadjust);
else if (((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] += stringadjust;
isfriked = false;
@ -3617,13 +3628,6 @@ retry:
if (((int *)glob)[pr_globaldefs32[i].ofs]) //don't change null funcs
((int *)glob)[pr_globaldefs32[i].ofs] |= progstype << 24;
break;
case ev_pointer:
if (((int *)glob)[pr_globaldefs32[i].ofs] & 0x80000000)
{
((int *)glob)[pr_globaldefs32[i].ofs] &= ~0x80000000;
((int *)glob)[pr_globaldefs32[i].ofs] += pointeradjust;
}
break;
}
}

View file

@ -1839,6 +1839,10 @@ static void PR_ExecuteCode (progfuncs_t *progfuncs, int s)
}
}
#if defined(__GNUC__) && defined(__GLIBC__)
#define __USE_GNU
#include <dlfcn.h>
#endif
void PDECL PR_ExecuteProgram (pubprogfuncs_t *ppf, func_t fnum)
{
@ -1871,7 +1875,12 @@ void PDECL PR_ExecuteProgram (pubprogfuncs_t *ppf, func_t fnum)
{
// if (pr_global_struct->self)
// ED_Print (PROG_TO_EDICT(pr_global_struct->self));
#if defined(__GNUC__) && !defined(FTE_TARGET_WEB)
#if defined(__GNUC__) && defined(__GLIBC__)
Dl_info info;
void *caller = __builtin_return_address(0);
dladdr(caller, &info);
externs->Printf("PR_ExecuteProgram: NULL function from %s+%p(%s)\n", info.dli_fname, (void*)((intptr_t)caller - (intptr_t)info.dli_fbase), info.dli_sname?info.dli_sname:"<function not known>");
#elif defined(__GNUC__) && !defined(FTE_TARGET_WEB)
externs->Printf("PR_ExecuteProgram: NULL function from exe (address %p)\n", __builtin_return_address(0));
#else
externs->Printf("PR_ExecuteProgram: NULL function from exe\n");
@ -1960,6 +1969,7 @@ struct qcthread_s *PDECL PR_ForkStack(pubprogfuncs_t *ppf)
int localsoffset, baselocalsoffset;
qcthread_t *thread = externs->memalloc(sizeof(qcthread_t));
const mfunction_t *f;
int curprogs = ppf->callprogs;
//notes:
//pr_stack[prinst.exitdepth] is a dummy entry, with null function refs. we don't care about it.
@ -1994,8 +2004,8 @@ struct qcthread_s *PDECL PR_ForkStack(pubprogfuncs_t *ppf)
{
if (i == prinst.pr_depth)
{ //top of the stack. whichever function called the builtin we're executing.
thread->fstack[thread->fstackdepth].fnum = prinst.pr_xfunction - current_progstate->functions;
thread->fstack[thread->fstackdepth].progsnum = prinst.pr_typecurrent;
thread->fstack[thread->fstackdepth].fnum = prinst.pr_xfunction - pr_progstate[curprogs].functions;
thread->fstack[thread->fstackdepth].progsnum = curprogs;
thread->fstack[thread->fstackdepth].statement = prinst.pr_xstatement;
thread->fstack[thread->fstackdepth].spushed = prinst.spushed;
thread->fstackdepth++;
@ -2090,7 +2100,7 @@ void PDECL PR_ResumeThread (pubprogfuncs_t *ppf, struct qcthread_s *thread)
//make the new stack frame from the working values so stuff gets restored properly...
prinst.pr_stack[prinst.pr_depth].f = prinst.pr_xfunction;
prinst.pr_stack[prinst.pr_depth].s = prinst.pr_xstatement;
prinst.pr_stack[prinst.pr_depth].progsnum = initial_progs;
prinst.pr_stack[prinst.pr_depth].progsnum = prinst.pr_typecurrent;
prinst.pr_stack[prinst.pr_depth].pushed = prinst.spushed;
prinst.pr_depth++;
@ -2123,7 +2133,7 @@ void PDECL PR_ResumeThread (pubprogfuncs_t *ppf, struct qcthread_s *thread)
PR_ExecuteCode(progfuncs, prinst.pr_xstatement);
PR_SwitchProgsParms(progfuncs, initial_progs);
PR_SwitchProgsParms(progfuncs, initial_progs); //just in case
#ifndef QCGC
PR_FreeTemps(progfuncs, tempdepth);
prinst.numtempstringsstack = tempdepth;

View file

@ -26,6 +26,16 @@
#define VARGS
#endif
#if __STDC_VERSION__ >= 202311L // c23
#define FALLTHROUGH [[fallthrough]];
#elif defined(__GNUC__) && __GNUC__ >= 7
#define FALLTHROUGH __attribute__((fallthrough));
#elif defined(__clang__) && __clang_major__ >= 7
#define FALLTHROUGH __attribute__((fallthrough));
#else
#define FALLTHROUGH
#endif
#if defined(_M_IX86) || defined(__i386__) //supported arch
#if defined(__GNUC__) || defined(_MSC_VER) //supported compilers (yay for inline asm)
//#define QCJIT
@ -181,6 +191,7 @@ struct pubprogfuncs_s
pbool (PDECL *Decompile) (pubprogfuncs_t *prinst, const char *fname);
int callargc; //number of args of built-in call
int callprogs; //which progs it was called from...
char *stringtable; //qc strings are all relative. add to a qc string. this is required for support of frikqcc progs that strip string immediates.
unsigned int stringtablesize;

View file

@ -486,7 +486,7 @@ typedef struct QCC_def_s
pbool initialized:1; //true when a declaration included "= immediate".
pbool isextern:1; //fteqw-specific lump entry
pbool isparameter:1; //its an engine parameter (thus preinitialised).
pbool addressed:1; //warned about it once, back off now.
pbool addressedwarned:1; //warned about it once, back off now.
pbool autoderef:1; //like C++'s int &foo; - probably local pointers to alloca memory.
const char *deprecated; //reason its deprecated (or empty for no reason given)
@ -697,6 +697,7 @@ extern pbool keyword_unused;
extern pbool keyword_used;
extern pbool keyword_local;
extern pbool keyword_static;
extern pbool keyword_auto;
extern pbool keyword_nonstatic;
extern pbool keyword_ignore;
@ -928,6 +929,7 @@ enum {
WARN_PARAMWITHNONAME,
WARN_ARGUMENTCHECK,
WARN_IGNOREDKEYWORD, //use of a keyword that fteqcc does not support at this time.
WARN_WORDSIZEUNDEFINED,
ERR_PARSEERRORS, //caused by qcc_pr_parseerror being called.
@ -1105,19 +1107,19 @@ extern int pr_error_count, pr_warning_count;
void QCC_PR_NewLine (pbool incomment);
#define GDF_NONE 0
#define GDF_SAVED 1
#define GDF_STATIC 2
#define GDF_CONST 4
#define GDF_STRIP 8 //always stripped, regardless of optimisations. used for class member fields
#define GDF_SILENT 16 //used by the gui, to suppress ALL warnings associated with querying the def.
#define GDF_INLINE 32 //attempt to inline calls to this function
#define GDF_USED 64 //don't strip this, ever.
#define GDF_BASICTYPE 128 //don't care about #merge types not being known correctly.
#define GDF_SCANLOCAL 256 //don't use the locals hash table
#define GDF_POSTINIT 512 //field must be initialised at the end of the compile (allows arrays to be extended later)
#define GDF_PARAMETER 1024
#define GDF_AUTODEREF 2048 //for hidden pointers (alloca-ed locals)
#define GDF_ALIAS 4096
#define GDF_SAVED (1<<0)
#define GDF_STATIC (1<<1)
#define GDF_CONST (1<<2)
#define GDF_STRIP (1<<3) //always stripped, regardless of optimisations. used for class member fields
#define GDF_SILENT (1<<4) //used by the gui, to suppress ALL warnings associated with querying the def.
#define GDF_INLINE (1<<5) //attempt to inline calls to this function
#define GDF_USED (1<<6) //don't strip this, ever.
#define GDF_BASICTYPE (1<<7) //don't care about #merge types not being known correctly.
#define GDF_SCANLOCAL (1<<8) //don't use the locals hash table
#define GDF_POSTINIT (1<<9) //field must be initialised at the end of the compile (allows arrays to be extended later)
#define GDF_PARAMETER (1<<10)
#define GDF_AUTODEREF (1<<11) //for hidden pointers (alloca-ed locals)
#define GDF_ALIAS (1<<12) //symbol is a later alias within the root symbol rather than a core part of it - don't insert extra defs. generally paired with GDF_STRIP.
QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, const char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags);
QCC_sref_t QCC_PR_GetSRef (QCC_type_t *type, const char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags);
void QCC_FreeTemp(QCC_sref_t t);
@ -1241,6 +1243,9 @@ typedef struct qcc_includechunk_s {
} qcc_includechunk_t;
extern qcc_includechunk_t *currentchunk;
pbool QCC_Include(const char *filename, pbool newunit);
void QCC_PR_IncludeChunkEx (char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst);
int QCC_CopyString (const char *str);
int QCC_CopyStringLength (const char *str, size_t length);
@ -1308,7 +1313,7 @@ static void inline QCC_PR_Expect (const char *string)
}
#endif
CompilerConstant_t *QCC_PR_DefineName(const char *name);
CompilerConstant_t *QCC_PR_DefineName(const char *name, const char *value);
void editbadfile(const char *fname, int line);
char *TypeName(QCC_type_t *type, char *buffer, int buffersize);
void QCC_PR_AddIncludePath(const char *newinc);

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,6 @@ pbool QCC_PR_UndefineName(const char *name);
const char *QCC_PR_CheckCompConstString(const char *def);
CompilerConstant_t *QCC_PR_CheckCompConstDefined(const char *def);
int QCC_PR_CheckCompConst(void);
pbool QCC_Include(const char *filename);
void QCC_FreeDef(QCC_def_t *def);
void QCC_PR_LexComment(char **comment);
@ -165,7 +164,7 @@ void QCC_PR_AddIncludePath(const char *newinc)
QCC_PR_ParseWarning(WARN_STRINGTOOLONG, "Too many include dirs. Ignoring and hoping the stars align.");
}
}
static void QCC_PR_IncludeChunkEx (char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst)
void QCC_PR_IncludeChunkEx (char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst)
{
qcc_includechunk_t *chunk = qccHunkAlloc(sizeof(qcc_includechunk_t));
chunk->prev = currentchunk;
@ -311,7 +310,7 @@ void QCC_FindBestInclude(char *newfile, char *currentfile, pbool includetype)
externs->Printf("including %s\n", fullname);
}
}
QCC_Include(fullname);
QCC_Include(fullname, includetype);
}
pbool defaultnoref;
@ -1036,10 +1035,10 @@ static pbool QCC_PR_Precompiler(void)
pr_file_p = "";
QCC_PR_ParseError(0, NULL);
}
QCC_PR_SkipToEndOfLine(true);
QCC_FindBestInclude(msg, compilingfile, false);
QCC_PR_SkipToEndOfLine(true);
QCC_PR_Precompiler(); //preprocessor directives normally require a leading newline to be considered a directive. we won't have one in the new file so lets just do it explicitly.
}
else if (!strncmp(directive, "datafile", 8))
{
@ -2952,7 +2951,7 @@ pbool QCC_PR_UndefineName(const char *name)
return true;
}
CompilerConstant_t *QCC_PR_DefineName(const char *name)
CompilerConstant_t *QCC_PR_DefineName(const char *name, const char *value)
{
int i;
CompilerConstant_t *cnst;
@ -2963,7 +2962,8 @@ CompilerConstant_t *QCC_PR_DefineName(const char *name)
cnst = pHash_Get(&compconstantstable, name);
if (cnst)
{
QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "Duplicate definition for Precompiler constant %s", name);
if (strcmp(cnst->value, value?value:"") || cnst->numparams!=-1 || cnst->varg)
QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "Duplicate definition for Precompiler constant %s", name);
Hash_Remove(&compconstantstable, name);
}
@ -2980,6 +2980,9 @@ CompilerConstant_t *QCC_PR_DefineName(const char *name)
pHash_Add(&compconstantstable, cnst->name, cnst, qccHunkAlloc(sizeof(bucket_t)));
if (value && *value)
cnst->value = strcpy(qccHunkAlloc(strlen(value)+1), value);
return cnst;
}
@ -3002,7 +3005,7 @@ void QCC_PR_PreProcessor_Define(pbool append)
if (oldcnst)
Hash_Remove(&compconstantstable, oldcnst->name);
cnst = QCC_PR_DefineName(pr_token);
cnst = QCC_PR_DefineName(pr_token, NULL);
if (*pr_file_p == '(')
{
@ -3341,6 +3344,10 @@ static const struct tm *QCC_CurrentTime(void)
return localtime(&t);
}
#if _POSIX_C_SOURCE >= 2 || defined(_WIN32)
#define HAVE_POPEN
#endif
#ifdef HAVE_POPEN
static char *QCC_PR_PopenMacro(const char *macroname, const char *cmd, char *retbuf, size_t retbufsize)
{
char *ret = retbuf;
@ -3393,6 +3400,7 @@ static char *QCC_PR_PopenMacro(const char *macroname, const char *cmd, char *ret
#endif
return ret;
}
#endif
static char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t retbufsize)
{
@ -3408,6 +3416,7 @@ static char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t
strftime( retbuf, retbufsize, "\"%a %d %b %Y\"", QCC_CurrentTime());
return retbuf;
}
#ifdef HAVE_POPEN
if (!strcmp(constname, "__GITURL__"))
return QCC_PR_PopenMacro(constname, "git remote get-url origin", retbuf, retbufsize); //some git url...
if (!strcmp(constname, "__GITHASH__"))
@ -3418,6 +3427,7 @@ static char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t
return QCC_PR_PopenMacro(constname, "git log -1 --format=%ci", retbuf, retbufsize); //YYYY-MM-DD HH:MM:SS +TZ
if (!strcmp(constname, "__GITDESC__"))
return QCC_PR_PopenMacro(constname, "git describe", retbuf, retbufsize);
#endif
if (!strcmp(constname, "__RAND__"))
{
QC_snprintfz(retbuf, retbufsize, "%i", rand());
@ -4068,7 +4078,7 @@ void QCC_PR_ParsePrintDef (int type, QCC_def_t *def)
modifiers = "static ";
if (def && def->initialized && def->constant && !def->arraysize)
if (def && def->initialized && def->constant && !def->arraysize && def->symboldata)
{
const QCC_eval_t *ev = (const QCC_eval_t*)&def->symboldata[0];
switch(def->type->type)
@ -4341,6 +4351,8 @@ void QCC_PR_Expect (const char *string)
else
QCC_PR_ParseError (ERR_EXPECTED, "expected %s%s%s, found %s\"%s\"%s", col_location, string, col_none, col_name, pr_token, col_none);
}
else if (pr_token_type == tt_eof)
QCC_PR_ParseError (ERR_EXPECTED, "expected %s%s%s, found %s%s%s", col_location, string, col_none, col_name, "<EOF>", col_none);
else
QCC_PR_ParseError (ERR_EXPECTED, "expected %s%s%s, found %s%s%s", col_location, string, col_none, col_name, pr_token, col_none);
}
@ -5204,7 +5216,12 @@ QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype)
t = QCC_PR_ParseType(false, maybename, false);
if (!t)
return NULL;
{
if (!flag_qcfuncs)
t = type_integer; //c89 assumes int.
else
return NULL;
}
}
if (!ftype)
@ -5227,9 +5244,6 @@ QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype)
while (QCC_PR_CheckToken("*"))
paramlist[numparms].type = QCC_PointerTypeTo(paramlist[numparms].type);
if (paramlist[numparms].type->type == ev_void)
break; //float(void) has no actual args
if (inout < 0 && QCC_PR_CheckToken("&"))
{ //accept c++ syntax, at least on arguments. its not quite the same, but it'll do.
paramlist[numparms].out = true;
@ -5238,58 +5252,65 @@ QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype)
// type->name = "FUNC PARAMETER";
paramlist[numparms].paramname = "";
if (STRCMP(pr_token, ",") && STRCMP(pr_token, ")"))
if (QCC_PR_CheckToken ("..."))
{
if (QCC_PR_CheckToken ("..."))
{
ftype->vargs = true;
break;
}
newtype = true;
if (QCC_PR_CheckToken("("))
{
QCC_PR_CheckToken("*"); //one is normal for a function pointer/ref. non-ptr makes no sense here.
name = QCC_PR_ParseName ();
QCC_PR_Expect(")");
QCC_PR_Expect("(");
paramlist[numparms].type = QCC_PR_ParseFunctionType(false, paramlist[numparms].type);
if (!paramlist[numparms].type)
QCC_PR_ParseError(ERR_BADNOTTYPE, "expected function arg list");
}
else
name = QCC_PR_ParseName ();
paramlist[numparms].paramname = name;
if (definenames)
strcpy (pr_parm_names[numparms], name);
ftype->vargs = true;
break;
}
name = "";
if (QCC_PR_CheckToken("("))
{
QCC_PR_CheckToken("*"); //one is normal for a function pointer/ref. non-ptr makes no sense here.
name = QCC_PR_ParseName ();
if (QCC_PR_CheckToken("["))
{
if (QCC_PR_CheckToken("]")) //length omitted. just treat it as a pointer...?
{
//QCC_PR_ParseWarning(ERR_BADARRAYSIZE, "unsized array argument");
paramlist[numparms].type = QCC_PointerTypeTo(paramlist[numparms].type);
}
else
{ //proper array
int arraysize;
paramlist[numparms].type = QCC_PR_ParseArrayType(paramlist[numparms].type, &arraysize);
if (flag_qcfuncs)
paramlist[numparms].arraysize = arraysize;
else
paramlist[numparms].type = QCC_PointerTypeTo(paramlist[numparms].type); //just turn it into a pointer and ditch the top-level size. C style.
if (!QCC_PR_CheckToken("]"))
{ //don't really care, doesn't matter
paramlist[numparms].arraysize = QCC_PR_IntConstExpr();
QCC_PR_Expect("]");
}
}
QCC_PR_Expect(")");
if (!flag_qcfuncs)
{ //if its an array type, promote it to pointer here.
if (t->type == ev_union && t->num_parms == 1 && !t->params[0].paramname)
paramlist[numparms].type = QCC_PointerTypeTo(t->params[0].type);
QCC_PR_Expect("(");
paramlist[numparms].type = QCC_PR_ParseFunctionType(false, paramlist[numparms].type);
if (!paramlist[numparms].type)
QCC_PR_ParseError(ERR_BADNOTTYPE, "expected function arg list");
}
else if (pr_token_type == tt_name)
name = QCC_PR_ParseName ();
paramlist[numparms].paramname = name;
if (definenames)
strcpy (pr_parm_names[numparms], name);
if (*name)
newtype = true;
else if (paramlist[numparms].type->type == ev_void)
break; //float(void) has no actual args
if (!paramlist[numparms].arraysize && QCC_PR_CheckToken("["))
{
if (QCC_PR_CheckToken("]")) //length omitted. just treat it as a pointer...?
{
//QCC_PR_ParseWarning(ERR_BADARRAYSIZE, "unsized array argument");
paramlist[numparms].type = QCC_PointerTypeTo(paramlist[numparms].type);
}
else
{ //proper array
int arraysize;
paramlist[numparms].type = QCC_PR_ParseArrayType(paramlist[numparms].type, &arraysize);
if (flag_qcfuncs)
paramlist[numparms].arraysize = arraysize;
else
paramlist[numparms].type = QCC_PointerTypeTo(paramlist[numparms].type); //just turn it into a pointer and ditch the top-level size. C style.
}
}
else if (definenames)
strcpy (pr_parm_names[numparms], "");
if (!flag_qcfuncs)
{ //if its an array type, promote it to pointer here.
if (t->type == ev_union && t->num_parms == 1 && !t->params[0].paramname)
paramlist[numparms].type = QCC_PointerTypeTo(t->params[0].type);
}
if (QCC_PR_CheckToken("="))
{
@ -5316,7 +5337,7 @@ QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype)
else
QCC_PR_Expect (")");
}
ftype->vargtodouble = flag_assume_double;
ftype->vargtodouble = !flag_qcfuncs && flag_assume_double;
ftype->num_parms = numparms;
ftype->params = qccHunkAlloc(sizeof(*ftype->params) * numparms);
memcpy(ftype->params, paramlist, sizeof(*ftype->params) * numparms);
@ -5638,7 +5659,7 @@ extern char *basictypenames[];
extern QCC_type_t **basictypes[];
static QCC_type_t *QCC_PR_ParseStruct(etype_t structtype)
{
QCC_type_t *newt, *type, *newparm;
QCC_type_t *newt, *type, *newparm, *oldtype = NULL;
struct QCC_typeparam_s *parms = NULL, *oldparm;
int numparms = 0;
int ofs, bitofs;
@ -5685,19 +5706,19 @@ static QCC_type_t *QCC_PR_ParseStruct(etype_t structtype)
else if (parenttype && newt->parentclass != parenttype)
QCC_PR_ParseError(ERR_NOTANAME, "Redeclaration of struct with different parent type");
if (newt->size)
{
if (QCC_PR_CheckToken("{"))
QCC_PR_ParseError(ERR_NOTANAME, "%s %s is already defined", structtype==ev_union?"union":"struct", newt->name);
return newt;
}
//struct declaration only, not definition.
if (parenttype)
QCC_PR_Expect("{");
else if (!QCC_PR_CheckToken("{"))
return newt;
if (newt->size)
{
// QCC_PR_ParseError(ERR_NOTANAME, "%s %s is already defined", structtype==ev_union?"union":"struct", newt->name);
oldtype = newt;
newt = QCC_PR_NewType(tname, ev_struct, false);
newt->parentclass = parenttype;
}
}
if (newt->parentclass)
newt->size = newt->parentclass->size;
@ -5989,6 +6010,13 @@ static QCC_type_t *QCC_PR_ParseStruct(etype_t structtype)
memcpy(newt->params, parms, sizeof(*type->params) * numparms);
free(parms);
if (oldtype)
{
if (typecmp_strict(newt, oldtype))
QCC_PR_ParseError(ERR_NOTANAME, "%s %s redeclared differently", structtype==ev_union?"union":"struct", newt->name);
newt = oldtype;
}
return newt;
}

View file

@ -370,6 +370,7 @@ compiler_flag_t compiler_flag[] = {
{&keyword_static, defaultkeyword, "static", "Keyword: static", "Disables the 'static' keyword. 'static' means that a variable has altered scope. On globals, the variable is visible only to the current .qc file. On locals, the variable's value does not change between calls to the function. On class variables, specifies that the field is a scoped global instead of a local. On class functions, specifies that 'this' is expected to be invalid and that the function will access any memembers via it."},
{&keyword_nonstatic, defaultkeyword, "nonstatic", "Keyword: nonstatic", "Disables the 'nonstatic' keyword. 'nonstatic' acts upon globals+functions, reverting the defaultstatic pragma on a per-variable basis. For use by people who prefer to keep their APIs explicit."},
{&keyword_ignore, nondefaultkeyword, "ignore", "Keyword: ignore", "Disables the 'ignore' keyword. 'ignore' is expected to typically be hidden behind a 'csqconly' define, and in such a context can be used to conditionally compile functions a little more gracefully. The opposite of the 'used' keyword. These variables/functions/members are ALWAYS stripped, and effectively ignored."},
{&keyword_auto, nondefaultkeyword, "auto", "Keyword: auto", "Disables the 'auto' keyword. This keyword denotes the variable as one that acts like C does, allowing locals to be passed recursively without screwing up the value. Can also be used on uninitialised globals to allocate at loadtime to reduce the size of the dat."},
{&keyword_nosave, defaultkeyword, "nosave", "Keyword: nosave", "Disables the 'nosave' keyword."}, //don't write the def to the output.
{&keyword_inline, defaultkeyword, "inline", "Keyword: inline", "Disables the 'inline' keyword."}, //don't write the def to the output.
@ -557,7 +558,7 @@ int QCC_CopyString (const char *str)
old = strofs;
len = strlen(str)+1;
if ( (strofs + len) > MAX_STRINGS)
QCC_Error(ERR_INTERNAL, "QCC_CopyString: stringtable size limit exceeded\n");
QCC_Error(ERR_INTERNAL, "QCC_CopyString: -max_strings %i limit exceeded\n", MAX_STRINGS);
memcpy (strings+strofs, str, len);
strofs += len;
return old;
@ -574,7 +575,7 @@ int QCC_CopyStringLength (const char *str, size_t length)
old = strofs;
if ( (strofs + length) > MAX_STRINGS)
QCC_Error(ERR_INTERNAL, "QCC_CopyString: stringtable size limit exceeded\n");
QCC_Error(ERR_INTERNAL, "QCC_CopyString: -max_strings %i limit exceeded\n", MAX_STRINGS);
memcpy (strings+strofs, str, length);
strings[strofs+length] = 0;
strofs += length+1;
@ -701,10 +702,10 @@ static void QCC_DumpSymbolNames (const char *outputname)
{
if ((def->scope && !def->isstatic) || !strcmp(def->name, "IMMEDIATE"))
continue;
if (def->symbolheader != def && def->symbolheader->type != def->type)
if (def->symbolheader != def /*&& def->symbolheader->type != def->type*/)
continue; //try to exclude vector components.
snprintf(line, sizeof(line), "%s\n", def->name);
snprintf(line, sizeof(line), "%s %10i %s\n", def->initialized?"data":"bss ", def->symbolsize*(int)sizeof(float), def->name);
SafeWrite(h, line, strlen(line));
}
SafeClose(h);
@ -1277,9 +1278,11 @@ static void QCC_InitData (void)
def_ret.constant = false;
def_ret.type = NULL;
def_ret.symbolheader = &def_ret;
def_ret.symbolsize = type_size[ev_vector];
for (i=0 ; i<MAX_PARMS ; i++)
{
def_parms[i].symbolheader = &def_parms[i];
def_parms[i].symbolsize = type_size[ev_vector];
def_parms[i].temp = NULL;
def_parms[i].type = NULL;
def_parms[i].ofs = OFS_PARM0 + 3*i;
@ -1403,7 +1406,7 @@ static void QCC_FinaliseDef(QCC_def_t *def)
// for (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)
// sub->referenced = true;
}
else
else if (def->used)
{
//touch children to silence annoying warnings.
for (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)
@ -1530,7 +1533,7 @@ static void QCC_FinaliseDef(QCC_def_t *def)
{
def->reloc->used = true;
QCC_FinaliseDef(def->reloc);
if (def->type->type == ev_function/*misordered inits/copies*/ || def->type->type == ev_integer/*dp-style global index*/)
if (def->type->type == ev_function/*misordered inits/copies*/ || def->type->type == ev_integer/*dp-style global index*/ || def->type->type == ev_string/*immediates...*/)
{
//printf("func Reloc %s %s@%i==%x -> %s@%i==%x==%s\n", def->symbolheader->name, def->name,def->ofs, def->symboldata->_int, def->reloc->name,def->reloc->ofs, def->reloc->symboldata->_int, functions[def->reloc->symboldata->_int].name);
def->symboldata->_int += def->reloc->symboldata->_int;
@ -1599,6 +1602,9 @@ static void QCC_UnmarshalLocals(void)
unsigned int onum, biggest, eog;
size_t i;
for (d = pr.def_head.next ; d ; d = d->next)
;
//finalise all the globals that we've seen so far
for (d = pr.def_head.next ; d ; d = d->next)
{
@ -2243,13 +2249,30 @@ static pbool QCC_WriteData (int crc)
continue;
}
else if (def->type->type == ev_pointer && (def->symboldata[0]._int & 0x80000000))
{
{ //pointer relocs must never be stripped as we don't know the final addresses in advance. would screw stuff up.
if (opt_constant_names && !def->nostrip)
def->name = ""; //reloc, can't strip it (engine needs to fix em up), but can clear its name.
}
else if (def->scope && !def->scope->privatelocals && !def->isstatic)
continue; //def is a local, which got shared and should be 0...
else if (((opt_constant_names&&(def->scope||def->constant))||(flag_noreflection&&strncmp(def->name, "autocvar_", 9))) && (def->type->type != ev_string || (strncmp(def->name, "dotranslate_", 12) && opt_constant_names_strings)))
else if ((def->type->type == ev_pointer || def->type->type == ev_string) && def->initialized && (def->symboldata[0]._int || !strncmp(def->name, "dotranslate_", 12)))
{ //string types in addons cannot be stripped - the engine needs to update offset to the new string table.
if (!def->nostrip && (def->scope||def->constant||flag_noreflection))
if (strncmp(def->name, "dotranslate_", 12) && strncmp(def->name, "autocvar_", 9)) //special crap.
{ //we can at least strip the name
if (opt_constant_names_strings)
continue; //drop entirely.
if (opt_constant_names)
{
optres_constant_names_strings += strlen(def->name);
/*char *n = qccHunkAlloc(7+strlen(def->name)+1);
sprintf(n, "STRIP_%s", def->name);
def->name = n;*/
def->name = "";
}
}
}
else if ((opt_constant_names&&(def->scope||def->constant))||(flag_noreflection&&strncmp(def->name, "autocvar_", 9)))// && (def->type->type != ev_string || (opt_constant_names_strings && strncmp(def->name, "dotranslate_", 12))))
{
if (!def->nostrip)
{
@ -3815,7 +3838,7 @@ static int QCC_PR_FinishCompilation (void)
}
if (d->unused && !d->used)
{
d->initialized = 1;
//d->initialized = 1;
continue;
}
QCC_PR_Warning(ERR_NOFUNC, d->filen, d->s_line, "function %s has no body",d->name);
@ -4547,7 +4570,6 @@ pbool QCC_RegisterSourceFile(const char *filename)
static void QCC_PR_CommandLinePrecompilerOptions (void)
{
CompilerConstant_t *cnst;
int i, j, p;
const char *name, *val;
pbool werror = false;
@ -4611,17 +4633,12 @@ static void QCC_PR_CommandLinePrecompilerOptions (void)
char *t = malloc(val-name+1);
memcpy(t, name, val-name);
t[val-name] = 0;
cnst = QCC_PR_DefineName(t);
free(t);
val++;
QCC_PR_DefineName(t, val);
free(t);
}
else
cnst = QCC_PR_DefineName(name);
if (val)
{
cnst->value = qccHunkAlloc(strlen(val)+1);
memcpy(cnst->value, val, strlen(val)+1);
}
QCC_PR_DefineName(name, NULL);
}
else if ( !strncmp(myargv[i], "-I", 2) )
{
@ -4705,7 +4722,7 @@ static void QCC_PR_CommandLinePrecompilerOptions (void)
if (!stricmp(myargv[i]+5, "C") || !stricmp(myargv[i]+5, "c++") ||!stricmp(myargv[i]+5, "c89") || !stricmp(myargv[i]+5, "c90") || !stricmp(myargv[i]+5, "c99") || !stricmp(myargv[i]+5, "c11") || !stricmp(myargv[i]+5, "c17"))
{ //set up for greatest C compatibility... variations from C are bugs, not features.
keyword_asm = false;
keyword_break = keyword_continue = keyword_for = keyword_goto = keyword_const = keyword_extern = keyword_static = true;
keyword_break = keyword_continue = keyword_for = keyword_goto = keyword_const = keyword_extern = keyword_static = keyword_auto = true;
keyword_switch = keyword_case = keyword_default = true;
keyword_accessor = keyword_class = keyword_var = keyword_inout = keyword_optional = keyword_state = keyword_inline = keyword_nosave = keyword_shared = keyword_noref = keyword_unused = keyword_used = keyword_local = keyword_nonstatic = keyword_ignore = keyword_strip = false;
@ -4725,6 +4742,7 @@ static void QCC_PR_CommandLinePrecompilerOptions (void)
flag_qcfuncs = false; //there's a few parsing quirks where our attempt to parse qc functions will misparse valid C.
flag_macroinstrings = false;//hacky preqcc hack.
flag_boundchecks = false; //nope... not C's style.
flag_iffloat = true; //C code won't like this bug (and is mostly ints anyway, so emulation shouldn't be quite so expensive).
qccwarningaction[WARN_UNINITIALIZED] = WA_WARN; //C doesn't like that, might as well warn here too.
qccwarningaction[WARN_TOOMANYPARAMS] = WA_ERROR; //too many args to function is weeeeird.
@ -4732,11 +4750,15 @@ static void QCC_PR_CommandLinePrecompilerOptions (void)
qccwarningaction[WARN_ASSIGNMENTTOCONSTANT] = WA_ERROR; //const is const. at least its not const by default.
qccwarningaction[WARN_SAMENAMEASGLOBAL] = WA_IGNORE; //shadowing of globals.
qccwarningaction[WARN_OCTAL_IMMEDIATE] = WA_IGNORE; //0400!=400 is normal for C code.
qccwarningaction[WARN_POINTLESSSTATEMENT] = WA_IGNORE; //common in C.
QCC_PR_DefineName("__STDC_HOSTED__", "0");
if (!stricmp(myargv[i]+5, "c++"))
{
keyword_class = /*keyword_new =*/ keyword_inline = true;
cnst = QCC_PR_DefineName("__cplusplus");
QCC_PR_DefineName("__cplusplus", NULL);
val = NULL;
}
else if (!stricmp(myargv[i]+5, "c89") || !stricmp(myargv[i]+5, "c90"))
@ -4751,12 +4773,12 @@ static void QCC_PR_CommandLinePrecompilerOptions (void)
val = "202311L";
else
val = NULL;
cnst = QCC_PR_DefineName("__STDC_VERSION__");
if (val)
{
cnst->value = qccHunkAlloc(strlen(val)+1);
memcpy(cnst->value, val, strlen(val)+1);
}
QCC_PR_DefineName("__STDC_VERSION__", val);
QCC_PR_DefineName("__STDC_NO_THREADS__", val); //added c11
QCC_PR_DefineName("__STDC_NO_ATOMICS__", val); //added c11
QCC_PR_DefineName("__STDC_NO_COMPLEX__", val); //optional in c11 (complex mandatory in c99).
QCC_PR_DefineName("__STDC_NO_VLA__", val); //optional in c11 (vla mandatory in c99). we support vla only for the outer array.
}
else if (!strcmp(myargv[i]+5, "qccx"))
{
@ -5108,8 +5130,8 @@ static void QCC_SetDefaultProperties (void)
*qccmsourcedir = 0;
QCC_PR_CloseProcessor();
QCC_PR_DefineName("FTEQCC");
QCC_PR_DefineName("__FTEQCC__");
QCC_PR_DefineName("FTEQCC", NULL);
QCC_PR_DefineName("__FTEQCC__", NULL);
if ((FWDSLASHARGS && QCC_CheckParm("/O0")) || QCC_CheckParm("-O0"))
level = 0;
@ -5362,6 +5384,7 @@ pbool QCC_main (int argc, const char **argv) //as part of the quake engine
qcccol[p] = "";
s_filen = "cmdline";
s_unitn = "";
s_filed = 0;
pr_source_line = 0;
@ -5398,7 +5421,7 @@ pbool QCC_main (int argc, const char **argv) //as part of the quake engine
pHash_Add = &Hash_Add;
pHash_RemoveData = &Hash_RemoveData;
MAX_REGS = 1<<19;
MAX_REGS = 1<<21;
MAX_STRINGS = 1<<21;
MAX_GLOBALS = 1<<17;
MAX_FIELDS = 1<<13;