//------------------------------------------------------------------------- /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //------------------------------------------------------------------------- #include "m32script.h" #include "m32def.h" #include "cache1d.h" #include "sounds_mapster32.h" //#include "osd.h" #include "keys.h" char g_szScriptFileName[BMAX_PATH] = "(none)"; // file we're currently compiling static char g_szCurrentBlockName[BMAX_PATH] = "(none)", g_szLastBlockName[BMAX_PATH] = "NULL"; ////// compiler state vvv static const char *textptr, *start_textptr, *g_curkwptr; static int32_t def_tw; int32_t g_totalLines, g_lineNumber; int32_t g_numCompilerErrors, g_numCompilerWarnings; int32_t g_didDefineSomething; typedef struct { int32_t currentStateIdx; ofstype currentStateOfs; // the offset to the start of the currently parsed states' code 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, -1, -1, 0, 0, NULL, NULL, 0, 0, 0, 0}; ////// ------------------- instype *script = NULL; instype *g_scriptPtr; int32_t g_scriptSize = 65536; int32_t *constants, 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, 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], tlabel2[MAXLABELLEN]; int32_t g_iReturnVar=0; int32_t m32_sortvar1, m32_sortvar2; char *ScriptQuotes[MAXQUOTES+1], *ScriptQuoteRedefinitions[MAXQUOTES+1]; int32_t g_numQuoteRedefinitions = 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, g_systemVarCount=0; int32_t g_gameArrayCount=0, 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 const 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 }, { "alignto", SECTOR_ALIGNTO, 0, 0, 0 }, // aka filler, not used { "lotag", SECTOR_LOTAG, 0, 0, 0 }, { "hitag", SECTOR_HITAG, 0, 0, 0 }, { "extra", SECTOR_EXTRA, 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 }, { "detail", SPRITE_DETAIL, 1, 0, 0 }, // aka filler, not used { "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, 1, 0, 0 }, { "statnum", SPRITE_STATNUM, 1, 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 }; const tokenmap_t iter_tokens[] = { { "allsprites", ITER_ALLSPRITES }, { "allsectors", ITER_ALLSECTORS }, { "allwalls", ITER_ALLWALLS }, { "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 }, { "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 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_add(&h_sector,"filler", SECTOR_ALIGNTO, 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_add(&h_sprite,"filler", SPRITE_DETAIL, 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-script); ofstype ocaseScriptOfs = (unsigned)(cs.caseScriptPtr-script); ofstype ocaseCodeOfs = (unsigned)(cs.caseCodePtr-script); 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 *)Brealloc(script, g_scriptSize * sizeof(instype)); if (newscript == NULL) { C_ReportError(-1); initprintf("%s:%d: out of memory: Aborted (%ud)\n", g_szScriptFileName, g_lineNumber, (unsigned)(g_scriptPtr-script)); g_numCompilerErrors++; // initprintf(tempbuf); return 1; } if (size >= osize) Bmemset(&newscript[osize], 0, (size-osize) * sizeof(instype)); if (script != newscript) { initprintf("Relocating compiled code from to 0x%lx to 0x%lx\n", (unsigned long)script, (unsigned long)newscript); script = newscript; } g_scriptPtr = (instype *)(script+oscriptOfs); // initprintf("script: %d, \n",script); initprintf("offset: %d\n",(unsigned)(g_scriptPtr-script)); if (cs.caseScriptPtr != NULL) cs.caseScriptPtr = (instype *)(script+ocaseScriptOfs); if (cs.caseCodePtr != NULL) cs.caseCodePtr = (instype *)(script+ocaseCodeOfs); return 0; } static inline int32_t char_whitespace(char c) { return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='(' || c==')' || c==',' || c==';'; } static inline 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-script) > (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 = Brealloc(label, 2*label_allocsize*MAXLABELLEN*sizeof(char)); labelval = Brealloc(labelval, 2*label_allocsize*sizeof(labelval[0])); labeltype = Brealloc(labeltype, 2*label_allocsize*sizeof(labeltype[0])); if (label==NULL || labelval==NULL || labeltype==NULL) { label_allocsize = 0; initprintf("Out of memory!"); return 1; } 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 (tolower(textptr[1])=='x') sscanf(textptr+2, "%" SCNx32, num); else { long lnum; errno = 0; lnum = strtol(textptr, NULL, 10); if (errno || (sizeof(long)>4 && (lnumINT_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,atol(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 = Brealloc(constants, constants_allocsize * sizeof(constants[0])); if (!constants) { initprintf("C_GetNextVarType(): ERROR: out of memory!\n"); g_numCompilerErrors++; return; } } 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 lLabelID; flags = M32_FLAG_STRUCT; if (*textptr=='-') { flags |= M32_FLAG_NEGATE; textptr++; } textptr++; /// now pointing at 'xxx' C_GetNextLabelName(0); lLabelID = C_GetLabelNameID(SpriteLabels, &h_sprite, Bstrtolower(tlabel)); if (lLabelID == -1) { g_numCompilerErrors++; C_ReportError(ERROR_SYMBOLNOTRECOGNIZED); return; } *g_scriptPtr++ = ((M32_THISACTOR_VAR_ID<<16) | flags | (lLabelID<<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,atol(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 lLabelID = -1, aridx; 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 >= 4) { C_CUSTOMERROR("symbol `%s' is neither an array name nor one of `(t)sprite', `sector' or `wall'.", tlabel); return; } 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 != '.') { static const char *types[4] = {"sprite","sector","wall","tsprite"}; C_CUSTOMERROR("syntax error. Expected `.