//------------------------------------------------------------------------- /* Copyright (C) 2010 EDuke32 developers and contributors This file is part of EDuke32. EDuke32 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //------------------------------------------------------------------------- // This object is shared by the editors of *all* Build games! #include "m32script.h" #include "m32def.h" #include "cache1d.h" #include "sounds_mapster32.h" //#include "osd.h" #include "keys.h" #include "vfs.h" char g_szScriptFileName[BMAX_PATH] = "(none)"; // file we're currently compiling static char g_szCurrentBlockName[BMAX_PATH] = "(none)"; static char g_szLastBlockName[BMAX_PATH] = "NULL"; ////// compiler state vvv static char const *textptr; static char const *start_textptr; static char const *g_curkwptr; static int32_t def_tw; int32_t g_totalLines; int32_t g_lineNumber; int32_t g_numCompilerErrors; int32_t g_numCompilerWarnings; int32_t g_didDefineSomething; typedef struct { int32_t currentStateIdx; ofstype currentStateOfs; // the offset to the start of the currently parsed states' code char *curStateMenuName; int32_t currentEvent; ofstype parsingEventOfs; int32_t checkingSwitch; int32_t numCases; instype *caseScriptPtr; // the pointer to the start of the case table in a switch statement // first entry is 'default' code. instype *caseCodePtr; // the pointer to the start of the different cases' code int32_t labelsOnly; int32_t numBraces; int32_t checkingIfElse, ifElseAborted; } compilerstate_t; static compilerstate_t cs; static compilerstate_t cs_default = {-1, -1, NULL, -1, -1, 0, 0, NULL, NULL, 0, 0, 0, 0}; ////// ------------------- instype * apScript = NULL; instype * g_scriptPtr; int32_t g_scriptSize = 65536; int32_t * constants; int32_t constants_allocsize = 1024; int32_t g_numSavedConstants = 0; static int32_t g_wasConstant = 0; char * label; int32_t * labelval; uint8_t * labeltype; int32_t g_numLabels = 0; int32_t g_numDefaultLabels = 0; static int32_t label_allocsize = 512; int32_t g_stateCount = 0; statesinfo_t * statesinfo = NULL; static int32_t statesinfo_allocsize = 512; static int32_t interactive_compilation = 0; static char tempbuf[2048]; static char tlabel[MAXLABELLEN]; static char tlabel2[MAXLABELLEN]; int32_t g_iReturnVar = 0; int32_t m32_sortvar1; int32_t m32_sortvar2; char * apStrings[MAXQUOTES + 1]; char * apXStrings[MAXQUOTES + 1]; int32_t g_numXStrings = 0; ofstype aEventOffsets[MAXEVENTS]; int32_t aEventSizes[MAXEVENTS]; uint16_t aEventNumLocals[MAXEVENTS]; gamevar_t aGameVars[MAXGAMEVARS]; gamearray_t aGameArrays[MAXGAMEARRAYS]; int32_t g_gameVarCount = 0; int32_t g_systemVarCount = 0; int32_t g_gameArrayCount = 0; int32_t g_systemArrayCount = 0; // "magic" number for { and }, overrides line number in compiled code for later detection #define IFELSE_MAGIC 31337 enum ScriptLabel_t { LABEL_ANY = -1, LABEL_DEFINE = 1, LABEL_EVENT = 2 }; static const char *LabelTypeText[] = { "define", "event" }; static char *C_GetLabelType(int32_t type) { uint32_t i; char x[64]; x[0] = 0; for (i=0; i" }; const memberlabel_t SectorLabels[]= { { "wallptr", SECTOR_WALLPTR, 1, 0, 0 }, { "wallnum", SECTOR_WALLNUM, 1, 0, 0 }, { "ceilingz", SECTOR_CEILINGZ, 0, 0, 0 }, { "floorz", SECTOR_FLOORZ, 0, 0, 0 }, { "ceilingstat", SECTOR_CEILINGSTAT, 0, 0, 0 }, { "floorstat", SECTOR_FLOORSTAT, 0, 0, 0 }, { "ceilingpicnum", SECTOR_CEILINGPICNUM, 0, 0, MAXTILES-1 }, { "ceilingslope", SECTOR_CEILINGSLOPE, 0, 0, 0}, { "ceilingshade", SECTOR_CEILINGSHADE, 0, 0, 0 }, { "ceilingpal", SECTOR_CEILINGPAL, 0, 0, 0 }, { "ceilingxpanning", SECTOR_CEILINGXPANNING, 0, 0, 0 }, { "ceilingypanning", SECTOR_CEILINGYPANNING, 0, 0, 0 }, { "floorpicnum", SECTOR_FLOORPICNUM, 0, 0, MAXTILES-1 }, { "floorslope", SECTOR_FLOORSLOPE, 0, 0, 0 }, { "floorshade", SECTOR_FLOORSHADE, 0, 0, 0 }, { "floorpal", SECTOR_FLOORPAL, 0, 0, 0 }, { "floorxpanning", SECTOR_FLOORXPANNING, 0, 0, 0 }, { "floorypanning", SECTOR_FLOORYPANNING, 0, 0, 0 }, { "visibility", SECTOR_VISIBILITY, 0, 0, 0 }, { "fogpal", SECTOR_FOGPAL, 0, 0, 0 }, // formerly filler { "lotag", SECTOR_LOTAG, 0, 0, 0 }, { "hitag", SECTOR_HITAG, 0, 0, 0 }, { "extra", SECTOR_EXTRA, 0, 0, 0 }, // aliases { "alignto", SECTOR_FOGPAL, 0, 0, 0 }, // formerly filler { "ceilingheinum", SECTOR_CEILINGSLOPE, 0, 0, 0}, { "floorheinum", SECTOR_FLOORSLOPE, 0, 0, 0}, { "", -1, 0, 0, 0 } // END OF LIST }; const memberlabel_t WallLabels[]= { { "x", WALL_X, 0, -BXY_MAX, BXY_MAX }, { "y", WALL_Y, 0, -BXY_MAX, BXY_MAX }, { "point2", WALL_POINT2, 1, 0, 0 }, { "nextwall", WALL_NEXTWALL, 1, 0, 0 }, { "nextsector", WALL_NEXTSECTOR, 1, 0, 0 }, { "cstat", WALL_CSTAT, 0, 0, 0 }, { "picnum", WALL_PICNUM, 0, 0, MAXTILES-1 }, { "overpicnum", WALL_OVERPICNUM, 0, 0, MAXTILES-1 }, { "shade", WALL_SHADE, 0, 0, 0 }, { "pal", WALL_PAL, 0, 0, 0 }, { "xrepeat", WALL_XREPEAT, 0, 0, 0 }, { "yrepeat", WALL_YREPEAT, 0, 0, 0 }, { "xpanning", WALL_XPANNING, 0, 0, 0 }, { "ypanning", WALL_YPANNING, 0, 0, 0 }, { "lotag", WALL_LOTAG, 0, 0, 0 }, { "hitag", WALL_HITAG, 0, 0, 0 }, { "extra", WALL_EXTRA, 0, 0, 0 }, { "", -1, 0, 0, 0 } // END OF LIST }; const memberlabel_t SpriteLabels[]= { { "x", SPRITE_X, 0, -BXY_MAX, BXY_MAX }, { "y", SPRITE_Y, 0, -BXY_MAX, BXY_MAX }, { "z", SPRITE_Z, 0, 0, 0 }, { "cstat", SPRITE_CSTAT, 0, 0, 0 }, { "picnum", SPRITE_PICNUM, 0, 0, MAXTILES-1 }, { "shade", SPRITE_SHADE, 0, 0, 0 }, { "pal", SPRITE_PAL, 0, 0, 0 }, { "clipdist", SPRITE_CLIPDIST, 0, 0, 0 }, { "blend", SPRITE_BLEND, 0, 0, 0 }, { "xrepeat", SPRITE_XREPEAT, 0, 0, 0 }, { "yrepeat", SPRITE_YREPEAT, 0, 0, 0 }, { "xoffset", SPRITE_XOFFSET, 0, 0, 0 }, { "yoffset", SPRITE_YOFFSET, 0, 0, 0 }, { "sectnum", SPRITE_SECTNUM, 0, 0, 0 }, { "statnum", SPRITE_STATNUM, 0, 0, 0 }, { "ang", SPRITE_ANG, 0, 0, 0 }, { "owner", SPRITE_OWNER, 0, 0, 0 }, { "xvel", SPRITE_XVEL, 0, 0, 0 }, { "yvel", SPRITE_YVEL, 0, 0, 0 }, { "zvel", SPRITE_ZVEL, 0, 0, 0 }, { "lotag", SPRITE_LOTAG, 0, 0, 0 }, { "hitag", SPRITE_HITAG, 0, 0, 0 }, { "extra", SPRITE_EXTRA, 0, 0, 0 }, { "", -1, 0, 0, 0 } // END OF LIST }; #ifndef POLYMER # define PR_MAXLIGHTPRIORITY 6 #endif const memberlabel_t LightLabels[]= { { "x", LIGHT_X, 0, -BXY_MAX, BXY_MAX }, { "y", LIGHT_Y, 0, -BXY_MAX, BXY_MAX }, { "z", LIGHT_Z, 0, 0, 0 }, { "horiz", LIGHT_HORIZ, 0, 0, 0 }, { "range", LIGHT_RANGE, 0, 0, 0 }, { "angle", LIGHT_ANGLE, 0, 0, 0 }, { "faderadius", LIGHT_FADERADIUS, 0, 0, 0 }, { "radius", LIGHT_RADIUS, 0, 0, 0 }, { "sector", LIGHT_SECTOR, 0, 0, 0 }, { "r", LIGHT_R, 0, 0, 255 }, { "g", LIGHT_G, 0, 0, 255 }, { "b", LIGHT_B, 0, 0, 255 }, { "priority", LIGHT_PRIORITY, 0, 0, PR_MAXLIGHTPRIORITY-1 }, { "tilenum", LIGHT_TILENUM, 0, 0, MAXTILES-1 }, { "minshade", LIGHT_MINSHADE, 0, -128, 127 }, { "maxshade", LIGHT_MAXSHADE, 0, -128, 127 }, // { "active", LIGHT_ACTIVE, 0, 0, 1 }, { "", -1, 0, 0, 0 } // END OF LIST }; const tokenmap_t iter_tokens[] = { { "allsprites", ITER_ALLSPRITES }, { "allsectors", ITER_ALLSECTORS }, { "allwalls", ITER_ALLWALLS }, { "activelights", ITER_ACTIVELIGHTS }, { "selsprites", ITER_SELSPRITES }, { "selsectors", ITER_SELSECTORS }, { "selwalls", ITER_SELWALLS }, { "drawnsprites", ITER_DRAWNSPRITES }, { "spritesofsector", ITER_SPRITESOFSECTOR }, { "loopofwall", ITER_LOOPOFWALL }, { "wallsofsector", ITER_WALLSOFSECTOR }, { "range", ITER_RANGE }, // vvv alternatives go here vvv { "selspr", ITER_SELSPRITES }, { "selsec", ITER_SELSECTORS }, { "lights", ITER_ACTIVELIGHTS }, { "sprofsec", ITER_SPRITESOFSECTOR }, { "walofsec", ITER_WALLSOFSECTOR }, { "", -1 } // END OF LIST }; hashtable_t h_gamevars = { MAXGAMEVARS>>1, NULL }; hashtable_t h_arrays = { MAXGAMEARRAYS>>1, NULL }; hashtable_t h_labels = { 11262>>1, NULL }; static hashtable_t h_localvars = { MAXGAMEVARS>>2, NULL }; // values: offset|(array?(size<<16):0) static hashtable_t h_states = { 1264>>1, NULL }; static hashtable_t h_keywords = { CON_END>>1, NULL }; static hashtable_t h_iter = { ITER_END, NULL }; static hashtable_t h_sector = { SECTOR_END>>1, NULL }; static hashtable_t h_wall = { WALL_END>>1, NULL }; static hashtable_t h_sprite = { SPRITE_END>>1, NULL }; static hashtable_t h_light = { SPRITE_END>>1, NULL }; static void C_InitHashes() { int32_t i; hash_init(&h_gamevars); hash_init(&h_arrays); hash_init(&h_localvars); hash_init(&h_labels); hash_init(&h_states); hash_init(&h_keywords); for (i=NUMKEYWORDS-1; i>=0; i--) hash_add(&h_keywords, keyw[i], i, 0); for (i=0; i=0; i++) hash_add(&h_sector,SectorLabels[i].name,i, 0); hash_init(&h_wall); for (i=0; WallLabels[i].lId >=0; i++) hash_add(&h_wall,WallLabels[i].name,i, 0); hash_init(&h_sprite); for (i=0; SpriteLabels[i].lId >=0; i++) hash_add(&h_sprite,SpriteLabels[i].name,i, 0); hash_init(&h_light); for (i=0; LightLabels[i].lId >=0; i++) hash_add(&h_light,LightLabels[i].name,i, 0); hash_init(&h_iter); for (i=0; iter_tokens[i].val >=0; i++) hash_add(&h_iter, iter_tokens[i].token, iter_tokens[i].val, 0); } // returns: 0:success, 1:failure static int32_t C_SetScriptSize(int32_t size) { ofstype oscriptOfs = (unsigned)(g_scriptPtr-apScript); ofstype ocaseScriptOfs = (unsigned)(cs.caseScriptPtr-apScript); ofstype ocaseCodeOfs = (unsigned)(cs.caseCodePtr-apScript); instype *newscript; int32_t osize = g_scriptSize; if (g_scriptSize >= size) return 0; //initprintf("offset: %d\n",(unsigned)(g_scriptPtr-script)); g_scriptSize = size; initprintf("Resizing code buffer to %d*%d bytes\n", g_scriptSize, (int32_t)sizeof(instype)); newscript = (instype *)Xrealloc(apScript, g_scriptSize * sizeof(instype)); if (size >= osize) Bmemset(&newscript[osize], 0, (size-osize) * sizeof(instype)); if (apScript != newscript) { buildprint("Relocating compiled code from to 0x", hex((intptr_t)apScript), " to 0x", hex((intptr_t)newscript), "\n"); apScript = newscript; } g_scriptPtr = (instype *)(apScript+oscriptOfs); // initprintf("script: %d, \n",script); initprintf("offset: %d\n",(unsigned)(g_scriptPtr-script)); if (cs.caseScriptPtr != NULL) cs.caseScriptPtr = (instype *)(apScript+ocaseScriptOfs); if (cs.caseCodePtr != NULL) cs.caseCodePtr = (instype *)(apScript+ocaseCodeOfs); return 0; } static int32_t char_whitespace(char c) { return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='(' || c==')' || c==',' || c==';'; } static int32_t char_alnumtok(char c) { return isalnum(c) || c == '#' || c == '{' || c == '}' || c == '/' || c == '\\' || c == '*' || c == '-' || c == '_' || c == '.' || c == '"'; } static int32_t C_SkipComments(void) { char c = *textptr; do { if (char_whitespace(c)) { textptr++; g_lineNumber += (c=='\n'); } else if (c == '/' && textptr[1] == '/') { while (*textptr && *textptr != 0x0a && *textptr != 0x0d) textptr++; } else if (c == '/' && textptr[1] == '*') { textptr += 2; while (*textptr && !(textptr[0] == '*' && textptr[1] == '/')) { if (*textptr == '\n') g_lineNumber++; textptr++; } if (!*textptr) { C_CUSTOMERROR("found `/*' with no `*/'."); cs.numBraces = 0; cs.currentStateIdx = -1; break; } textptr += 2; } else break; } while ((c = *textptr)); // Be sure to have enough space allocated for the command to be parsed next. // Currently, the commands that potentially need the most space are // various string handling function that accept inline strings. if ((unsigned)(g_scriptPtr-apScript) > (unsigned)(g_scriptSize - max(40, MAXQUOTELEN/sizeof(instype)+8))) return C_SetScriptSize(g_scriptSize<<1); return 0; } static inline int32_t C_GetLabelNameID(const memberlabel_t *pLabel, hashtable_t *tH, const char *psz) { // find the label psz in the table pLabel. // returns the ID for the label, or -1 int32_t l = hash_findcase(tH, psz); if (l>=0) l = pLabel[l].lId; return l; } // returns: 1 on EOF or (checkkeyw and keyword encountered), 0 else static int32_t C_GetNextLabelName(int32_t checkkeyw) { int32_t i; C_SkipComments(); tlabel[0] = 0; if (*textptr == 0) { C_CUSTOMERROR("unexpected EOF where label was expected."); return 1; } while (*textptr && (char_whitespace(*textptr) || *textptr=='-')) //!isalnum(*textptr)) { g_lineNumber += (*textptr == 0x0a); textptr++; } if (!isalpha(*textptr)) C_CUSTOMERROR("label names must start with an alphabetic character, encountered '%c'.", *textptr); i = 0; while (*textptr && !char_whitespace(*textptr) && *textptr!='['&& *textptr!=']') { if (i < MAXLABELLEN-1) tlabel[i++] = *(textptr++); else textptr++; } tlabel[i] = 0; if (checkkeyw) { if (hash_find(&h_keywords, tlabel)>=0) { g_numCompilerErrors++; C_ReportError(ERROR_ISAKEYWORD); return 1; } } // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug > 1) // initprintf("%s:%d: debug: got label `%s'.\n",g_szScriptFileName,g_lineNumber,tlabel); return 0; } static int32_t C_CopyLabel(void) { if (g_numLabels >= label_allocsize) { label = (char *)Xrealloc(label, 2*label_allocsize*MAXLABELLEN*sizeof(char)); labelval = (int32_t *)Xrealloc(labelval, 2*label_allocsize*sizeof(labelval[0])); labeltype = (uint8_t *)Xrealloc(labeltype, 2*label_allocsize*sizeof(labeltype[0])); label_allocsize *= 2; } Bmemcpy(label+(g_numLabels*MAXLABELLEN), tlabel, MAXLABELLEN); return 0; } // returns: -1 on EOF or not a keyword, keyword index (==bytecode value) else static int32_t C_GetKeyword(void) { int32_t i; const char *temptextptr; C_SkipComments(); if (*textptr == 0) return -1; temptextptr = textptr; while (!char_alnumtok(*temptextptr)) { temptextptr++; if (!*temptextptr) return -1; } i = 0; while (char_alnumtok(*temptextptr)) tempbuf[i++] = *(temptextptr++); tempbuf[i] = 0; return hash_find(&h_keywords, tempbuf); } //Returns its code # if keyword, -1 on eof or non-keyword static int32_t C_GetNextKeyword(void) { int32_t i, l, olinenum = g_lineNumber, havequickstate=0; const char *otextptr = textptr; C_SkipComments(); if (*textptr == 0) return -1; while (!char_alnumtok(*textptr)) { g_lineNumber += (*textptr == 0x0a); if (!*textptr) return -1; textptr++; } l = 0; while (char_alnumtok(textptr[l]) && textptr[l] != '.') { tempbuf[l] = textptr[l]; l++; } while (char_alnumtok(textptr[l])) { tempbuf[l] = textptr[l]; l++; } tempbuf[l] = 0; g_curkwptr = textptr; textptr += l; i = hash_find(&h_keywords, tempbuf); if (i<0) { if (tempbuf[0]=='{' && tempbuf[1]) { C_CUSTOMERROR("expected whitespace between `{' and `%s'", tempbuf+1); return -1; } else if (tempbuf[0]=='}' && tempbuf[1]) { C_CUSTOMERROR("expected whitespace between `}' and `%s'", tempbuf+1); return -1; } else { // if compiling from OSD, try state name if (interactive_compilation) i = hash_find(&h_states, tempbuf); if (i<0) { C_ReportError(ERROR_EXPECTEDKEYWORD); g_numCompilerErrors++; return -1; } else { havequickstate = 1; i = CON_STATE; } } } if (i == CON_LEFTBRACE || i == CON_RIGHTBRACE || i == CON_NULLOP) *g_scriptPtr = i + (IFELSE_MAGIC<<12); else *g_scriptPtr = i + (g_lineNumber<<12); if (havequickstate) { g_lineNumber = olinenum; // reset textptr so that case CON_STATE in C_ParseCommand() can insert the state number textptr = otextptr; } g_scriptPtr++; // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug) // initprintf("%s:%d: debug: translating keyword `%s'.\n",g_szScriptFileName,g_lineNumber,keyw[i]); return i; } static int32_t GetGamevarID(const char *szGameLabel, int32_t searchlocals) { if (searchlocals) { int32_t retid = hash_find(&h_localvars, szGameLabel); if (retid>=0 && retid MAXGAMEVARS) // it's a local array return retid; } return hash_find(&h_arrays, szGameLabel); } // gamevar type restrictions #define GV_WRITABLE GAMEVAR_READONLY #define GV_SIMPLE GAMEVAR_SPECIAL static int32_t parse_integer_literal(int32_t *num) { if (textptr[0] == '0' && tolower(textptr[1])=='x') sscanf(textptr+2, "%" SCNx32, (uint32_t *)&num); else { long lnum; errno = 0; lnum = Bstrtol(textptr, NULL, 10); if (errno || (sizeof(long)>4 && (lnumINT32_MAX))) { C_CUSTOMERROR("integer literal exceeds bitwidth."); return 1; } *num = (int32_t)lnum; } return 0; } static void C_GetNextVarType(int32_t type) { int32_t i, id=0, flags=0, num, indirect=0; //, thenum; C_SkipComments(); if (*textptr == 0) { C_CUSTOMERROR("unexpected EOF where variable was expected."); return; } g_wasConstant = 0; if (*textptr == '"') { C_CUSTOMERROR("String literal encountered where not expected, skipping."); while (*textptr && *textptr!='"' && *textptr!=0x0a) textptr++; if (*textptr == '"') textptr++; return; } else if (!(type&GV_WRITABLE) && !cs.labelsOnly && (isdigit(textptr[0]) || (textptr[0]=='-' && isdigit(textptr[1])))) { // literal numeric constant where gamevar expected // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug) // initprintf("%s:%d: debug: accepted constant %d in place of gamevar.\n",g_szScriptFileName,g_lineNumber,Batol(textptr)); parse_integer_literal(&num); //thenum=num; if (type==GV_SIMPLE && (num<0 || num>=65536)) C_CUSTOMERROR("array index %d out of bounds. (max: 65535)", num); if (g_numCompilerErrors==0 && type!=GV_SIMPLE && num != (int16_t)num) { // Constant doesn't fit in 16 bits, make it indirect. // Array indices are interpreted as unsigned, so they always fit into the high bits of the instruction. indirect = 1; for (i=g_numSavedConstants-1; i>=0; i--) if (constants[i] == num) { num = i; break; } if (i<0) { i = g_numSavedConstants; if (i>=constants_allocsize) { constants_allocsize *= 2; constants = (int32_t *)Xrealloc(constants, constants_allocsize * sizeof(constants[0])); } constants[i] = num; num = i; g_numSavedConstants++; } } if (type!=GV_SIMPLE) g_wasConstant = 1; //printf("num:%d, idx:%d, indir:%d\n",thenum,num,indirect); *g_scriptPtr++ = (num<<16) | M32_FLAG_CONSTANT | indirect; while (!char_whitespace(*textptr) && *textptr != ']') textptr++; return; } else if (type != GV_SIMPLE && (textptr[0]=='.' || (textptr[0]=='-' && textptr[1]=='.'))) { // current sprite shortcut access int32_t labelNum; flags = M32_FLAG_STRUCT; if (*textptr=='-') { flags |= M32_FLAG_NEGATE; textptr++; } textptr++; /// now pointing at 'xxx' C_GetNextLabelName(0); labelNum = C_GetLabelNameID(SpriteLabels, &h_sprite, Bstrtolower(tlabel)); if (labelNum == -1) { g_numCompilerErrors++; C_ReportError(ERROR_SYMBOLNOTRECOGNIZED); return; } *g_scriptPtr++ = ((M32_THISACTOR_VAR_ID<<16) | flags | (labelNum<<2) | M32_SPRITE_VAR_ID); return; } else if (textptr[0]=='-' && textptr[1] != '.') // && !isdigit(*(textptr+1)) { // negation if (type==0) { // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug) // initprintf("%s:%d: debug: flagging gamevar as negative.\n",g_szScriptFileName,g_lineNumber,Batol(textptr)); flags = M32_FLAG_NEGATE; } else { if (type==GV_WRITABLE) C_CUSTOMERROR("syntax error. Tried negating written-to variable."); else if (type==GV_SIMPLE) C_CUSTOMERROR("syntax error. Tried negating variable in context that doesn't allow it."); else // type==GV_WRITABLE|GV_SIMPLE C_CUSTOMERROR("syntax error. Tried negating written-to variable in context that doesn't allow it."); C_GetNextLabelName(1); return; } } // at this point, flags can be 0 or M32_FLAG_NEGATE C_GetNextLabelName(1); C_SkipComments(); //skip comments and whitespace if (*textptr == '[') //read of array as a gamevar { int32_t labelNum = -1, aridx; int32_t lightp = 0; textptr++; flags |= M32_FLAG_ARRAY; if (type & GV_SIMPLE) { g_numCompilerErrors++; C_ReportError(ERROR_EXPECTEDSIMPLEVAR); return; } id = GetGamearrayID(tlabel, 1); if (id < 0) { id = GetGamevarID(tlabel, 0); if (id < 0 || id >= 5) { C_CUSTOMERROR("symbol `%s' is neither an array name nor one of `(t)sprite', `sector', `wall' or `light'.", tlabel); return; } if (id==4) { id = 3; // smuggle lights into tspr lightp = 1; } flags &= ~M32_FLAG_ARRAY; // not an array flags |= M32_FLAG_STRUCT; } else if (id>16)&0xffff); // decode... } // ---------- C_SkipComments(); if (*textptr != ']') { g_numCompilerErrors++; C_ReportError(ERROR_GAMEARRAYBNC); return; } textptr++; // ---------- if ((flags & M32_VARTYPE_MASK) == M32_FLAG_LOCAL) { // id: local ofs | (size<<16) int32_t ar_ofs=id&(MAXGAMEVARS-1), ar_size=(id>>16)&0xffff; if (aridx<0 || aridx>=ar_size) C_CUSTOMERROR("local array index %d out of bounds. Size of local array `%s': %d.", aridx, tlabel2, ar_size); *g_scriptPtr++ = (flags|(ar_ofs+aridx)); } if ((flags & M32_VARTYPE_MASK) == M32_FLAG_ARRAY) { if ((aridx & M32_BITS_MASK) == M32_FLAG_CONSTANTINDEX) *g_scriptPtr++ = (aridx | flags | id); else // simple or local gamevar *g_scriptPtr++ = (aridx<<16 | flags | id); } else if ((flags & M32_VARTYPE_MASK) == M32_FLAG_STRUCT) { while (*textptr && *textptr != 0x0a && *textptr != '.') textptr++; if (*textptr != '.') { const char *types[4] = {"sprite","sector","wall","tsprite"}; if (lightp) types[3] = "light"; C_CUSTOMERROR("syntax error. Expected `.