From dc010d6ec8aea4df2d1a4cccf9cddb0a83ec1827 Mon Sep 17 00:00:00 2001 From: Shpoike Date: Tue, 19 Nov 2024 00:37:12 +0000 Subject: [PATCH] fteqw: add memrealloc builtin. fteqw: make it a bit clearer when there's no tls drivers compiled/loaded. fteqcc: fix decompiler code to not crash nor misbehave on 64bit cpus. fteqcc: add -TDP_20241108 to target dp's most recent additions. fteqcc: add -Fundefwordsize (autoenabled when targetting dp) to tell the compiler to not make any assumptions about runtime pointer types. this skips some optimisations, blocks sizeof(float), casts between pointer and string, and a few other sizing things, unsafe operations will become errors. fteqcc: add -FILP32 - changes 'long' datatype to int32_t, which should match common C assumptions around long/size_t/intptr_t fteqcc: add -Fpointerrelocs (autoenabled with a recent enough fte target). this finally allows pointer globals to be preinitialised with the addresses of other globals. fteqcc: add -Fomitinternals. this omits the reflection data in the progs roughly equivelent to visibility=hidden om linux. This WILL break saved games, and probably a few other things too, but will greatly reduce stringtable sizes. fteqcc: improve compat when compiling C code qclib: fix op_push --- CMakeLists.txt | 33 +- engine/client/pr_csqc.c | 11 +- engine/client/pr_menu.c | 1 + engine/common/net_wins.c | 8 +- engine/common/pr_bgcmd.c | 32 +- engine/common/pr_common.h | 1 + engine/qclib/Makefile | 6 +- engine/qclib/comprout.c | 1 + engine/qclib/decomp.c | 413 +++++--- engine/qclib/execloop.h | 5 +- engine/qclib/initlib.c | 51 + engine/qclib/pr_edict.c | 39 + engine/qclib/pr_exec.c | 19 +- engine/qclib/progsint.h | 7 +- engine/qclib/progslib.h | 1 + engine/qclib/progtype.h | 2 + engine/qclib/qcc.h | 46 +- engine/qclib/qcc_cmdlib.c | 2 +- engine/qclib/qcc_pr_comp.c | 620 +++++++---- engine/qclib/qcc_pr_lex.c | 2010 ++++++++++++++++++------------------ engine/qclib/qccmain.c | 70 +- engine/qclib/test.c | 298 +++++- engine/server/pr_cmds.c | 1 + engine/server/sv_main.c | 3 +- engine/web/fteshell.html | 1 + 25 files changed, 2256 insertions(+), 1425 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 599661848..c954cc5ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,6 +161,7 @@ IF(ZLIB_FOUND) SET(FTE_LIBS ${FTE_LIBS} ${ZLIB_LIBRARIES}) SET(FTESV_LIBS ${FTESV_LIBS} ${ZLIB_LIBRARIES}) SET(FTEQCC_LIBS ${FTEQCC_LIBS} ${ZLIB_LIBRARIES}) + SET(FTEQTV_LIBS ${FTEQTV_LIBS} ${ZLIB_LIBRARIES}) ELSE() MESSAGE(WARNING "libz library NOT available. compressed pk3, ICE, Q2E, etc etc, yada yada, blah blah will not be available.") SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_ZLIB) @@ -202,16 +203,18 @@ ELSE() SET(JPEG_LIBRARIES) ENDIF() -SET(FTE_DEP_DBUS true CACHE BOOL "Link against libdbus.") -IF(FTE_DEP_DBUS) - FIND_PACKAGE(DBus1) -ENDIF() -IF(DBUS1_FOUND) - INCLUDE_DIRECTORIES( ${DBus1_INCLUDE_DIRS} ) - SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};HAVE_DBUS) - SET(FTE_LIBS ${FTE_LIBS} ${DBus1_LIBRARIES}) -ELSE() - MESSAGE(WARNING "libdbus-1 library NOT available. Who cares?") +IF(NOT ${WIN32}) + SET(FTE_DEP_DBUS true CACHE BOOL "Link against libdbus.") + IF(FTE_DEP_DBUS) + FIND_PACKAGE(DBus1) + ENDIF() + IF(DBUS1_FOUND) + INCLUDE_DIRECTORIES( ${DBus1_INCLUDE_DIRS} ) + SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};HAVE_DBUS) + SET(FTE_LIBS ${FTE_LIBS} ${DBus1_LIBRARIES}) + ELSE() + MESSAGE(WARNING "libdbus-1 library NOT available. Who cares?") + ENDIF() ENDIF() SET(FTE_DEP_PNG true CACHE BOOL "Link against libpng.") @@ -1192,16 +1195,16 @@ ELSE() fteqtv/libqtvc/glibc_sucks.c engine/common/sha1.c ) - SET_TARGET_PROPERTIES(qtv PROPERTIES COMPILE_DEFINITIONS "${FTE_REVISON}") + SET_TARGET_PROPERTIES(qtv PROPERTIES COMPILE_DEFINITIONS "${FTE_REVISON};${FTE_LIB_DEFINES}") IF(WIN32) - TARGET_LINK_LIBRARIES(qtv ws2_32 winmm ${SYS_LIBS} ${ZLIB_LIBRARIES}) + TARGET_LINK_LIBRARIES(qtv ws2_32 winmm ${SYS_LIBS} ${FTEQTV_LIBS}) ELSEIF(LINUX) - TARGET_LINK_LIBRARIES(qtv ${SYS_LIBS} ${ZLIB_LIBRARIES}) + TARGET_LINK_LIBRARIES(qtv ${SYS_LIBS} ${FTEQTV_LIBS}) # Add Epoll-shim for the Unixes here - Brad ELSE() FIND_LIBRARY(epoll-shim REQUIRED) TARGET_INCLUDE_DIRECTORIES(qtv PUBLIC "${EPOLL_INC_DIR}") - TARGET_LINK_LIBRARIES(qtv epoll-shim ${SYS_LIBS} ${ZLIB_LIBRARIES}) + TARGET_LINK_LIBRARIES(qtv epoll-shim ${SYS_LIBS} ${FTEQTV_LIBS}) ENDIF() SET(INSTALLTARGS ${INSTALLTARGS} qtv) ENDIF() @@ -1480,6 +1483,7 @@ IF(FTE_PLUG_IRC) EMBED_PLUGIN_META(irc "IRC Plugin" "Allows you to chat on IRC without tabbing out.") ENDIF() +IF(ZLIB_FOUND) #mpq package format plugin (blizzard games) SET(FTE_PLUG_MPQ false CACHE BOOL "Compile mpq junk.") IF(FTE_PLUG_MPQ) @@ -1493,6 +1497,7 @@ IF(FTE_PLUG_MPQ) EMBED_PLUGIN_META(irc "MPQ Archive Plugin" "Adds support for reading .mpq files. Not very useful...") ENDIF() +ENDIF() #vpk package format plugin (halflife2) SET(FTE_PLUG_HL2 true CACHE BOOL "Compile support for hl2 file formats.") diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index d5cc155eb..674da2282 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -4839,7 +4839,13 @@ static void QCBUILTIN PF_getlightstylergb (pubprogfuncs_t *prinst, struct global return; } - if (stnum >= cl_max_lightstyles || !cl_lightstyle[stnum].length) + if (stnum >= cl_max_lightstyles) + { + value = ('m'-'a')*22 * r_lightstylescale.value; + G_VECTOR(OFS_RETURN)[0] = G_VECTOR(OFS_RETURN)[1] = G_VECTOR(OFS_RETURN)[2] = value*(1.0/256); + return; + } + else if (!cl_lightstyle[stnum].length) value = ('m'-'a')*22 * r_lightstylescale.value; else if (cl_lightstyle[stnum].map[0] == '=') value = atof(cl_lightstyle[stnum].map+1)*256*r_lightstylescale.value; @@ -4847,7 +4853,7 @@ static void QCBUILTIN PF_getlightstylergb (pubprogfuncs_t *prinst, struct global { int v1, v2, vd, i; float f; - + f = (cl.time*r_lightstylespeed.value); if (f < 0) f = 0; @@ -7120,6 +7126,7 @@ static struct { {"releasecustomskin", PF_cs_releasecustomskin, 379}, {"memalloc", PF_memalloc, 384}, + {"memrealloc", PF_memrealloc, 0}, {"memfree", PF_memfree, 385}, {"memcmp", PF_memcmp, 0}, {"memcpy", PF_memcpy, 386}, diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 72a9b5845..804f3b657 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -2662,6 +2662,7 @@ static struct { {"setcustomskin", PF_m_setcustomskin, 376}, //gap {"memalloc", PF_memalloc, 384}, + {"memrealloc", PF_memrealloc, 0}, {"memfree", PF_memfree, 385}, {"memcmp", PF_memcmp, 0}, {"memcpy", PF_memcpy, 386}, diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 15dcf01b6..501477225 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -223,10 +223,16 @@ static void NET_TLS_Provider_Changed(struct cvar_s *var, char *oldvalue) } if (host_initialized && !var->ival) { - Con_Printf("%s: \"%s\" not loaded, valid values are:", var->name, var->string); + int found = 0; for (i = 0; i < cryptolib_count; i++) if (cryptolib[i]) + { + if (!found++) + Con_Printf("%s: \"%s\" not loaded, valid values are:", var->name, var->string); Con_Printf(" ^[%s\\type\\%s %s^]", cryptolib[i]->drivername, var->name, cryptolib[i]->drivername); + } + if (!found) + Con_Printf("%s: no tls plugins loaded", var->name); Con_Printf("\n"); } diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 1b80e700d..2e4921733 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -2040,6 +2040,8 @@ void QCBUILTIN PF_memalloc (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob { int size = G_INT(OFS_PARM0); void *ptr; + if (!size) + size = 1; //return something free can free. if (size <= 0 || size > 0x01000000) ptr = NULL; //don't let them abuse things too much. values that are too large might overflow. else @@ -2050,7 +2052,29 @@ void QCBUILTIN PF_memalloc (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob G_INT(OFS_RETURN) = (char*)ptr - prinst->stringtable; } else + { G_INT(OFS_RETURN) = 0; + PR_BIError(prinst, "PF_memalloc: failure (size %i)\n", size); + } +} +void QCBUILTIN PF_memrealloc (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + void *oldcptr = prinst->stringtable + G_INT(OFS_PARM0); + int size = G_INT(OFS_PARM1); + void *ptr; + if (!size) + size = 1; //return something free can free. + if (size <= 0 || size > 0x01000000) + ptr = NULL; //don't let them abuse things too much. values that are too large might overflow. + else + ptr = prinst->AddressableRealloc(prinst, oldcptr, size); + if (ptr) + G_INT(OFS_RETURN) = (char*)ptr - prinst->stringtable; + else + { + G_INT(OFS_RETURN) = 0; + PR_BIError(prinst, "PF_memrealloc: failure (size %i)\n", size); + } } void QCBUILTIN PF_memfree (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -3219,17 +3243,17 @@ void QCBUILTIN PF_fseek (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals G_INT(OFS_RETURN) = -1; if (fnum < 0 || fnum >= MAX_QC_FILES) { - PF_Warningf(prinst, "PF_fread: File out of range\n"); + PF_Warningf(prinst, "PF_fseek: File out of range\n"); return; //out of range } if (!pf_fopen_files[fnum].prinst) { - PF_Warningf(prinst, "PF_fread: File is not open\n"); + PF_Warningf(prinst, "PF_fseek: File is not open\n"); return; //not open } if (pf_fopen_files[fnum].prinst != prinst) { - PF_Warningf(prinst, "PF_fread: File is from wrong instance\n"); + PF_Warningf(prinst, "PF_fseek: File is from wrong instance\n"); return; //this just isn't ours. } @@ -3414,7 +3438,7 @@ void QCBUILTIN PF_fexists (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa void QCBUILTIN PF_rmtree (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *fname = PR_GetStringOfs(prinst, OFS_PARM0); - Con_Printf("rmtree(\"%s\"): rmtree is not implemented at this\n", fname); + Con_Printf("rmtree(\"%s\"): rmtree is not implemented at this time\n", fname); /*flocation_t loc; G_FLOAT(OFS_RETURN) = -1; //error diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 68267a078..f007dff29 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -593,6 +593,7 @@ void QCBUILTIN PF_base64encode(pubprogfuncs_t *prinst, struct globalvars_s *pr_g void QCBUILTIN PF_base64decode(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_memalloc (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_memrealloc(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_memfree (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_memcmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_memcpy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); diff --git a/engine/qclib/Makefile b/engine/qclib/Makefile index 00afac7c1..600274151 100644 --- a/engine/qclib/Makefile +++ b/engine/qclib/Makefile @@ -102,10 +102,10 @@ qcvm.a: $(QCC_OBJS) $(VM_OBJS) $(COMMON_OBJS) test.o: test.c $(DO_CC) -testapp.bin: test.o qcvm.a - $(CC) $(BASE_CFLAGS) $(CFLAGS) -o testapp.bin -O3 $(BASE_LDFLAGS) $^ -lm -lz +qcvm: test.o qcvm.a + $(CC) $(BASE_CFLAGS) $(CFLAGS) -o qcvm -O3 $(BASE_LDFLAGS) $^ -lm -lz -ggdb -tests: testapp.bin +tests: qcvm @echo Running Tests... @$(foreach a,$(wildcard tests/*.src), echo TEST: $a; rm progs.dat; ./testapp.bin progs.dat -srcfile $a; echo; echo) @echo Tests run. diff --git a/engine/qclib/comprout.c b/engine/qclib/comprout.c index 0578da1a3..4194041c2 100644 --- a/engine/qclib/comprout.c +++ b/engine/qclib/comprout.c @@ -65,6 +65,7 @@ void *qccHunkAlloc(size_t mem) void qccClearHunk(void) { struct qcchunk_s *t; + QCC_PurgeTemps(); while (qcc_hunk) { t = qcc_hunk; diff --git a/engine/qclib/decomp.c b/engine/qclib/decomp.c index 85879138a..77c909cb5 100644 --- a/engine/qclib/decomp.c +++ b/engine/qclib/decomp.c @@ -20,6 +20,25 @@ //#undef printf //#define printf GUIprintf +//custom types, because we're lazy and lame with strings. +typedef struct +{ + unsigned int type; // if DEF_SAVEGLOBAL bit is set the variable needs to be saved in savegames + unsigned int ofs; + const char *s_name; +} QCD_def_t; +typedef struct +{ + int first_statement; // negative numbers are builtins + int parm_start; + int locals; // total ints of parms + locals + int profile; // runtime + const char *s_name; + const char *s_file; // source file defined in + int numparms; + pbyte parm_size[MAX_PARMS]; +} QCD_function_t; + #define OP_MARK_END_DO 0x00010000 //do{ #define OP_MARK_END_ELSE 0x00000400 //} @@ -28,21 +47,23 @@ #define statements destatements #define functions defunctions #define strings destrings +#define fields defields static dstatement_t *statements; static float *pr_globals; static char *strings; -static QCC_ddef32_t *globals; -static dfunction_t *functions; +static QCD_def_t *globals; +static QCD_def_t *fields; +static QCD_function_t *functions; static int ofs_return; static int ofs_parms[MAX_PARMS]; static int ofs_size = 3; -static QCC_ddef_t *globalofsdef[MAX_REGS]; +static QCD_def_t *globalofsdef[MAX_REGS]; //forward declarations. -QCC_ddef_t *GetField(const char *name); +QCD_def_t *GetField(const char *name); #include @@ -62,13 +83,19 @@ QCC_ddef_t *GetField(const char *name); return p; }*/ -const char *GetString(dstring_t str) +const char *GetString(unsigned int str) { if (str >= strofs) + { + char s[256]; + QC_snprintfz(s, sizeof(s), "INVALIDSTRING[%i]", str); + return strdup(s); return "INVALIDSTRING"; + } else return strings+str; } +const char *GetNameString(const char *str){return str;} extern QCC_opcode_t pr_opcodes []; @@ -93,20 +120,27 @@ static char *type_names[] = "ev_field", "void()", "ev_pointer", + "int", + "__uint", + "__int64", + "__uint64", + "__double", + "__variant", - "ev_struct", - "ev_union", - "ev_accessor", - "ev_quat", - "ev_uinteger" + "__struct", + "__union", + "__accessor", + "__enum", + "__typedef", + "__boolean", }; const char *typetoname(QCC_type_t *type) { return type->name; } -const char *temp_type (int temp, dstatement_t *start, dfunction_t *df) +const char *temp_type (int temp, dstatement_t *start, QCD_function_t *df) { int i; dstatement_t *stat; @@ -147,13 +181,13 @@ const char *temp_type (int temp, dstatement_t *start, dfunction_t *df) } } - printf("warning: Could not determine return type for %s\n", GetString(df->s_name)); + printf("warning: Could not determine return type for %s\n", GetNameString(df->s_name)); return "float"; } -pbool IsConstant(QCC_ddef_t *def) +pbool IsConstant(QCD_def_t *def) { int i; @@ -182,15 +216,15 @@ pbool IsConstant(QCC_ddef_t *def) return true; } -char *type_name (QCC_ddef_t *def) +char *type_name (QCD_def_t *def) { - QCC_ddef_t *j; + QCD_def_t *j; switch(def->type&~DEF_SAVEGLOBAL) { case ev_field: case ev_pointer: - j = GetField(GetString(def->s_name)); + j = GetField(GetNameString(def->s_name)); if (j) return qcva(".%s",type_names[j->type]); else @@ -278,7 +312,7 @@ static char *PR_ValueString (etype_t type, void *val) { static char line[8192]; - dfunction_t *f; + QCD_function_t *f; switch (type) { @@ -289,11 +323,13 @@ static char *PR_ValueString (etype_t type, void *val) QC_snprintfz(line, sizeof(line), "entity %i", *(int *)val); break; case ev_function: - f = functions + *(int *)val; - if (!f) + if (*(unsigned int *)val >= numfunctions) QC_snprintfz(line, sizeof(line), "undefined function"); else - QC_snprintfz(line, sizeof(line), "%s()", GetString(f->s_name)); + { + f = functions + *(unsigned int *)val; + QC_snprintfz(line, sizeof(line), "%s()", GetNameString(f->s_name)); + } break; /* case ev_field: @@ -532,10 +568,10 @@ static struct { }; char *DecompileValueString(etype_t type, void *val); -QCC_ddef_t *DecompileGetParameter(gofs_t ofs); -QCC_ddef_t *DecompileFindGlobal(const char *name); -char *DecompilePrintParameter(QCC_ddef_t * def); -QCC_ddef_t *DecompileFunctionGlobal(int funcnum); +QCD_def_t *DecompileGetParameter(gofs_t ofs); +QCD_def_t *DecompileFindGlobal(const char *name); +char *DecompilePrintParameter(QCD_def_t * def); +QCD_def_t *DecompileFunctionGlobal(int funcnum); char *ReadProgsCopyright(char *buf, size_t bufsize) { @@ -572,7 +608,7 @@ int DecompileReadData(const char *srcfilename, char *buf, size_t bufsize) int i, j; void *p; char name[1024]; - QCC_ddef_t *fd; + QCD_def_t *fd; int stsz = 16, defsz=16; // int quakeforge = false; @@ -678,7 +714,7 @@ int DecompileReadData(const char *srcfilename, char *buf, size_t bufsize) externs->Sys_Error("Unrecognised progs version"); numfunctions = progs.numfunctions; - functions = (dfunction_t*)(buf+progs.ofs_functions); +// functions = (dfunction_t*)(buf+progs.ofs_functions); DecompileProfiles = calloc(numfunctions, sizeof(*DecompileProfiles)); rettypes = calloc(numfunctions, sizeof(*rettypes)); @@ -686,23 +722,78 @@ int DecompileReadData(const char *srcfilename, char *buf, size_t bufsize) numfielddefs = progs.numfielddefs; if (defsz == 16) { - const QCC_ddef16_t *gd16 = (const QCC_ddef16_t*)(buf+progs.ofs_globaldefs); + const dfunction_t *funcin = (const dfunction_t*)(buf+progs.ofs_functions); + const ddef16_t *gd16 = (const ddef16_t*)(buf+progs.ofs_globaldefs); + const ddef16_t *fd16 = (const ddef16_t*)(buf+progs.ofs_fielddefs); globals = malloc(numglobaldefs * sizeof(*globals)); for (i = 0; i < numglobaldefs; i++) { globals[i].ofs = gd16[i].ofs; - globals[i].s_name = gd16[i].s_name; + globals[i].s_name = GetString(gd16[i].s_name); globals[i].type = gd16[i].type; } - - gd16 = (const QCC_ddef16_t*)(buf+progs.ofs_fielddefs); fields = malloc(numfielddefs * sizeof(*fields)); for (i = 0; i < numfielddefs; i++) { - fields[i].ofs = gd16[i].ofs; - fields[i].s_name = gd16[i].s_name; - fields[i].type = gd16[i].type; + fields[i].ofs = fd16[i].ofs; + fields[i].s_name = GetString(fd16[i].s_name); + fields[i].type = fd16[i].type; + } + + functions = malloc(numfunctions * sizeof(*functions)); + for (i = 0; i < numfunctions; i++) + { + functions[i].first_statement = funcin[i].first_statement; // negative numbers are builtins + functions[i].parm_start = funcin[i].parm_start; + functions[i].locals = funcin[i].locals; // total ints of parms + locals + + functions[i].profile = funcin[i].profile; // runtime + + functions[i].s_name = GetString(funcin[i].s_name); + functions[i].s_file = GetString(funcin[i].s_file); // source file defined in + + functions[i].numparms = funcin[i].numparms; + for (j = 0; j < MAX_PARMS; j++) + functions[i].parm_size[j] = funcin[i].parm_size[j]; + } + } + else if (defsz == 32) + { + const dfunction_t *funcin = (const dfunction_t*)(buf+progs.ofs_functions); + const ddef32_t *gdin = (const ddef32_t*)(buf+progs.ofs_globaldefs); + const ddef32_t *fdin = (const ddef32_t*)(buf+progs.ofs_fielddefs); + globals = malloc(numglobaldefs * sizeof(*globals)); + for (i = 0; i < numglobaldefs; i++) + { + globals[i].ofs = gdin[i].ofs; + globals[i].s_name = GetString(gdin[i].s_name); + globals[i].type = gdin[i].type; + } + + fields = malloc(numfielddefs * sizeof(*fields)); + for (i = 0; i < numfielddefs; i++) + { + fields[i].ofs = fdin[i].ofs; + fields[i].s_name = GetString(fdin[i].s_name); + fields[i].type = fdin[i].type; + } + + functions = malloc(numfunctions * sizeof(*functions)); + for (i = 0; i < numfunctions; i++) + { + functions[i].first_statement = funcin[i].first_statement; // negative numbers are builtins + functions[i].parm_start = funcin[i].parm_start; + functions[i].locals = funcin[i].locals; // total ints of parms + locals + + functions[i].profile = funcin[i].profile; // runtime + + functions[i].s_name = strings + funcin[i].s_name; + functions[i].s_file = strings + funcin[i].s_file; // source file defined in + + functions[i].numparms = funcin[i].numparms; + for (j = 0; j < MAX_PARMS; j++) + functions[i].parm_size[j] = funcin[i].parm_size[j]; } } else if (defsz == -32) @@ -713,7 +804,7 @@ int DecompileReadData(const char *srcfilename, char *buf, size_t bufsize) for (i = 0; i < numglobaldefs; i++) { globals[i].ofs = gdqt[i].ofs; - globals[i].s_name = gdqt[i].s_name; + globals[i].s_name = strings + gdqt[i].s_name; globals[i].type = gdqt[i].type; } @@ -723,7 +814,7 @@ int DecompileReadData(const char *srcfilename, char *buf, size_t bufsize) for (i = 0; i < numfielddefs; i++) { fields[i].ofs = gdqt[i].ofs; - fields[i].s_name = gdqt[i].s_name; + fields[i].s_name = strings + gdqt[i].s_name; fields[i].type = gdqt[i].type; } @@ -736,8 +827,8 @@ int DecompileReadData(const char *srcfilename, char *buf, size_t bufsize) functions[i].profile = funcin[i].profile; // runtime - functions[i].s_name = funcin[i].s_name; - functions[i].s_file = funcin[i].s_file; // source file defined in + functions[i].s_name = strings + funcin[i].s_name; + functions[i].s_file = strings + funcin[i].s_file; // source file defined in functions[i].numparms = funcin[i].numparms; for (j = 0; j < MAX_PARMS; j++) @@ -746,8 +837,8 @@ int DecompileReadData(const char *srcfilename, char *buf, size_t bufsize) } else { - globals = (QCC_ddef32_t*)(buf+progs.ofs_globaldefs); - fields = (QCC_ddef32_t*)(buf+progs.ofs_fielddefs); + printf("fatal error: unsupported def size\n"); + exit(1); } pr_globals = (float*)(buf+progs.ofs_globals); @@ -833,7 +924,7 @@ int DecompileReadData(const char *srcfilename, char *buf, size_t bufsize) // fix up the functions for (i = 1; i < numfunctions; i++) { - if ((unsigned)functions[i].s_name >= (unsigned)strofs || strlen(GetString(functions[i].s_name)) <= 0) + if ((unsigned)(functions[i].s_name-strings) >= (unsigned)strofs || strlen(functions[i].s_name) <= 0) { fd = DecompileFunctionGlobal(i); if (fd) @@ -845,7 +936,7 @@ int DecompileReadData(const char *srcfilename, char *buf, size_t bufsize) name[strlen(name)] = 0; p = malloc(strlen(name) + 1); strcpy(p, name); - functions[i].s_name = (char *)p - strings; + functions[i].s_name = p; } if (functions[i].first_statement > 0 && !functions[i].locals && functions[i].numparms) { //vanilla qcc apparently had a bug @@ -876,7 +967,7 @@ static void DecompileDetermineArrays(void) } } -static etype_t DecompileGetFieldTypeByDef(QCC_ddef_t *def) +static etype_t DecompileGetFieldTypeByDef(QCD_def_t *def) { int i; int ofs = ((int*)pr_globals)[def->ofs]; @@ -884,7 +975,7 @@ static etype_t DecompileGetFieldTypeByDef(QCC_ddef_t *def) for (i = 1; i < numfielddefs; i++) if (fields[i].ofs == ofs) { - if (!strcmp(GetString(def->s_name), GetString(fields[i].s_name))) + if (!strcmp(GetNameString(def->s_name), GetNameString(fields[i].s_name))) return fields[i].type; } return ev_void; @@ -896,7 +987,7 @@ static const char *DecompileGetFieldNameIdxByFinalOffset(int ofs) for (i = 1; i < numfielddefs; i++) if (fields[i].ofs == ofs) { - return GetString(fields[i].s_name); + return GetNameString(fields[i].s_name); } return "UNKNOWN FIELD"; } @@ -908,17 +999,17 @@ void DecompileGetFieldNameIdxByFinalOffset2(char *out, size_t outsize, int ofs) { if (fields[i].ofs == ofs) { - QC_snprintfz(out, outsize, "%s", GetString(fields[i].s_name)); + QC_snprintfz(out, outsize, "%s", GetNameString(fields[i].s_name)); return; } else if (fields[i].type == ev_vector && fields[i].ofs+1 == ofs) { - QC_snprintfz(out, outsize, "%s_y", GetString(fields[i].s_name)); + QC_snprintfz(out, outsize, "%s_y", GetNameString(fields[i].s_name)); return; } else if (fields[i].type == ev_vector && fields[i].ofs+2 == ofs) { - QC_snprintfz(out, outsize, "%s_z", GetString(fields[i].s_name)); + QC_snprintfz(out, outsize, "%s_z", GetNameString(fields[i].s_name)); return; } } @@ -949,11 +1040,11 @@ DecompileAlreadySeen(char *fname, vfile_t **rfile) return ret; } -char *DecompileReturnType(dfunction_t *df); +char *DecompileReturnType(QCD_function_t *df); -char *DecompileAgressiveType(dfunction_t *df, dstatement_t *last, gofs_t ofs) +char *DecompileAgressiveType(QCD_function_t *df, dstatement_t *last, gofs_t ofs) { - QCC_ddef_t *par; + QCD_def_t *par; par = DecompileGetParameter(ofs); if (par) //single = intended { @@ -983,7 +1074,7 @@ char *DecompileAgressiveType(dfunction_t *df, dstatement_t *last, gofs_t ofs) return NULL; //got to start of function... shouldn't really happen. } -static unsigned int DecompileBuiltin(dfunction_t *df) +static unsigned int DecompileBuiltin(QCD_function_t *df) { unsigned int bi, i; if (df->first_statement > 0) @@ -991,9 +1082,9 @@ static unsigned int DecompileBuiltin(dfunction_t *df) bi = -df->first_statement; //okay, so this is kinda screwy, different mods have different sets of builtins, and a load of fte's are #0 too //so just try to match by name first... lots of scanning. :( - if (df->s_name>0 && df->s_name < strofs) + if (*df->s_name) { - const char *biname = GetString(df->s_name); + const char *biname = GetNameString(df->s_name); for (i = 0; i < (sizeof(builtins)/sizeof(builtins[0])); i++) { if (!builtins[i].name) @@ -1010,7 +1101,7 @@ static unsigned int DecompileBuiltin(dfunction_t *df) return bi; } -char *DecompileReturnType(dfunction_t *df) +char *DecompileReturnType(QCD_function_t *df) { dstatement_t *ds; unsigned short dom; @@ -1101,8 +1192,8 @@ void DecompileCalcProfiles(void) char *knew; static char fname[512]; static char line[512]; - dfunction_t *df; - QCC_ddef_t *par; + QCD_function_t *df; + QCD_def_t *par; for (i = 1; i < numfunctions; i++) { @@ -1116,11 +1207,11 @@ void DecompileCalcProfiles(void) { unsigned int bi = DecompileBuiltin(df); if (bi && builtins[bi].text) - QC_snprintfz(fname, sizeof(fname), "%s %s", builtins[bi].text, GetString(functions[i].s_name)); + QC_snprintfz(fname, sizeof(fname), "%s %s", builtins[bi].text, GetNameString(functions[i].s_name)); else { - QC_snprintfz(fname, sizeof(fname), "__variant(...) %s", GetString(functions[i].s_name)); - printf("warning: unknown builtin %s\n", GetString(functions[i].s_name)); + QC_snprintfz(fname, sizeof(fname), "__variant(...) %s", GetNameString(functions[i].s_name)); + printf("warning: unknown builtin %s\n", GetNameString(functions[i].s_name)); } } else @@ -1194,7 +1285,7 @@ void DecompileCalcProfiles(void) } strcat(fname, ") "); line[0] = '\0'; - QC_snprintfz(line, sizeof(line), "%s", GetString(functions[i].s_name)); + QC_snprintfz(line, sizeof(line), "%s", GetNameString(functions[i].s_name)); strcat(fname, line); } @@ -1206,9 +1297,9 @@ void DecompileCalcProfiles(void) } -QCC_ddef_t *GlobalAtOffset(dfunction_t *df, gofs_t ofs) +QCD_def_t *GlobalAtOffset(QCD_function_t *df, gofs_t ofs) { - QCC_ddef_t *def; + QCD_def_t *def; int i, j; def = globalofsdef[ofs]; @@ -1239,7 +1330,7 @@ QCC_ddef_t *GlobalAtOffset(dfunction_t *df, gofs_t ofs) if (ofs >= df->parm_start && ofs < df->parm_start + df->locals) { - static QCC_ddef_t parm[8]; + static QCD_def_t parm[8]; static char *parmnames[] = {"par0","par1","par2","par3","par4","par5","par6","par7"}; int parmofs = ofs - df->parm_start; for (i = 0; i < df->numparms && i < 8; i++) @@ -1247,7 +1338,7 @@ QCC_ddef_t *GlobalAtOffset(dfunction_t *df, gofs_t ofs) if (parmofs < df->parm_size[i]) { parm[i].ofs = ofs - parmofs; - parm[i].s_name = parmnames[i]-strings; + parm[i].s_name = parmnames[i]; parm[i].type = ev_void; ofs = parm[i].ofs; @@ -1257,11 +1348,11 @@ QCC_ddef_t *GlobalAtOffset(dfunction_t *df, gofs_t ofs) if (def->ofs == ofs) { char line[256], *buf; - sprintf(line, "%s_%c", GetString(def->s_name), 'x'+parmofs); //globals, which are defined after the locals of the function they are first used in... + sprintf(line, "%s_%c", GetNameString(def->s_name), 'x'+parmofs); //globals, which are defined after the locals of the function they are first used in... def = malloc(sizeof(*def)+strlen(line)+1); //must be static variables, but we can't handle them very well buf = (char*)(def+1); strcpy(buf, line); - def->s_name = buf - strings; + def->s_name = buf; def->type = ev_float; return def; } @@ -1285,10 +1376,10 @@ QCC_ddef_t *GlobalAtOffset(dfunction_t *df, gofs_t ofs) return NULL; } -char *DecompileGlobal(dfunction_t *df, gofs_t ofs, QCC_type_t * req_t) +char *DecompileGlobal(QCD_function_t *df, gofs_t ofs, QCC_type_t * req_t) { int i; - QCC_ddef_t *def; + QCD_def_t *def; static char line[8192]; char *res; @@ -1306,7 +1397,7 @@ char *DecompileGlobal(dfunction_t *df, gofs_t ofs, QCC_type_t * req_t) if (def) { - const char *defname = GetString(def->s_name); + const char *defname = GetNameString(def->s_name); if (!strcmp(defname, "IMMEDIATE") || !strcmp(defname, ".imm") || !strcmp(defname, "I+") || !def->s_name) { @@ -1327,33 +1418,33 @@ char *DecompileGlobal(dfunction_t *df, gofs_t ofs, QCC_type_t * req_t) { char line[16]; char *buf; - QCC_ddef_t *parent; + QCD_def_t *parent; if (ofs >= df->parm_start && ofs < df->parm_start + df->locals) goto lookslikealocal; else if ((parent = GlobalAtOffset(df, ofs-1)) && parent->type == ev_vector) { // _y - QC_snprintfz(line, sizeof(line), "%s_y", GetString(parent->s_name)); //globals, which are defined after the locals of the function they are first used in... + QC_snprintfz(line, sizeof(line), "%s_y", GetNameString(parent->s_name)); //globals, which are defined after the locals of the function they are first used in... buf = malloc(strlen(line)+1); //must be static variables, but we can't handle them very well strcpy(buf, line); - def->s_name = buf - strings; + def->s_name = buf; } else if ((parent = GlobalAtOffset(df, ofs-2)) && parent->type == ev_vector) { // _z - QC_snprintfz(line, sizeof(line), "%s_z", GetString(parent->s_name)); //globals, which are defined after the locals of the function they are first used in... + QC_snprintfz(line, sizeof(line), "%s_z", GetNameString(parent->s_name)); //globals, which are defined after the locals of the function they are first used in... buf = malloc(strlen(line)+1); //must be static variables, but we can't handle them very well strcpy(buf, line); - def->s_name = buf - strings; + def->s_name = buf; } else { QC_snprintfz(line, sizeof(line), "_sloc_%i", def->ofs); //globals, which are defined after the locals of the function they are first used in... buf = malloc(strlen(line)+1); //must be static variables, but we can't handle them very well strcpy(buf, line); - def->s_name = buf - strings; + def->s_name = buf; } } - QC_snprintfz(line, sizeof(line), "%s", GetString(def->s_name)); + QC_snprintfz(line, sizeof(line), "%s", GetNameString(def->s_name)); if (def->type == ev_field && req_t == type_field && req_t->aux_type == type_float && DecompileGetFieldTypeByDef(def) == ev_vector) strcat(line, "_x"); else if (def->type == ev_vector && req_t == type_float) @@ -1411,7 +1502,7 @@ static struct char *text; QCC_type_t *type; } IMMEDIATES[MAX_REGS]; -gofs_t DecompileScaleIndex(dfunction_t *df, gofs_t ofs) +gofs_t DecompileScaleIndex(QCD_function_t *df, gofs_t ofs) { gofs_t nofs = 0; @@ -1442,9 +1533,9 @@ void DecompileImmediate_Free(void) } } } -void DecompileImmediate_Insert(dfunction_t *df, gofs_t ofs, char *knew, QCC_type_t *type) +void DecompileImmediate_Insert(QCD_function_t *df, gofs_t ofs, char *knew, QCC_type_t *type) { - QCC_ddef_t *d; + QCD_def_t *d; int nofs; nofs = DecompileScaleIndex(df, ofs); @@ -1466,7 +1557,7 @@ void DecompileImmediate_Insert(dfunction_t *df, gofs_t ofs, char *knew, QCC_type IMMEDIATES[nofs].text = NULL; IMMEDIATES[nofs].type = NULL; - QCC_CatVFile(Decompileofile, "%s = %s;\n", GetString(d->s_name), knew); + QCC_CatVFile(Decompileofile, "%s = %s;\n", GetNameString(d->s_name), knew); } else { @@ -1494,7 +1585,7 @@ void FloatToString(char *out, size_t outsize, float f) } } -char *DecompileImmediate_Get(dfunction_t *df, gofs_t ofs, QCC_type_t *req_t) +char *DecompileImmediate_Get(QCD_function_t *df, gofs_t ofs, QCC_type_t *req_t) { char *res; @@ -1554,7 +1645,7 @@ char *DecompileImmediate_Get(dfunction_t *df, gofs_t ofs, QCC_type_t *req_t) char *out; if (((int*)pr_globals)[ofs] < 0 || ((int*)pr_globals)[ofs] > strofs) { - printf("Hey! That's not a string! error in %s\n", GetString(df->s_name)); + printf("Hey! That's not a string! error in %s\n", GetNameString(df->s_name)); QC_snprintfz(temp, sizeof(temp), "%f", pr_globals[ofs]); break; } @@ -1655,7 +1746,7 @@ char *DecompileImmediate_Get(dfunction_t *df, gofs_t ofs, QCC_type_t *req_t) if (!((int*)pr_globals)[ofs]) QC_snprintfz(temp, sizeof(temp), "__NULL__/*func*/"); else if (((int*)pr_globals)[ofs] > 0 && ((int*)pr_globals)[ofs] < numfunctions && functions[((int*)pr_globals)[ofs]].s_name>0) - QC_snprintfz(temp, sizeof(temp), "%s/*immediate*/", GetString(functions[((int*)pr_globals)[ofs]].s_name)); + QC_snprintfz(temp, sizeof(temp), "%s/*immediate*/", GetNameString(functions[((int*)pr_globals)[ofs]].s_name)); else QC_snprintfz(temp, sizeof(temp), "((__variant(...))%i)", ((int*)pr_globals)[ofs]); break; @@ -1685,7 +1776,7 @@ char *DecompileImmediate_Get(dfunction_t *df, gofs_t ofs, QCC_type_t *req_t) return NULL; } -char *DecompileGet(dfunction_t *df, gofs_t ofs, QCC_type_t *req_t) +char *DecompileGet(QCD_function_t *df, gofs_t ofs, QCC_type_t *req_t) { char *farg1; /*if (req_t == &def_short) @@ -1719,7 +1810,7 @@ void DecompileIndent(int c) } } -void DecompileOpcode(dfunction_t *df, int a, int b, int c, char *opcode, QCC_type_t *typ1, QCC_type_t *typ2, QCC_type_t *typ3, int usebrackets, int *indent) +void DecompileOpcode(QCD_function_t *df, int a, int b, int c, char *opcode, QCC_type_t *typ1, QCC_type_t *typ2, QCC_type_t *typ3, int usebrackets, int *indent) { static char line[8192]; char *arg1, *arg2, *arg3; @@ -1746,7 +1837,7 @@ void DecompileOpcode(dfunction_t *df, int a, int b, int c, char *opcode, QCC_typ } static dstatement_t *jumptable; -void DecompileDecompileStatement(dfunction_t * df, dstatement_t * s, int *indent) +void DecompileDecompileStatement(QCD_function_t * df, dstatement_t * s, int *indent) { static char line[8192]; static char fnam[512]; @@ -1755,7 +1846,7 @@ void DecompileDecompileStatement(dfunction_t * df, dstatement_t * s, int *indent dstatement_t *t; unsigned int dom, doc, ifc, tom; QCC_type_t *typ1, *typ2, *typ3; - QCC_ddef_t *par; + QCD_def_t *par; dstatement_t *k; int dum; @@ -1904,7 +1995,7 @@ void DecompileDecompileStatement(dfunction_t * df, dstatement_t * s, int *indent { if (s->op == OP_ADDRESS) { - QCC_ddef_t *def = GlobalAtOffset(df, s->b); + QCD_def_t *def = GlobalAtOffset(df, s->b); if (def && DecompileGetFieldTypeByDef(def) == ev_vector) typ3 = type_vector; } @@ -1944,10 +2035,10 @@ void DecompileDecompileStatement(dfunction_t * df, dstatement_t * s, int *indent { //well, this is it. int fn = ((int*)pr_globals)[k->a]; - dfunction_t *cf = &functions[fn]; + QCD_function_t *cf = &functions[fn]; int bi = DecompileBuiltin(cf); int pn; - QCC_ddef_t *def; + QCD_def_t *def; if (bi) { //builtins don't have valid parm_start values QCC_type_t **p = builtins[bi].params[(s->b-ofs_parms[0])/ofs_size]; @@ -2358,7 +2449,7 @@ void DecompileDecompileStatement(dfunction_t * df, dstatement_t * s, int *indent else if (s->op == OPD_GOTO_FORSTART) { DecompileIndent(*indent); - QCC_CatVFile(Decompileofile, "do_tail\n", (s-statements) + (signed int)s->a); + QCC_CatVFile(Decompileofile, "do_tail\n"); DecompileIndent(*indent); QCC_CatVFile(Decompileofile, "{\n"); (*indent)++; @@ -2432,7 +2523,7 @@ void DecompileDecompileStatement(dfunction_t * df, dstatement_t * s, int *indent if (s->c) QCC_CatVFile(Decompileofile, ", %s", DecompileGet(df, s->c, typ1)); QCC_CatVFile(Decompileofile, "]\n"); - printf("warning: Unknown opcode %i in %s\n", op, GetString(df->s_name)); + printf("warning: Unknown opcode %i in %s\n", op, GetNameString(df->s_name)); } } @@ -2451,7 +2542,7 @@ void DecompileDecompileStatement(dfunction_t * df, dstatement_t * s, int *indent return; } -pbool DecompileDecompileFunction(dfunction_t * df, dstatement_t *altdone) +pbool DecompileDecompileFunction(QCD_function_t * df, dstatement_t *altdone) { dstatement_t *ds; int indent; @@ -2484,7 +2575,7 @@ pbool DecompileDecompileFunction(dfunction_t * df, dstatement_t *altdone) if (indent != 1) { - printf("warning: Indentation structure corrupt (in func %s)\n", GetString(df->s_name)); + printf("warning: Indentation structure corrupt (in func %s)\n", GetNameString(df->s_name)); return false; } return true; @@ -2585,7 +2676,7 @@ char *DecompileValueString(etype_t type, void *val) break; case ev_function: if (*(int *)val>0 && *(int *)vals_name]) //null string... + if (!*def->s_name) //null string... { QC_snprintfz(line, sizeof(line), "%s _p_%i%s", type_name(def), def->ofs, debug); } - else if (!strcmp(GetString(def->s_name), "IMMEDIATE") || !strcmp(GetString(def->s_name), ".imm") || !strcmp(GetString(def->s_name), "I+")) + else if (!strcmp(GetNameString(def->s_name), "IMMEDIATE") || !strcmp(GetNameString(def->s_name), ".imm") || !strcmp(GetNameString(def->s_name), "I+")) { QC_snprintfz(line, sizeof(line), "%s%s", DecompileValueString((etype_t)(def->type), &pr_globals[def->ofs]), debug); } else { - QC_snprintfz(line, sizeof(line), "%s %s%s", type_name(def), GetString(def->s_name), debug); + QC_snprintfz(line, sizeof(line), "%s %s%s", type_name(def), GetNameString(def->s_name), debug); } return line; } //we only work with prior fields. -const char *GetMatchingField(QCC_ddef_t *field) +const char *GetMatchingField(QCD_def_t *field) { int i; - QCC_ddef_t *def; + QCD_def_t *def; int ld, lf; const char *ret = NULL; @@ -2644,18 +2735,18 @@ const char *GetMatchingField(QCC_ddef_t *field) { if (((int*)pr_globals)[def->ofs] == field->ofs) { - if (!strcmp(GetString(def->s_name), GetString(field->s_name))) + if (!strcmp(GetNameString(def->s_name), GetNameString(field->s_name))) return NULL; //found ourself, give up. - lf = strlen(GetString(field->s_name)); - ld = strlen(GetString(def->s_name)); + lf = strlen(GetNameString(field->s_name)); + ld = strlen(GetNameString(def->s_name)); if (lf - 2 == ld) { - if ((GetString(field->s_name)[lf-2]) == '_' && (GetString(field->s_name)[lf-1]) == 'x') - if (!strncmp(GetString(field->s_name), GetString(def->s_name), ld)) + if ((GetNameString(field->s_name)[lf-2]) == '_' && (GetNameString(field->s_name)[lf-1]) == 'x') + if (!strncmp(GetNameString(field->s_name), GetNameString(def->s_name), ld)) return NULL; //vector found foo_x } if (!ret) - ret = GetString(def->s_name); + ret = GetNameString(def->s_name); } } } @@ -2663,10 +2754,10 @@ const char *GetMatchingField(QCC_ddef_t *field) return ret; } -QCC_ddef_t *GetField(const char *name) +QCD_def_t *GetField(const char *name) { int i; - QCC_ddef_t *d; + QCD_def_t *d; if (!*name) return NULL; @@ -2677,15 +2768,15 @@ QCC_ddef_t *GetField(const char *name) { d = &fields[i]; - if (!strcmp(GetString(d->s_name), name)) + if (!strcmp(GetNameString(d->s_name), name)) return d; } return NULL; } -QCC_ddef_t *DecompileGetParameter(gofs_t ofs) +QCD_def_t *DecompileGetParameter(gofs_t ofs) { int i; - QCC_ddef_t *def; + QCD_def_t *def; def = NULL; @@ -2701,10 +2792,10 @@ QCC_ddef_t *DecompileGetParameter(gofs_t ofs) return NULL; } -QCC_ddef_t *DecompileFindGlobal(const char *findname) +QCD_def_t *DecompileFindGlobal(const char *findname) { int i; - QCC_ddef_t *def; + QCD_def_t *def; const char *defname; def = NULL; @@ -2712,7 +2803,7 @@ QCC_ddef_t *DecompileFindGlobal(const char *findname) for (i = 0; i < numglobaldefs; i++) { def = &globals[i]; - defname = GetString(def->s_name); + defname = GetNameString(def->s_name); if (!strcmp(findname, defname)) { @@ -2723,10 +2814,10 @@ QCC_ddef_t *DecompileFindGlobal(const char *findname) return NULL; } -QCC_ddef_t *DecompileFunctionGlobal(int funcnum) +QCD_def_t *DecompileFunctionGlobal(int funcnum) { int i; - QCC_ddef_t *def; + QCD_def_t *def; def = NULL; @@ -2748,9 +2839,9 @@ QCC_ddef_t *DecompileFunctionGlobal(int funcnum) void DecompilePreceedingGlobals(int start, int end, const char *name) { - QCC_ddef_t *par; + QCD_def_t *par; int j; - QCC_ddef_t *ef; + QCD_def_t *ef; static char line[8192]; char asize[64]; int arraysize; @@ -2778,19 +2869,19 @@ void DecompilePreceedingGlobals(int start, int end, const char *name) if (par->type == ev_function) { - if (strcmp(GetString(par->s_name), "IMMEDIATE") && strcmp(GetString(par->s_name), ".imm") && strcmp(GetString(par->s_name), "I+")) + if (strcmp(GetNameString(par->s_name), "IMMEDIATE") && strcmp(GetNameString(par->s_name), ".imm") && strcmp(GetNameString(par->s_name), "I+")) { - if (strcmp(GetString(par->s_name), name)) + if (strcmp(GetNameString(par->s_name), name)) { int f = ((int*)pr_globals)[par->ofs]; //DecompileGetFunctionIdxByName(strings + par->s_name); - if (f && strcmp(GetString(functions[f].s_name), GetString(par->s_name))) + if (f && strcmp(GetNameString(functions[f].s_name), GetNameString(par->s_name))) { char *s = strrchr(DecompileProfiles[f], ' '); //happens with void() func = otherfunc; //such functions thus don't have their own type+body *s = 0; - QCC_CatVFile(Decompileofile, "var %s %s%s = %s;\n", DecompileProfiles[f], GetString(par->s_name), asize, s+1); + QCC_CatVFile(Decompileofile, "var %s %s%s = %s;\n", DecompileProfiles[f], GetNameString(par->s_name), asize, s+1); *s = ' '; } else @@ -2800,18 +2891,18 @@ void DecompilePreceedingGlobals(int start, int end, const char *name) } else if (par->type != ev_pointer) { - if (strcmp(GetString(par->s_name), "IMMEDIATE") && strcmp(GetString(par->s_name), ".imm") && strcmp(GetString(par->s_name), "I+") && par->s_name) + if (strcmp(GetNameString(par->s_name), "IMMEDIATE") && strcmp(GetNameString(par->s_name), ".imm") && strcmp(GetNameString(par->s_name), "I+") && par->s_name) { if (par->type == ev_field) { - ef = GetField(GetString(par->s_name)); + ef = GetField(GetNameString(par->s_name)); if (!ef) { - QCC_CatVFile(Decompileofile, "var .unknowntype %s%s;\n", GetString(par->s_name), asize); - printf("Fatal Error: Could not locate a field named \"%s\"\n", GetString(par->s_name)); + QCC_CatVFile(Decompileofile, "var .unknowntype %s%s;\n", GetNameString(par->s_name), asize); + printf("Fatal Error: Could not locate a field named \"%s\"\n", GetNameString(par->s_name)); } else { @@ -2821,7 +2912,7 @@ void DecompilePreceedingGlobals(int start, int end, const char *name) matchingfield = GetMatchingField(ef); #ifndef DONT_USE_DIRTY_TRICKS //could try scanning for an op_address+op_storep_fnc pair - if ((ef->type == ev_function) && !strcmp(GetString(ef->s_name), "th_pain")) + if ((ef->type == ev_function) && !strcmp(GetNameString(ef->s_name), "th_pain")) { QCC_CatVFile(Decompileofile, ".void(entity attacker, float damage) th_pain;\n"); } @@ -2829,9 +2920,9 @@ void DecompilePreceedingGlobals(int start, int end, const char *name) #endif { if (matchingfield) - QCC_CatVFile(Decompileofile, "var .%s %s%s = %s;\n", type_name(ef), GetString(ef->s_name), asize, matchingfield); + QCC_CatVFile(Decompileofile, "var .%s %s%s = %s;\n", type_name(ef), GetNameString(ef->s_name), asize, matchingfield); else - QCC_CatVFile(Decompileofile, ".%s %s%s;\n", type_name(ef), GetString(ef->s_name), asize); + QCC_CatVFile(Decompileofile, ".%s %s%s;\n", type_name(ef), GetNameString(ef->s_name), asize); // fprintf(Decompileofile, "//%i %i %i %i\n", ef->ofs, ((int*)pr_globals)[ef->ofs], par->ofs, ((int*)pr_globals)[par->ofs]); } @@ -2846,7 +2937,7 @@ void DecompilePreceedingGlobals(int start, int end, const char *name) if (par->type == ev_entity || par->type == ev_void) { - QCC_CatVFile(Decompileofile, "%s %s%s;\n", type_name(par), GetString(par->s_name), asize); + QCC_CatVFile(Decompileofile, "%s %s%s;\n", type_name(par), GetNameString(par->s_name), asize); } else @@ -2859,7 +2950,7 @@ void DecompilePreceedingGlobals(int start, int end, const char *name) if (*asize) { int k; - QCC_CatVFile(Decompileofile, "%s %s%s = {", type_name(par), GetString(par->s_name), asize); + QCC_CatVFile(Decompileofile, "%s %s%s = {", type_name(par), GetNameString(par->s_name), asize); for (k = 0; k < arraysize; k++) QCC_CatVFile(Decompileofile, "%s%s", DecompileValueString((etype_t)(par->type), &pr_globals[par->ofs+type_size[par->type]*k]), (k+1)==arraysize?"":", "); QCC_CatVFile(Decompileofile, "};\n"); @@ -2867,7 +2958,7 @@ void DecompilePreceedingGlobals(int start, int end, const char *name) else { QC_snprintfz(line, sizeof(line), "%s", DecompileValueString((etype_t)(par->type), &pr_globals[par->ofs])); - QCC_CatVFile(Decompileofile, "%s %s%s = %s;\n", type_name(par), GetString(par->s_name), asize, line); + QCC_CatVFile(Decompileofile, "%s %s%s = %s;\n", type_name(par), GetNameString(par->s_name), asize, line); } } else @@ -2879,10 +2970,10 @@ void DecompilePreceedingGlobals(int start, int end, const char *name) if (k != type_size[par->type]) { QC_snprintfz(line, sizeof(line), "%s", DecompileValueString((etype_t)(par->type), &pr_globals[par->ofs])); - QCC_CatVFile(Decompileofile, "%s %s%s /* = %s */;\n", type_name(par), GetString(par->s_name), asize, line); + QCC_CatVFile(Decompileofile, "%s %s%s /* = %s */;\n", type_name(par), GetNameString(par->s_name), asize, line); } else - QCC_CatVFile(Decompileofile, "%s %s%s;\n", type_name(par), GetString(par->s_name), asize); + QCC_CatVFile(Decompileofile, "%s %s%s;\n", type_name(par), GetNameString(par->s_name), asize); } } } @@ -2895,8 +2986,8 @@ void DecompileFunction(const char *name, int *lastglobal) { int i, findex, ps; dstatement_t *ds, *ts, *altdone; - dfunction_t *df; - QCC_ddef_t *par; + QCD_function_t *df; + QCD_def_t *par; char *arg2; unsigned short dom, tom; int j, start, end; @@ -2908,7 +2999,7 @@ void DecompileFunction(const char *name, int *lastglobal) for (i = 1; i < numfunctions; i++) - if (!strcmp(name, GetString(functions[i].s_name))) + if (!strcmp(name, GetNameString(functions[i].s_name))) break; if (i == numfunctions) { @@ -3113,10 +3204,10 @@ void DecompileFunction(const char *name, int *lastglobal) par = DecompileGetParameter(ds->a); if (!par) { - static QCC_ddef_t pars; + static QCD_def_t pars; //must be a global (gotta be a float), create the def as needed pars.ofs = ds->a; - pars.s_name = "IMMEDIATE"-strings; + pars.s_name = "IMMEDIATE"; pars.type = ev_float; par = &pars; // printf("Fatal Error - Can't determine frame number."); @@ -3177,12 +3268,12 @@ void DecompileFunction(const char *name, int *lastglobal) if (par) { - if (!strings[par->s_name]) + if (!*par->s_name) { QC_snprintfz(line, sizeof(line), "_p_%i", par->ofs); arg2 = malloc(strlen(line)+1); strcpy(arg2, line); - par->s_name = arg2 - strings; + par->s_name = arg2; } } @@ -3211,15 +3302,15 @@ void DecompileFunction(const char *name, int *lastglobal) } else { - if (!strcmp(GetString(par->s_name), "IMMEDIATE") || !strcmp(GetString(par->s_name), ".imm") || !strcmp(GetString(par->s_name), "I+")) + if (!strcmp(GetNameString(par->s_name), "IMMEDIATE") || !strcmp(GetNameString(par->s_name), ".imm") || !strcmp(GetNameString(par->s_name), "I+")) continue; // immediates don't belong - if (!GetString(par->s_name)) + if (!GetNameString(par->s_name)) { QC_snprintfz(line, sizeof(line), "_l_%i", par->ofs); arg2 = malloc(strlen(line)+1); strcpy(arg2, line); - par->s_name = arg2 - strings; + par->s_name = arg2; } if (par->type == ev_function) @@ -3248,7 +3339,7 @@ void DecompileFunction(const char *name, int *lastglobal) if (!DecompileDecompileFunction(df, altdone)) { - QCC_InsertVFile(Decompileofile, startpos, "#error Corrupt Function: %s\n#if 0\n", GetString(df->s_name)); + QCC_InsertVFile(Decompileofile, startpos, "#error Corrupt Function: %s\n#if 0\n", GetNameString(df->s_name)); QCC_CatVFile(Decompileofile, "#endif\n"); } @@ -3280,13 +3371,13 @@ void DecompileDecompileFunctions(const char *origcopyright) { int i; unsigned int o; - dfunction_t *d; + QCD_function_t *d; pbool bogusname; vfile_t *f = NULL; char fname[1024]; int lastglob = 1; - int lastfileofs = 0; - QCC_ddef_t *def; + const char *lastfileofs = NULL; + QCD_def_t *def; DecompileCalcProfiles(); @@ -3303,7 +3394,7 @@ void DecompileDecompileFunctions(const char *origcopyright) QCC_CatVFile(Decompileprogssrc, "#pragma flag enable lax //remove this line once you've fixed up any decompiler bugs...\n"); if (origcopyright) QCC_CatVFile(Decompileprogssrc, "//#pragma copyright \"%s\"\n", origcopyright); - QCC_CatVFile(Decompileprogssrc, "\n", origcopyright); + QCC_CatVFile(Decompileprogssrc, "\n"); def = DecompileFindGlobal("end_sys_fields"); lastglob = def?def->ofs+1:1; @@ -3335,8 +3426,8 @@ void DecompileDecompileFunctions(const char *origcopyright) { lastfileofs = d->s_file; fname[0] = '\0'; - if (d->s_file <= strofs && d->s_file >= 0) - sprintf(fname, "%s", GetString(d->s_file)); + //if (d->s_file <= strofs && d->s_file >= 0) + sprintf(fname, "%s", GetNameString(d->s_file)); // FrikaC -- not sure if this is cool or what? bogusname = false; if (strlen(fname) <= 0) @@ -3366,7 +3457,7 @@ void DecompileDecompileFunctions(const char *origcopyright) { synth_name[0] = 0; } - if(!TrySynthName(qcva("%s", GetString(d->s_name))) && !synth_name[0]) + if(!TrySynthName(qcva("%s", GetNameString(d->s_name))) && !synth_name[0]) QC_snprintfz(synth_name, sizeof(synth_name), "frik%i.qc", fake_name++); QC_snprintfz(fname, sizeof(fname), "%s", synth_name); @@ -3389,7 +3480,7 @@ void DecompileDecompileFunctions(const char *origcopyright) } } Decompileofile = f; - DecompileFunction(GetString(d->s_name), &lastglob); + DecompileFunction(GetNameString(d->s_name), &lastglob); } } @@ -3436,7 +3527,7 @@ void DecompileProgsDat(const char *name, void *buf, size_t bufsize) char *DecompileGlobalStringNoContents(gofs_t ofs) { int i; - QCC_ddef_t *def; + QCD_def_t *def; static char line[128]; line[0] = '0'; @@ -3449,7 +3540,7 @@ char *DecompileGlobalStringNoContents(gofs_t ofs) if (def->ofs == ofs) { line[0] = '0'; - QC_snprintfz(line, sizeof(line), "%i(%s)", def->ofs, GetString(def->s_name)); + QC_snprintfz(line, sizeof(line), "%i(%s)", def->ofs, GetNameString(def->s_name)); break; } } @@ -3466,7 +3557,7 @@ char *DecompileGlobalString(gofs_t ofs) { char *s; int i; - QCC_ddef_t *def; + QCD_def_t *def; static char line[128]; line[0] = '0'; @@ -3480,13 +3571,13 @@ char *DecompileGlobalString(gofs_t ofs) { line[0] = '0'; - if (!strcmp(GetString(def->s_name), "IMMEDIATE") || !strcmp(GetString(def->s_name), ".imm") || !strcmp(GetString(def->s_name), "I+")) + if (!strcmp(GetNameString(def->s_name), "IMMEDIATE") || !strcmp(GetNameString(def->s_name), ".imm") || !strcmp(GetNameString(def->s_name), "I+")) { s = PR_ValueString((etype_t)(def->type), &pr_globals[ofs]); QC_snprintfz(line, sizeof(line), "%i(%s)", def->ofs, s); } else - QC_snprintfz(line, sizeof(line), "%i(%s)", def->ofs, GetString(def->s_name)); + QC_snprintfz(line, sizeof(line), "%i(%s)", def->ofs, GetNameString(def->s_name)); } } @@ -3536,10 +3627,10 @@ void DecompilePrintFunction(char *name) { int i; dstatement_t *ds; - dfunction_t *df; + QCD_function_t *df; for (i = 0; i < numfunctions; i++) - if (!strcmp(name, GetString(functions[i].s_name))) + if (!strcmp(name, GetNameString(functions[i].s_name))) break; if (i == numfunctions) { diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index eb2fed8ed..9356ecbf2 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -1602,14 +1602,15 @@ reeval: return s; */ } break; - case OP_PUSH: + case OP_PUSH: //note: OPA is words, not bytes. OPC->_int = ENGINEPOINTER(&prinst.localstack[prinst.localstack_used+prinst.spushed]); prinst.spushed += OPA->_int; if (prinst.spushed + prinst.localstack_used >= LOCALSTACK_SIZE) { + i = prinst.spushed; prinst.spushed = 0; prinst.pr_xstatement = st-pr_statements; - PR_RunError(&progfuncs->funcs, "Progs pushed too much"); + PR_RunError(&progfuncs->funcs, "Progs pushed too much (%i bytes, %i parents, max %i)", i, prinst.localstack_used, LOCALSTACK_SIZE); } break; /* case OP_POP: diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index 70c36a9af..5078d9bbc 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -445,6 +445,56 @@ static void PDECL PR_memfree (pubprogfuncs_t *ppf, void *memptr) PR_memvalidate(progfuncs); } +static void *PDECL PR_memrealloc (pubprogfuncs_t *ppf, void *memptr, unsigned int newsize) +{ + progfuncs_t *progfuncs = (progfuncs_t*)ppf; + qcmemusedblock_t *ub; + unsigned int ptr = memptr?((char*)memptr - progfuncs->funcs.stringtable):0; + void *newptr; + unsigned int oldsize; + + /*freeing NULL is ignored*/ + if (!ptr) //realloc instead of malloc is accepted. + return PR_memalloc(ppf, newsize); + PR_memvalidate(progfuncs); + ptr -= sizeof(qcmemusedblock_t); + if (/*ptr < 0 ||*/ ptr >= prinst.addressableused) + { + ptr += sizeof(qcmemusedblock_t); + if (ptr < prinst.addressableused && !*(char*)memptr) + { + //the empty string is a point of contention. while we can detect it from fteqcc, its best to not give any special favours (other than nicer debugging, where possible) + //we might not actually spot it from other qccs, so warning about it where possible is probably a very good thing. + externs->Printf("PR_memrealloc: unable to free the non-null empty string constant at %x\n", ptr); + } + else + externs->Printf("PR_memrealloc: pointer invalid - out of range (%x >= %x)\n", ptr, (unsigned int)prinst.addressableused); + PR_StackTrace(&progfuncs->funcs, false); + return NULL; + } + + //this is the used block that we're trying to free + ub = (qcmemusedblock_t*)(progfuncs->funcs.stringtable + ptr); + if (ub->marker != MARKER_USED || ub->size <= sizeof(*ub) || ptr + ub->size > (unsigned int)prinst.addressableused) + { + externs->Printf("PR_memrealloc: pointer lacks marker - double-freed?\n"); + PR_StackTrace(&progfuncs->funcs, false); + return NULL; + } + oldsize = ub->size; + oldsize -= sizeof(qcmemusedblock_t); //ignore the header. + + newptr = PR_memalloc(ppf, newsize); + if (oldsize > newsize) + oldsize = newsize; //don't copy it all. + memcpy(newptr, memptr, oldsize); + newsize -= oldsize; + memset((char*)newptr+oldsize, 0, newsize); //clear out any extended part. + PR_memfree(ppf, memptr); //free the old. + + return newptr; +} + void PRAddressableFlush(progfuncs_t *progfuncs, size_t totalammount) { prinst.addressableused = 0; @@ -1685,6 +1735,7 @@ static pubprogfuncs_t deffuncs = { ED_NewString, QC_HunkAlloc, PR_memalloc, + PR_memrealloc, PR_memfree, PR_AllocTempString, PR_AllocTempStringLen, diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index ff5c046bd..1f4358a61 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -1036,6 +1036,20 @@ char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs) return line; } +char *PR_GlobalStringImmediate (progfuncs_t *progfuncs, int ofs) +{ + int i; + static char line[128]; + sprintf (line,"%i", ofs); + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +} + /* ============= ED_Print @@ -2742,6 +2756,7 @@ pbool PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, progstat int reorg = prinst.reorganisefields || prinst.numfields; int stringadjust; + int pointeradjust; int *basictypetable; @@ -3027,9 +3042,15 @@ retry: glob = pr_globals = (float *)s; if (progfuncs->funcs.stringtable) + { stringadjust = pr_strings - progfuncs->funcs.stringtable; + pointeradjust = (char*)glob - progfuncs->funcs.stringtable; + } else + { stringadjust = 0; + pointeradjust = (char*)glob - pr_strings; + } if (!pr_linenums) { @@ -3552,6 +3573,13 @@ 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; @@ -3589,6 +3617,13 @@ 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; } } @@ -3628,6 +3663,10 @@ retry: if (progfuncs->funcs.stringtablesize + progfuncs->funcs.stringtable < pr_strings + pr_progs->numstrings) progfuncs->funcs.stringtablesize = (pr_strings + pr_progs->numstrings) - progfuncs->funcs.stringtable; + //make sure the localstack is addressable to the qc, so we can OP_PUSH okay. + if (!prinst.localstack) + prinst.localstack = PRAddressableExtend(progfuncs, NULL, 0, sizeof(float)*LOCALSTACK_SIZE); + if (externs->MapNamedBuiltin) { for (i=0,fnc2=pr_cp_functions; inumfunctions; i++, fnc2++) diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index 3b70e62d2..72289a290 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -128,8 +128,8 @@ static void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum) if ( (unsigned)op < OP_NUMOPS) { int i; - externs->Printf ("%s ", pr_opcodes[op].name); - i = strlen(pr_opcodes[op].name); + externs->Printf ("%s ", pr_opcodes[op].opname); + i = strlen(pr_opcodes[op].opname); for ( ; i<10 ; i++) externs->Printf (" "); } @@ -141,16 +141,22 @@ static void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum) #define TYPEHINT(a) NULL #endif - if (op == OP_IF_F || op == OP_IFNOT_F) + if (op == OP_IF_F || op == OP_IFNOT_F || op == OP_IF_I || op == OP_IFNOT_I || op == OP_IF_S || op == OP_IFNOT_S) externs->Printf ("%sbranch %i",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)),arg[1]); else if (op == OP_GOTO) { externs->Printf ("branch %i",arg[0]); } + else if (op == OP_BOUNDCHECK) + { + externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a))); + externs->Printf ("%s",PR_GlobalStringImmediate(progfuncs, arg[1])); + externs->Printf ("%s",PR_GlobalStringImmediate(progfuncs, arg[2])); + } else if ( (unsigned)(op - OP_STORE_F) < 6) { externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a))); - externs->Printf ("%s", PR_GlobalStringNoContents(progfuncs, arg[1])); + externs->Printf ("%s",PR_GlobalStringNoContents(progfuncs, arg[1])); } else { @@ -159,7 +165,7 @@ static void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum) if (arg[1]) externs->Printf ("%s",PR_GlobalString(progfuncs, arg[1], TYPEHINT(b))); if (arg[2]) - externs->Printf ("%s", PR_GlobalStringNoContents(progfuncs, arg[2])); + externs->Printf ("%s",PR_GlobalStringNoContents(progfuncs, arg[2])); } externs->Printf ("\n"); } @@ -1354,7 +1360,10 @@ static const char *lastfile = NULL; { PR_PrintStatement(progfuncs, statement); if (fatal) + { progfuncs->funcs.debug_trace = DEBUG_TRACE_ABORTERROR; + progfuncs->funcs.parms->Abort ("%s", fault?fault:"Debugger Abort"); + } return statement; } diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index 486f0927b..1c56a3503 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -167,12 +167,12 @@ typedef struct prinst_s #define MAX_STACK_DEPTH 1024 //insanely high value requried for xonotic. prstack_t pr_stack[MAX_STACK_DEPTH]; int pr_depth; - int spushed; //locals -#define LOCALSTACK_SIZE 16384 - int localstack[LOCALSTACK_SIZE]; +#define LOCALSTACK_SIZE 65536 //in words + int *localstack; int localstack_used; + int spushed; //extra //step-by-step debug state int debugstatement; @@ -557,6 +557,7 @@ const char *ASMCALL PR_StringToNative (pubprogfuncs_t *inst, string_t str); char *PR_GlobalString (progfuncs_t *progfuncs, int ofs, struct QCC_type_s **typehint); char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs); +char *PR_GlobalStringImmediate (progfuncs_t *progfuncs, int ofs); pbool CompileFile(progfuncs_t *progfuncs, const char *filename); diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 5863d2978..6fe5cc2ef 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -200,6 +200,7 @@ struct pubprogfuncs_s char *(PDECL *AddString) (pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup); //dump a string into the progs memory (for setting globals and whatnot) void *(PDECL *Tempmem) (pubprogfuncs_t *prinst, int ammount, char *whatfor); //grab some mem for as long as the progs stays loaded void *(PDECL *AddressableAlloc) (pubprogfuncs_t *progfuncs, unsigned int ammount); /*returns memory within the qc block, use stringtoprogs to get a usable qc pointer/string*/ + void *(PDECL *AddressableRealloc) (pubprogfuncs_t *progfuncs, void *oldptr, unsigned int newammount); /*returns memory within the qc block, use stringtoprogs to get a usable qc pointer/string*/ void (PDECL *AddressableFree) (pubprogfuncs_t *progfuncs, void *mem); /*frees a block of addressable memory*/ string_t (PDECL *TempString) (pubprogfuncs_t *prinst, const char *str); string_t (PDECL *AllocTempString) (pubprogfuncs_t *prinst, char **str, unsigned int len); diff --git a/engine/qclib/progtype.h b/engine/qclib/progtype.h index 4a415daea..e3c2b28e1 100644 --- a/engine/qclib/progtype.h +++ b/engine/qclib/progtype.h @@ -42,6 +42,7 @@ typedef unsigned int puint_t; #define pPRIi64 "I64i" #define pPRIu64 "I64u" #define pPRIx64 "I64x" + #define pPRIuSIZE PRIxPTR #else #include typedef int64_t pint64_t QC_ALIGN(4); @@ -54,6 +55,7 @@ typedef unsigned int puint_t; #define pPRIi64 PRIi64 #define pPRIu64 PRIu64 #define pPRIx64 PRIx64 + #define pPRIuSIZE PRIxPTR #endif #define QCVM_32 #endif diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index a637fa838..643da7463 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -49,6 +49,23 @@ extern progfuncs_t *qccprogfuncs; #define STRINGIFY(s) STRINGIFY2(s) #endif +#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) + #define FTE_DEPRECATED __attribute__((__deprecated__)) //no idea about the actual gcc version + #if defined(_WIN32) + #include + #ifdef __MINGW_PRINTF_FORMAT + #define LIKEPRINTF(x) __attribute__((format(__MINGW_PRINTF_FORMAT,x,x+1))) + #else + #define LIKEPRINTF(x) __attribute__((format(ms_printf,x,x+1))) + #endif + #else + #define LIKEPRINTF(x) __attribute__((format(printf,x,x+1))) + #endif +#endif +#ifndef LIKEPRINTF + #define LIKEPRINTF(x) +#endif + void *qccHunkAlloc(size_t mem); void qccClearHunk(void); @@ -429,6 +446,7 @@ typedef struct QCC_def_s int refcount; //if 0, temp can be reused. tracked on globals too in order to catch bugs that would otherwise be a little too obscure. int timescalled; //part of the opt_stripfunctions optimisation. + const char *unitn; //for static globals. const char *filen; int s_filed; int s_line; @@ -517,7 +535,8 @@ struct QCC_function_s int code; // first statement. if -1, is a builtin. dfunction_t *merged; // this function was merged. this is the index to use to ensure that the parms are sized correctly.. string_t s_filed; // source file with definition - const char *filen; + const char *filen; // full scope, printed for debugging. + const char *unitn; //scope for static variables. int line; int line_end; char *name; //internal name of function @@ -686,9 +705,13 @@ extern pbool flag_allowuninit; extern pbool flag_cpriority; extern pbool flag_qcfuncs; extern pbool flag_embedsrc; +extern pbool flag_noreflection; extern pbool flag_nopragmafileline; extern pbool flag_utf8strings; extern pbool flag_reciprocalmaths; +extern pbool flag_ILP32; +extern pbool flag_undefwordsize; +extern pbool flag_pointerrelocs; extern pbool opt_overlaptemps; extern pbool opt_shortenifnots; @@ -771,14 +794,14 @@ void QCC_PR_Expect (const char *string); pbool QCC_PR_CheckKeyword(int keywordenabled, const char *string); #endif pbool QCC_PR_CheckTokenComment(const char *string, char **comment); -NORETURN void VARGS QCC_PR_ParseError (int errortype, const char *error, ...); -pbool VARGS QCC_PR_ParseWarning (int warningtype, const char *error, ...); -pbool VARGS QCC_PR_Warning (int type, const char *file, int line, const char *error, ...); -void VARGS QCC_PR_Note (int type, const char *file, int line, const char *error, ...); +NORETURN void VARGS QCC_PR_ParseError (int errortype, const char *error, ...) LIKEPRINTF(2); +pbool VARGS QCC_PR_ParseWarning (int warningtype, const char *error, ...) LIKEPRINTF(2); +pbool VARGS QCC_PR_Warning (int type, const char *file, int line, const char *error, ...) LIKEPRINTF(4); +void VARGS QCC_PR_Note (int type, const char *file, int line, const char *error, ...) LIKEPRINTF(4); void QCC_PR_ParsePrintDef (int warningtype, QCC_def_t *def); void QCC_PR_ParsePrintSRef (int warningtype, QCC_sref_t sref); -NORETURN void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, const char *error, ...); -NORETURN void VARGS QCC_PR_ParseErrorPrintSRef (int errortype, QCC_sref_t sref, const char *error, ...); +NORETURN void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, const char *error, ...) LIKEPRINTF(3); +NORETURN void VARGS QCC_PR_ParseErrorPrintSRef (int errortype, QCC_sref_t sref, const char *error, ...) LIKEPRINTF(3); QCC_type_t *QCC_PR_MakeThiscall(QCC_type_t *orig, QCC_type_t *thistype); @@ -848,6 +871,7 @@ enum { WARN_DEPRECACTEDSYNTAX, //triggered when syntax is used that I'm trying to kill WARN_DEPRECATEDVARIABLE, //triggered from usage of a symbol that someone tried to kill WARN_MUTEDEPRECATEDVARIABLE, //triggered from usage of a symbol that someone tried to kill (without having been muted). + WARN_OCTAL_IMMEDIATE, // 0400!=400... (not found in WARN_GMQCC_SPECIFIC, //extension created by gmqcc that conflicts or isn't properly implemented. WARN_FTE_SPECIFIC, //extension that only FTEQCC will have a clue about. WARN_EXTENSION_USED, //extension that frikqcc also understands @@ -931,7 +955,6 @@ enum { ERR_INITIALISEDLOCALFUNCTION, ERR_NOTDEFINED, ERR_ARRAYNEEDSSIZE, - ERR_ARRAYNEEDSBRACES, ERR_TOOMANYINITIALISERS, ERR_TYPEINVALIDINSTRUCT, ERR_NOSHAREDLOCALS, @@ -1029,6 +1052,7 @@ extern compiler_flag_t compiler_flag[]; extern unsigned char qccwarningaction[WARN_MAX]; extern jmp_buf pr_parse_abort; // longjump with this on parse error +extern const char *s_unitn; //used to track compilation units, for global statics. extern const char *s_filen; //name of the file we're currently compiling. extern QCC_string_t s_filed; //name of the file we're currently compiling, as seen by whoever reads the .dat extern int pr_source_line; @@ -1046,6 +1070,8 @@ void *QCC_PR_Malloc (int size); #define OFS_PARM4 16 #define RESERVED_OFS 28 +#define VMWORDSIZE 4 + extern struct QCC_function_s *pr_scope; extern int pr_error_count, pr_warning_count; @@ -1272,8 +1298,8 @@ typedef struct qcc_cachedsourcefile_s vfile_t; void QCC_CloseAllVFiles(void); vfile_t *QCC_FindVFile(const char *name); vfile_t *QCC_AddVFile(const char *name, void *data, size_t size); -void QCC_CatVFile(vfile_t *, const char *fmt, ...); -void QCC_InsertVFile(vfile_t *, size_t pos, const char *fmt, ...); +void QCC_CatVFile(vfile_t *, const char *fmt, ...) LIKEPRINTF(2); +void QCC_InsertVFile(vfile_t *, size_t pos, const char *fmt, ...) LIKEPRINTF(3); char *ReadProgsCopyright(char *buf, size_t bufsize); //void *QCC_ReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile); diff --git a/engine/qclib/qcc_cmdlib.c b/engine/qclib/qcc_cmdlib.c index 7a2317f2c..a3e5c5593 100644 --- a/engine/qclib/qcc_cmdlib.c +++ b/engine/qclib/qcc_cmdlib.c @@ -1270,7 +1270,7 @@ long QCC_LoadFile (char *filename, void **bufferptr) else if (!mem[check]) { if (!warned) - QCC_PR_Warning(WARN_UNEXPECTEDPUNCT, filename, line, "file contains null bytes %u/%u", check, len); + QCC_PR_Warning(WARN_UNEXPECTEDPUNCT, filename, line, "file contains null bytes %u/%"pPRIuSIZE, check, len); warned = true; //fixme: insert modified-utf-8 nulls instead. mem[check] = ' '; diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 18e9b9b74..9844db89f 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -140,10 +140,10 @@ pbool flag_msvcstyle; //MSVC style warnings, so msvc's ide works properly pbool flag_debugmacros; //Print out #defines as they are expanded, for debugging. pbool flag_assume_integer; //5 - is that an integer or a float? qcc says float. but we support int too, so maybe we want that instead? pbool flag_assume_double; //5.0 - is that single or double precision? QC says float, but C says double. should probably only be used with assume-int enabled too. -pbool flag_filetimes; +pbool flag_filetimes; //only rebuild if files were modified. pbool flag_typeexplicit; //no implicit type conversions, you must do the casts yourself. -pbool flag_boundchecks; //Disable generation of bound check instructions. -pbool flag_guiannotate; +pbool flag_boundchecks; //Disable generation of bound check instructions. +pbool flag_guiannotate; //spit out lots of extra text that the gui can interpret to display some inline asm pbool flag_brokenarrays; //return array; returns array[0] instead of &array; pbool flag_rootconstructor; //if true, class constructors are ordered to call the super constructor first, rather than the child constructor pbool flag_qccx; //accept qccx syntax. you may wish to disable warnings separately. @@ -154,9 +154,13 @@ pbool flag_cpriority; //operator precidence should adhere to C standards, inste pbool flag_qcfuncs; //void() is a function type, and not a syntax error. pbool flag_allowuninit; //ignore uninitialised locals, avoiding all private locals. pbool flag_embedsrc; //embed all source files inside the .dat (can be opened with any zip program) +pbool flag_noreflection; //no reflection stuff, for smaller unsavable binaries. pbool flag_nopragmafileline;//ignore #pragma file and #pragma line, so that I can actually read+debug xonotic's code. pbool flag_utf8strings; //strings default to u8"" string rules. -pbool flag_reciprocalmaths; //unsafe maths optimisations +pbool flag_reciprocalmaths; //unsafe maths optimisations +pbool flag_ILP32; //restrict long to 32 bits. long long still exists. +pbool flag_undefwordsize; //pointers are NOT always multiples of 4. sizeof becomes an error foo.length will still work though. pointer maths will resort to OP_ADD_PIW in order to still work (or just break). char and short types are unusable. casting between strings and pointers is an error (though you can still contrive casts past it). for compat with DP and its potential 64bit qcvm. +pbool flag_pointerrelocs; //engine accepts ev_pointer globaldefs and biases them by the in-memory globals. 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. @@ -270,7 +274,7 @@ QCC_sref_t QCC_MakeInt64Const(longlong llvalue); QCC_sref_t QCC_MakeUInt64Const(unsigned longlong llvalue); static QCC_sref_t QCC_MakeUniqueConst(QCC_type_t *type, void *data); QCC_sref_t QCC_MakeVectorConst(pvec_t a, pvec_t b, pvec_t c); -static QCC_sref_t QCC_MakeGAddress(QCC_type_t *type, QCC_def_t *relocof); +static QCC_sref_t QCC_MakeGAddress(QCC_type_t *type, QCC_def_t *relocof, int idx); enum { @@ -304,6 +308,7 @@ QCC_function_t *pr_assumetermscope; unsigned int pr_assumetermflags; //GDF_ pbool pr_ignoredeprecation; pbool pr_dumpasm; +const char *s_unitn; const char *s_filen; QCC_string_t s_filed; // filename for function definition @@ -1383,7 +1388,10 @@ static pbool QCC_OPCodeValidForTarget(qcc_targetformat_t targfmt, unsigned int q case QCF_FTE: case QCF_FTEDEBUG: #define QCTARGVER_FTE_DEF 5768//5529 -#define QCTARGVER_FTE_MAX 5768 +#define QCTARGVER_FTE_PRELOCS 6614//FIXME +#define QCTARGVER_FTE_MAX QCTARGVER_FTE_PRELOCS + if (num == OP_PUSH) + return (qcc_targetversion>=QCTARGVER_FTE_PRELOCS); //OP_PUSH was buggy before this. if (num >= OP_LT_U) //uint+double+int64+uint64 ops return (qcc_targetversion>=5768); if (num >= OP_STOREP_B) //byte @@ -1536,8 +1544,9 @@ static pbool QCC_OPCodeValidForTarget(qcc_targetformat_t targfmt, unsigned int q } return false; case QCF_DARKPLACES: -#define QCTARGVER_DP_DEF 12901 -#define QCTARGVER_DP_MAX 12901 +#define QCTARGVER_DP_DEF 12901 +#define QCTARGVER_DP_POINTERS 20241108 //FIXME: set properly once https://github.com/DarkPlacesEngine/DarkPlaces/pull/215 is merged. +#define QCTARGVER_DP_MAX 20241108 //all id opcodes. if (num < OP_MULSTORE_F) return true; @@ -1545,6 +1554,24 @@ static pbool QCC_OPCodeValidForTarget(qcc_targetformat_t targfmt, unsigned int q //extended opcodes. switch(num) { + case OP_ADD_PIW: + case OP_GLOBALADDRESS: + case OP_LOADA_F: + case OP_LOADA_V: + case OP_LOADA_S: + case OP_LOADA_ENT: + case OP_LOADA_FLD: + case OP_LOADA_FNC: + case OP_LOADA_I: + case OP_LOAD_P: + case OP_LOADP_F: + case OP_LOADP_V: + case OP_LOADP_S: + case OP_LOADP_ENT: + case OP_LOADP_FLD: + case OP_LOADP_FNC: + case OP_LOADP_I: + return (qcc_targetversion>=QCTARGVER_DP_POINTERS); //opcodes that were buggy in DP. case OP_ADD_IF: //dp wrote these to ints, which doesn't match our defined opcodes. not really a problem. case OP_SUB_IF: //dp wrote these to ints, which doesn't match our defined opcodes. revert to _F. @@ -1649,6 +1676,8 @@ static pbool QCC_OPCode_StorePOffset(void) case QCF_FTEH2: case QCF_FTEDEBUG: return (qcc_targetversion>=5712); + case QCF_DARKPLACES: + return (qcc_targetversion>=QCTARGVER_DP_POINTERS); case QCF_QSS: return true; default: @@ -1661,6 +1690,8 @@ void QCC_OPCodeSetTarget(qcc_targetformat_t targfmt, unsigned int targver) size_t i; qcc_targetformat = targfmt; qcc_targetversion = targver; + flag_undefwordsize = false; + flag_pointerrelocs = false; switch(qcc_targetformat) { @@ -1673,8 +1704,11 @@ void QCC_OPCodeSetTarget(qcc_targetformat_t targfmt, unsigned int targver) QCC_PR_ParseWarning(WARN_BADTARGET, "target revision %u is unknown, assuming revision %u", qcc_targetversion, QCTARGVER_FTE_MAX); qcc_targetversion = QCTARGVER_FTE_MAX; } + + flag_pointerrelocs = qcc_targetversion >= QCTARGVER_FTE_PRELOCS; break; case QCF_DARKPLACES: + flag_undefwordsize = true; if (qcc_targetversion > QCTARGVER_DP_MAX) { if (qcc_targetversion != ~0u) @@ -1816,7 +1850,7 @@ static pbool QCC_SRef_IsNull(QCC_sref_t ref) //retrieves the data associated with the reference if its constant and thus readable at compile time. static const QCC_eval_t *QCC_SRef_EvalConst(QCC_sref_t ref) { - if (ref.sym && ref.sym->initialized && ref.sym->constant) + if (ref.sym && ref.sym->initialized && ref.sym->constant && !ref.sym->reloc) { ref.sym->referenced = true; return (const QCC_eval_t*)&ref.sym->symboldata[ref.ofs]; @@ -1824,6 +1858,37 @@ static const QCC_eval_t *QCC_SRef_EvalConst(QCC_sref_t ref) return NULL; } +//retrieves the int value associated with the reference if its constant and thus readable at compile time. +static pint64_t QCC_Eval_Int(const QCC_eval_t *eval, QCC_type_t *type) +{ + if (!eval) + { + QCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, "Not an initialised constant"); + return 0; + } + switch(type->type) + { + case ev_float: return eval->_float; + case ev_double: return eval->_double; + case ev_integer: return eval->_int; +// case ev_function: +// case ev_entity: +// case ev_field: +// case ev_pointer: + case ev_uint: return eval->_uint; + case ev_boolean: return QCC_Eval_Int(eval, type->aux_type); //bools are weird. + case ev_int64: return eval->i64; + case ev_uint64: return eval->u64; +// case ev_string: +// case ev_vector: + case ev_struct: + case ev_union: + default: + QCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, "Unable to evaluate to numeric constant"); + return 0; + } +} + //retrieves the data associated with the reference if its constant and thus readable at compile time. static pbool QCC_Eval_Truth(const QCC_eval_t *eval, QCC_type_t *type, pbool assume) { @@ -2309,6 +2374,15 @@ void QCC_PurgeTemps(void) tempsused = 0; aliases = NULL; allaliases = NULL; + + + max_labels = 0; + max_gotos = 0; + free(pr_labels); + pr_labels = NULL; + free(pr_gotos); + pr_gotos = NULL; + num_gotos = num_labels = 0; } //temps that are still in use over a function call can be considered dodgy. @@ -2530,6 +2604,11 @@ const char *QCC_VarAtOffset(QCC_sref_t ref) type = !val?-1:ref.cast->type; if (type == ev_variant) type = ref.sym->type->type; + if (ref.sym->reloc)// && ref.cast->type == ev_pointer) + { + QC_snprintfz(message, sizeof(message), "&%s", ref.sym->reloc->name); + return message; + } switch(type) { case ev_string: @@ -2753,6 +2832,10 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ //both are constants switch (op - pr_opcodes) //improve some of the maths. { +// case OP_GLOBALADDRESS: +// if (flag_pointerrelocs) +// return QCC_MakeGAddress(type_pointer, var_a.sym, var_a.ofs + QCC_Eval_Int(QCC_SRef_EvalConst(var_b), var_b.cast)); +// break; case OP_AND_ANY: // case OP_AND_F: case OP_AND_FI: @@ -2977,9 +3060,11 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ return QCC_MakeFloatConst(eval_a->_float + eval_b->_int); case OP_ADD_PIW: + if (flag_undefwordsize) + break; //don't make assumptions. QCC_FreeTemp(var_a); QCC_FreeTemp(var_b); optres_constantarithmatic++; - return QCC_MakeIntConst(eval_a->_int + eval_b->_int*4); + return QCC_MakeIntConst(eval_a->_int + eval_b->_int*VMWORDSIZE); case OP_SUB_IF: QCC_FreeTemp(var_a); QCC_FreeTemp(var_b); @@ -3349,7 +3434,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ { pint64_t d = eval_a->_int; if ((pint_t)d != eval_a->_int) - QCC_PR_ParseWarning(WARN_OVERFLOW, "Numerical truncation of %#"pPRIi" to %#"pPRIx64".", eval_a->_int, (pint64_t)d); + QCC_PR_ParseWarning(WARN_OVERFLOW, "Numerical truncation of %"pPRIi" to %#"pPRIx64".", eval_a->_int, (pint64_t)d); return QCC_MakeInt64Const(d); } case OP_CONV_UI64: @@ -3358,7 +3443,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ { puint64_t d = eval_a->_uint; if ((puint_t)d != eval_a->_uint) - QCC_PR_ParseWarning(WARN_OVERFLOW, "Numerical truncation of %#"pPRIu" to %#"pPRIx64".", eval_a->_uint, (pint64_t)d); + QCC_PR_ParseWarning(WARN_OVERFLOW, "Numerical truncation of %"pPRIu" to %#"pPRIx64".", eval_a->_uint, (pint64_t)d); return QCC_MakeUInt64Const(d); } case OP_CONV_I64I: @@ -3367,7 +3452,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ { pint_t d = eval_a->i64; if ((pint64_t)d != eval_a->i64) - QCC_PR_ParseWarning(WARN_OVERFLOW, "Numerical truncation of %#"pPRIx64" to %#"pPRIx".", eval_a->i64, (pint_t)d); + QCC_PR_ParseWarning(WARN_OVERFLOW, "Numerical truncation of %"pPRIx64" to %#"pPRIx".", eval_a->i64, (pint_t)d); return QCC_MakeIntConst(d); } @@ -3486,6 +3571,10 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ //b is const, a is not switch (op - pr_opcodes) { + case OP_GLOBALADDRESS: + if (flag_pointerrelocs) + return QCC_MakeGAddress(type_pointer, var_a.sym, var_a.ofs + QCC_Eval_Int(QCC_SRef_EvalConst(var_b), var_b.cast)); + break; case OP_AND_ANY: case OP_AND_F: case OP_AND_FI: @@ -3577,10 +3666,10 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ case OP_DIV_VF: if (!eval_b->_float) QCC_PR_ParseWarning(WARN_DIVISIONBY0, "Division by 0\n"); - else if (flag_reciprocalmaths) + else if (flag_reciprocalmaths && eval_b->_int&(0xff<<23)) { QCC_FreeTemp(var_b); - var_b = QCC_MakeFloatConst(1 / eval_b->_float); + var_b = QCC_MakeFloatConst(1.0 / eval_b->_float); return QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_VF], var_a, var_b, outstatement, 0); } if (eval_b->_float == 1) @@ -3595,11 +3684,11 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ case OP_DIV_IF: if (!eval_b->_float) QCC_PR_ParseWarning(WARN_DIVISIONBY0, "Division by 0\n"); - else if (flag_reciprocalmaths) + else if (flag_reciprocalmaths && eval_b->_int&(0xff<<23)) { QCC_FreeTemp(var_b); - var_b = QCC_MakeFloatConst(1 / eval_b->_float); - return QCC_PR_StatementFlags((op == &pr_opcodes[OP_ADD_FP])?&pr_opcodes[OP_MUL_F]:&pr_opcodes[OP_MUL_IF], var_a, var_b, outstatement, 0); + var_b = QCC_MakeFloatConst(1.0 / eval_b->_float); + return QCC_PR_StatementFlags((op == &pr_opcodes[OP_DIV_F])?&pr_opcodes[OP_MUL_F]:&pr_opcodes[OP_MUL_IF], var_a, var_b, outstatement, 0); } //fallthrough case OP_MUL_F: @@ -3983,7 +4072,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ } } } - else if (op - pr_opcodes == OP_ADD_I && statements[numstatements-1].op == OP_MUL_I && opt_assignments) + else if (op - pr_opcodes == OP_ADD_I && statements[numstatements-1].op == OP_MUL_I && opt_assignments && !flag_undefwordsize) { //mul_i idx 4i tmp //add_i tmp 2i out @@ -3992,7 +4081,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ // const QCC_eval_t *eval_a = QCC_SRef_EvalConst(var_a); const QCC_eval_t *eval_b = QCC_SRef_EvalConst(statements[numstatements-1].b); - if (eval_b && eval_b->_int == 4) + if (eval_b && eval_b->_int == VMWORDSIZE) { if (var_a.cast && var_a.sym == statements[numstatements-1].c.sym && var_a.ofs == statements[numstatements-1].c.ofs) if (var_a.sym && var_b.sym && var_a.sym->temp && var_a.sym->refcount==1) @@ -4115,11 +4204,20 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ if (op == &pr_opcodes[OP_SUB_PF]) var_b = QCC_SupplyConversion(var_b, ev_integer, true); //FIXME: this should be an unconditional float->int conversion if (var_c.cast->aux_type->type == ev_void) //void* is treated as a byte type. + { + if (flag_undefwordsize) + QCC_PR_ParseWarning(ERR_BADEXTENSION, "void* maths was disabled."); var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], var_c, var_b, NULL, 0); + } + else if (1) + { + var_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, QCC_MakeIntConst(-var_c.cast->aux_type->size), NULL, 0); + var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], var_c, var_b/*negative, making this a subtraction*/, NULL, 0); + } else { - //fixme: word size - var_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, QCC_MakeIntConst(var_c.cast->aux_type->size*4), NULL, 0); + QCC_sref_t idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_PIW], QCC_MakeIntConst(0), QCC_MakeIntConst(var_c.cast->aux_type->size), NULL); + var_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, idx, NULL, 0); var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], var_c, var_b, NULL, 0); } var_c.cast = var_a.cast; @@ -4130,9 +4228,13 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ //determine byte offset var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], var_a, var_b, NULL, 0); if (var_a.cast->aux_type->type == ev_void) + { + if (flag_undefwordsize) + QCC_PR_ParseWarning(ERR_BADEXTENSION, "void* maths was disabled."); return var_c; //we're done if we're using void/bytes - //determine divisor (fixme: word size) - var_b = QCC_MakeIntConst(var_a.cast->aux_type->size*4); + } + //determine divisor + var_b = QCC_PR_Statement(&pr_opcodes[OP_ADD_PIW], QCC_MakeIntConst(0), QCC_MakeIntConst(var_a.cast->aux_type->size), NULL); //divide the result return QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_I], var_c, var_b, NULL, 0); @@ -4454,7 +4556,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ if (!var_c.cast) { //with denormals, 5.0 * 1i -> 5i, and 5i / 1i = 5.0 - QCC_PR_ParseWarning(WARN_DENORMAL, "itof emulation: denormals are unsafe"); + QCC_PR_ParseWarning(WARN_DENORMAL, "itof emulation: denormals are unsafe %s", var_a.sym->name); var_a = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], var_a, QCC_MakeIntConst(1), NULL, 0); } else @@ -5406,7 +5508,9 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ } break; case OP_ADD_PIW: - var_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], QCC_MakeIntConst(4), var_b, NULL, flags&STFL_PRESERVEB); + if (flag_undefwordsize) //fine if its the engine's doing it, not so fine if we're assuming it in the qcc. + QCC_PR_ParseWarning(ERR_BADEXTENSION, "Making assumptions about pointer sizes was blocked."); + var_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], QCC_MakeIntConst(VMWORDSIZE), var_b, NULL, flags&STFL_PRESERVEB); var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], var_a, var_b, NULL, flags&~STFL_PRESERVEB); var_c.cast = var_a.cast; return var_c; @@ -5475,7 +5579,17 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ } if (!pr_scope) - QCC_PR_ParseError(ERR_BADEXTENSION, "Unable to generate statements at global scope."); + { + switch(op - pr_opcodes) + { + case OP_GLOBALADDRESS: + if (flag_pointerrelocs) + return QCC_MakeGAddress(type_pointer, var_a.sym, var_a.ofs + (var_b.cast?QCC_Eval_Int(QCC_SRef_EvalConst(var_b), var_b.cast):0)); + break; + } + + QCC_PR_ParseWarning(ERR_BADEXTENSION, "Unable to generate statements at global scope (%s).", op->opname); + } if (op->type_c == &type_void || op->associative==ASSOC_RIGHT || op->type_c == NULL) { @@ -5748,6 +5862,8 @@ static QCC_ref_t *QCC_PR_GenerateAddressOf(QCC_ref_t *retbuf, QCC_ref_t *operand else QCC_PR_ParseError (ERR_BADEXTENSION, "Address-of operator is not supported in this form without extensions. Consider the use of: #pragma target fte"); } + if (operand->base.sym->scope && !operand->base.sym->isstatic) + QCC_PR_ParseWarning (WARN_NOTSTANDARDBEHAVIOUR, "Address-of operator on local"); //&foo (or &((&foo)[5]), which is basically an array). the result is a temp and thus cannot be assigned to (but should be possible to dereference further). return QCC_PR_BuildRef(retbuf, @@ -5781,7 +5897,7 @@ static QCC_ref_t *QCC_PR_GenerateAddressOf(QCC_ref_t *retbuf, QCC_ref_t *operand } QCC_sref_t QCC_DP_GlobalAddress(QCC_sref_t base, QCC_sref_t index, unsigned int flags) { - QCC_sref_t ptr = QCC_MakeGAddress(type_integer, base.sym); + QCC_sref_t ptr = QCC_MakeGAddress(type_integer, base.sym, 0); base.sym->used = true; if (!(flags&STFL_PRESERVEA)) QCC_FreeTemp(base); @@ -5967,7 +6083,7 @@ static void QCC_VerifyFormatString (const char *funcname, QCC_ref_t **arglist, u return; //don't bother if its not relevant anyway. s = strings + formatstring->string; -#define ARGTYPE(a) (((a)>=firstarg && (a)cast->type) : ev_void) +#define ARGTYPE(a) (((a)>=firstarg && (a)cast->type==ev_boolean)?arglist[a]->cast->parentclass->type:arglist[a]->cast->type) : ev_void) #define ARGCTYPE(a) (((a)>=firstarg && (a)cast) : type_void) for(;;) @@ -6365,7 +6481,7 @@ static void QCC_VerifyArgs_sendevent (const char *funcname, QCC_ref_t **arglist, } if (arg >= argcount) { - QCC_PR_ParseWarning(WARN_ARGUMENTCHECK, "%s: arg type list", funcname, col_name, *argtypes, col_none); + QCC_PR_ParseWarning(WARN_ARGUMENTCHECK, "%s: arg type list longer than args", funcname); break; } else if (!t) @@ -7734,7 +7850,7 @@ static QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the f sz = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], sz, QCC_MakeIntConst(3), NULL); sz = QCC_PR_Statement(&pr_opcodes[OP_DIV_I], sz, QCC_MakeIntConst(4), NULL); QCC_FreeTemp(sz); - ret = QCC_GetTemp(QCC_PointerTypeTo(type_variant)); + ret = QCC_GetTemp(QCC_PointerTypeTo(type_void)); QCC_PR_SimpleStatement(&pr_opcodes[OP_PUSH], sz, nullsref, ret, false); //push *(int*)&a elements return ret; } @@ -7757,7 +7873,7 @@ static QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the f } else { - QCC_PR_ParseErrorPrintSRef (ERR_TYPEMISMATCHPARM, func, "_() intrinsic accepts only a string immediate", 1); + QCC_PR_ParseErrorPrintSRef (ERR_TYPEMISMATCHPARM, func, "_() intrinsic accepts only a string immediate"); d = nullsref; } @@ -7772,7 +7888,10 @@ static QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the f QCC_type_t *type; va_list = QCC_PR_GetSRef(type_vector, "__va_list", pr_scope, false, 0, 0); idx = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA); - idx = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], idx, QCC_MakeFloatConst(3), NULL); + if (idx.cast->type == ev_float) + idx = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], idx, QCC_MakeFloatConst(3), NULL); + else + idx = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], QCC_SupplyConversion(idx, ev_integer, true), QCC_MakeIntConst(3), NULL); QCC_PR_Expect(","); type = QCC_PR_ParseType(false, false); QCC_PR_Expect(")"); @@ -8566,7 +8685,7 @@ static QCC_sref_t QCC_MakeUniqueConst(QCC_type_t *type, void *data) return QCC_MakeSRefForce(cn, 0, type); } -static QCC_sref_t QCC_MakeGAddress(QCC_type_t *type, QCC_def_t *relocof) +static QCC_sref_t QCC_MakeGAddress(QCC_type_t *type, QCC_def_t *relocof, int idx) { QCC_def_t *cn; @@ -8592,9 +8711,11 @@ static QCC_sref_t QCC_MakeGAddress(QCC_type_t *type, QCC_def_t *relocof) cn->symboldata = (QCC_eval_basic_t*)(cn+1); cn->reloc = relocof; - relocof->gaddress = cn; + if (!idx) + relocof->gaddress = cn; memset(cn->symboldata, 0, sizeof(pint_t) * type->size); + cn->symboldata->_int = idx; return QCC_MakeSRefForce(cn, 0, type); } @@ -9314,16 +9435,10 @@ QCC_ref_t *QCC_PR_ParseRefArrayPointer (QCC_ref_t *retbuf, QCC_ref_t *r, pbool a { /*array notation on vector*/ vectorarrayindex: - if (tmp.sym->constant) + if ((eval=QCC_SRef_EvalConst(tmp))) { - unsigned int i; - if (tmp.cast->type == ev_integer) - i = tmp.sym->symboldata[tmp.ofs]._int; - else if (tmp.cast->type == ev_float) - i = tmp.sym->symboldata[tmp.ofs]._float; - else - i = -1; - if ((unsigned)i >= 3u) + unsigned long i = QCC_Eval_Int(eval, tmp.cast); + if (i >= 3u) QCC_PR_ParseErrorPrintSRef(0, r->base, "(vector) array index out of bounds"); } else if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK]) && flag_boundchecks) @@ -9339,22 +9454,8 @@ vectorarrayindex: fieldarrayindex: if ((eval=QCC_SRef_EvalConst(tmp))) { - unsigned long i; - if (tmp.cast->type == ev_uint) - i = eval->_uint; - else if (tmp.cast->type == ev_integer) - i = eval->_int; - else if (tmp.cast->type == ev_uint64) - i = eval->u64; - else if (tmp.cast->type == ev_int64) - i = eval->i64; - else if (tmp.cast->type == ev_float) - i = eval->_float; - else if (tmp.cast->type == ev_double) - i = eval->_double; - else - i = -1; - if ((unsigned long)i >= 3u) + unsigned long i = QCC_Eval_Int(eval, tmp.cast); + if (i >= 3u) QCC_PR_ParseErrorPrintSRef(0, r->base, "(vector) array index out of bounds"); } else if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK]) && flag_boundchecks) @@ -9370,22 +9471,8 @@ fieldarrayindex: } else if ((eval=QCC_SRef_EvalConst(tmp))) { - unsigned long i; - if (tmp.cast->type == ev_uint) - i = eval->_uint; - else if (tmp.cast->type == ev_integer) - i = eval->_int; - else if (tmp.cast->type == ev_uint64) - i = eval->u64; - else if (tmp.cast->type == ev_int64) - i = eval->i64; - else if (tmp.cast->type == ev_float) - i = eval->_float; - else if (tmp.cast->type == ev_double) - i = eval->_double; - else - i = -1; - if ((unsigned long)i >= (unsigned long)arraysize) + unsigned i = QCC_Eval_Int(eval, tmp.cast); + if (i >= (unsigned)arraysize) { QCC_PR_ParseWarning(WARN_ERROR, "(constant) array index out of bounds (0 <= %i < %i)", i, arraysize); QCC_PR_ParsePrintSRef(WARN_ERROR, r->base); @@ -9623,6 +9710,12 @@ fieldarrayindex: else base = QCC_RefToDef(r, true); + if (t->type == ev_union && t->num_parms == 1 && !t->params[0].paramname) //FIXME + { + arraysize = t->params[0].arraysize; + t = t->params[0].type; + } + //okay, not a pointer, we'll have to read it in somehow if (dereference) r = QCC_PR_BuildRef(retbuf, REF_POINTER, base, idx, t, r->readonly); @@ -10002,7 +10095,7 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo else QCC_FreeTemp(d); d = t; - QCC_PR_ParseWarning (WARN_SELFNOTTHIS, "'self' used inside OO function, use 'this'.", pr_scope->name); + QCC_PR_ParseWarning (WARN_SELFNOTTHIS, "'self' used inside OO function, use 'this'."); } if (!d.cast) @@ -10338,6 +10431,11 @@ static QCC_sref_t QCC_TryEvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool im } src.cast = cast; } + else if (flag_undefwordsize && ((totype == ev_pointer && src.cast->type == ev_string) || (totype == ev_pointer && src.cast->type == ev_string))) + { //pointer<->string is blocked when pointers are not bytes. if words are 8 bytes then everything is screwed, or if pointers cannot store byte offsets... + QCC_PR_ParseWarning(ERR_BADEXTENSION, "pointer<->string casts were disabled."); + src.cast = cast; + } //can cast any pointer type to void* else if (totype == ev_pointer && tmp->aux_type->type == ev_void && (src.cast->type == ev_pointer || src.cast->type == ev_function)) src.cast = cast; @@ -10385,9 +10483,7 @@ static QCC_sref_t QCC_TryEvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool im /*these casts are acceptable but probably an error (so warn when implicit)*/ else if ( /*you may explicitly cast between pointers and ints (strings count as pointers - WARNING: some strings may not be expressable as pointers)*/ - ((totype == ev_pointer || totype == ev_string || totype == ev_integer) && (src.cast->type == ev_pointer || src.cast->type == ev_string || src.cast->type == ev_integer)) - //functions can be explicitly cast from one to another - || (totype == ev_function && src.cast->type == ev_function) + ((totype == ev_pointer || totype == ev_string || totype == ev_integer || totype == ev_uint || totype == ev_function) && (src.cast->type == ev_pointer || src.cast->type == ev_string || src.cast->type == ev_integer || src.cast->type == ev_uint || src.cast->type == ev_function)) //ents->ints || ints->ents. WARNING: the integer value of ent types is engine specific. || (totype == ev_entity && src.cast->type == ev_integer) || (totype == ev_entity && src.cast->type == ev_float && flag_qccx) @@ -10570,6 +10666,8 @@ static QCC_ref_t *QCC_PR_RefTerm (QCC_ref_t *retbuf, unsigned int exprflags) } else if (e.cast->type == ev_pointer) //FIXME: arrays return QCC_PR_BuildRef(retbuf, REF_POINTER, e, nullsref, e.cast->aux_type, false); + else if (e.cast->type == ev_string) //FIXME: arrays + return QCC_PR_BuildRef(retbuf, REF_STRING, e, nullsref, type_integer, false); else if (e.cast->accessors) { struct accessor_s *acc; @@ -10733,7 +10831,9 @@ static QCC_ref_t *QCC_PR_RefTerm (QCC_ref_t *retbuf, unsigned int exprflags) sz = strlen(&strings[QCC_SRef_EvalConst(r->base)->string]) + 1; //sizeof("hello") includes the null, and is bytes not codepoints else { - sz = 4; //4 bytes per word. we don't support char/short (our string type is logically char*) + if (flag_undefwordsize) + QCC_PR_ParseWarning(ERR_BADEXTENSION, "sizeof was disabled."); + sz = VMWORDSIZE; //4 bytes per word. we don't support char/short (our string type is logically char*) if (r->type == REF_ARRAYHEAD && !r->index.cast) sz *= r->base.sym->arraysize; sz *= r->cast->size; @@ -11551,7 +11651,7 @@ QCC_sref_t QCC_LoadFromArray(QCC_sref_t base, QCC_sref_t index, QCC_type_t *t, p flags = STFL_PRESERVEA; QCC_UnFreeTemp(index); } - QCC_SupplyConversion(index, ev_integer, true); + index = QCC_SupplyConversion(index, ev_integer, true); } switch(t->type) { @@ -11890,6 +11990,7 @@ QCC_sref_t QCC_RefToDef(QCC_ref_t *ref, pbool freetemps) case ev_double: QCC_StoreSRefToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_D], ret, QCC_MakeDoubleConst(inc), NULL, STFL_PRESERVEA), false, !freetemps); break; + case ev_string: case ev_integer: QCC_StoreSRefToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], ret, QCC_MakeIntConst(inc), NULL, STFL_PRESERVEA), false, !freetemps); break; @@ -11990,12 +12091,12 @@ QCC_sref_t QCC_RefToDef(QCC_ref_t *ref, pbool freetemps) case REF_STRING: if (ref->cast->type == ev_float) { - idx = QCC_SupplyConversion(ref->index, ev_float, true); + idx = ref->index.cast?QCC_SupplyConversion(ref->index, ev_float, true):nullsref; return QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_C], ref->base, idx, NULL, freetemps?0:(STFL_PRESERVEA|STFL_PRESERVEB)); } else { - idx = QCC_SupplyConversion(ref->index, ev_integer, true); + idx = ref->index.cast?QCC_SupplyConversion(ref->index, ev_integer, true):nullsref; return QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_B], ref->base, idx, NULL, freetemps?0:(STFL_PRESERVEA|STFL_PRESERVEB)); } case REF_ACCESSOR: @@ -12114,12 +12215,12 @@ QCC_sref_t QCC_StoreSRefToRef(QCC_ref_t *dest, QCC_sref_t source, pbool readable switch(dest->type) { case REF_ARRAYHEAD: - QCC_PR_ParseWarning(ERR_PARSEERRORS, "left operand must be an l-value (add you mean %s[0]?)", QCC_GetSRefName(dest->base)); + QCC_PR_ParseWarning(ERR_PARSEERRORS, "left operand must be an l-value (did you mean %s[0]?)", QCC_GetSRefName(dest->base)); if (!preservedest) QCC_PR_DiscardRef(dest); break; default: - QCC_PR_ParseWarning(ERR_PARSEERRORS, "left operand must be an l-value (unsupported reference type)", QCC_GetSRefName(dest->base)); + QCC_PR_ParseWarning(ERR_PARSEERRORS, "left operand must be an l-value (unsupported reference type)"); if (!preservedest) QCC_PR_DiscardRef(dest); break; @@ -12174,17 +12275,23 @@ QCC_sref_t QCC_StoreSRefToRef(QCC_ref_t *dest, QCC_sref_t source, pbool readable case REF_STRING: { QCC_sref_t addr; + int op = (source.cast->type==ev_float)?OP_STOREP_C:OP_STOREP_B; + + if (op == OP_STOREP_C) + source = QCC_SupplyConversion(source, ev_float, true); + else + source = QCC_SupplyConversion(source, ev_integer, true); if (dest->index.sym && !QCC_OPCode_StorePOffset()) { //can't do an offset store yet... bake any index into the pointer. addr = dest->base; if (dest->index.cast) addr = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], addr, QCC_SupplyConversion(dest->index, ev_integer, true), NULL); - QCC_PR_StatementFlags(&pr_opcodes[OP_STOREP_C], source, addr, NULL, STFL_DISCARDRESULT|(preservedest?STFL_PRESERVEB:0)|(readable?STFL_PRESERVEA:0)); + QCC_PR_StatementFlags(&pr_opcodes[op], source, addr, NULL, STFL_DISCARDRESULT|(preservedest?STFL_PRESERVEB:0)|(readable?STFL_PRESERVEA:0)); } else { - QCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_C], source, dest->base, QCC_SupplyConversion(dest->index, ev_integer, true), false); + QCC_PR_SimpleStatement(&pr_opcodes[op], source, dest->base, dest->index.cast?QCC_SupplyConversion(dest->index, ev_integer, true):nullsref, false); } } break; @@ -12377,6 +12484,7 @@ void QCC_PR_DiscardRef(QCC_ref_t *ref) case ev_double: oval = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_D], oval, QCC_MakeDoubleConst(inc), NULL, 0); break; + case ev_string: case ev_integer: oval = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], oval, QCC_MakeIntConst(inc), NULL, 0); break; @@ -13940,6 +14048,10 @@ void QCC_PR_ParseStatement (void) ( !STRCMP ("_Bool", pr_token)) || (keyword_static && !STRCMP ("static", pr_token)) || (keyword_class && !STRCMP ("class", pr_token)) || + (keyword_struct && !STRCMP ("struct", pr_token)) || + (keyword_union && !STRCMP ("union", pr_token)) || + (keyword_enum && !STRCMP ("enum", pr_token)) || + (keyword_extern && !STRCMP ("extern", pr_token)) || (keyword_typedef && !STRCMP ("typedef", pr_token)) || (keyword_const && !STRCMP ("const", pr_token))) { @@ -15832,6 +15944,7 @@ static QCC_function_t *QCC_PR_GenerateBuiltinFunction (QCC_def_t *def, int built QCC_PR_ParseError(ERR_INTERNAL, "Too many functions - %i\nAdd '-max_functions %i' to the commandline", numfunctions, (numfunctions+4096)&~4095); func = &functions[numfunctions++]; func->filen = s_filen; + func->unitn = s_unitn; func->s_filed = s_filed; func->line = def->s_line; //FIXME if (builtinname==def->name) @@ -15904,6 +16017,7 @@ static QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *ty if (!func) func = &functions[numfunctions++]; func->filen = s_filen; + func->unitn = s_unitn; func->s_filed = s_filed; func->line = pr_source_line;//def?def->s_line:0; //FIXME func->name = def?def->name:""; @@ -16910,6 +17024,7 @@ QCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, const char *name, QCC_function_t *s } def->s_line = pr_source_line; + def->unitn = s_unitn; def->filen = s_filen; def->s_filed = s_filed; if (a>=0) @@ -17148,7 +17263,7 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, const char *name, struct QCC_functio QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s%s%s. %s%s%s, should be %s%s%s",col_symbol,name,col_none, col_type,TypeName(type, typebuf1, sizeof(typebuf1)),col_none, col_type,TypeName(def->type, typebuf2, sizeof(typebuf2)),col_none); } if (def->arraysize != arraysize && arraysize>=0) - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHARRAYSIZE, def, "Array sizes for redecleration of %s%s%s do not match",col_symbol,name,col_none); + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHARRAYSIZE, def, "Array sizes for redecleration of %s%s%s do not match (%i -> %i)",col_symbol,name,col_none, def->arraysize,arraysize); if (allocate && scope && !(flags & GDF_STATIC)) { if (pr_subscopedlocals) @@ -17192,7 +17307,7 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, const char *name, struct QCC_functio } //ignore it if its static in some other file. - if (def->isstatic && strcmp(def->filen, scope?scope->filen:s_filen)) + if (def->isstatic && strcmp(def->unitn, scope?scope->unitn:s_unitn)) { if (!foundstatic) foundstatic = def; //save it off purely as a warning. @@ -17254,8 +17369,10 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, const char *name, struct QCC_functio } else { - //unequal even when we're lax - QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s%s%s. %s%s%s, should be %s%s%s",col_symbol,name,col_none, col_type,TypeName(type, typebuf1, sizeof(typebuf1)),col_none, col_type,TypeName(def->type, typebuf2, sizeof(typebuf2)),col_none); + if (type->type == ev_function && type->vargs && !type->num_parms && def->type->type == ev_function) + ; //c89-style dumb redeclarations... + else //unequal even when we're lax + QCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, "Type mismatch on redeclaration of %s%s%s. %s%s%s, should be %s%s%s",col_symbol,name,col_none, col_type,TypeName(type, typebuf1, sizeof(typebuf1)),col_none, col_type,TypeName(def->type, typebuf2, sizeof(typebuf2)),col_none); } } } @@ -17279,7 +17396,7 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, const char *name, struct QCC_functio } } if (def->arraysize != arraysize && arraysize>=0) - QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCHARRAYSIZE, def, "Array sizes for redecleration of %s do not match",name); + QCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCHARRAYSIZE, def, "Array sizes for redecleration of %s do not match (%i->%i)",name, def->arraysize,arraysize); if (allocate && scope && !(flags & GDF_STATIC)) { if (allocate == 2) @@ -17518,15 +17635,35 @@ static pbool QCC_PR_GenerateInitializerType(QCC_def_t *basedef, QCC_sref_t tmp, else { tmp.sym->referenced = true; - if (!tmp.sym->initialized) + if (!basedef->scope && tmp.sym->reloc) { - //FIXME: we NEED to support relocs somehow - QCC_PR_ParseWarning(WARN_UNINITIALIZED, "initializer is not initialised, %s will be treated as 0", QCC_GetSRefName(tmp)); - QCC_PR_ParsePrintSRef(WARN_UNINITIALIZED, tmp); + QCC_def_t *dt; + if (!flag_pointerrelocs) + QCC_PR_ParseWarning(ERR_BADEXTENSION, "preinitialised pointer variables is disabled for this target"); + dt = QCC_PR_DummyDef(type_pointer, "$reloc", basedef->scope, 0, def.sym, def.ofs, false, GDF_CONST); + dt->reloc = tmp.sym->reloc; + dt->referenced = true; + dt->constant = 1; + dt->initialized = 1; + dt->strip = true; //engine does need to know it... but there should be some other def that we're mapping intohere. + + for (i = 0; (unsigned)i < type->size; i++) + def.sym->symboldata[def.ofs+i]._int = tmp.ofs + tmp.sym->reloc->symbolheader->arraylengthprefix; + + QCC_FreeTemp(tmp); + return ret; } if (basedef->initialized && !basedef->unused && !(flags & PIF_STRONGER)) - { + { //dupe initialisation. compare the two. + + if (!tmp.sym->initialized) + { + //FIXME: we NEED to support relocs somehow + QCC_PR_ParseWarning(WARN_UNINITIALIZED, "initializer is not initialised, %s will be treated as 0", QCC_GetSRefName(tmp)); + QCC_PR_ParsePrintSRef(WARN_UNINITIALIZED, tmp); + } + for (i = 0; (unsigned)i < type->size; i++) if (def.sym->symboldata[def.ofs+i]._int != tmp.sym->symboldata[tmp.ofs+i]._int) { @@ -17551,10 +17688,29 @@ static pbool QCC_PR_GenerateInitializerType(QCC_def_t *basedef, QCC_sref_t tmp, const int *srcdata = (const void*)QCC_SRef_EvalConst(tmp); if (!srcdata) { - QCC_PR_ParseWarning(WARN_NOTCONSTANT, "initializer is not initialised yet, %s will be treated as 0", QCC_GetSRefName(tmp)); - QCC_PR_ParsePrintSRef(WARN_NOTCONSTANT, tmp); - for (i = 0; (unsigned)i < type->size; i++) - def.sym->symboldata[def.ofs+i]._int = 0; + if ((!basedef->scope||basedef->isstatic) && def.cast->type == ev_function && def.sym->symboldata==basedef->symboldata) + { //set to a function which is not yet initialised. insert a function reloc at this location, so we can update it once it is actually known. + for (i = 0; (unsigned)i < type->size; i++) + { + QCC_def_t *dt; + dt = QCC_PR_DummyDef(type_function, "$relocf", basedef->scope, 0, def.sym, def.ofs, false, GDF_CONST); + dt->reloc = tmp.sym; + dt->referenced = true; + dt->constant = 1; + dt->initialized = 1; + dt->strip = true; //hide it. engine doesn't need to know. + + def.sym->symboldata[def.ofs+i]._int = 0; + } + } + else + { + QCC_PR_ParseWarning(WARN_NOTCONSTANT, "initializer for %s is not initialised yet, %s will be treated as 0", QCC_GetSRefName(def), QCC_GetSRefName(tmp)); + QCC_PR_ParsePrintSRef(WARN_NOTCONSTANT, tmp); + + for (i = 0; (unsigned)i < type->size; i++) + def.sym->symboldata[def.ofs+i]._int = 0; + } } else { @@ -17705,21 +17861,39 @@ QCC_sref_t QCC_PR_ParseInitializerType_Internal(int arraysize, QCC_def_t *basede if (arraysize) { - //arrays go recursive - QCC_PR_Expect("{"); - if (!QCC_PR_CheckToken("}")) + if (pr_token_type == tt_immediate && pr_immediate_type == type_string && def.cast->type == ev_integer/*fixme: char*/) { + if (pr_immediate_strlen > arraysize) //problem! + QCC_PR_ParseWarning (ERR_BADARRAYSIZE, "initializer-string too long"); + for (i = 0; i < arraysize; i++) { - ret &= QCC_PR_ParseInitializerType(0, basedef, def, flags); + tmp = QCC_MakeIntConst((i < pr_immediate_strlen)?pr_immediate_string[i]:0); + tmp = QCC_EvaluateCast(tmp, def.cast, true); + // QCC_ForceUnFreeDef(tmp.sym); + ret &= QCC_PR_GenerateInitializerType(basedef, tmp, def, def.cast, flags); def.ofs += def.cast->size; - if (!QCC_PR_CheckToken(",")) + } + QCC_PR_Lex(); + } + else + { + //arrays go recursive + QCC_PR_Expect("{"); + if (!QCC_PR_CheckToken("}")) + { + for (i = 0; i < arraysize; i++) { - QCC_PR_Expect("}"); - break; + ret &= QCC_PR_ParseInitializerType(0, basedef, def, flags); + def.ofs += def.cast->size; + if (!QCC_PR_CheckToken(",")) + { + QCC_PR_Expect("}"); + break; + } + if (QCC_PR_CheckToken("}")) + break; } - if (QCC_PR_CheckToken("}")) - break; } } } @@ -18141,7 +18315,7 @@ QCC_type_t *QCC_PR_ParseEnum(pbool flags) { const char *name = NULL; QCC_sref_t sref; - unsigned longlong next_i = flags?1:0; + puint64_t next_i = flags?1:0; double next_f = next_i; struct accessor_s *acc; QCC_type_t *enumtype = NULL, *basetype; @@ -18289,7 +18463,7 @@ QCC_type_t *QCC_PR_ParseEnum(pbool flags) if (flags) { int bits = 0; - unsigned longlong i; + puint64_t i; if (basetype->type == ev_float) i = (longlong)next_f; else if (basetype->type == ev_double) @@ -18315,7 +18489,7 @@ QCC_type_t *QCC_PR_ParseEnum(pbool flags) i>>=1u; } if (bits > 1) //be mute about 0. - QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, "enumflags - %#x(%i) has multiple bits set", next_i, next_i); + QCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, "enumflags - %#"pPRIx64"(%"pPRIi64") has multiple bits set", next_i, next_i); } } @@ -18377,6 +18551,122 @@ QCC_type_t *QCC_PR_ParseEnum(pbool flags) return enumtype?enumtype:basetype; } +QCC_sref_t QCC_PR_ParseDefArray(QCC_type_t **type, char *name, pbool istypedef) +{ + QCC_sref_t exr; + QCC_sref_t dynlength = nullsref; + size_t dim[16]; + int dims = 0; + do + { + dim[dims] = 0; //not known yet... + + if (dims== 0 && QCC_PR_CheckToken("]")) + { + //not specified, but that may be okay... perhaps. + } + else + { + const QCC_eval_t *eval; + exr = QCC_PR_Expression(TOP_PRIORITY, 0); + eval = QCC_SRef_EvalConst(exr); + if (eval) + { + if (exr.cast->type == ev_integer) dim[dims] = eval->_int; + else if (exr.cast->type == ev_uint) dim[dims] = eval->_uint; + else if (exr.cast->type == ev_int64) dim[dims] = eval->i64; + else if (exr.cast->type == ev_uint64) dim[dims] = eval->u64; + else if (exr.cast->type == ev_float) + { + dim[dims] = eval->_float; + if ((float)dim[dims] != eval->_float) + QCC_PR_ParseWarning (ERR_BADARRAYSIZE, "Array has non-integral length"); + } + else if (exr.cast->type == ev_double) + { + dim[dims] = eval->_double; + if ((double)dim[dims] != eval->_double) + QCC_PR_ParseWarning (ERR_BADARRAYSIZE, "Array has non-integral length"); + } + else + QCC_PR_ParseError (ERR_BADARRAYSIZE, "Definition of array (%s) size is not of a numerical value", name); + QCC_FreeTemp(exr); + } + else if (!pr_scope) + dynlength = exr; + else if (dims) + { + QCC_PR_ParseWarning (ERR_BADARRAYSIZE, "Array has incomplete element"); + QCC_FreeTemp(exr); + } + QCC_PR_Expect("]"); + } + dims++; + } while(QCC_PR_CheckToken ("[")); + + if (dim[0] == 0 && !istypedef && !dynlength.cast) + { + char *oldprfile = pr_file_p; + int oldline = pr_token_line_last; + int oldsline = pr_source_line; + int depth; + + //FIXME: preprocessor will hate this with a passion. + if (QCC_PR_CheckToken("=")) + { + if (pr_token_type == tt_immediate && pr_immediate_type == type_string) + dim[0] = pr_immediate_strlen+1; + else + { + QCC_PR_Expect("{"); + dim[0]++; + depth = 1; + while(1) + { + if(pr_token_type == tt_eof) + { + QCC_PR_ParseError (ERR_EOF, "EOF inside definition of %s", name); + break; + } + else if (depth == 1 && QCC_PR_CheckToken(",")) + { + if (QCC_PR_CheckToken("}")) + break; + dim[0]++; + } + else if (QCC_PR_CheckToken("{") || QCC_PR_CheckToken("[")) + depth++; + else if (QCC_PR_CheckToken("}") || QCC_PR_CheckToken("]")) + { + depth--; + if (depth == 0) + break; + } + else + QCC_PR_Lex(); + } + } + + pr_file_p = oldprfile; + pr_token_line = oldline; + pr_source_line = oldsline; + + pr_token_type = tt_punct; + pr_immediate_type = type_void; + strcpy(pr_token, "="); + } + } + + while (dims-- > 1) + { //go backwards. last parsed is the innermost array. + *type = QCC_GenArrayType(*type, dim[dims]); + } + + if (!dynlength.cast) + dynlength.ofs = dim[0]; + return dynlength; +} + /* ================ PR_ParseDefs @@ -18408,6 +18698,7 @@ void QCC_PR_ParseDefs (const char *classname, pbool fatal_unused) pbool doweak = false; pbool forceused = false; pbool accumulate = false; + pbool hadmodifier = false; //if true then its a def. and we assume int when the tye was omitted. const char *deprecated = NULL; int arraysize; unsigned int gd_flags; @@ -18696,11 +18987,17 @@ void QCC_PR_ParseDefs (const char *classname, pbool fatal_unused) } else break; + hadmodifier = true; } - basetype = QCC_PR_ParseType (false, false); + basetype = QCC_PR_ParseType (false, hadmodifier); if (basetype == NULL) //ignore - return; + { + if (hadmodifier) + basetype = type_integer; + else + return; + } inlinefunction = type_inlinefunction; @@ -18856,84 +19153,16 @@ void QCC_PR_ParseDefs (const char *classname, pbool fatal_unused) //check for an array dynlength = nullsref; - arraysize = 0; - while(!dynlength.cast && QCC_PR_CheckToken ("[")) + if (QCC_PR_CheckToken ("[")) { - char *oldprfile = pr_file_p; - int oldline = pr_source_line; - int depth; - if (arraysize) - type = QCC_GenArrayType(type, arraysize); - arraysize = 0; - if (QCC_PR_CheckToken("]")) - { - //FIXME: preprocessor will hate this with a passion. - if (!istypedef && QCC_PR_CheckToken("=")) - { - QCC_PR_Expect("{"); - arraysize++; - depth = 1; - while(1) - { - if(pr_token_type == tt_eof) - { - QCC_PR_ParseError (ERR_EOF, "EOF inside definition of %s", name); - break; - } - else if (depth == 1 && QCC_PR_CheckToken(",")) - { - if (QCC_PR_CheckToken("}")) - break; - arraysize++; - } - else if (QCC_PR_CheckToken("{") || QCC_PR_CheckToken("[")) - depth++; - else if (QCC_PR_CheckToken("}") || QCC_PR_CheckToken("]")) - { - depth--; - if (depth == 0) - break; - } - else - QCC_PR_Lex(); - } - } - pr_file_p = oldprfile; - pr_source_line = oldline; - QCC_PR_Lex(); - } - else - { - const QCC_eval_t *eval; + dynlength = QCC_PR_ParseDefArray(&type, name, istypedef); + if (dynlength.cast) arraysize = 0; - dynlength = QCC_PR_Expression(TOP_PRIORITY, 0); - eval = QCC_SRef_EvalConst(dynlength); - if (eval) - { - if (dynlength.cast->type == ev_integer) - arraysize = eval->_int; - else if (dynlength.cast->type == ev_float) - { - int i = eval->_float; - if ((float)i == eval->_float) - arraysize = i; - } - else - QCC_PR_ParseError (ERR_BADARRAYSIZE, "Definition of array (%s) size is not of a numerical value", name); - QCC_FreeTemp(dynlength); - dynlength = nullsref; - } - else if (!pr_scope) - dynlength = nullsref; - QCC_PR_Expect("]"); - } - - if (arraysize < 1 && !dynlength.cast) - { - QCC_PR_ParseError (ERR_BADARRAYSIZE, "Definition of array (%s) size is not of a numerical value", name); - arraysize=1; //grrr... - } + else + arraysize = dynlength.ofs; } + else + arraysize = 0; if (QCC_PR_CheckToken("(")) { @@ -19131,7 +19360,7 @@ void QCC_PR_ParseDefs (const char *classname, pbool fatal_unused) if (aliasof) QCC_PR_ParseError (ERR_SHAREDINITIALISED, "alias %s may not be initialised", name); if (def->shared) - QCC_PR_ParseError (ERR_SHAREDINITIALISED, "shared values may not be assigned an initial value", name); + QCC_PR_ParseError (ERR_SHAREDINITIALISED, "shared values may not be assigned an initial value"); //if weak, only use the first non-weak version of the function if (autoprototype || dostrip || (def->initialized && doweak) || (!def->initialized && doweak && dowrap)) @@ -19347,7 +19576,7 @@ pbool QCC_PR_CompileFile (char *string, char *filename) compilingfile = filename; - s_filen = tmp = qccHunkAlloc(strlen(filename)+1); + s_unitn = s_filen = tmp = qccHunkAlloc(strlen(filename)+1); strcpy(tmp, filename); if (opt_filenames) { @@ -19522,11 +19751,12 @@ pbool QCC_PR_CompileFile (char *string, char *filename) return (pr_error_count == 0); } -pbool QCC_Include(char *filename) +pbool QCC_Include(char *filename, pbool newunit) { char *newfile; char fname[512]; char *opr_file_p; + const char *os_unitn; const char *os_filen; QCC_string_t os_filed; int opr_source_line; @@ -19534,6 +19764,7 @@ pbool QCC_Include(char *filename) struct qcc_includechunk_s *oldcurrentchunk; ocompilingfile = compilingfile; + os_unitn = s_unitn; os_filen = s_filen; os_filed = s_filed; opr_source_line = pr_source_line; @@ -19548,6 +19779,7 @@ pbool QCC_Include(char *filename) currentchunk = oldcurrentchunk; compilingfile = ocompilingfile; + s_unitn = os_unitn; s_filen = os_filen; s_filed = os_filed; pr_source_line = opr_source_line; diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 08217f6fc..30cb79a1f 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -2036,7 +2036,7 @@ static void QCC_PR_LexNumber (void) { pr_file_p++; if (*pr_file_p >= '0' && *pr_file_p <= '9') - QCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, "A leading 0 is interpreted as base-8."); + QCC_PR_ParseWarning(WARN_OCTAL_IMMEDIATE, "A leading 0 is interpreted as base-8."); base = 8; pr_token[tokenlen++] = '0'; @@ -2396,7 +2396,7 @@ static void QCC_PR_LexPunctuation (void) if (pr_file_p[0] == '*' && pr_file_p[1] == '*' && flag_dblstarexp) { //for compat with gmqcc. fteqcc uses *^ internally (which does not conflict with multiplying by dereferenced pointers - sucks for MSCLR c++ syntax) - QCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, "** is unsafe around pointers, use *^ instead.", pr_token); + QCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, "** is unsafe around pointers, use *^ instead."); strcpy (pr_token, "*^"); pr_file_p += 2; return; @@ -3680,6 +3680,8 @@ int QCC_PR_CheckCompConst(void) || *end == '}' || *end == ';' || *end == ':' + || *end == '<' + || *end == '>' || *end == ',' || *end == '.' || *end == '#') @@ -4486,7 +4488,7 @@ char *QCC_PR_ParseName (void) if (pr_token_type != tt_name) { if (pr_token_type == tt_eof) - QCC_PR_ParseError (ERR_EOF, "unexpected EOF", pr_token); + QCC_PR_ParseError (ERR_EOF, "unexpected EOF"); else if (strcmp(pr_token, "...")) //seriously? someone used '...' as an intrinsic NAME? QCC_PR_ParseError (ERR_NOTANAME, "\"%s%s%s\" - not a name", col_name, pr_token, col_none); } @@ -4796,7 +4798,7 @@ char *TypeName(QCC_type_t *type, char *buffer, int buffersize) if (buffersize < 0) return buffer; TypeName(type->aux_type, buffer, buffersize-2); - Q_strlcat(buffer, " *", buffersize); + Q_strlcat(buffer, "*", buffersize); return buffer; } @@ -4811,6 +4813,7 @@ char *TypeName(QCC_type_t *type, char *buffer, int buffersize) if (type->type == ev_function) { int args = type->num_parms; + size_t l; pbool vargs = type->vargs; unsigned int i; Q_strlcat(buffer, type->aux_type->name, buffersize); @@ -4818,29 +4821,30 @@ char *TypeName(QCC_type_t *type, char *buffer, int buffersize) for (i = 0; i < type->num_parms; ) { if (type->params[i].out) - Q_strlcat(buffer, "inout ", buffersize); + Q_strlcat(buffer, "inout ", buffersize-1); if (type->params[i].optional) - Q_strlcat(buffer, "optional ", buffersize); + Q_strlcat(buffer, "optional ", buffersize-1); args--; - Q_strlcat(buffer, type->params[i].type->name, buffersize); + l = strlen(buffer); + TypeName(type->params[i].type, buffer+l, buffersize-1-l); if (type->params[i].paramname && *type->params[i].paramname) { - Q_strlcat(buffer, " ", buffersize); - Q_strlcat(buffer, type->params[i].paramname, buffersize); + Q_strlcat(buffer, " ", buffersize-1); + Q_strlcat(buffer, type->params[i].paramname, buffersize-1); } if (type->params[i].defltvalue.cast) { - Q_strlcat(buffer, " = ", buffersize); - Q_strlcat(buffer, QCC_VarAtOffset(type->params[i].defltvalue), buffersize); + Q_strlcat(buffer, " = ", buffersize-1); + Q_strlcat(buffer, QCC_VarAtOffset(type->params[i].defltvalue), buffersize-1); } if (++i < type->num_parms || vargs) - Q_strlcat(buffer, ", ", buffersize); + Q_strlcat(buffer, ", ", buffersize-1); } if (vargs) - Q_strlcat(buffer, "...", buffersize); + Q_strlcat(buffer, "...", buffersize-1); Q_strlcat(buffer, ")", buffersize); } else if (type->type == ev_entity && type->parentclass) @@ -5014,7 +5018,12 @@ QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype) if (definenames) pr_parm_argcount_name = NULL; - if (!QCC_PR_CheckToken (")")) + if (QCC_PR_CheckToken (")")) + { + if (!flag_qcfuncs) + ftype->vargs = true; //'void name()' is vargs/undefined in C89 (disallowed in c99, interpreted as void in c23). + } + else { do { @@ -5120,7 +5129,7 @@ QCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype) { if (QCC_PR_CheckToken("]")) //length omitted. just treat it as a pointer...? { - QCC_PR_ParseError(0, "unsized array argument\n"); + //QCC_PR_ParseWarning(ERR_BADARRAYSIZE, "unsized array argument"); paramlist[numparms].type = QCC_PointerTypeTo(paramlist[numparms].type); } else @@ -5481,17 +5490,897 @@ struct accessor_s *QCC_PR_ParseAccessorMember(QCC_type_t *classtype, pbool isinl extern char *basictypenames[]; extern QCC_type_t **basictypes[]; +static QCC_type_t *QCC_PR_ParseStruct(etype_t structtype) +{ + QCC_type_t *newt, *type, *newparm; + struct QCC_typeparam_s *parms = NULL, *oldparm; + int numparms = 0; + int ofs; + unsigned int arraysize; + char *parmname; + + pbool isnonvirt = false; + pbool isstatic = false; + pbool isvirt = false; + pbool definedsomething = false; + + if (QCC_PR_CheckToken("{")) + { + //nameless struct + newt = QCC_PR_NewType(structtype==ev_union?"":"", structtype, false); + } + else + { + QCC_type_t *parenttype; + char *tname = QCC_PR_ParseName(); + + if (structtype == ev_struct && QCC_PR_CheckToken(":")) + { + char *parentname = QCC_PR_ParseName(); + parenttype = QCC_TypeForName(parentname); + if (!parenttype) + QCC_PR_ParseError(ERR_NOTANAME, "Parent type %s was not yet defined", parentname); + if (parenttype->type != ev_struct) + QCC_PR_ParseError(ERR_NOTANAME, "Parent type %s is not a struct", parentname); + } + else + parenttype = NULL; + + newt = QCC_TypeForName(tname); + if (!newt) + { + newt = QCC_PR_NewType(tname, ev_struct, true); + newt->parentclass = parenttype; + } + else if (!newt->size && !newt->parentclass) + newt->parentclass = parenttype; + 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->parentclass) + newt->size = newt->parentclass->size; + else + newt->size=0; + + type = NULL; + + newparm = NULL; + for (;;) + { + //in qc, functions are assignable references like anything else, so no modifiers means the qc will need to assign to it somewhere. + //virtual functions are still references, but we initialise them somewhere + //nonvirtual functions and static functions are kinda the same thing + QCC_sref_t defaultval; + if (QCC_PR_CheckToken("}")) + { + if (newparm) + QCC_PR_ParseError(ERR_EXPECTED, "missing semi-colon"); + break; + } + else if (QCC_PR_CheckToken(";")) + { + newparm = NULL; + continue; + } + else if (QCC_PR_CheckToken(",")) + { //same as last type, unless initial/after-semicolon + if (!newparm) + QCC_PR_ParseError(ERR_EXPECTED, "element missing type"); + } + else + { //new type! + if (newparm) + QCC_PR_ParseError(ERR_EXPECTED, "missing semi-colon"); //allow a missing semi-colon on functions, for mixed-style functions. + + //reset these... + isnonvirt = false; + isstatic = false; + isvirt = false; + + //parse field modifiers + if (QCC_PR_CheckKeyword(1, "public")) + { + /*ispublic = true;*/ + QCC_PR_Expect(":"); + continue; + } + else if (QCC_PR_CheckKeyword(1, "private")) + { + /*isprivate = true;*/ + QCC_PR_Expect(":"); + continue; + } + else if (QCC_PR_CheckKeyword(1, "protected")) + { + /*isprotected = true;*/ + QCC_PR_Expect(":"); + continue; + } + + //if (QCC_PR_CheckKeyword(1, "nonvirtual")) + // isnonvirt = true; + //else + if (QCC_PR_CheckKeyword(1, "static")) + isstatic = true; + else if (QCC_PR_CheckKeyword(1, "virtual")) + isvirt = true; +// else if (QCC_PR_CheckKeyword(1, "ignore")) +// isignored = true; +// else if (QCC_PR_CheckKeyword(1, "strip")) +// isignored = true; + + //now parse the actual type. + newparm = QCC_PR_ParseType(false, false); + definedsomething = false; + } + type = newparm; + + while (QCC_PR_CheckToken("*")) + type = QCC_PointerTypeTo(type); + + arraysize = 0; + if (QCC_PR_CheckToken(";")) + { //annonymous structs do weird scope stuff. + if (!definedsomething && (type->type != ev_struct && type->type != ev_union)) + { + if (flag_qcfuncs && type->type == ev_function && type->aux_type == newt && QCC_PR_PeekToken(";")) + QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "constructors are not supported in structs at this time"); + else + QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "declaration does not declare anything (%s)", newparm->name); + } + newparm = NULL; + parmname = ""; + } + else + { + if (QCC_PR_CheckToken("(")) + { + QCC_PR_CheckToken("*"); //this is fine... + parmname = QCC_PR_ParseName(); + QCC_PR_Expect(")"); + } + else + parmname = QCC_PR_ParseName(); + definedsomething = true; + while (QCC_PR_CheckToken("[")) + { + int nsize=QCC_PR_IntConstExpr(); + if (arraysize) + type = QCC_GenArrayType(type, arraysize); + if (!nsize) + QCC_PR_ParseError(ERR_NOTANAME, "cannot cope with 0-sized arrays"); + QCC_PR_Expect("]"); + arraysize = nsize; + } + + if (QCC_PR_CheckToken("(")) + type = QCC_PR_ParseFunctionType(false, type); + } + + if (type == newt || ((type->type == ev_struct || type->type == ev_union) && !type->size)) + { + QCC_PR_ParseWarning(ERR_NOTANAME, "type %s not fully defined yet", type->name); + continue; + } + + if (QCC_PR_CheckToken(":")) + { + QCC_PR_IntConstExpr(); + QCC_PR_ParseWarning(WARN_IGNOREDKEYWORD, "bitfields are not supported"); + } + + if ((isnonvirt || isvirt) && type->type != ev_function) + QCC_PR_ParseWarning(ERR_INTERNAL, "[non]virtual members must be functions"); + + //static members are technically just funny-named globals, and do not generate fields. + if (isnonvirt || isstatic || isvirt) + { //either way its a regular global. the difference being static has no implicit this/self argument. + QCC_def_t *d; + char membername[2048]; + if (!isstatic) + type = QCC_PR_MakeThiscall(type, newt); + + QC_snprintfz(membername, sizeof(membername), "%s::%s", newt->name, parmname); + d = QCC_PR_GetDef(type, membername, NULL, true, 0, (type->type==ev_function)?GDF_CONST:0); + if (QCC_PR_CheckToken("=") || (type->type == ev_function && QCC_PR_PeekToken("{"))) + { +//FIXME: methods cannot be compiled yet, as none of the fields are not actually defined yet. + pr_classtype = newt; + QCC_PR_ParseInitializerDef(d, 0); + pr_classtype = NULL; + } + QCC_FreeDef(d); + if (!QCC_PR_PeekToken(",")) + newparm = NULL; + + if (isvirt) + { + defaultval.ofs = 0; + defaultval.cast = d->type; + defaultval.sym = d; + } + else + continue; + } + else + { + defaultval.cast = NULL; + if (QCC_PR_CheckToken("=")) + { + defaultval = QCC_PR_ParseDefaultInitialiser(type); + QCC_PR_ParseWarning(ERR_INTERNAL, "TODO: pre-initialised struct members are not implemented yet"); + } + } + + parms = realloc(parms, sizeof(*parms) * (numparms+4)); + oldparm = QCC_PR_FindStructMember(newt, parmname, &ofs); + if (oldparm && oldparm->arraysize == arraysize && !typecmp_lax(oldparm->type, type)) + { + if (!isvirt) + continue; + } + else if (structtype == ev_union) + { + ofs = 0; + if (type->size*(arraysize?arraysize:1) > newt->size) + newt->size = type->size*(arraysize?arraysize:1); + } + else + { + ofs = newt->size; + newt->size += type->size*(arraysize?arraysize:1); + } + + parms[numparms].ofs = ofs; + parms[numparms].arraysize = arraysize; + parms[numparms].out = false; + parms[numparms].optional = false; + parms[numparms].isvirtual = isvirt; + parms[numparms].paramname = parmname; + parms[numparms].type = type; + parms[numparms].defltvalue = defaultval; + numparms++; + + /*if (type->type == ev_vector && arraysize == 0) + { //add in vec_x/y/z members too. + int c; + for (c = 0; c < 3; c++) + { + parms[numparms].ofs = ofs + c; + parms[numparms].arraysize = arraysize; + parms[numparms].out = false; + parms[numparms].optional = true; + parms[numparms].isvirtual = isvirt; + parms[numparms].paramname = qccHunkAlloc(strlen(parmname)+3); + sprintf(parms[numparms].paramname, "%s_%c", parmname, 'x'+c); + parms[numparms].type = type_float; + parms[numparms].defltvalue = nullsref; + numparms++; + } + }*/ + } + if (!numparms) + QCC_PR_ParseError(ERR_NOTANAME, "%s %s has no members", structtype==ev_union?"union":"struct", newt->name); + + newt->num_parms = numparms; + newt->params = qccHunkAlloc(sizeof(*type->params) * numparms); + memcpy(newt->params, parms, sizeof(*type->params) * numparms); + free(parms); + + return newt; +} + +QCC_type_t *QCC_PR_ParseEntClass(void) +{ + QCC_type_t *newt, *type, *fieldtype, *newparm; + char membername[2048]; + char *classname; + int forwarddeclaration; + int numparms = 0; + struct QCC_typeparam_s *parms = NULL; + char *parmname; + int arraysize; + pbool redeclaration; + int basicindex; + QCC_def_t *d; + QCC_type_t *pc; + QCC_type_t *basetype; + pbool found = false; + int assumevirtual = 0; //0=erk, 1=yes, -1=no + + parmname = QCC_PR_ParseName(); + classname = qccHunkAlloc(strlen(parmname)+1); + strcpy(classname, parmname); + + newt = 0; + + if (QCC_PR_CheckToken(":")) + { + char *parentname = QCC_PR_ParseName(); + fieldtype = QCC_TypeForName(parentname); + if (!fieldtype) + QCC_PR_ParseError(ERR_NOTANAME, "Parent class %s was not yet defined", parentname); + + //FIXME: we should allow inheriting from 'object' too. + if (fieldtype->type != ev_entity) + QCC_PR_ParseError(ERR_NOTANAME, "Parent type %s is not a class/entity", parentname); + forwarddeclaration = false; + + QCC_PR_Expect("{"); + } + else + { + fieldtype = type_entity; + forwarddeclaration = !QCC_PR_CheckToken("{"); + } + + newt = QCC_TypeForName(classname); + if (newt && newt->num_parms != 0) + redeclaration = true; + else + redeclaration = false; + + if (!newt) + { + newt = QCC_PR_NewType(classname, fieldtype->type, true); + newt->size=type_entity->size; + } + + type = NULL; + + if (forwarddeclaration) + return newt; + + if (pr_scope) + QCC_PR_ParseError(ERR_REDECLARATION, "Declaration of class %s%s%s within function", col_type,classname,col_none); + if (redeclaration && fieldtype != newt->parentclass) + QCC_PR_ParseError(ERR_REDECLARATION, "Parent class changed on redeclaration of %s%s%s", col_type,classname,col_none); + newt->parentclass = fieldtype; + newt->filen = s_filen; + newt->line = pr_source_line; + + if (QCC_PR_CheckToken(",")) + QCC_PR_ParseError(ERR_NOTANAME, "member missing name"); + while (!QCC_PR_CheckToken("}")) + { + unsigned int gdf_flags; + + pbool wasvirt = false; + pbool wasnonvirt = false; + pbool wasstatic = false; + pbool isconst = false; + pbool isvar = false; + pbool isignored = false; + pbool isinline = false; + pbool isget = false; + pbool isset = false; +// pbool ispublic = false; +// pbool isprivate = false; +// pbool isprotected = false; + pbool firstkeyword = true; + + while(1) + { + if (QCC_PR_CheckKeyword(1, "nonvirtual")) + { + if (firstkeyword && QCC_PR_CheckToken(":")) + { //fteqcc-specific + assumevirtual = -1; + continue; + } + wasnonvirt = true; + } + else if (QCC_PR_CheckKeyword(1, "static")) + wasstatic = true; + else if (QCC_PR_CheckKeyword(1, "const")) + isconst = wasstatic = true; + else if (QCC_PR_CheckKeyword(1, "var")) + isvar = true; + else if (QCC_PR_CheckKeyword(1, "virtual")) + { + if (firstkeyword && QCC_PR_CheckToken(":")) + { //fteqcc-specific + assumevirtual = 1; + continue; + } + wasvirt = true; + } + else if (QCC_PR_CheckKeyword(1, "ignore")) + isignored = true; + else if (QCC_PR_CheckKeyword(1, "strip")) + isignored = true; + else if (QCC_PR_CheckKeyword(1, "inline")) + isinline = true; + else if (QCC_PR_CheckKeyword(1, "get")) + isget = true; + else if (QCC_PR_CheckKeyword(1, "set")) + isset = true; + else if (firstkeyword && QCC_PR_CheckKeyword(1, "public")) + { + /*ispublic = true;*/ + QCC_PR_Expect(":"); + continue; + } + else if (firstkeyword && QCC_PR_CheckKeyword(1, "private")) + { + QCC_PR_Expect(":"); + /*isprivate = true; */ + continue; + } + else if (firstkeyword && QCC_PR_CheckKeyword(1, "protected")) + { + QCC_PR_Expect(":"); + /*isprotected = true;*/ QCC_PR_Expect(":"); + continue; + } + else + break; + firstkeyword = false; + } + if (isget || isset) + { + if (wasvirt) + QCC_PR_ParseWarning(ERR_INTERNAL, "virtual accessors are not supported at this time"); + if (wasstatic) + QCC_PR_ParseError(ERR_INTERNAL, "static accessors are not supported"); + QCC_PR_ParseAccessorMember(newt, isinline, isset); + QCC_PR_CheckToken(";"); + continue; + } + + basetype = QCC_PR_ParseType(false, false); + + if (!basetype) + QCC_PR_ParseError(ERR_INTERNAL, "In class %s, expected type, found %s", classname, pr_token); + + if (basetype->type == ev_struct || basetype->type == ev_union) //we wouldn't be able to handle it. + QCC_PR_ParseError(ERR_INTERNAL, "Struct or union in class %s", classname); + + + for (;basetype;) + { + pbool havebody = false; + pbool isnull = false; + pbool isstatic = wasstatic; + pbool isvirt = wasvirt; + pbool isnonvirt = wasnonvirt; + + if (flag_qcfuncs && basetype->type == ev_function && basetype->aux_type == newt && QCC_PR_PeekToken(";")) + { //C++ style constructor (ie: missing return type followed by class name) + //swap the class out for the appropriate function type... + newparm = QCC_PR_GenFunctionType(type_void, basetype->params, basetype->num_parms); + parmname = classname; + arraysize = 0; + } + else if (!flag_qcfuncs && basetype == newt && QCC_PR_CheckToken("(")) + { + newparm = QCC_PR_ParseFunctionType(false, type_void); + parmname = classname; + arraysize = 0; + } + else + { + parmname = QCC_PR_ParseName(); + if (QCC_PR_CheckToken("[")) + { + arraysize = QCC_PR_IntConstExpr(); + QCC_PR_Expect("]"); + } + else + arraysize = 0; + + if (QCC_PR_CheckToken("(")) + { + //int fnc(), fld; is valid. + newparm = QCC_PR_ParseFunctionType(false, basetype); + } + else + newparm = basetype; + } + + if (newparm->type == ev_function) + { + if (!strcmp(classname, parmname)) + { + if (isstatic) + QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "Constructor %s::%s should not be static.", classname, pr_token); + if (!isvirt) + isnonvirt = true;//silently promote constructors to static + if (newparm->aux_type->type != ev_void) + QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "Constructor %s::%s does not return void.", classname, pr_token); + if (newparm->num_parms != 0 || newparm->vargs) + QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "Constructor arguments are not supported at this time %s::%s. Use named fields instead.", classname, pr_token); + } + else if (!isvirt && !isnonvirt && !isstatic) + { + if (assumevirtual == 1) + isvirt = true; + else if (assumevirtual == -1) + isnonvirt = true; + else + { + QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "%s::%s was not qualified. Assuming non-virtual.", classname, parmname); + isnonvirt = true; + } + } + else if (isvirt+isnonvirt+isstatic != 1) + QCC_PR_ParseError(ERR_INTERNAL, "Multiple conflicting qualifiers on %s::%s.", classname, pr_token); + + if (isstatic) + { //static and non-virtual functions differ only in that static should not have a 'this' available. however there's no hiding 'self' for calling in to other functions. + isstatic = false; + isnonvirt = true; +// QCC_PR_ParseError(ERR_INTERNAL, "%s::%s static member functions are not supported at this time.", classname, parmname); + } + } + else + { + if (isvirt||isnonvirt) + QCC_Error(ERR_INTERNAL, "virtual keyword on member that is not a function"); + if (isinline) + QCC_Error(ERR_INTERNAL, "inline keyword on member that is not a function"); + } + + if (QCC_PR_CheckToken("=")) + havebody = true; + else if (newparm->type == ev_function && QCC_PR_PeekToken("{")) + havebody = true; + if (isinline && (!havebody || isvirt)) + QCC_Error(ERR_INTERNAL, "inline keyword on function prototype or virtual function"); + + gdf_flags = 0; + if ((newparm->type == ev_function && !arraysize && !isvar) || isconst) + gdf_flags = GDF_CONST; + + if (havebody) + { + QCC_def_t *def; + if (pr_scope) + QCC_Error(ERR_INTERNAL, "Nested function declaration"); + + isnull = (QCC_PR_CheckImmediate("0") || QCC_PR_CheckImmediate("0i")); + QC_snprintfz(membername, sizeof(membername), "%s::%s", classname, parmname); + if (isnull) + { + if (isignored) + def = NULL; + else + { + def = QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags); + def->symboldata[def->ofs].function = 0; + def->initialized = 1; + } + } + else + { + if (isignored) + def = NULL; + else + def = QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags); + + if (newparm->type != ev_function && !isstatic) + QCC_Error(ERR_INTERNAL, "Can only initialise member functions"); + else + { + if (autoprototype || isignored) + { + if (QCC_PR_CheckToken("[")) + { + while (!QCC_PR_CheckToken("]")) + { + if (pr_token_type == tt_eof) + break; + QCC_PR_Lex(); + } + } + QCC_PR_Expect("{"); + + { + int blev = 1; + //balance out the { and } + while(blev) + { + if (pr_token_type == tt_eof) + break; + if (QCC_PR_CheckToken("{")) + blev++; + else if (QCC_PR_CheckToken("}")) + blev--; + else + QCC_PR_Lex(); //ignore it. + } + } + } + else + { + pr_classtype = newt; + QCC_PR_ParseInitializerDef(def, 0); + pr_classtype = NULL; + /* + f = QCC_PR_ParseImmediateStatements (def, newparm); + pr_classtype = NULL; + pr_scope = NULL; + def->symboldata[def->ofs].function = f - functions; + f->def = def; + def->initialized = 1;*/ + } + } + } + if (def) + QCC_FreeDef(def); + + if (!isvirt && !isignored) + { + QCC_def_t *fdef; + QCC_type_t *pc; + unsigned int i; + + for (pc = newt->parentclass; pc; pc = pc->parentclass) + { + for (i = 0; i < pc->num_parms; i++) + { + if (!strcmp(pc->params[i].paramname, parmname)) + { + QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "%s::%s is virtual inside parent class '%s'. Did you forget the 'virtual' keyword?", newt->name, parmname, pc->name); + break; + } + } + if (i < pc->num_parms) + break; + } + if (!pc) + { + fdef = QCC_PR_GetDef(NULL, parmname, NULL, false, 0, GDF_CONST); + if (fdef && fdef->type->type == ev_field) + { + QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "%s::%s is virtual inside parent class 'entity'. Did you forget the 'virtual' keyword?", newt->name, parmname); + QCC_PR_ParsePrintDef(0, fdef); + } + else if (fdef) + { + QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "%s::%s shadows a global", newt->name, parmname); + QCC_PR_ParsePrintDef(0, fdef); + } + if (fdef) + QCC_FreeDef(fdef); + } + } + } + + if (!QCC_PR_CheckToken(",")) + { + QCC_PR_Expect(";"); + basetype = NULL; + } + + if (isignored) //member doesn't really exist + continue; + + //static members are technically just funny-named globals, and do not generate fields. + if (isnonvirt || isstatic || (newparm->type == ev_function && !arraysize)) + { + QC_snprintfz(membername, sizeof(membername), "%s::%s", classname, parmname); + QCC_FreeDef(QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags)); + + if (isnonvirt || isstatic) + continue; + } + + + fieldtype = QCC_PR_NewType(parmname, ev_field, false); + fieldtype->aux_type = newparm; + fieldtype->size = newparm->size; + + parms = realloc(parms, sizeof(*parms) * (numparms+1)); + parms[numparms].ofs = 0; + parms[numparms].out = false; + parms[numparms].optional = false; + parms[numparms].isvirtual = isvirt; + parms[numparms].paramname = parmname; + parms[numparms].arraysize = arraysize; + parms[numparms].type = newparm; + parms[numparms].defltvalue.cast = NULL; + + basicindex = 0; + found = false; + for(pc = newt; pc && !found; pc = pc->parentclass) + { + struct QCC_typeparam_s *pp; + int numpc; + int i; + if (pc == newt) + { + pp = parms; + numpc = numparms; + } + else + { + pp = pc->params; + numpc = pc->num_parms; + } + for (i = 0; i < numpc; i++) + { + if (pp[i].type->type == newparm->type) + { + if (!strcmp(pp[i].paramname, parmname)) + { + if (typecmp(pp[i].type, newparm)) + { + char bufc[256]; + char bufp[256]; + TypeName(pp[i].type, bufp, sizeof(bufp)); + TypeName(newparm, bufc, sizeof(bufc)); + QCC_PR_ParseError(0, "%s defined as %s in %s, but %s in %s\n", parmname, bufc, newt->name, bufp, pc->name); + } + basicindex = pp[i].ofs; + found = true; + break; + } + if ((unsigned int)basicindex < pp[i].ofs+pp[i].type->size*(pp[i].arraysize?pp[i].arraysize:1)) //if we found one with the index + basicindex = pp[i].ofs+pp[i].type->size*(pp[i].arraysize?pp[i].arraysize:1); //make sure we don't union it. + } + } + } + + + /* iterate over every parent-class and see if our method already exists in conflicting nonvirtual type form */ + if (isvirt) + { + for(pc = newt; pc && !found; pc = pc->parentclass) + { + struct QCC_typeparam_s *pp; + int numpc; + int i; + found = false; + + if (pc == newt) + continue; + + pp = parms; + numpc = numparms; + + /* iterate over all of the virtual methods */ + for (i = 0; i < numpc; i++) + { + if (pp[i].type->type == newparm->type) + { + /* if we found it, abandon this loop - we still have to check the other classes however */ + if (!strcmp(pp[i].paramname, parmname)) + { + found = true; + break; + } + } + } + + /* we didn't find it as a field... so check if it exists as a nonvirtual method */ + if (found == false) + { + QC_snprintfz(membername, sizeof(membername), "%s::%s", pc->name, parmname); + + /* if we found it, game over */ + if (QCC_PR_GetDef(NULL, membername, NULL, false, 0, 0)) + QCC_PR_ParseError(0, "%s defined as virtual in %s, but nonvirtual in %s\n", parmname, newt->name, pc->name); + } + } + } + + parms[numparms].ofs = basicindex; //ulp, its new + numparms++; + + if (found) + continue; + + if (!*basictypes[newparm->type]) + QCC_PR_ParseError(0, "members of type %s are not supported (%s::%s)\n", basictypenames[newparm->type], classname, parmname); + + //make sure the union is okay + d = QCC_PR_GetDef(NULL, parmname, NULL, 0, 0, GDF_CONST); + if (d) + basicindex = 0; + else + { //don't go all weird with unioning generic fields + QC_snprintfz(membername, sizeof(membername), "::*%s", basictypenames[newparm->type]); + d = QCC_PR_GetDef(NULL, membername, NULL, 0, 0, GDF_CONST); + if (!d) + { + d = QCC_PR_GetDef(QCC_PR_FieldType(*basictypes[newparm->type]), membername, NULL, 2, 0, GDF_CONST|GDF_POSTINIT|GDF_USED); +// for (i = 0; (unsigned int)i < newparm->size*(arraysize?arraysize:1); i++) +// d->symboldata[i]._int = pr.size_fields+i; +// pr.size_fields += i; + + d->used = true; + d->referenced = true; //always referenced, so you can inherit safely. + } + if (d->arraysize < basicindex+(arraysize?arraysize:1)) + { + if (d->symboldata) + QCC_PR_ParseError(ERR_INTERNAL, "array members are kinda limited, sorry. try rearranging them or adding padding for alignment\n"); //FIXME: add relocs to cope with this all of a type can then be contiguous and thus allow arrays. + else + { + int newsize = basicindex+(arraysize?arraysize:1); + if (d->type->type == ev_union || d->type->type == ev_struct) + d->arraysize = newsize; + else while(d->arraysize < newsize) + { + QC_snprintfz(membername, sizeof(membername), "::%s[%i]", basictypenames[newparm->type], d->arraysize/d->type->size); + QCC_PR_DummyDef(d->type, membername, d->scope, 0, d, d->arraysize, true, GDF_CONST); + d->arraysize+=d->type->size; + } + } + } + } + QCC_FreeDef(d); + + //and make sure we can do member::__fname + //actually, that seems pointless. + QC_snprintfz(membername, sizeof(membername), "%s::"MEMBERFIELDNAME, classname, parmname); +// externs->Printf("define %s -> %s\n", membername, d->name); + d = QCC_PR_DummyDef(fieldtype, membername, pr_scope, arraysize, d, basicindex, true, (isnull?0:GDF_CONST)|(opt_classfields?GDF_STRIP:0)); + d->referenced = true; //always referenced, so you can inherit safely. + } + } + + if (redeclaration) + { + int i; + redeclaration = newt->num_parms != numparms; + + for (i = 0; i < numparms && (unsigned int)i < newt->num_parms; i++) + { + if (newt->params[i].arraysize != parms[i].arraysize || typecmp(newt->params[i].type, parms[i].type) || strcmp(newt->params[i].paramname, parms[i].paramname)) + { + QCC_PR_ParseError(ERR_REDECLARATION, "Incompatible redeclaration of class %s. %s differs.", classname, parms[i].paramname); + break; + } + } + if (newt->num_parms != numparms) + QCC_PR_ParseError(ERR_REDECLARATION, "Incompatible redeclaration of class %s.", classname); + } + else + { + newt->num_parms = numparms; + newt->params = qccHunkAlloc(sizeof(*type->params) * numparms); + memcpy(newt->params, parms, sizeof(*type->params) * numparms); + } + free(parms); + + { + QCC_def_t *d; + //if there's a constructor, make sure the spawnfunc_ function is defined so that its available to maps. + QC_snprintfz(membername, sizeof(membername), "spawnfunc_%s", classname); + d = QCC_PR_GetDef(type_function, membername, NULL, true, 0, GDF_CONST); + d->funccalled = true; + d->referenced = true; + QCC_FreeDef(d); + } + + return newt; +} + pbool type_inlinefunction; /*newtype=true: creates a new type always silentfail=true: function is permitted to return NULL if it was not given a type, otherwise never returns NULL */ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) { - QCC_type_t *newparm; QCC_type_t *newt; QCC_type_t *type; char *name; - etype_t structtype; type_inlinefunction = false; //doesn't really matter so long as its not from an inline function type @@ -5668,1014 +6557,137 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) } if (flag_qcfuncs && QCC_PR_CheckKeyword (keyword_class, "class")) - { -// int parms; - QCC_type_t *fieldtype; - char membername[2048]; - char *classname; - int forwarddeclaration; - int numparms = 0; - struct QCC_typeparam_s *parms = NULL; - char *parmname; - int arraysize; - pbool redeclaration; - int basicindex; - QCC_def_t *d; - QCC_type_t *pc; - QCC_type_t *basetype; - pbool found = false; - int assumevirtual = 0; //0=erk, 1=yes, -1=no - - parmname = QCC_PR_ParseName(); - classname = qccHunkAlloc(strlen(parmname)+1); - strcpy(classname, parmname); - - newt = 0; - - if (QCC_PR_CheckToken(":")) - { - char *parentname = QCC_PR_ParseName(); - fieldtype = QCC_TypeForName(parentname); - if (!fieldtype) - QCC_PR_ParseError(ERR_NOTANAME, "Parent class %s was not yet defined", parentname); - - //FIXME: we should allow inheriting from 'object' too. - if (fieldtype->type != ev_entity) - QCC_PR_ParseError(ERR_NOTANAME, "Parent type %s is not a class/entity", parentname); - forwarddeclaration = false; - - QCC_PR_Expect("{"); - } - else - { - fieldtype = type_entity; - forwarddeclaration = !QCC_PR_CheckToken("{"); - } - - newt = QCC_TypeForName(classname); - if (newt && newt->num_parms != 0) - redeclaration = true; - else - redeclaration = false; - - if (!newt) - { - newt = QCC_PR_NewType(classname, fieldtype->type, true); - newt->size=type_entity->size; - } - - type = NULL; - - if (forwarddeclaration) - return newt; - - if (pr_scope) - QCC_PR_ParseError(ERR_REDECLARATION, "Declaration of class %s%s%s within function", col_type,classname,col_none); - if (redeclaration && fieldtype != newt->parentclass) - QCC_PR_ParseError(ERR_REDECLARATION, "Parent class changed on redeclaration of %s%s%s", col_type,classname,col_none); - newt->parentclass = fieldtype; - newt->filen = s_filen; - newt->line = pr_source_line; - - if (QCC_PR_CheckToken(",")) - QCC_PR_ParseError(ERR_NOTANAME, "member missing name"); - while (!QCC_PR_CheckToken("}")) - { - unsigned int gdf_flags; - - pbool wasvirt = false; - pbool wasnonvirt = false; - pbool wasstatic = false; - pbool isconst = false; - pbool isvar = false; - pbool isignored = false; - pbool isinline = false; - pbool isget = false; - pbool isset = false; -// pbool ispublic = false; -// pbool isprivate = false; -// pbool isprotected = false; - pbool firstkeyword = true; - - while(1) - { - if (QCC_PR_CheckKeyword(1, "nonvirtual")) - { - if (firstkeyword && QCC_PR_CheckToken(":")) - { //fteqcc-specific - assumevirtual = -1; - continue; - } - wasnonvirt = true; - } - else if (QCC_PR_CheckKeyword(1, "static")) - wasstatic = true; - else if (QCC_PR_CheckKeyword(1, "const")) - isconst = wasstatic = true; - else if (QCC_PR_CheckKeyword(1, "var")) - isvar = true; - else if (QCC_PR_CheckKeyword(1, "virtual")) - { - if (firstkeyword && QCC_PR_CheckToken(":")) - { //fteqcc-specific - assumevirtual = 1; - continue; - } - wasvirt = true; - } - else if (QCC_PR_CheckKeyword(1, "ignore")) - isignored = true; - else if (QCC_PR_CheckKeyword(1, "strip")) - isignored = true; - else if (QCC_PR_CheckKeyword(1, "inline")) - isinline = true; - else if (QCC_PR_CheckKeyword(1, "get")) - isget = true; - else if (QCC_PR_CheckKeyword(1, "set")) - isset = true; - else if (firstkeyword && QCC_PR_CheckKeyword(1, "public")) - { - /*ispublic = true;*/ - QCC_PR_Expect(":"); - continue; - } - else if (firstkeyword && QCC_PR_CheckKeyword(1, "private")) - { - QCC_PR_Expect(":"); - /*isprivate = true; */ - continue; - } - else if (firstkeyword && QCC_PR_CheckKeyword(1, "protected")) - { - QCC_PR_Expect(":"); - /*isprotected = true;*/ QCC_PR_Expect(":"); - continue; - } - else - break; - firstkeyword = false; - } - if (isget || isset) - { - if (wasvirt) - QCC_PR_ParseWarning(ERR_INTERNAL, "virtual accessors are not supported at this time"); - if (wasstatic) - QCC_PR_ParseError(ERR_INTERNAL, "static accessors are not supported"); - QCC_PR_ParseAccessorMember(newt, isinline, isset); - QCC_PR_CheckToken(";"); - continue; - } - - basetype = QCC_PR_ParseType(false, false); - - if (!basetype) - QCC_PR_ParseError(ERR_INTERNAL, "In class %s, expected type, found %s", classname, pr_token); - - if (basetype->type == ev_struct || basetype->type == ev_union) //we wouldn't be able to handle it. - QCC_PR_ParseError(ERR_INTERNAL, "Struct or union in class %s", classname); - - - for (;basetype;) - { - pbool havebody = false; - pbool isnull = false; - pbool isstatic = wasstatic; - pbool isvirt = wasvirt; - pbool isnonvirt = wasnonvirt; - - if (flag_qcfuncs && basetype->type == ev_function && basetype->aux_type == newt && QCC_PR_PeekToken(";")) - { //C++ style constructor (ie: missing return type followed by class name) - //swap the class out for the appropriate function type... - newparm = QCC_PR_GenFunctionType(type_void, basetype->params, basetype->num_parms); - parmname = classname; - arraysize = 0; - } - else if (!flag_qcfuncs && basetype == newt && QCC_PR_CheckToken("(")) - { - newparm = QCC_PR_ParseFunctionType(false, type_void); - parmname = classname; - arraysize = 0; - } - else - { - parmname = QCC_PR_ParseName(); - if (QCC_PR_CheckToken("[")) - { - arraysize = QCC_PR_IntConstExpr(); - QCC_PR_Expect("]"); - } - else - arraysize = 0; - - if (QCC_PR_CheckToken("(")) - { - //int fnc(), fld; is valid. - newparm = QCC_PR_ParseFunctionType(false, basetype); - } - else - newparm = basetype; - } - - if (newparm->type == ev_function) - { - if (!strcmp(classname, parmname)) - { - if (isstatic) - QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "Constructor %s::%s should not be static.", classname, pr_token); - if (!isvirt) - isnonvirt = true;//silently promote constructors to static - if (newparm->aux_type->type != ev_void) - QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "Constructor %s::%s does not return void.", classname, pr_token); - if (newparm->num_parms != 0 || newparm->vargs) - QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "Constructor arguments are not supported at this time %s::%s. Use named fields instead.", classname, pr_token); - } - else if (!isvirt && !isnonvirt && !isstatic) - { - if (assumevirtual == 1) - isvirt = true; - else if (assumevirtual == -1) - isnonvirt = true; - else - { - QCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, "%s::%s was not qualified. Assuming non-virtual.", classname, parmname); - isnonvirt = true; - } - } - else if (isvirt+isnonvirt+isstatic != 1) - QCC_PR_ParseError(ERR_INTERNAL, "Multiple conflicting qualifiers on %s::%s.", classname, pr_token); - - if (isstatic) - { //static and non-virtual functions differ only in that static should not have a 'this' available. however there's no hiding 'self' for calling in to other functions. - isstatic = false; - isnonvirt = true; -// QCC_PR_ParseError(ERR_INTERNAL, "%s::%s static member functions are not supported at this time.", classname, parmname); - } - } - else - { - if (isvirt||isnonvirt) - QCC_Error(ERR_INTERNAL, "virtual keyword on member that is not a function"); - if (isinline) - QCC_Error(ERR_INTERNAL, "inline keyword on member that is not a function"); - } - - if (QCC_PR_CheckToken("=")) - havebody = true; - else if (newparm->type == ev_function && QCC_PR_PeekToken("{")) - havebody = true; - if (isinline && (!havebody || isvirt)) - QCC_Error(ERR_INTERNAL, "inline keyword on function prototype or virtual function"); - - gdf_flags = 0; - if ((newparm->type == ev_function && !arraysize && !isvar) || isconst) - gdf_flags = GDF_CONST; - - if (havebody) - { - QCC_def_t *def; - if (pr_scope) - QCC_Error(ERR_INTERNAL, "Nested function declaration"); - - isnull = (QCC_PR_CheckImmediate("0") || QCC_PR_CheckImmediate("0i")); - QC_snprintfz(membername, sizeof(membername), "%s::%s", classname, parmname); - if (isnull) - { - if (isignored) - def = NULL; - else - { - def = QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags); - def->symboldata[def->ofs].function = 0; - def->initialized = 1; - } - } - else - { - if (isignored) - def = NULL; - else - def = QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags); - - if (newparm->type != ev_function && !isstatic) - QCC_Error(ERR_INTERNAL, "Can only initialise member functions"); - else - { - if (autoprototype || isignored) - { - if (QCC_PR_CheckToken("[")) - { - while (!QCC_PR_CheckToken("]")) - { - if (pr_token_type == tt_eof) - break; - QCC_PR_Lex(); - } - } - QCC_PR_Expect("{"); - - { - int blev = 1; - //balance out the { and } - while(blev) - { - if (pr_token_type == tt_eof) - break; - if (QCC_PR_CheckToken("{")) - blev++; - else if (QCC_PR_CheckToken("}")) - blev--; - else - QCC_PR_Lex(); //ignore it. - } - } - } - else - { - pr_classtype = newt; - QCC_PR_ParseInitializerDef(def, 0); - pr_classtype = NULL; - /* - f = QCC_PR_ParseImmediateStatements (def, newparm); - pr_classtype = NULL; - pr_scope = NULL; - def->symboldata[def->ofs].function = f - functions; - f->def = def; - def->initialized = 1;*/ - } - } - } - if (def) - QCC_FreeDef(def); - - if (!isvirt && !isignored) - { - QCC_def_t *fdef; - QCC_type_t *pc; - unsigned int i; - - for (pc = newt->parentclass; pc; pc = pc->parentclass) - { - for (i = 0; i < pc->num_parms; i++) - { - if (!strcmp(pc->params[i].paramname, parmname)) - { - QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "%s::%s is virtual inside parent class '%s'. Did you forget the 'virtual' keyword?", newt->name, parmname, pc->name); - break; - } - } - if (i < pc->num_parms) - break; - } - if (!pc) - { - fdef = QCC_PR_GetDef(NULL, parmname, NULL, false, 0, GDF_CONST); - if (fdef && fdef->type->type == ev_field) - { - QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "%s::%s is virtual inside parent class 'entity'. Did you forget the 'virtual' keyword?", newt->name, parmname); - QCC_PR_ParsePrintDef(0, fdef); - } - else if (fdef) - { - QCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, "%s::%s shadows a global", newt->name, parmname); - QCC_PR_ParsePrintDef(0, fdef); - } - if (fdef) - QCC_FreeDef(fdef); - } - } - } - - if (!QCC_PR_CheckToken(",")) - { - QCC_PR_Expect(";"); - basetype = NULL; - } - - if (isignored) //member doesn't really exist - continue; - - //static members are technically just funny-named globals, and do not generate fields. - if (isnonvirt || isstatic || (newparm->type == ev_function && !arraysize)) - { - QC_snprintfz(membername, sizeof(membername), "%s::%s", classname, parmname); - QCC_FreeDef(QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags)); - - if (isnonvirt || isstatic) - continue; - } - - - fieldtype = QCC_PR_NewType(parmname, ev_field, false); - fieldtype->aux_type = newparm; - fieldtype->size = newparm->size; - - parms = realloc(parms, sizeof(*parms) * (numparms+1)); - parms[numparms].ofs = 0; - parms[numparms].out = false; - parms[numparms].optional = false; - parms[numparms].isvirtual = isvirt; - parms[numparms].paramname = parmname; - parms[numparms].arraysize = arraysize; - parms[numparms].type = newparm; - parms[numparms].defltvalue.cast = NULL; - - basicindex = 0; - found = false; - for(pc = newt; pc && !found; pc = pc->parentclass) - { - struct QCC_typeparam_s *pp; - int numpc; - int i; - if (pc == newt) - { - pp = parms; - numpc = numparms; - } - else - { - pp = pc->params; - numpc = pc->num_parms; - } - for (i = 0; i < numpc; i++) - { - if (pp[i].type->type == newparm->type) - { - if (!strcmp(pp[i].paramname, parmname)) - { - if (typecmp(pp[i].type, newparm)) - { - char bufc[256]; - char bufp[256]; - TypeName(pp[i].type, bufp, sizeof(bufp)); - TypeName(newparm, bufc, sizeof(bufc)); - QCC_PR_ParseError(0, "%s defined as %s in %s, but %s in %s\n", parmname, bufc, newt->name, bufp, pc->name); - } - basicindex = pp[i].ofs; - found = true; - break; - } - if ((unsigned int)basicindex < pp[i].ofs+pp[i].type->size*(pp[i].arraysize?pp[i].arraysize:1)) //if we found one with the index - basicindex = pp[i].ofs+pp[i].type->size*(pp[i].arraysize?pp[i].arraysize:1); //make sure we don't union it. - } - } - } - - - /* iterate over every parent-class and see if our method already exists in conflicting nonvirtual type form */ - if (isvirt) - { - for(pc = newt; pc && !found; pc = pc->parentclass) - { - struct QCC_typeparam_s *pp; - int numpc; - int i; - found = false; - - if (pc == newt) - continue; - - pp = parms; - numpc = numparms; - - /* iterate over all of the virtual methods */ - for (i = 0; i < numpc; i++) - { - if (pp[i].type->type == newparm->type) - { - /* if we found it, abandon this loop - we still have to check the other classes however */ - if (!strcmp(pp[i].paramname, parmname)) - { - found = true; - break; - } - } - } - - /* we didn't find it as a field... so check if it exists as a nonvirtual method */ - if (found == false) - { - QC_snprintfz(membername, sizeof(membername), "%s::%s", pc->name, parmname); - - /* if we found it, game over */ - if (QCC_PR_GetDef(NULL, membername, NULL, false, 0, 0)) - QCC_PR_ParseError(0, "%s defined as virtual in %s, but nonvirtual in %s\n", parmname, newt->name, pc->name); - } - } - } - - parms[numparms].ofs = basicindex; //ulp, its new - numparms++; - - if (found) - continue; - - if (!*basictypes[newparm->type]) - QCC_PR_ParseError(0, "members of type %s are not supported (%s::%s)\n", basictypenames[newparm->type], classname, parmname); - - //make sure the union is okay - d = QCC_PR_GetDef(NULL, parmname, NULL, 0, 0, GDF_CONST); - if (d) - basicindex = 0; - else - { //don't go all weird with unioning generic fields - QC_snprintfz(membername, sizeof(membername), "::*%s", basictypenames[newparm->type]); - d = QCC_PR_GetDef(NULL, membername, NULL, 0, 0, GDF_CONST); - if (!d) - { - d = QCC_PR_GetDef(QCC_PR_FieldType(*basictypes[newparm->type]), membername, NULL, 2, 0, GDF_CONST|GDF_POSTINIT|GDF_USED); -// for (i = 0; (unsigned int)i < newparm->size*(arraysize?arraysize:1); i++) -// d->symboldata[i]._int = pr.size_fields+i; -// pr.size_fields += i; - - d->used = true; - d->referenced = true; //always referenced, so you can inherit safely. - } - if (d->arraysize < basicindex+(arraysize?arraysize:1)) - { - if (d->symboldata) - QCC_PR_ParseError(ERR_INTERNAL, "array members are kinda limited, sorry. try rearranging them or adding padding for alignment\n"); //FIXME: add relocs to cope with this all of a type can then be contiguous and thus allow arrays. - else - { - int newsize = basicindex+(arraysize?arraysize:1); - if (d->type->type == ev_union || d->type->type == ev_struct) - d->arraysize = newsize; - else while(d->arraysize < newsize) - { - QC_snprintfz(membername, sizeof(membername), "::%s[%i]", basictypenames[newparm->type], d->arraysize/d->type->size); - QCC_PR_DummyDef(d->type, membername, d->scope, 0, d, d->arraysize, true, GDF_CONST); - d->arraysize+=d->type->size; - } - } - } - } - QCC_FreeDef(d); - - //and make sure we can do member::__fname - //actually, that seems pointless. - QC_snprintfz(membername, sizeof(membername), "%s::"MEMBERFIELDNAME, classname, parmname); -// externs->Printf("define %s -> %s\n", membername, d->name); - d = QCC_PR_DummyDef(fieldtype, membername, pr_scope, arraysize, d, basicindex, true, (isnull?0:GDF_CONST)|(opt_classfields?GDF_STRIP:0)); - d->referenced = true; //always referenced, so you can inherit safely. - } - } - - if (redeclaration) - { - int i; - redeclaration = newt->num_parms != numparms; - - for (i = 0; i < numparms && (unsigned int)i < newt->num_parms; i++) - { - if (newt->params[i].arraysize != parms[i].arraysize || typecmp(newt->params[i].type, parms[i].type) || strcmp(newt->params[i].paramname, parms[i].paramname)) - { - QCC_PR_ParseError(ERR_REDECLARATION, "Incompatible redeclaration of class %s. %s differs.", classname, parms[i].paramname); - break; - } - } - if (newt->num_parms != numparms) - QCC_PR_ParseError(ERR_REDECLARATION, "Incompatible redeclaration of class %s.", classname); - } - else - { - newt->num_parms = numparms; - newt->params = qccHunkAlloc(sizeof(*type->params) * numparms); - memcpy(newt->params, parms, sizeof(*type->params) * numparms); - } - free(parms); - - { - QCC_def_t *d; - //if there's a constructor, make sure the spawnfunc_ function is defined so that its available to maps. - QC_snprintfz(membername, sizeof(membername), "spawnfunc_%s", classname); - d = QCC_PR_GetDef(type_function, membername, NULL, true, 0, GDF_CONST); - d->funccalled = true; - d->referenced = true; - QCC_FreeDef(d); - } - - QCC_PR_Expect(";"); - return NULL; - } - - if (QCC_PR_CheckKeyword(keyword_enum, "enum")) - { - return QCC_PR_ParseEnum(false); - } - if (QCC_PR_CheckKeyword(keyword_enumflags, "enumflags")) - { - return QCC_PR_ParseEnum(true); - } - - structtype = ev_void; - if (QCC_PR_CheckKeyword (keyword_union, "union")) - structtype = ev_union; + type = QCC_PR_ParseEntClass(); + else if (QCC_PR_CheckKeyword(keyword_enum, "enum")) + type = QCC_PR_ParseEnum(false); + else if (QCC_PR_CheckKeyword(keyword_enumflags, "enumflags")) + type = QCC_PR_ParseEnum(true); + else if (QCC_PR_CheckKeyword (keyword_union, "union")) + type = QCC_PR_ParseStruct(ev_union); else if (QCC_PR_CheckKeyword (keyword_struct, "struct")) - structtype = ev_struct; //defaults to public + type = QCC_PR_ParseStruct(ev_struct); //defaults to public // else if (QCC_PR_CheckKeyword (keyword_class, "class")) // structtype = ev_struct; //defaults to private. no other difference. - if (structtype != ev_void) + else { - struct QCC_typeparam_s *parms = NULL, *oldparm; - int numparms = 0; - int ofs; - unsigned int arraysize; - char *parmname; - - pbool isnonvirt = false; - pbool isstatic = false; - pbool isvirt = false; - pbool definedsomething = false; - - if (QCC_PR_CheckToken("{")) - { - //nameless struct - newt = QCC_PR_NewType(structtype==ev_union?"":"", structtype, false); - } + type = QCC_TypeForName(name); + if (type) + QCC_PR_Lex (); else { - QCC_type_t *parenttype; - char *tname = QCC_PR_ParseName(); - - if (structtype == ev_struct && QCC_PR_CheckToken(":")) + if (!*name) { - char *parentname = QCC_PR_ParseName(); - parenttype = QCC_TypeForName(parentname); - if (!parenttype) - QCC_PR_ParseError(ERR_NOTANAME, "Parent type %s was not yet defined", parentname); - if (parenttype->type != ev_struct) - QCC_PR_ParseError(ERR_NOTANAME, "Parent type %s is not a struct", parentname); + QCC_PR_ParseError(ERR_NOTANAME, "type missing name"); + return NULL; } + + //some reacc types... + if (flag_acc && !stricmp("Void", name)) + type = type_void; + else if (flag_acc && !stricmp("Real", name)) + type = type_float; + else if (flag_acc && !stricmp("Vector", name)) + type = type_vector; + else if (flag_acc && !stricmp("Object", name)) + type = type_entity; + else if (flag_acc && !stricmp("String", name)) + type = type_string; + else if (flag_acc && !stricmp("PFunc", name)) + type = type_function; else - parenttype = NULL; - - newt = QCC_TypeForName(tname); - if (!newt) { - newt = QCC_PR_NewType(tname, ev_struct, true); - newt->parentclass = parenttype; - } - else if (!newt->size && !newt->parentclass) - newt->parentclass = parenttype; - else if (parenttype && newt->parentclass != parenttype) - QCC_PR_ParseError(ERR_NOTANAME, "Redeclaration of struct with different parent type", tname, parenttype->name); + //try and handle C's types, which have weird and obtuse combinations (like long long, long int, short int). + pbool isokay = false; + pbool issigned = false; + pbool isunsigned = false; + pbool islong = false; + pbool isfloat = false; + int bits = 0; - 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; - } + #define longlongbits 64 + #define longbits (flag_ILP32?32:64) - //struct declaration only, not definition. - if (parenttype) - QCC_PR_Expect("{"); - else if (!QCC_PR_CheckToken("{")) - return newt; - } - if (newt->parentclass) - newt->size = newt->parentclass->size; - else - newt->size=0; - - type = NULL; - - newparm = NULL; - for (;;) - { - //in qc, functions are assignable references like anything else, so no modifiers means the qc will need to assign to it somewhere. - //virtual functions are still references, but we initialise them somewhere - //nonvirtual functions and static functions are kinda the same thing - QCC_sref_t defaultval; - if (QCC_PR_CheckToken("}")) - { - if (newparm) - QCC_PR_ParseError(ERR_EXPECTED, "missing semi-colon"); - break; - } - else if (QCC_PR_CheckToken(";")) - { - newparm = NULL; - continue; - } - else if (QCC_PR_CheckToken(",")) - { //same as last type, unless initial/after-semicolon - if (!newparm) - QCC_PR_ParseError(ERR_EXPECTED, "element missing type"); - } - else - { //new type! - if (newparm) - QCC_PR_ParseError(ERR_EXPECTED, "missing semi-colon"); //allow a missing semi-colon on functions, for mixed-style functions. - - //reset these... - isnonvirt = false; - isstatic = false; - isvirt = false; - - //parse field modifiers - if (QCC_PR_CheckKeyword(1, "public")) + while(true) { - /*ispublic = true*/; - QCC_PR_Expect(":"); - continue; - } - else if (QCC_PR_CheckKeyword(1, "private")) - { - /*isprivate = true*/; - QCC_PR_Expect(":"); - continue; - } - else if (QCC_PR_CheckKeyword(1, "protected")) - { - /*isprotected = true*/; - QCC_PR_Expect(":"); - continue; - } + if (!isunsigned && !issigned && QCC_PR_CheckKeyword(keyword_signed, "signed")) + issigned = isokay = true; + else if (!issigned && !isunsigned && QCC_PR_CheckKeyword(keyword_unsigned, "unsigned")) + isunsigned = isokay = true; + else if (!bits && QCC_PR_CheckKeyword(keyword_long, "long")) + { + if (islong) + bits = longlongbits; + islong = isokay = true; + } + else if ((!bits || bits==16) && (QCC_PR_CheckKeyword(keyword_int, "int") || QCC_PR_CheckKeyword(keyword_integer, "integer"))) + { //long int, short int, etc are allowed + if (!bits) + bits = 32; + isokay = true; + } + else if (!bits && QCC_PR_CheckKeyword(keyword_short, "short")) + bits = 16, isokay = true; + else if (!bits && QCC_PR_CheckKeyword(keyword_char, "char")) + bits = 8, isokay = true; + else if (!bits && !issigned && !isunsigned && QCC_PR_CheckKeyword(true, "_Bool")) //c99 + { + if (keyword_int) + type = type_bint; + else + type = type_bfloat; + goto wasctype; + } - //if (QCC_PR_CheckKeyword(1, "nonvirtual")) - // isnonvirt = true; - //else - if (QCC_PR_CheckKeyword(1, "static")) - isstatic = true; - else if (QCC_PR_CheckKeyword(1, "virtual")) - isvirt = true; -// else if (QCC_PR_CheckKeyword(1, "ignore")) -// isignored = true; -// else if (QCC_PR_CheckKeyword(1, "strip")) -// isignored = true; - - //now parse the actual type. - newparm = QCC_PR_ParseType(false, false); - definedsomething = false; - } - type = newparm; - - while (QCC_PR_CheckToken("*")) - type = QCC_PointerTypeTo(type); - - arraysize = 0; - if (QCC_PR_CheckToken(";")) - { //annonymous structs do weird scope stuff. - if (!definedsomething && (type->type != ev_struct && type->type != ev_union)) - { - if (flag_qcfuncs && type->type == ev_function && type->aux_type == newt && QCC_PR_PeekToken(";")) - QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "constructors are not supported in structs at this time", newparm->name); + else if (!bits && !islong && QCC_PR_CheckKeyword(keyword_float, "float")) + bits = 32, isfloat = isokay = true; + else if ((!bits||islong) && QCC_PR_CheckKeyword(keyword_double, "double")) + bits = islong?128:64, islong=false, isfloat = isokay = true; else - QCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, "declaration does not declare anything (%s)", newparm->name); + break; } - newparm = NULL; - parmname = ""; - } - else - { - if (QCC_PR_CheckToken("(")) + if (isokay) { - QCC_PR_CheckToken("*"); //this is fine... - parmname = QCC_PR_ParseName(); - QCC_PR_Expect(")"); - } - else - parmname = QCC_PR_ParseName(); - definedsomething = true; - while (QCC_PR_CheckToken("[")) - { - int nsize=QCC_PR_IntConstExpr(); - if (arraysize) - type = QCC_GenArrayType(type, arraysize); - if (!nsize) - QCC_PR_ParseError(ERR_NOTANAME, "cannot cope with 0-sized arrays"); - QCC_PR_Expect("]"); - arraysize = nsize; - } - - if (QCC_PR_CheckToken("(")) - type = QCC_PR_ParseFunctionType(false, type); - } - - if (type == newt || ((type->type == ev_struct || type->type == ev_union) && !type->size)) - { - QCC_PR_ParseWarning(ERR_NOTANAME, "type %s not fully defined yet", type->name); - continue; - } - - if (QCC_PR_CheckToken(":")) - { - QCC_PR_IntConstExpr(); - QCC_PR_ParseWarning(WARN_IGNOREDKEYWORD, "bitfields are not supported"); - } - - if ((isnonvirt || isvirt) && type->type != ev_function) - QCC_PR_ParseWarning(ERR_INTERNAL, "[non]virtual members must be functions", type->name); - - //static members are technically just funny-named globals, and do not generate fields. - if (isnonvirt || isstatic || isvirt) - { //either way its a regular global. the difference being static has no implicit this/self argument. - QCC_def_t *d; - char membername[2048]; - if (!isstatic) - type = QCC_PR_MakeThiscall(type, newt); - - QC_snprintfz(membername, sizeof(membername), "%s::%s", newt->name, parmname); - d = QCC_PR_GetDef(type, membername, NULL, true, 0, (type->type==ev_function)?GDF_CONST:0); - if (QCC_PR_CheckToken("=") || (type->type == ev_function && QCC_PR_PeekToken("{"))) - { -//FIXME: methods cannot be compiled yet, as none of the fields are not actually defined yet. - pr_classtype = newt; - QCC_PR_ParseInitializerDef(d, 0); - pr_classtype = NULL; - } - QCC_FreeDef(d); - if (!QCC_PR_PeekToken(",")) - newparm = NULL; - - if (isvirt) - { - defaultval.ofs = 0; - defaultval.cast = d->type; - defaultval.sym = d; - } - else - continue; - } - else - { - defaultval.cast = NULL; - if (QCC_PR_CheckToken("=")) - { - defaultval = QCC_PR_ParseDefaultInitialiser(type); - QCC_PR_ParseWarning(ERR_INTERNAL, "TODO: pre-initialised struct members are not implemented yet", type->name); - } - } - - parms = realloc(parms, sizeof(*parms) * (numparms+4)); - oldparm = QCC_PR_FindStructMember(newt, parmname, &ofs); - if (oldparm && oldparm->arraysize == arraysize && !typecmp_lax(oldparm->type, type)) - { - if (!isvirt) - continue; - } - else if (structtype == ev_union) - { - ofs = 0; - if (type->size*(arraysize?arraysize:1) > newt->size) - newt->size = type->size*(arraysize?arraysize:1); - } - else - { - ofs = newt->size; - newt->size += type->size*(arraysize?arraysize:1); - } - - parms[numparms].ofs = ofs; - parms[numparms].arraysize = arraysize; - parms[numparms].out = false; - parms[numparms].optional = false; - parms[numparms].isvirtual = isvirt; - parms[numparms].paramname = parmname; - parms[numparms].type = type; - parms[numparms].defltvalue = defaultval; - numparms++; - - /*if (type->type == ev_vector && arraysize == 0) - { //add in vec_x/y/z members too. - int c; - for (c = 0; c < 3; c++) - { - parms[numparms].ofs = ofs + c; - parms[numparms].arraysize = arraysize; - parms[numparms].out = false; - parms[numparms].optional = true; - parms[numparms].isvirtual = isvirt; - parms[numparms].paramname = qccHunkAlloc(strlen(parmname)+3); - sprintf(parms[numparms].paramname, "%s_%c", parmname, 'x'+c); - parms[numparms].type = type_float; - parms[numparms].defltvalue = nullsref; - numparms++; - } - }*/ - } - if (!numparms) - QCC_PR_ParseError(ERR_NOTANAME, "%s %s has no members", structtype==ev_union?"union":"struct", newt->name); - - newt->num_parms = numparms; - newt->params = qccHunkAlloc(sizeof(*type->params) * numparms); - memcpy(newt->params, parms, sizeof(*type->params) * numparms); - free(parms); - - return newt; - } - - type = QCC_TypeForName(name); - if (!type) - { - if (!*name) - { - QCC_PR_ParseError(ERR_NOTANAME, "type missing name"); - return NULL; - } - - //some reacc types... - if (flag_acc && !stricmp("Void", name)) - type = type_void; - else if (flag_acc && !stricmp("Real", name)) - type = type_float; - else if (flag_acc && !stricmp("Vector", name)) - type = type_vector; - else if (flag_acc && !stricmp("Object", name)) - type = type_entity; - else if (flag_acc && !stricmp("String", name)) - type = type_string; - else if (flag_acc && !stricmp("PFunc", name)) - type = type_function; - else - { - //try and handle C's types, which have weird and obtuse combinations (like long long, long int, short int). - pbool isokay = false; - pbool issigned = false; - pbool isunsigned = false; - pbool islong = false; - pbool isfloat = false; - int bits = 0; - - while(true) - { - if (!isunsigned && !issigned && QCC_PR_CheckKeyword(keyword_signed, "signed")) - issigned = isokay = true; - else if (!issigned && !isunsigned && QCC_PR_CheckKeyword(keyword_unsigned, "unsigned")) - isunsigned = isokay = true; - else if (!bits && QCC_PR_CheckKeyword(keyword_long, "long")) - { - if (islong) - bits = 128; - islong = isokay = true; - } - else if ((!bits || bits==16) && (QCC_PR_CheckKeyword(keyword_int, "int") || QCC_PR_CheckKeyword(keyword_integer, "integer"))) - { //long int, short int, etc are allowed if (!bits) - bits = 32; - isokay = true; - } - else if (!bits && QCC_PR_CheckKeyword(keyword_short, "short")) - bits = 16, isokay = true; - else if (!bits && QCC_PR_CheckKeyword(keyword_char, "char")) - bits = 8, isokay = true; - else if (!bits && !issigned && !isunsigned && QCC_PR_CheckKeyword(true, "_Bool")) //c99 - { - if (keyword_int) - type = type_bint; + bits = islong?longbits:32; // [int] + if (isfloat) + { + if (isunsigned) + QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "ignoring unsupported unsigned keyword, type will be signed"); + if (bits > 64) + type = type_double, QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "long doubles are not supported, using double"); //permitted + else if (bits == 64) + type = type_double; + else + type = type_float; + } else - type = type_bfloat; + { + if (bits > 64) + type = (isunsigned?type_uint64:type_int64), QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "long longs are not supported, using long"); //permitted + else if (bits > 32) + type = (isunsigned?type_uint64:type_int64); + else if (bits <= 8) + type = (isunsigned?type_uint:type_integer), QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "chars are not supported, using int"); //permitted + else if (bits <= 16) + type = (isunsigned?type_uint:type_integer), QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "shorts are not supported, using int"); //permitted + else + type = (isunsigned?type_uint:type_integer); + } goto wasctype; } - else if (!bits && !islong && QCC_PR_CheckKeyword(keyword_float, "float")) - bits = 32, isfloat = isokay = true; - else if ((!bits||islong) && QCC_PR_CheckKeyword(keyword_double, "double")) - bits = islong?128:64, islong=false, isfloat = isokay = true; - else - break; + + if (silentfail) + return NULL; + + QCC_PR_ParseError (ERR_NOTATYPE, "\"%s\" is not a type", name); + type = type_float; // shut up compiler warning } - if (isokay) - { - if (!bits) - bits = islong?64:32; // [int] - if (isfloat) - { - if (isunsigned) - QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "ignoring unsupported unsigned keyword, type will be signed"); - if (bits > 64) - type = type_double, QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "long doubles are not supported, using double"); //permitted - else if (bits == 64) - type = type_double; - else - type = type_float; - } - else - { - if (bits > 64) - type = (isunsigned?type_uint64:type_int64), QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "long longs are not supported, using long"); //permitted - else if (bits == 64) - type = (isunsigned?type_uint64:type_int64); - else if (bits == 16) - type = (isunsigned?type_uint:type_integer), QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "shorts are not supported, using int"); //permitted - else if (bits == 8) - type = (isunsigned?type_uint:type_integer), QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, "chars are not supported, using int"); //permitted - else - type = (isunsigned?type_uint:type_integer); - } - goto wasctype; - } - - - if (silentfail) - return NULL; - - QCC_PR_ParseError (ERR_NOTATYPE, "\"%s\" is not a type", name); - type = type_float; // shut up compiler warning } } - QCC_PR_Lex (); wasctype: + if (!type) + return NULL; while (QCC_PR_CheckToken("*")) { diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 401a93526..ffb3b2703 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -409,7 +409,7 @@ compiler_flag_t compiler_flag[] = { {&opt_logicops, FLAG_MIDCOMPILE,"lo", "Logic ops", "This changes the behaviour of your code. It generates additional if operations to early-out in if statements. With this flag, the line if (0 && somefunction()) will never call the function. It can thus be considered an optimisation. However, due to the change of behaviour, it is not considered so by fteqcc. Note that due to inprecisions with floats, this flag can cause runaway loop errors within the player walk and run functions (without iffloat also enabled). This code is advised:\nplayer_stand1:\n if (self.velocity_x || self.velocity_y)\nplayer_run\n if (!(self.velocity_x || self.velocity_y))"}, {&flag_msvcstyle, FLAG_MIDCOMPILE,"msvcstyle", "MSVC-style errors", "Generates warning and error messages in a format that msvc understands, to facilitate ide integration."}, {&flag_debugmacros, FLAG_MIDCOMPILE,"debugmacros", "Verbose Macro Expansion", "Print out the contents of macros that are expanded. This can help look inside macros that are expanded and is especially handy if people are using preprocessor hacks."}, - {&flag_filetimes, 0, "filetimes", "Check Filetimes", "Recompiles the progs only if the file times are modified."}, + {&flag_filetimes, hideflag, "filetimes", "Check Filetimes", "Recompiles the progs only if the file times are modified."}, {&flag_fasttrackarrays, FLAG_MIDCOMPILE,"fastarrays", "fast arrays where possible", "Generates extra instructions inside array handling functions to detect engine and use extension opcodes only in supporting engines.\nAdds a global which is set by the engine if the engine supports the extra opcodes. Note that this applies to all arrays or none."}, {&flag_assume_integer, FLAG_MIDCOMPILE,"assumeint", "Assume Integers", "Numerical constants are assumed to be integers, instead of floats."}, {&pr_subscopedlocals, FLAG_MIDCOMPILE,"subscope", "Subscoped Locals", "Restrict the scope of locals to the block they are actually defined within, as in C."}, @@ -427,9 +427,12 @@ compiler_flag_t compiler_flag[] = { // {&flag_lno, hidedefaultflag,"lno", "Gen Debugging Info", "Writes debugging info."}, {&flag_utf8strings, FLAG_MIDCOMPILE,"utf8", "Unicode", "String immediates will use utf-8 encoding, instead of quake's encoding."}, {&flag_reciprocalmaths, FLAG_MIDCOMPILE,"reciprocal-math","Reciprocal Maths", "Optimise x/const as x*(1/const)."}, + {&flag_ILP32, FLAG_MIDCOMPILE,"ILP32", "ILP32 Data Model", "Restricts the size of `long` to 32bits, consistent with pointers."}, + {&flag_undefwordsize, hideflag, "undefwordsize","Undefined Word Size", "Do not make assumptions about pointer types and word sizes. Block functionality that depends upon specific word sizes. Changed as part of target."}, + {&flag_pointerrelocs, hideflag, "pointerrelocs","Initialised Pointers", "Allow pointer types to be preinitialised at compile time, both at global scope and optimising local scope a little."}, + {&flag_noreflection, FLAG_MIDCOMPILE,"omitinternals","Omit Reflection Info", "Keeps internal symbols private (equivelent to unix's hidden visibility). This has the effect of reducing filesize, thwarting debuggers, and breaking saved games. This allows you to use arrays without massively bloating the size of your progs.\nWARNING: The bit about breaking saved games was NOT a joke, but does not apply to menuqc or csqc. It also interferes with FTE_MULTIPROGS."}, {&flag_embedsrc, FLAG_MIDCOMPILE,"embedsrc", "Embed Sources", "Write the sourcecode into the output file. The resulting .dat can be opened as a standard zip archive (or by fteqccgui).\nGood for GPL compliance!"}, -// {&flag_noreflection, FLAG_MIDCOMPILE,"omitinternals","Omit Reflection Info", "Keeps internal symbols private (equivelent to unix's hidden visibility). This has the effect of reducing filesize, thwarting debuggers, and breaking saved games. This allows you to use arrays without massively bloating the size of your progs.\nWARNING: The bit about breaking saved games was NOT a joke, but does not apply to menuqc or csqc. It also interferes with FTE_MULTIPROGS."}, {&flag_dumpfilenames, FLAG_MIDCOMPILE,"dumpfilenames","Write a .lst file", "Writes a .lst file which contains a list of all file names that we can detect from the qc. This file list can then be passed into external compression tools."}, {&flag_dumpfields, FLAG_MIDCOMPILE,"dumpfields", "Write a .fld file", "Writes a .fld file that shows which fields are defined, along with their offsets etc, for weird debugging."}, {&flag_dumpsymbols, FLAG_MIDCOMPILE,"dumpsymbols", "Write a .sym file", "Writes a .sym file alongside the dat which contains a list of all global symbols defined in the code (before stripping)"}, @@ -1525,7 +1528,21 @@ static void QCC_FinaliseDef(QCC_def_t *def) { def->reloc->used = true; QCC_FinaliseDef(def->reloc); - qcc_pr_globals[def->ofs]._int += def->reloc->ofs; + if (def->type->type == ev_function/*misordered inits/copies*/ || def->type->type == ev_integer/*dp-style global index*/) + qcc_pr_globals[def->ofs]._int += qcc_pr_globals[def->reloc->ofs]._int; + else if (def->type->type == ev_pointer/*signal to the engine to fix up the offset*/) + { + if (qcc_pr_globals[def->ofs]._int & 0x80000000) + QCC_Error(ERR_INTERNAL, "dupe reloc, type... %s", def->type->name); + else if (flag_undefwordsize) + QCC_PR_ParseWarning(ERR_BADEXTENSION, "pointer relocs are disabled for this target."); + qcc_pr_globals[def->ofs]._int += def->reloc->ofs; + qcc_pr_globals[def->ofs]._int *= VMWORDSIZE; + + qcc_pr_globals[def->ofs]._int |= 0x80000000; + } + else + QCC_Error(ERR_INTERNAL, "unknown reloc type... %s", def->type->name); } #ifdef DEBUG_DUMP_GLOBALMAP @@ -1777,7 +1794,12 @@ static pbool QCC_WriteData (int crc) if (i < numstatements) bigjumps = QCC_FunctionForStatement(i); - QCC_PR_CRCMessages(crc); + if (!def) + { + QCC_PR_Warning(WARN_SYSTEMCRC, NULL, 0, "no end_sys_fields defined. system headers missing."); + } + else + QCC_PR_CRCMessages(crc); switch (qcc_targetformat) { case QCF_HEXEN2: @@ -1865,10 +1887,10 @@ static pbool QCC_WriteData (int crc) if (compressoutput) progs.blockscompressed |=128; //types //include a type block? //types = debugtarget; - if (types && sizeof(char *) != sizeof(string_t)) +// if (types && sizeof(char *) != sizeof(string_t)) { //qcc_typeinfo_t has a char* inside it, which changes size - externs->Printf("AMD64 builds cannot write typeinfo structures\n"); +// externs->Printf("64bit builds cannot write typeinfo structures\n"); types = false; } @@ -2198,7 +2220,11 @@ static pbool QCC_WriteData (int crc) QCC_GenerateFieldDefs(def, def->name, 0, def->type->aux_type); continue; } - else if ((def->scope||def->constant) && (def->type->type != ev_string || (strncmp(def->name, "dotranslate_", 12) && opt_constant_names_strings))) + else if (def->type->type == ev_pointer && (def->symboldata[0]._int & 0x80000000)) + 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 ((def->scope||def->constant||flag_noreflection) && (def->type->type != ev_string || (strncmp(def->name, "dotranslate_", 12) && opt_constant_names_strings))) { if (opt_constant_names) { @@ -2209,9 +2235,11 @@ static pbool QCC_WriteData (int crc) #ifdef DEBUG_DUMP if (def->scope) - externs->Printf("code: %s:%i: strip local %s %s@%i;\n", def->filen, def->s_line, def->type->name, def->name, def->ofs); + externs->Printf("code: %s:%i: strip local %s %s @%i;\n", def->filen, def->s_line, def->type->name, def->name, def->ofs); else if (def->constant) - externs->Printf("code: %s:%i: strip const %s %s@%i;\n", def->filen, def->s_line, def->type->name, def->name, def->ofs); + externs->Printf("code: %s:%i: strip const %s %s @%i;\n", def->filen, def->s_line, def->type->name, def->name, def->ofs); + else + externs->Printf("code: %s:%i: strip globl %s %s @%i;\n", def->filen, def->s_line, def->type->name, def->name, def->ofs); #endif continue; } @@ -2334,7 +2362,7 @@ static pbool QCC_WriteData (int crc) for (i = 0; i < numsounds; i++) { if (!precache_sound[i].used) - dupewarncount+=QCC_PR_Warning(WARN_EXTRAPRECACHE, precache_sound[i].filename, precache_sound[i].fileline, (dupewarncount>10&&verbose < VERBOSE_STANDARD)?NULL:"Sound \"%s\" was precached but not directly used", precache_sound[i].name, dupewarncount?"":" (annotate the usage with the used_sound intrinsic to silence this warning)"); + dupewarncount+=QCC_PR_Warning(WARN_EXTRAPRECACHE, precache_sound[i].filename, precache_sound[i].fileline, (dupewarncount>10&&verbose < VERBOSE_STANDARD)?NULL:"Sound \"%s\" was precached but not directly used%s", precache_sound[i].name, dupewarncount?"":" (annotate the usage with the used_sound intrinsic to silence this warning)"); else if (!precache_sound[i].block) dupewarncount+=QCC_PR_Warning(WARN_NOTPRECACHED, precache_sound[i].filename, precache_sound[i].fileline, (dupewarncount>10&&verbose < VERBOSE_STANDARD)?NULL:"Sound \"%s\" was used but not directly precached", precache_sound[i].name); } @@ -3720,7 +3748,7 @@ static int QCC_PR_FinishCompilation (void) { if (!externokay) { - QCC_PR_Warning(ERR_NOFUNC, d->filen, d->s_line, "extern is not supported with this target format",d->name); + QCC_PR_Warning(ERR_NOFUNC, d->filen, d->s_line, "extern is not supported with this target format"); QCC_PR_ParsePrintDef(ERR_NOFUNC, d); errors = true; } @@ -3751,7 +3779,7 @@ static int QCC_PR_FinishCompilation (void) continue; } } - if (d->unused) + if (d->unused && !d->used) { d->initialized = 1; continue; @@ -4647,20 +4675,22 @@ static void QCC_PR_CommandLinePrecompilerOptions (void) keyword_double = keyword_long = keyword_short = keyword_char = keyword_signed = keyword_unsigned = true; keyword_thinktime = keyword_until = keyword_loop = false; + flag_ILP32 = true; //C code generally expects intptr_t==size_t==long, we'll get better compat if we don't give it surprises. opt_logicops = true; //early out like C. flag_assumevar = true; //const only if explicitly const. pr_subscopedlocals = true; //locals shadow other locals rather than being the same one. flag_cpriority = true; //fiddle with operator precedence. flag_assume_integer = true; //unqualified numeric constants are assumed to be ints, consistent with C. flag_assume_double = true; //and any immediates with a decimal points are assumed to be doubles, consistent with C. - flag_qcfuncs = false; - flag_macroinstrings = false; + 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. 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. qccwarningaction[WARN_TOOFEWPARAMS] = WA_ERROR; //missing args should be fatal. 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. if (!stricmp(myargv[i]+5, "c++")) { @@ -4746,6 +4776,7 @@ static void QCC_PR_CommandLinePrecompilerOptions (void) flag_boundchecks = false; //gmqcc doesn't support dynamic bound checks, so xonotic is buggy shite. we don't want to generate code that will crash. flag_macroinstrings = false; flag_reciprocalmaths = true; //optimise x/y to x*(1/y) in constants. + flag_undefwordsize = true; //assume we're targetting DP and go into lame mode. opt_logicops = true; //we have to disable some of these warnings, because xonotic insists on using -Werror. use -Wextra to override. @@ -4758,6 +4789,7 @@ static void QCC_PR_CommandLinePrecompilerOptions (void) qccwarningaction[WARN_IFSTRING_USED] = WA_IGNORE; //and many people would argue that this was a feature rather than a bug qccwarningaction[WARN_UNINITIALIZED] = WA_IGNORE; //all locals get 0-initialised anyway, and our checks are not quite up to scratch. qccwarningaction[WARN_GMQCC_SPECIFIC] = WA_IGNORE; //we shouldn't warn about gmqcc syntax when we're trying to be compatible with it. there's always -Wextra. + qccwarningaction[WARN_OCTAL_IMMEDIATE] = WA_IGNORE; //we shouldn't warn about gmqcc syntax when we're trying to be compatible with it. there's always -Wextra. qccwarningaction[WARN_SYSTEMCRC] = WA_IGNORE; //lameness qccwarningaction[WARN_SYSTEMCRC2] = WA_IGNORE; //extra lameness qccwarningaction[WARN_ARGUMENTCHECK] = WA_IGNORE; //gmqcc is often used on DP mods, and DP is just too horrible to fix its problems. Also there's a good chance its using some undocumented/new thing. @@ -5321,7 +5353,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<<17; + MAX_REGS = 1<<19; MAX_STRINGS = 1<<21; MAX_GLOBALS = 1<<17; MAX_FIELDS = 1<<13; @@ -5803,7 +5835,7 @@ void QCC_ContinueCompile(void) if(setjmp(pr_parse_abort)) { if (++pr_error_count > MAX_ERRORS) - QCC_Error (ERR_PARSEERRORS, "Errors have occured\n"); + QCC_Error (ERR_PARSEERRORS, "%i errors have occured\n", pr_error_count); return; //just try move onto the next file, gather errors. } else @@ -5849,7 +5881,7 @@ void QCC_ContinueCompile(void) QCC_LoadFile (qccmfilename, (void *)&qccmsrc2); if (!QCC_PR_CompileFile (qccmsrc2, qccmfilename) ) - QCC_Error (ERR_PARSEERRORS, "Errors have occured\n"); + QCC_Error (ERR_PARSEERRORS, "%i errors have occured\n", pr_error_count); */ } void QCC_FinishCompile(void) @@ -6026,7 +6058,7 @@ void new_QCC_ContinueCompile(void) { // if (pr_error_count != 0) { - QCC_Error (ERR_PARSEERRORS, "Errors have occured"); + QCC_Error (ERR_PARSEERRORS, "%i errors have occured\n", pr_error_count); return; } QCC_PR_SkipToSemicolon (); @@ -6037,7 +6069,7 @@ void new_QCC_ContinueCompile(void) if (pr_token_type == tt_eof) { if (pr_error_count) - QCC_Error (ERR_PARSEERRORS, "Errors have occured"); + QCC_Error (ERR_PARSEERRORS, "%i errors have occured\n", pr_error_count); if (autoprototype && !parseonly) { diff --git a/engine/qclib/test.c b/engine/qclib/test.c index 233608564..ee0589b39 100644 --- a/engine/qclib/test.c +++ b/engine/qclib/test.c @@ -13,6 +13,7 @@ #include #include #include +#include enum{false,true}; @@ -25,6 +26,93 @@ void PF_puts (pubprogfuncs_t *prinst, struct globalvars_s *gvars) printf("%s", s); } +void PF_strcat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + G_INT(OFS_RETURN) = prinst->TempString(prinst, prinst->VarString(prinst, 0)); +} +void PF_ftos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + char temp[64]; + sprintf(temp, "%g", G_FLOAT(OFS_PARM0)); + G_INT(OFS_RETURN) = prinst->TempString(prinst, temp); +} +void PF_vtos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + char temp[64]; + sprintf(temp, "'%g %g %g'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]); + G_INT(OFS_RETURN) = prinst->TempString(prinst, temp); +} +void PF_etos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + char temp[64]; + sprintf(temp, "%i", G_INT(OFS_PARM0)); + G_INT(OFS_RETURN) = prinst->TempString(prinst, temp); +} +void PF_itos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + char temp[64]; + sprintf(temp, "%x", G_INT(OFS_PARM0)); + G_INT(OFS_RETURN) = prinst->TempString(prinst, temp); +} +void PF_ltos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + char temp[64]; + sprintf(temp, "%"PRIx64, G_INT64(OFS_PARM0)); + G_INT(OFS_RETURN) = prinst->TempString(prinst, temp); +} +void PF_dtos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + char temp[64]; + sprintf(temp, "%g", G_DOUBLE(OFS_PARM0)); + G_INT(OFS_RETURN) = prinst->TempString(prinst, temp); +} +void PF_stof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + G_FLOAT(OFS_RETURN) = strtod(PR_GetStringOfs(prinst, OFS_PARM0), NULL); +} +void PF_stov (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + sscanf(PR_GetStringOfs(prinst, OFS_PARM0), " ' %f %f %f ' ", + &G_FLOAT(OFS_RETURN+0), + &G_FLOAT(OFS_RETURN+1), + &G_FLOAT(OFS_RETURN+2)); +} +void PF_strcmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + if (prinst->callargc >= 3) + G_FLOAT(OFS_RETURN) = strncmp(PR_GetStringOfs(prinst, OFS_PARM0), PR_GetStringOfs(prinst, OFS_PARM1), G_FLOAT(OFS_PARM2)); + else + G_FLOAT(OFS_RETURN) = strcmp(PR_GetStringOfs(prinst, OFS_PARM0), PR_GetStringOfs(prinst, OFS_PARM1)); +} +void PF_vlen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + float *v = G_VECTOR(OFS_PARM0); + G_FLOAT(OFS_RETURN) = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); +} +void PF_normalize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + float *v = G_VECTOR(OFS_PARM0); + double l = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); + if (l) + l = 1./l; + else + l = 0; + G_VECTOR(OFS_RETURN)[0] = v[0] * l; + G_VECTOR(OFS_RETURN)[1] = v[1] * l; + G_VECTOR(OFS_RETURN)[2] = v[2] * l; +} +void PF_floor (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0)); +} +void PF_pow (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + G_FLOAT(OFS_RETURN) = pow(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1)); +} +void PF_sqrt (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + G_FLOAT(OFS_RETURN) = sqrt(G_FLOAT(OFS_PARM0)); +} void PF_putv (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -391,6 +479,15 @@ void PF_printf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) printf("%s", outbuf); } +struct edict_s +{ + enum ereftype_e ereftype; + float freetime; // realtime when the object was freed + unsigned int entnum; + unsigned int fieldsize; + pbool readonly; //causes error when QC tries writing to it. (quake's world entity) + void *fields; +}; void PF_spawn (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { struct edict_s *ed; @@ -398,6 +495,22 @@ void PF_spawn (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) pr_globals = PR_globals(prinst, PR_CURRENT); RETURN_EDICT(prinst, ed); } +void PF_remove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + struct edict_s *ed = (void*)G_EDICT(prinst, OFS_PARM0); + if (ed->ereftype == ER_FREE) + { + printf("Tried removing free entity\n"); + PR_StackTrace(prinst, false); + return; + } + ED_Free (prinst, (void*)ed); +} + +void PF_error(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + PF_puts(prinst, pr_globals); +} void PF_bad (pubprogfuncs_t *prinst, struct globalvars_s *gvars) { @@ -407,10 +520,28 @@ void PF_bad (pubprogfuncs_t *prinst, struct globalvars_s *gvars) builtin_t builtins[] = { PF_bad, PF_puts, + PF_ftos, + PF_spawn, + PF_remove, + PF_vtos, + PF_error, + PF_vlen, + PF_etos, + PF_stof, + PF_strcat, + PF_strcmp, + PF_normalize, + PF_sqrt, + PF_floor, + PF_pow, + PF_stov, + PF_itos, + PF_ltos, + PF_dtos, + PF_puts, PF_putv, PF_putf, PF_printf, - PF_spawn }; @@ -483,7 +614,120 @@ pbool Sys_WriteFile (const char *fname, void *data, int len) return 1; } -void runtest(const char *progsname) + +void ASMCALL StateOp(pubprogfuncs_t *prinst, float var, func_t func) +{ //note: inefficient. stupid globals abuse and not making assumptions about fields + int *selfg = (int*)PR_FindGlobal(prinst, "self", 0, NULL); + struct edict_s *ed = PROG_TO_EDICT(prinst, selfg?*selfg:0); + float *time = (float*)PR_FindGlobal(prinst, "time", 0, NULL); + eval_t *think = prinst->GetEdictFieldValue(prinst, ed, "think", ev_function, NULL); + eval_t *nextthink = prinst->GetEdictFieldValue(prinst, ed, "nextthink", ev_float, NULL); + eval_t *frame = prinst->GetEdictFieldValue(prinst, ed, "frame", ev_float, NULL); + if (time && nextthink) + nextthink->_float = *time+0.1; + if (think) + think->function = func; + if (frame) + frame->_float = var; +} +void ASMCALL CStateOp(pubprogfuncs_t *progs, float first, float last, func_t currentfunc) +{ +/* + float min, max; + float step; + float *vars = PROG_TO_WEDICT(progs, *w->g.self)->fields; + float frame = e->v->frame; + +// if (progstype == PROG_H2) +// e->v->nextthink = *w->g.time+0.05; +// else + e->v->nextthink = *w->g.time+0.1; + e->v->think = currentfunc; + + if (csqcg.cycle_wrapped) + *csqcg.cycle_wrapped = false; + + if (first > last) + { //going backwards + min = last; + max = first; + step = -1.0; + } + else + { //forwards + min = first; + max = last; + step = 1.0; + } + if (frame < min || frame > max) + frame = first; //started out of range, must have been a different animation + else + { + frame += step; + if (frame < min || frame > max) + { //became out of range, must have wrapped + if (csqcg.cycle_wrapped) + *csqcg.cycle_wrapped = true; + frame = first; + } + } + e->v->frame = frame; +*/ +} +static void ASMCALL CWStateOp (pubprogfuncs_t *prinst, float first, float last, func_t currentfunc) +{ +/* + float min, max; + float step; + world_t *w = prinst->parms->user; + wedict_t *e = PROG_TO_WEDICT(prinst, *w->g.self); + float frame = e->v->weaponframe; + + e->v->nextthink = *w->g.time+0.1; + e->v->think = currentfunc; + + if (csqcg.cycle_wrapped) + *csqcg.cycle_wrapped = false; + + if (first > last) + { //going backwards + min = last; + max = first; + step = -1.0; + } + else + { //forwards + min = first; + max = last; + step = 1.0; + } + if (frame < min || frame > max) + frame = first; //started out of range, must have been a different animation + else + { + frame += step; + if (frame < min || frame > max) + { //became out of range, must have wrapped + if (csqcg.cycle_wrapped) + *csqcg.cycle_wrapped = true; + frame = first; + } + } + e->v->weaponframe = frame; +*/ +} +void ASMCALL ThinkTimeOp(pubprogfuncs_t *prinst, struct edict_s *ed, float var) +{ + float *self = (float*)PR_FindGlobal(prinst, "self", 0, NULL); + float *time = (float*)PR_FindGlobal(prinst, "time", 0, NULL); + int *nextthink = (int*)PR_FindGlobal(prinst, "nextthink", 0, NULL); + float *vars = PROG_TO_EDICT(prinst, self?*self:0)->fields; + if (time && nextthink) + vars[*nextthink] = *time+0.1; +} + + +void runtest(const char *progsname, const char **args) { pubprogfuncs_t *pf; func_t func; @@ -497,6 +741,10 @@ void runtest(const char *progsname) ext.FileSize= Sys_FileSize; ext.Abort = Sys_Abort; ext.Printf = printf; + ext.stateop = StateOp; + ext.cstateop = CStateOp; + ext.cwstateop = CWStateOp; + ext.thinktimeop = ThinkTimeOp; ext.numglobalbuiltins = sizeof(builtins)/sizeof(builtins[0]); ext.globalbuiltins = builtins; @@ -520,15 +768,34 @@ void runtest(const char *progsname) if (!func) printf("Couldn't find function\n"); else + { //feed it some complex args. + void *pr_globals = PR_globals(pf, PR_CURRENT); + int i; + const char *atypes = *args++; + for (i = 0; atypes[i]; i++) switch(atypes[i]) + { + case 'f': + G_FLOAT(OFS_PARM0+i*3) = atof(*args++); + break; + case 'v': + sscanf(*args++, " %f %f %f ", &G_VECTOR(OFS_PARM0+i*3)[0], &G_VECTOR(OFS_PARM0+i*3)[1], &G_VECTOR(OFS_PARM0+i*3)[2]); + break; + case 's': + G_INT(OFS_PARM0+i*3) = pf->TempString(pf, *args++); + break; + } + pf->ExecuteProgram(pf, func); //call the function + } } pf->Shutdown(pf); } //Run a compiler and nothing else. //Note that this could be done with an autocompile of PR_COMPILEALWAYS. -void compile(int argc, const char **argv) +pbool compile(int argc, const char **argv) { + pbool success = false; pubprogfuncs_t *pf; progparms_t ext; @@ -565,25 +832,44 @@ void compile(int argc, const char **argv) { while(pf->ContinueCompile(pf) == 1) ; + success = true; } else printf("compilation failed to start\n"); } else printf("no compiler in this qcvm build\n"); + pf->Shutdown(pf); + return success; } int main(int argc, const char **argv) { + int i, a=0; + char atypes[9]; + const char *args[9] = {atypes}; + const char *dat = NULL; if (argc < 2) { printf("Invalid arguments!\nPlease run as, for example:\n%s testprogs.dat -srcfile progs.src\nThe first argument is the name of the progs.dat to run, the remaining arguments are the qcc args to use", argv[0]); return 0; } - compile(argc-1, argv+1); - runtest(argv[1]); + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-float")) {atypes[a] = 'f'; args[++a] = argv[++i];} + else if (!strcmp(argv[i], "-vector")) {atypes[a] = 'v'; args[++a] = argv[++i];} + else if (!strcmp(argv[i], "-string")) {atypes[a] = 's'; args[++a] = argv[++i];} + else if (!strcmp(argv[i], "-srcfile")) {if (!compile(argc-i, argv+i))return EXIT_FAILURE; break;} //compile it, woo. consume the rest of the args, too + else if (!dat && argv[i][0] != '-') {dat = argv[i];} + else {printf("unknown arg %s\n", argv[i]); return EXIT_FAILURE;} + } + atypes[a] = 0; + if (dat) + runtest(dat, args); + else + printf("Nothing to run\n"); - return 0; + return EXIT_SUCCESS; } diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 1931c32cf..0990c1362 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -12031,6 +12031,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs //END EXT_CSQC {"memalloc", PF_memalloc, 0, 0, 0, 384, D("__variant*(int size)", "Allocate an arbitary block of memory")}, + {"memrealloc", PF_memrealloc, 0, 0, 0, 0, D("__variant*(void *oldptr, int newsize)", "Allocate a new block of memory to replace an old one.")}, {"memfree", PF_memfree, 0, 0, 0, 385, D("void(__variant *ptr)", "Frees a block of memory that was allocated with memfree")}, {"memcmp", PF_memcmp, 0, 0, 0, 0, D("int(__variant *dst, __variant *src, int size, optional int dstoffset, int srcoffset)", "Compares two blocks of memory. Returns 0 if equal.")}, {"memcpy", PF_memcpy, 0, 0, 0, 386, D("void(__variant *dst, __variant *src, int size, optional int dstoffset, int srcoffset)", "Copys memory from one location to another")}, diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 06e72ee97..60b9a6b96 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -1665,8 +1665,9 @@ qboolean SVC_GetChallenge (qboolean respond_dp) if (!protocols) { + COM_ParseOut(Cmd_Argv(2), tprot,sizeof(tprot)); COM_ParseOut(com_protocolname.string, oprot,sizeof(oprot)); - pname = va("print\nGame mismatch: This is a %s server\n", oprot); + pname = va("print\nGame mismatch: This is a %s server but you are using %s\n", oprot, tprot); Netchan_OutOfBand(NCF_SERVER, &net_from, strlen(pname), pname); return false; } diff --git a/engine/web/fteshell.html b/engine/web/fteshell.html index ac2c5bba6..cfbc92ed9 100644 --- a/engine/web/fteshell.html +++ b/engine/web/fteshell.html @@ -57,6 +57,7 @@ var Module = { "showpic touch_weapons.tga weap 80 -80 bm 32 32 +weaponwheel 5\n" "showpic touch_menu.tga menu -32 0 tr 32 32 togglemenu 10\n"), */ }, +// autostart: true, //uncomment to start up straight away without asking about files or not. hope your files are okay. // quiturl: "/", //url to jump to when 'quitting' (otherwise uses history.back). // arguments:["+alias","f_startup","connect","wss://theservertojoin", "-manifest","default.fmf"], //beware the scheme registration stuff (pwa+js methods). // manifest: "index.html.fmf", // '-manifest' arg if args are not explicit. also inhibits the #foo.fmf thing.