//------------------------------------------------------------------------- /* 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. */ //------------------------------------------------------------------------- #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 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 *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<sizeof(LabelTypeText)/sizeof(char *); i++) { if (!(type & (1<<i))) continue; if (x[0]) Bstrcat(x, " or "); Bstrcat(x, LabelTypeText[i]); } return Xstrdup(x); } #define NUMKEYWORDS (int32_t)ARRAY_SIZE(keyw) #define NUMALTKEYWORDS (int32_t)ARRAY_SIZE(altkeyw) const tokenmap_t altkeyw[] = { { "#define", CON_DEFINE }, { "al", CON_ADDLOGVAR }, { "var", CON_GAMEVAR }, { "array", CON_GAMEARRAY }, { "shiftl", CON_SHIFTVARVARL }, { "shiftr", CON_SHIFTVARVARR }, { "rand", CON_RANDVARVAR }, { "set", CON_SETVARVAR }, { "add", CON_ADDVARVAR }, { "sub", CON_SUBVARVAR }, { "mul", CON_MULVARVAR }, { "div", CON_DIVVARVAR }, { "mod", CON_MODVARVAR }, { "and", CON_ANDVARVAR }, { "or", CON_ORVARVAR }, { "xor", CON_XORVARVAR }, { "ifl", CON_IFVARVARL }, { "ifle", CON_IFVARVARLE }, { "ifg", CON_IFVARVARG }, { "ifge", CON_IFVARVARGE }, { "ife", CON_IFVARVARE }, { "ifn", CON_IFVARVARN }, { "ifand", CON_IFVARVARAND }, { "ifor", CON_IFVARVAROR }, { "ifxor", CON_IFVARVARXOR }, { "ifeither", CON_IFVARVAREITHER }, { "ifboth", CON_IFVARVARBOTH }, { "whilen", CON_WHILEVARVARN }, { "whilel", CON_WHILEVARVARL }, }; const char *keyw[] = { "nullop", "define", "include", "defstate", // * "ends", "state", "onevent", "endevent", "gamevar", "else", "return", "break", "switch", "case", "default", "endswitch", "getcurraddress", "jump", "{", "}", "gamearray", "setarray", "getarraysize", "resizearray", "copy", "randvar", "displayrandvar", "setvar", "addvar", "subvar", "mulvar", "divvar", "modvar", "andvar", "orvar", "xorvar", "shiftvarl", "shiftvarr", "randvarvar", "displayrandvarvar", "setvarvar", // * "addvarvar", "subvarvar", "mulvarvar", "divvarvar", "modvarvar", "andvarvar", "orvarvar", "xorvarvar", "shiftvarvarl", "shiftvarvarr", "sin", "cos", "displayrand", "itof", "ftoi", "clamp", "inv", // inversion function.. not internal "sqrt", "mulscale", "divscale", "dist", "ldist", "calchypotenuse", "getangle", "getincangle", "a2xy", "ah2xyz", "collectsectors", "sort", "for", // * "ifvarl", "ifvarle", "ifvarg", "ifvarge", "ifvare", "ifvarn", "ifvarand", "ifvaror", "ifvarxor", "ifvareither", "ifvarboth", "whilevarn", "whilevarl", "ifvarvarl", "ifvarvarle", "ifvarvarg", "ifvarvarge", "ifvarvare", "ifvarvarn", "ifvarvarand", "ifvarvaror", "ifvarvarxor", "ifvarvareither", "ifvarvarboth", "whilevarvarn", "whilevarvarl", "ifhitkey", "ifholdkey", "ifrnd", "ifangdiffl", "ifspritepal", "ifhighlighted", "ifactor", "ifsound", "ifpdistl", "ifpdistg", "ifinside", "ifeitheralt", "ifeitherctrl", "ifeithershift", "ifawayfromwall", "ifcansee", "ifonwater", "ifinwater", "ifoutside", "ifnosounds", "ifin3dmode", "ifaimingsprite", "ifaimingwall", "ifaimingsector", "ifinteractive", // Mostly BUILD functions "resetkey", "setkey", "insertsprite", "dupsprite", "tdupsprite", "deletesprite", "getspritelinktype", // not BUILD "lastwall", "updatecursectnum", "updatesector", "updatesectorz", "getzrange", "clipmove", "lineintersect", "rayintersect", "hitscan", "cansee", "canseespr", "neartag", "rotatepoint", "dragpoint", "getceilzofslope", "getflorzofslope", "alignceilslope", "alignflorslope", "bsetsprite", // * "setfirstwall", "changespritestat", "changespritesect", "headspritestat", "prevspritestat", "nextspritestat", "headspritesect", "prevspritesect", "nextspritesect", "sectorofwall", "fixrepeats", "getclosestcol", "updatehighlight", "updatehighlightsector", "sethighlight", "sethighlightsector", "addlogvar", "addlog", "debug", "definequote", "redefinequote", "print", "quote", "error", "printmessage16", "printmessage256", "printext256", "printext16", "drawlabel", "getnumber16", "getnumber256", "getnumberfromuser", "qsprintf", "qstrcat", "qstrcpy", "qstrlen", // "qgetsysstr", "qstrncat", "qsubstr", "findnearsprite", "findnearspritevar", "findnearsprite3d", "findnearsprite3dvar", "findnearspritez", "findnearspritezvar", "getticks", "gettimedate", "setaspect", "seti", "sizeat", "cstat", "cstator", "clipdist", "spritepal", "cactor", "spgetlotag", "spgethitag", "sectgetlotag", "sectgethitag", "gettexturefloor", "gettextureceiling", "sound", //var "soundonce", //var "stopallsounds", "stopsound", //var "globalsound", //var "getsoundflags", /// "killit", "drawline16", "drawline16b", "drawline16z", "drawcircle16", "drawcircle16b", "drawcircle16z", "rotatespritea", "rotatesprite16", "rotatesprite", "setgamepalette", "<null>" }; 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, 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 }; #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<NUMALTKEYWORDS; i++) hash_add(&h_keywords, altkeyw[i].token, altkeyw[i].val, 0); hash_init(&h_sector); for (i=0; SectorLabels[i].lId >=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-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 *)Xrealloc(script, g_scriptSize * sizeof(instype)); if (size >= osize) Bmemset(&newscript[osize], 0, (size-osize) * sizeof(instype)); if (script != newscript) { initprintf("Relocating compiled code from to 0x%" PRIxPTR " to 0x%" PRIxPTR "\n", (intptr_t)script, (intptr_t)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 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-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 = (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 variable return M32_FLAG_LOCAL | retid; } return hash_find(&h_gamevars, szGameLabel); } static int32_t GetGamearrayID(const char *szGameLabel, int32_t searchlocals) { if (searchlocals) { int32_t retid = hash_find(&h_localvars, szGameLabel); if (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 = Bstrtol(textptr, NULL, 10); if (errno || (sizeof(long)>4 && (lnum<INT32_MIN || lnum>INT32_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 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,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 lLabelID = -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<MAXGAMEARRAYS) // simple (non-local) gamearrays { if (type & GV_WRITABLE) { const int32_t dwFlags = aGameArrays[id].dwFlags; // NOTE: GAMEARRAY_STRIDE2 arrays cannot be written to // regardless of whether we're in expert mode or not. if (!m32_script_expertmode || (dwFlags & GAMEARRAY_STRIDE2)) { if (dwFlags & GAMEARRAY_READONLY) { C_ReportError(ERROR_ARRAYREADONLY); g_numCompilerErrors++; } else if (dwFlags & GAMEARRAY_WARN) { C_CUSTOMWARNING("writing to expert-mode array. Be sure to know what you're doing!"); } } } } else // local array { Bmemcpy(tlabel2, tlabel, MAXLABELLEN); flags |= M32_FLAG_LOCAL; } // ---------- get index: // GV_SIMPLE signifies that we want only simple vars or a constant C_GetNextVarType(GV_SIMPLE); g_scriptPtr--; aridx = *g_scriptPtr; if ((flags & M32_VARTYPE_MASK) == M32_FLAG_LOCAL) { if ((aridx & M32_BITS_MASK) != M32_FLAG_CONSTANTINDEX) C_CUSTOMERROR("local arrays can only be indexed with constants."); aridx = (int16_t)((aridx>>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 `.<label>' after `%s[...]'", types[id&3]); return; } textptr++; /// now pointing at 'xxx' C_GetNextLabelName(0); /*initprintf("found xxx label of \"%s\"\n", label+(g_numLabels*MAXLABELLEN));*/ if (lightp) lLabelID = C_GetLabelNameID(LightLabels, &h_light, Bstrtolower(tlabel)); else if (id==M32_SPRITE_VAR_ID || id==M32_TSPRITE_VAR_ID) lLabelID = C_GetLabelNameID(SpriteLabels, &h_sprite, Bstrtolower(tlabel)); else if (id==M32_SECTOR_VAR_ID) lLabelID = C_GetLabelNameID(SectorLabels, &h_sector, Bstrtolower(tlabel)); else if (id==M32_WALL_VAR_ID) lLabelID = C_GetLabelNameID(WallLabels, &h_wall, Bstrtolower(tlabel)); //printf("LabelID is %d\n",lLabelID); if (lLabelID == -1) { g_numCompilerErrors++; C_ReportError(ERROR_SYMBOLNOTRECOGNIZED); return; } if ((aridx & M32_BITS_MASK) == M32_FLAG_CONSTANTINDEX) *g_scriptPtr++ = (aridx | flags | (lLabelID<<2) | id); else // simple or local gamevar *g_scriptPtr++ = (aridx<<16 | flags | (lLabelID<<2) | id); } return; } // initprintf("not an array"); id = GetGamevarID(tlabel, 1); if (id < 0) //gamevar not found { if (!(type&GV_WRITABLE) && !cs.labelsOnly) { //try looking for a define instead id = hash_find(&h_labels, tlabel); if (id>=0 && labeltype[id]==LABEL_DEFINE) { // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug) // initprintf("%s:%d: debug: accepted defined label `%s' instead of gamevar.\n",g_szScriptFileName,g_lineNumber,label+(id*MAXLABELLEN)); num = (1-2*!!(flags&M32_FLAG_NEGATE))*labelval[id]; if (type==GV_SIMPLE && (num<0 || num>=65536)) { C_CUSTOMERROR("label %s=%d not suitable as array index. (max: 65535)", label+(id*MAXLABELLEN), num); } else if (num != (int16_t)num) { indirect = 2; num = id; } if (type!=GV_SIMPLE) g_wasConstant = 1; // note: For direct constants, the NEGATE flag is ignored since they're interpreted // as signed. For label-indirect constants however, the NEGATE flag is considered. // See m32vars.c --> Gv_GetVarX() // note 2: demotions like int32_t -> int16_t are implementation-defined *g_scriptPtr++ = M32_FLAG_CONSTANT | (num<<16) | flags | indirect; return; } } g_numCompilerErrors++; C_ReportError(ERROR_NOTAGAMEVAR); textptr++; return; } else if (id<MAXGAMEVARS) // it's an ordinary var (not a local one) { if (m32_script_expertmode) type &= ~GV_WRITABLE; if (type==GV_WRITABLE && (aGameVars[id].dwFlags & GV_WRITABLE)) { g_numCompilerErrors++; C_ReportError(ERROR_VARREADONLY); return; } else if (aGameVars[id].dwFlags & type) { // g_numCompilerErrors++; C_CUSTOMERROR("variable `%s' is of the wrong type: expected simple var.", tlabel); // C_ReportError(ERROR_VARTYPEMISMATCH); return; } } // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug > 1) // initprintf("%s:%d: debug: accepted gamevar `%s'.\n",g_szScriptFileName,g_lineNumber,tlabel); // if it's a local, "id" ors M32_FLAG_LOCAL to the flags *g_scriptPtr++ = (flags|id); } static inline void C_GetNextVar() { C_GetNextVarType(0); } static inline void C_GetManyVarsType(int32_t type, int32_t num) { int32_t i; for (i=num-1; i>=0; i--) C_GetNextVarType(type); } static inline void C_GetManyVars(int32_t num) { C_GetManyVarsType(0,num); } // used in various quote commands: they either take a string // enclosed in double quotes or a variable that contains a quote number // returns: // -1: error // 0: variable // 1: string, also does *g_scriptPtr++ = -1 and inlines // the string in the code (terminator: (instype)0) static int32_t C_GetNextVarOrString() { C_SkipComments(); if (*textptr == 0) { C_CUSTOMERROR("unexpected EOF where variable or string literal was expected."); return -1; } if (*textptr == '"') { const char *beg = ++textptr; int32_t nchars; while (*textptr!='"') { switch (*textptr) { case 0x0a: C_CUSTOMERROR("string literal must end on the same line."); return -1; case 0: C_CUSTOMERROR("unexpected EOF inside string literal."); return -1; } textptr++; } nchars = textptr-beg; if (nchars >= MAXQUOTELEN) { nchars = MAXQUOTELEN-1; C_CUSTOMWARNING("truncating string literal to %d characters.", nchars); } *g_scriptPtr++ = -1; // special indicator meaning "inline string follows" Bmemcpy((char *)g_scriptPtr, beg, nchars); ((char *)g_scriptPtr)[nchars] = 0; g_scriptPtr += (nchars + sizeof(instype)-1)/sizeof(instype); *g_scriptPtr++ = 0; textptr++; return 1; } else { C_GetNextVar(); return 0; } } static int32_t C_GetNextValue(int32_t type) { int32_t i, l, thesign=1; C_SkipComments(); if (*textptr == 0) { C_CUSTOMERROR("unexpected EOF where constant label or value was expected."); return -1; } while (!char_alnumtok(*textptr)) { g_lineNumber += (*textptr == 0x0a); textptr++; if (!*textptr) return -1; // eof } if (*textptr=='-' && !isdigit(textptr[1])) { // in case constant label is negated, e.g. -MAXSPRITES thesign = -1; textptr++; } l = 0; while (char_alnumtok(*(textptr+l))) { tempbuf[l] = textptr[l]; l++; } tempbuf[l] = 0; if (hash_find(&h_keywords, tempbuf)>=0) { g_numCompilerErrors++; C_ReportError(ERROR_ISAKEYWORD); textptr+=l; } i = hash_find(&h_labels, tempbuf); if (i >= 0) { char *el,*gl; if (labeltype[i] & type) { // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug > 1) // { // gl = (char *)C_GetLabelType(labeltype[i]); // initprintf("%s:%d: debug: accepted %s label `%s'.\n",g_szScriptFileName,g_lineNumber,gl,label+(i*MAXLABELLEN)); // Bfree(gl); // } *(g_scriptPtr++) = thesign*labelval[i]; textptr += l; return labeltype[i]; } *(g_scriptPtr++) = 0; textptr += l; el = (char *)C_GetLabelType(type); gl = (char *)C_GetLabelType(labeltype[i]); C_CUSTOMERROR("expected %s, found %s.", el, gl); // initprintf("i=%d, %s!!! lt:%d t:%d\n", i, label+(i*MAXLABELLEN), labeltype[i], type); Bfree(el); Bfree(gl); return -1; // valid label name, but wrong type } if (!isdigit(*textptr) && *textptr != '-') { C_ReportError(ERROR_PARAMUNDEFINED); g_numCompilerErrors++; *g_scriptPtr = 0; g_scriptPtr++; textptr += l; return -1; // error! } if (isdigit(*textptr) && cs.labelsOnly) { C_ReportError(WARNING_LABELSONLY); g_numCompilerWarnings++; } i = l-1; do { // FIXME: check for 0-9 A-F for hex if (textptr[0] == '0' && textptr[1] == 'x') break; // kill the warning for hex if (!isdigit(textptr[i--])) { C_CUSTOMERROR("invalid character `%c' in definition!", textptr[i+1]); break; } } while (i > 0); // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug > 1) // initprintf("%s:%d: debug: accepted constant %d.\n",g_szScriptFileName,g_lineNumber,Batol(textptr)); parse_integer_literal(g_scriptPtr); g_scriptPtr++; textptr += l; return 0; // literal value } static inline int32_t C_IntPow2(int32_t v) { return ((v!=0) && (v&(v-1))==0); } static inline uint32_t C_Pow2IntLogBase2(int32_t v) { static const uint32_t b[] = {0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; register uint32_t r = (v & b[0]) != 0; int32_t i = 4; for (; i > 0; i--) r |= ((v & b[i]) != 0) << i; return r; } static int32_t C_CheckMalformedBranch(ofstype lastScriptOfs) { switch (C_GetKeyword()) { case CON_RIGHTBRACE: case CON_ENDEVENT: case CON_ENDS: case CON_ELSE: g_scriptPtr = script + lastScriptOfs; cs.ifElseAborted = 1; C_CUSTOMWARNING("malformed `%s' branch", keyw[*g_scriptPtr & 0xFFF]); return 1; } return 0; } static int32_t C_CheckEmptyBranch(int32_t tw, ofstype lastScriptOfs) { // ifrnd actually does something when the condition is executed if ((Bstrncmp(keyw[tw], "if", 2) && tw != CON_ELSE) || tw == CON_IFRND) { cs.ifElseAborted = 0; return 0; } if ((*(g_scriptPtr) & 0xFFF) != CON_NULLOP || *(g_scriptPtr)>>12 != IFELSE_MAGIC) cs.ifElseAborted = 0; if (cs.ifElseAborted) { g_scriptPtr = script + lastScriptOfs; C_CUSTOMWARNING("empty `%s' branch", keyw[*g_scriptPtr & 0xFFF]); *g_scriptPtr = (CON_NULLOP + (IFELSE_MAGIC<<12)); return 1; } return 0; } static int32_t C_ParseCommand(void); static int32_t C_CountCaseStatements() { int32_t lCount; const char *temptextptr = textptr; int32_t temp_ScriptLineNumber = g_lineNumber; ofstype scriptoffset = (unsigned)(g_scriptPtr-script); ofstype caseoffset = (unsigned)(cs.caseScriptPtr-script); cs.numCases=0; cs.caseScriptPtr=NULL; //Bsprintf(g_szBuf,"CSS: %.12s",textptr); AddLog(g_szBuf); while (C_ParseCommand() == 0) { //Bsprintf(g_szBuf,"CSSL: %.20s",textptr); AddLog(g_szBuf); ; } // since we processed the endswitch, we need to re-increment cs.checkingSwitch cs.checkingSwitch++; textptr = temptextptr; g_scriptPtr = (instype *)(script+scriptoffset); g_lineNumber = temp_ScriptLineNumber; lCount = cs.numCases; cs.numCases = 0; cs.caseScriptPtr = (instype *)(script+caseoffset); return lCount; } // returns: 0:keep going, 1:done static int32_t C_ParseCommand(void) { int32_t i, j=0, k=0, done, tw; // const char *temptextptr; instype *tempscrptr = NULL; if (g_numCompilerErrors >= ABORTERRCNT || (*textptr == '\0') || (*(textptr+1) == '\0')) return 1; // if (g_scriptDebug) // C_ReportError(-1); /// if (cs.checkingSwitch > 0) /// Bsprintf(g_szBuf,"PC(): '%.25s'",textptr); AddLog(g_szBuf); tw = C_GetNextKeyword(); def_tw = tw; // Bsprintf(tempbuf,"%s",keyw[tw]); AddLog(tempbuf); if (C_SkipComments()) return 1; switch (tw) { default: case -1: case -2: return 0; //End // *** basic commands case CON_NULLOP: if (C_GetKeyword() != CON_ELSE) { C_CUSTOMWARNING("`nullop' found without `else'"); g_scriptPtr--; cs.ifElseAborted = 1; } return 0; case CON_DEFINE: { if (cs.currentStateIdx >=0 || cs.currentEvent >= 0) { C_CUSTOMERROR("Can only `define' at top level."); return 1; } if (C_GetNextLabelName(1)) return 1; // Check to see it's already defined i = hash_find(&h_gamevars, tlabel); if (i>=0) { g_numCompilerWarnings++; C_ReportError(WARNING_NAMEMATCHESVAR); } if (hash_find(&h_states, tlabel) >= 0) { g_numCompilerErrors++; C_ReportError(ERROR_LABELINUSE); return 1; } C_GetNextValue(LABEL_DEFINE); i = hash_find(&h_labels, tlabel); if (i == -1) { // printf("Defining Definition \"%s\" to be '%d'\n",label+(g_numLabels*MAXLABELLEN),*(g_scriptPtr-1)); // Bmemcpy(label+(g_numLabels*MAXLABELLEN), tlabel, MAXLABELLEN); C_CopyLabel(); hash_add(&h_labels, label+(g_numLabels*MAXLABELLEN), g_numLabels, 0); labeltype[g_numLabels] = LABEL_DEFINE; labelval[g_numLabels++] = *(g_scriptPtr-1); } // else if (i>=g_numDefaultLabels) // { // if (labeltype[i] == LABEL_DEFINE) // labelval[i] = *(g_scriptPtr-1); // } g_scriptPtr -= 2; return 0; } case CON_INCLUDE: g_scriptPtr--; while (!char_alnumtok(*textptr)) { g_lineNumber += (*textptr == 0x0a); textptr++; if (!*textptr) break; } j = 0; while (char_alnumtok(*textptr)) { tempbuf[j] = *(textptr++); j++; } tempbuf[j] = '\0'; { int32_t temp_ScriptLineNumber; int32_t temp_ifelse_check; const char *origtptr; char *mptr; char parentScriptFileName[255]; int32_t fp; fp = kopen4load(tempbuf, 0 /*g_loadFromGroupOnly*/); if (fp < 0) { g_numCompilerErrors++; initprintf("%s:%d: error: could not find file `%s'.\n",g_szScriptFileName,g_lineNumber,tempbuf); return 1; } j = kfilelength(fp); mptr = (char *)Xmalloc(j+1); initprintf(" Including: %s (%d bytes)\n", tempbuf, j); kread(fp, mptr, j); kclose(fp); mptr[j] = 0; if (*textptr == '"') // skip past the closing quote if it's there so we don't screw up the next line textptr++; origtptr = textptr; Bstrcpy(parentScriptFileName, g_szScriptFileName); Bstrcpy(g_szScriptFileName, tempbuf); temp_ScriptLineNumber = g_lineNumber; g_lineNumber = 1; temp_ifelse_check = cs.checkingIfElse; cs.checkingIfElse = 0; textptr = mptr; do done = C_ParseCommand(); while (!done); Bstrcpy(g_szScriptFileName, parentScriptFileName); g_totalLines += g_lineNumber; g_lineNumber = temp_ScriptLineNumber; cs.checkingIfElse = temp_ifelse_check; textptr = origtptr; Bfree(mptr); } return 0; case CON_DEFSTATE: g_scriptPtr--; if (cs.parsingEventOfs >= 0 || cs.currentStateIdx >= 0) { g_numCompilerErrors++; C_ReportError(ERROR_FOUNDWITHIN); return 1; } else { if (C_GetNextLabelName(1)) return 1; if (hash_find(&h_gamevars, tlabel)>=0) { g_numCompilerWarnings++; C_ReportError(WARNING_NAMEMATCHESVAR); } j = hash_find(&h_labels, tlabel); if (j>=0) { g_numCompilerErrors++; C_ReportError(ERROR_LABELINUSE); return 1; } cs.currentStateOfs = (g_scriptPtr-script); j = hash_find(&h_states, tlabel); if (j>=0) // only redefining { cs.currentStateIdx = j; } else // new state definition { cs.currentStateIdx = j = g_stateCount; if (g_stateCount >= statesinfo_allocsize) { statesinfo_allocsize *= 2; statesinfo = (statesinfo_t *)Xrealloc(statesinfo, statesinfo_allocsize * sizeof(statesinfo[0])); } Bstrcpy(statesinfo[j].name, tlabel); hash_add(&h_states, tlabel, j, 0); } statesinfo[j].numlocals = 0; Bsprintf(g_szCurrentBlockName, "%s", statesinfo[j].name); if (cs.curStateMenuName) Bfree(cs.curStateMenuName); cs.curStateMenuName = NULL; if (C_GetKeyword() < 0) { ofstype *oscriptptr = g_scriptPtr; if (C_GetNextVarOrString() == 1) // inline string { cs.curStateMenuName = Xstrdup((const char *)(oscriptptr+1)); g_scriptPtr = oscriptptr; } else { C_CUSTOMERROR("expected inline string to be used as menu name."); return 1; } } else if (j != g_stateCount) { // unregister that state with the menu if redefining and no menu name registerMenuFunction(NULL, j); } return 0; } case CON_ENDS: if (cs.currentStateIdx < 0) { C_CUSTOMERROR("found `ends' without open `state'."); return 1; //+ } hash_init(&h_localvars); if (cs.numBraces > 0) { C_ReportError(ERROR_OPENBRACKET); g_numCompilerErrors++; } else if (cs.numBraces < 0) { C_ReportError(ERROR_CLOSEBRACKET); g_numCompilerErrors++; } if (cs.checkingSwitch > 0) { C_ReportError(ERROR_NOENDSWITCH); g_numCompilerErrors++; cs.checkingSwitch = 0; // can't be checking anymore... } if (g_numCompilerErrors) { g_scriptPtr = script+cs.currentStateOfs; cs.currentStateOfs = -1; cs.currentStateIdx = -1; Bsprintf(g_szCurrentBlockName,"(none)"); return 0; } j = cs.currentStateIdx; if (cs.currentStateIdx == g_stateCount) // we were defining a new state { statesinfo[j].ofs = cs.currentStateOfs; statesinfo[j].codesize = (g_scriptPtr-script) - cs.currentStateOfs; g_stateCount++; initprintf(" Defined State %3d `%s'.\n", j, g_szCurrentBlockName); // initprintf(" o:%d s:%d\n", statesinfo[j].ofs, statesinfo[j].codesize); } else // we were redefining a state { int32_t oofs = statesinfo[j].ofs; int32_t nofs = cs.currentStateOfs; int32_t osize = statesinfo[j].codesize; int32_t nsize = (g_scriptPtr-script) - nofs; if (nsize == osize) { int32_t ii, equal=2, linedif = 0, ow, nw; for (ii=0; ii<nsize; ii++) { ow = *(script+oofs+ii); nw = *(script+nofs+ii); if (ow != nw) { int32_t ld = (nw>>12) - (ow>>12); if (equal==2) { equal = 1; linedif = ld; } if (linedif != ld || ((nw&0xFFF) != (ow&0xFFF))) { equal = 0; break; } } } if (equal!=2) Bmemcpy(script+oofs, script+nofs, nsize*sizeof(instype)); if (equal==0) initprintf(" Redefined State %3d `%s'.\n", j, g_szCurrentBlockName); // initprintf(" oo:%d os:%d, no:%d ns:%d\n", oofs, osize, nofs, nsize); } else { int32_t ii; uint32_t movedcodesize = g_scriptPtr - (script+oofs+osize); Bmemmove(script+oofs, script+oofs+osize, movedcodesize*sizeof(instype)); for (ii=0; ii<g_stateCount; ii++) { if (statesinfo[ii].ofs > oofs) statesinfo[ii].ofs -= osize; } for (ii=0; ii<MAXEVENTS; ii++) if (/*ii != j &&*/ aEventOffsets[ii] > oofs) aEventOffsets[ii] -= osize; statesinfo[j].ofs = nofs-osize; statesinfo[j].codesize = nsize; initprintf(" Redefined State %3d `%s'.\n", j, g_szCurrentBlockName); // initprintf(" oo:%d os:%d, no:%d ns:%d\n", oofs, osize, nofs, nsize); } g_scriptPtr -= osize; } if (cs.curStateMenuName) { registerMenuFunction(cs.curStateMenuName, j); Bfree(cs.curStateMenuName); cs.curStateMenuName = NULL; } g_didDefineSomething = 1; cs.currentStateOfs = -1; cs.currentStateIdx = -1; Bsprintf(g_szCurrentBlockName,"(none)"); return 0; case CON_COLLECTSECTORS: case CON_SORT: if (C_GetNextLabelName(1)) return 1; i = GetGamearrayID(tlabel, 0); if (i >= 0) { *g_scriptPtr++ = i; if (aGameArrays[i].dwFlags & GAMEARRAY_READONLY) { C_ReportError(ERROR_ARRAYREADONLY); g_numCompilerErrors++; } if (aGameArrays[i].dwFlags & GAMEARRAY_TYPE_MASK) C_CUSTOMERROR("Array for %s must be user-defined.", tw==CON_SORT?"sorting":"collecting sectors"); } else { C_ReportError(ERROR_NOTAGAMEARRAY); g_numCompilerErrors++; } C_SkipComments(); // element count to sort (SORT) / starting sector (COLLECTSECTORS): C_GetNextVar(); if (tw==CON_COLLECTSECTORS) { // the variable to store the number of collected sectors C_GetNextVarType(GV_WRITABLE); } else if (C_GetKeyword() >= 0) { // default sorting state that compares values numerically *g_scriptPtr++ = -1; return 0; } // fall-through case CON_STATE: if (C_GetNextLabelName(1)) return 1; j = hash_find(&h_states, tlabel); if (j>=0) { // if (!(g_numCompilerErrors || g_numCompilerWarnings) && g_scriptDebug > 1) // initprintf("%s:%d: debug: accepted state label `%s'.\n",g_szScriptFileName,g_lineNumber,label+(k*MAXLABELLEN)); *g_scriptPtr++ = j; return 0; } C_CUSTOMERROR("state `%s' not found.", tlabel); g_scriptPtr++; return 0; case CON_ONEVENT: if (cs.currentStateIdx >= 0 || cs.parsingEventOfs >= 0) { C_ReportError(ERROR_FOUNDWITHIN); g_numCompilerErrors++; return 1; } g_scriptPtr--; cs.numBraces = 0; C_SkipComments(); j = 0; while (char_alnumtok(*(textptr+j))) { g_szCurrentBlockName[j] = textptr[j]; j++; } g_szCurrentBlockName[j] = 0; cs.labelsOnly = 1; C_GetNextValue(LABEL_EVENT); cs.labelsOnly = 0; g_scriptPtr--; j = *g_scriptPtr; // event number cs.currentEvent = j; aEventNumLocals[j] = 0; cs.parsingEventOfs = g_scriptPtr-script; //Bsprintf(g_szBuf,"Adding Event for %d at %lX",j, g_parsingEventPtr); AddLog(g_szBuf); if (j<0 || j >= MAXEVENTS) { initprintf("%s:%d: error: invalid event ID.\n",g_szScriptFileName,g_lineNumber); g_numCompilerErrors++; return 0; } cs.checkingIfElse = 0; return 0; case CON_ENDEVENT: if (cs.parsingEventOfs < 0) { C_CUSTOMERROR("found `endevent' without open `onevent'."); return 1; } hash_init(&h_localvars); if (cs.numBraces > 0) { C_ReportError(ERROR_OPENBRACKET); g_numCompilerErrors++; } if (cs.numBraces < 0) { C_ReportError(ERROR_CLOSEBRACKET); g_numCompilerErrors++; } if (g_numCompilerErrors) { g_scriptPtr = script+cs.parsingEventOfs; cs.parsingEventOfs = -1; cs.currentEvent = -1; Bsprintf(g_szCurrentBlockName, "(none)"); return 0; } j = cs.currentEvent; if (aEventOffsets[j] >= 0) // if event was previously declared, overwrite it { int32_t oofs = aEventOffsets[j], nofs = cs.parsingEventOfs; int32_t osize = aEventSizes[j], nsize = (g_scriptPtr-script) - nofs; if (osize == nsize) { int32_t ii, equal=2, linedif=0, nw, ow; for (ii=0; ii<nsize; ii++) { ow = *(script+oofs+ii); nw = *(script+nofs+ii); if (ow != nw) { int32_t ld = (nw>>12) - (ow>>12); if (equal==2) { equal = 1; linedif = ld; } if (linedif != ld || ((nw&0xFFF) != (ow&0xFFF))) { equal = 0; break; } } } if (equal!=2) Bmemcpy(script+oofs, script+nofs, nsize*sizeof(instype)); if (equal==0) initprintf(" Redefined Event %3d `%s'.\n", j, g_szCurrentBlockName); // initprintf(" oo:%d os:%d, no:%d ns:%d\n", oofs, osize, nofs, nsize); } else { int32_t ii; uint32_t movedcodesize = g_scriptPtr - (script+oofs + osize); Bmemmove(script+oofs, script+oofs + osize, movedcodesize*sizeof(instype)); for (ii=0; ii<g_stateCount; ii++) { if (statesinfo[ii].ofs > oofs) statesinfo[ii].ofs -= osize; } for (ii=0; ii<MAXEVENTS; ii++) if (/*ii != j &&*/ aEventOffsets[ii] > oofs) aEventOffsets[ii] -= osize; aEventOffsets[j] = nofs - osize; aEventSizes[j] = nsize; initprintf(" Redefined Event %3d `%s'.\n", j, g_szCurrentBlockName); // initprintf(" oo:%d os:%d, no:%d ns:%d\n", oofs, osize, nofs, nsize); } g_scriptPtr -= osize; } else // event defined for the first time { aEventOffsets[j] = cs.parsingEventOfs; aEventSizes[j] = (g_scriptPtr-script) - cs.parsingEventOfs; initprintf(" Defined Event %3d `%s'.\n", j, g_szCurrentBlockName); // initprintf(" o:%d s:%d\n", aEventOffsets[j], aEventSizes[j]); } aEventEnabled[j] = 1; g_didDefineSomething = 1; cs.parsingEventOfs = -1; cs.currentEvent = -1; Bsprintf(g_szCurrentBlockName,"(none)"); return 0; // *** control flow case CON_ELSE: if (cs.checkingIfElse) { ofstype offset; ofstype lastScriptOfs = (g_scriptPtr-script) - 1; instype *tscrptr; cs.ifElseAborted = 0; cs.checkingIfElse--; if (C_CheckMalformedBranch(lastScriptOfs)) return 0; offset = (unsigned)(g_scriptPtr-script); g_scriptPtr++; //Leave a spot for the fail location C_ParseCommand(); if (C_CheckEmptyBranch(tw, lastScriptOfs)) return 0; tscrptr = (instype *)script+offset; *tscrptr = (ofstype)(g_scriptPtr-script)-offset; // relative offset } else { instype *tscrptr; g_scriptPtr--; tscrptr = g_scriptPtr; C_CUSTOMWARNING("found `else' with no `if'."); if (C_GetKeyword() == CON_LEFTBRACE) { C_GetNextKeyword(); cs.numBraces++; do done = C_ParseCommand(); while (done == 0); } else C_ParseCommand(); g_scriptPtr = tscrptr; } return 0; case CON_RETURN: case CON_BREAK: if (cs.checkingSwitch) { //Bsprintf(g_szBuf," * (L%d) case Break statement.\n",g_lineNumber); AddLog(g_szBuf); return 1; } return 0; case CON_SWITCH: { ofstype tempoffset; //AddLog("Got Switch statement"); // if (cs.checkingSwitch) Bsprintf(g_szBuf,"ERROR::%s %d: cs.checkingSwitch=",__FILE__,__LINE__, cs.checkingSwitch); AddLog(g_szBuf); cs.checkingSwitch++; // allow nesting (if other things work) C_GetNextVar(); // Get The ID of the DEF tempoffset = (unsigned)(g_scriptPtr-script); *g_scriptPtr++ = 0; // leave spot for end location (for after processing) *g_scriptPtr++ = 0; // count of case statements cs.caseScriptPtr = g_scriptPtr; // the first case's pointer. *g_scriptPtr++ = -1; // leave spot for 'default' offset to cases' code (-1 if none) // temptextptr = textptr; // probably does not allow nesting... //AddLog("Counting Case Statements..."); j = C_CountCaseStatements(); // initprintf("Done Counting Case Statements for switch %d: found %d.\n", cs.checkingSwitch,j); g_scriptPtr += j*2; cs.caseCodePtr = g_scriptPtr; C_SkipComments(); g_scriptPtr -= j*2; // allocate buffer for the table tempscrptr = (instype *)(script+tempoffset); // if (cs.checkingSwitch>1) Bsprintf(g_szBuf,"ERROR::%s %d: cs.checkingSwitch=",__FILE__,__LINE__, cs.checkingSwitch); AddLog(g_szBuf); if (j<0) return 1; if (tempscrptr) tempscrptr[1] = j; // save count of cases // else // Bsprintf(g_szBuf,"ERROR::%s %d",__FILE__,__LINE__); AddLog(g_szBuf); while (j--) { // leave room for statements *g_scriptPtr++ = 0; // value check *g_scriptPtr++ = -1; // code offset C_SkipComments(); } //Bsprintf(g_szBuf,"SWITCH1: '%.22s'",textptr); AddLog(g_szBuf); cs.numCases = 0; while (C_ParseCommand() == 0) { //Bsprintf(g_szBuf,"SWITCH2: '%.22s'",textptr); AddLog(g_szBuf); } tempscrptr = (instype *)(script+tempoffset); //Bsprintf(g_szBuf,"SWITCHXX: '%.22s'",textptr); AddLog(g_szBuf); // done processing switch. clean up. // if (cs.checkingSwitch < 1) Bsprintf(g_szBuf,"ERROR::%s %d: cs.checkingSwitch=%d",__FILE__,__LINE__, cs.checkingSwitch); AddLog(g_szBuf); if (tempscrptr) { int32_t t,n; // !!! for (i=3; i<3+tempscrptr[1]*2-2; i+=2) // sort them { t = tempscrptr[i]; n=i; for (j=i+2; j<3+tempscrptr[1]*2; j+=2) if (tempscrptr[j] < t) t = tempscrptr[j], n=j; if (n != i) { t = tempscrptr[i]; tempscrptr[i] = tempscrptr[n]; tempscrptr[n] = t; t = tempscrptr[i+1]; tempscrptr[i+1] = tempscrptr[n+1]; tempscrptr[n+1] = t; } } // for (j=3;j<3+tempscrptr[1]*2;j+=2)initprintf("%5d %8x\n",tempscrptr[j],tempscrptr[j+1]); tempscrptr[0] = (ofstype)(g_scriptPtr-cs.caseCodePtr); // save 'end' location as offset from code-place } // else Bsprintf(g_szBuf,"ERROR::%s %d",__FILE__,__LINE__); AddLog(g_szBuf); cs.numCases = 0; cs.caseScriptPtr = NULL; cs.caseCodePtr = NULL; // decremented in endswitch. Don't decrement here... // cs.checkingSwitch--; // allow nesting (maybe if other things work) tempscrptr = NULL; // if (cs.checkingSwitch) Bsprintf(g_szBuf,"ERROR::%s %d: cs.checkingSwitch=%d",__FILE__,__LINE__, cs.checkingSwitch); AddLog(g_szBuf); //AddLog("End of Switch statement"); } break; case CON_CASE: { ofstype tempoffset = 0; //AddLog("Found Case"); repeatcase: g_scriptPtr--; // don't save in code if (cs.checkingSwitch < 1) { C_CUSTOMERROR("found `case' statement when not in switch"); return 1; } cs.numCases++; //Bsprintf(g_szBuf,"case1: %.12s",textptr); AddLog(g_szBuf); C_GetNextValue(LABEL_DEFINE); if (*textptr == ':') textptr++; //Bsprintf(g_szBuf,"case2: %.12s",textptr); AddLog(g_szBuf); j = *(--g_scriptPtr); // get value //Bsprintf(g_szBuf,"case: Value of case %d is %d",(int32_t)cs.numCases,(int32_t)j); AddLog(g_szBuf); if (cs.caseScriptPtr) { for (i=(cs.numCases/2)-1; i>=0; i--) if (cs.caseScriptPtr[i*2+1] == j) { g_numCompilerWarnings++; C_ReportError(WARNING_DUPLICATECASE); break; } //AddLog("Adding value to script"); cs.caseScriptPtr[cs.numCases++] = j; // save value cs.caseScriptPtr[cs.numCases] = (ofstype)(g_scriptPtr - cs.caseCodePtr); // offset from beginning of cases' code } //Bsprintf(g_szBuf,"case3: %.12s",textptr); AddLog(g_szBuf); j = C_GetKeyword(); if (j == CON_CASE) { //AddLog("Found Repeat Case"); C_GetNextKeyword(); // eat 'case' goto repeatcase; } //Bsprintf(g_szBuf,"case4: '%.12s'",textptr); AddLog(g_szBuf); tempoffset = (unsigned)(tempscrptr-script); while (C_ParseCommand() == 0) { //Bsprintf(g_szBuf,"case5 '%.25s'",textptr); AddLog(g_szBuf); j = C_GetKeyword(); if (j == CON_CASE) { //AddLog("Found Repeat Case"); C_GetNextKeyword(); // eat 'case' tempscrptr = (instype *)(script+tempoffset); goto repeatcase; } } tempscrptr = (instype *)(script+tempoffset); //AddLog("End Case"); return 0; // break; } case CON_DEFAULT: g_scriptPtr--; // don't save if (cs.checkingSwitch < 1) { C_CUSTOMERROR("found `default' statement when not in switch"); return 1; } if (cs.caseScriptPtr && cs.caseScriptPtr[0]!=-1) { C_CUSTOMERROR("multiple `default' statements found in switch"); } if (cs.caseScriptPtr) cs.caseScriptPtr[0] = (ofstype)(g_scriptPtr-cs.caseCodePtr); // save offset from cases' code //Bsprintf(g_szBuf,"default: '%.22s'",textptr); AddLog(g_szBuf); while (C_ParseCommand() == 0) { //Bsprintf(g_szBuf,"defaultParse: '%.22s'",textptr); AddLog(g_szBuf); ; } break; case CON_ENDSWITCH: //AddLog("End Switch"); cs.checkingSwitch--; if (cs.checkingSwitch < 0) C_CUSTOMERROR("found `endswitch' without matching `switch'"); return 1; // end of block // break; case CON_GETCURRADDRESS: C_GetNextVarType(GV_WRITABLE); return 0; case CON_JUMP: C_GetNextVar(); return 0; case CON_LEFTBRACE: // if (!(cs.currentStateIdx >= 0 || cs.parsingEventOfs >= 0)) // { // g_numCompilerErrors++; // C_ReportError(ERROR_SYNTAXERROR); // } cs.numBraces++; do done = C_ParseCommand(); while (!done); return 0; case CON_RIGHTBRACE: cs.numBraces--; // rewrite "{ }" into "nullop" if (*(g_scriptPtr-2) == CON_LEFTBRACE + (IFELSE_MAGIC<<12)) { // initprintf("%s:%d: rewriting empty braces '{ }' as 'nullop' from right\n",g_szScriptFileName,g_lineNumber); *(g_scriptPtr-2) = CON_NULLOP + (IFELSE_MAGIC<<12); g_scriptPtr -= 2; if (C_GetKeyword() != CON_ELSE && (*(g_scriptPtr-2)&0xFFF) != CON_ELSE) cs.ifElseAborted = 1; else cs.ifElseAborted = 0; j = C_GetKeyword(); if (cs.checkingIfElse && j != CON_ELSE) cs.checkingIfElse--; return 1; } if (cs.numBraces < 0) { if (cs.checkingSwitch) C_ReportError(ERROR_NOENDSWITCH); C_CUSTOMERROR("found more `}' than `{'."); } if (cs.checkingIfElse && j != CON_ELSE) cs.checkingIfElse--; return 1; // *** more basic commands case CON_GAMEVAR: // syntax: gamevar <var1> <initial value> <flags> // defines var1 and sets initial value. // flags are used to define usage: 0:global, 1:per-block g_scriptPtr--; if (cs.currentStateIdx < 0 && cs.currentEvent < 0) { // global gamevar if (C_GetNextLabelName(1)) return 1; C_GetNextValue(LABEL_DEFINE); // get initial value C_GetNextValue(LABEL_DEFINE); // get flags //Bsprintf(g_szBuf,"Adding GameVar=\"%s\", val=%l, flags=%lX",label+(g_numLabels*MAXLABELLEN), *(g_scriptPtr-2), *(g_scriptPtr-1)); AddLog(g_szBuf); // global var: can't define default if (g_numCompilerErrors==0) { Gv_NewVar(tlabel, *(g_scriptPtr-2), *(g_scriptPtr-1)); g_scriptPtr -= 2; // no need to save in script... } } else // local var { do { C_GetNextLabelName(1); if (hash_find(&h_localvars, tlabel) >= 0) C_CUSTOMERROR("local variable `%s' already defined.", tlabel); else { uint16_t *numlocals = (cs.currentStateIdx >= 0) ? &statesinfo[cs.currentStateIdx].numlocals : &aEventNumLocals[cs.currentEvent]; if (((int32_t)(*numlocals))+1 > M32_MAX_LOCALS) C_CUSTOMERROR("too much local storage required (max: %d gamevar equivalents).", M32_MAX_LOCALS); else { hash_add(&h_localvars, tlabel, (int32_t)(*numlocals), 0); *numlocals += 1; } } } while (C_GetKeyword() < 0 && g_numCompilerErrors<ABORTERRCNT && *textptr); } //AddLog("Added gamevar"); return 0; // *** arrays case CON_GAMEARRAY: if (C_GetNextLabelName(1)) return 1; if (hash_find(&h_gamevars, tlabel) >= 0) { g_numCompilerWarnings++; C_ReportError(WARNING_NAMEMATCHESVAR); } C_GetNextValue(LABEL_DEFINE); { const int32_t asize = *(g_scriptPtr-1); if (cs.currentStateIdx < 0 && cs.currentEvent < 0) Gv_NewArray(tlabel, NULL, asize, GAMEARRAY_NORMAL); else // local array { uint16_t *numlocals = (cs.currentStateIdx >= 0) ? &statesinfo[cs.currentStateIdx].numlocals : &aEventNumLocals[cs.currentEvent]; //OSD_Printf("s%d,e%d: array `%s', numlocals of `%s' is %d.\n", cs.currentStateIdx, cs.currentEvent, // tlabel, g_szCurrentBlockName, (int32_t)*numlocals); if (((int32_t)(*numlocals))+asize > M32_MAX_LOCALS) C_CUSTOMERROR("too much local storage required (max: %d gamevar equivalents).", M32_MAX_LOCALS); else { hash_add(&h_localvars, tlabel, ((int32_t)(*numlocals))|(asize<<16), 0); *numlocals += asize; } } } g_scriptPtr -= 2; // no need to save in script... return 0; case CON_COPY: if (C_GetNextLabelName(1)) return 1; i = GetGamearrayID(tlabel, 0); if (i >= 0) *g_scriptPtr++ = i; else { C_ReportError(ERROR_NOTAGAMEARRAY); g_numCompilerErrors++; } C_SkipComments(); if (*textptr != '[') { g_numCompilerErrors++; C_ReportError(ERROR_GAMEARRAYBNO); return 1; } textptr++; C_GetNextVar(); C_SkipComments(); if (*textptr != ']') { g_numCompilerErrors++; C_ReportError(ERROR_GAMEARRAYBNC); return 1; } textptr++; // fall-through case CON_SETARRAY: if (C_GetNextLabelName(1)) return 1; i = GetGamearrayID(tlabel, 0); if (i >= 0) { *g_scriptPtr++ = i; if (aGameArrays[i].dwFlags & GAMEARRAY_READONLY) { C_ReportError(ERROR_ARRAYREADONLY); g_numCompilerErrors++; } } else { C_ReportError(ERROR_NOTAGAMEARRAY); g_numCompilerErrors++; } C_SkipComments(); if (*textptr != '[') { g_numCompilerErrors++; C_ReportError(ERROR_GAMEARRAYBNO); return 1; } textptr++; C_GetNextVar(); C_SkipComments(); if (*textptr != ']') { g_numCompilerErrors++; C_ReportError(ERROR_GAMEARRAYBNC); return 1; } textptr++; C_GetNextVar(); return 0; case CON_GETARRAYSIZE: case CON_RESIZEARRAY: if (C_GetNextLabelName(1)) return 1; i = GetGamearrayID(tlabel, 0); if (i >= 0) { *g_scriptPtr++ = i; if (tw==CON_RESIZEARRAY && (aGameArrays[i].dwFlags & GAMEARRAY_TYPE_MASK)) { C_CUSTOMERROR("can't resize system array `%s'.", tlabel); } } else { C_ReportError(ERROR_NOTAGAMEARRAY); g_numCompilerErrors++; } C_SkipComments(); C_GetNextVar(); return 0; // *** var ops case CON_RANDVAR: case CON_DISPLAYRANDVAR: case CON_SETVAR: case CON_ADDVAR: case CON_SUBVAR: case CON_MULVAR: case CON_DIVVAR: case CON_MODVAR: case CON_ANDVAR: case CON_ORVAR: case CON_XORVAR: case CON_SHIFTVARL: case CON_SHIFTVARR: { instype *inst = g_scriptPtr-1; const char *tptr = textptr; // syntax: [rand|add|set]var <var1> <const1> // sets var1 to const1 // adds const1 to var1 (const1 can be negative...) //printf("Found [add|set]var at line= %d\n",g_lineNumber); // get the ID of the DEF C_GetNextVarType(GV_WRITABLE); C_GetNextValue(LABEL_DEFINE); // the number to check against... if (tw == CON_MULVAR && *(g_scriptPtr-1) == -1) { *inst = CON_INV; g_scriptPtr--; return 0; } if (tw == CON_DIVVAR || (tw == CON_MULVAR && *(g_scriptPtr-1) > 0)) { int32_t i = *(g_scriptPtr-1); j = klabs(i); if (C_IntPow2(j)) { *inst = ((tw == CON_DIVVAR) ? CON_SHIFTVARR : CON_SHIFTVARL); *(g_scriptPtr-1) = C_Pow2IntLogBase2(j); // initprintf("%s:%d: replacing multiply/divide with shift\n",g_szScriptFileName,g_lineNumber); if (i == j) return 0; *g_scriptPtr++ = CON_INV + (g_lineNumber<<12); textptr = tptr; C_GetNextVarType(GV_WRITABLE); C_GetNextValue(LABEL_DEFINE); g_scriptPtr--; // initprintf("%s:%d: adding inversion\n",g_szScriptFileName,g_lineNumber); } } } return 0; // *** varvar ops case CON_RANDVARVAR: case CON_DISPLAYRANDVARVAR: case CON_SETVARVAR: case CON_ADDVARVAR: case CON_SUBVARVAR: case CON_MULVARVAR: case CON_DIVVARVAR: case CON_MODVARVAR: case CON_ANDVARVAR: case CON_ORVARVAR: case CON_XORVARVAR: case CON_SHIFTVARVARL: case CON_SHIFTVARVARR: { instype *inst = (g_scriptPtr-1); const char *otextptr; C_GetNextVarType(GV_WRITABLE); otextptr = textptr; g_wasConstant = 0; C_GetNextVar(); if (!g_numCompilerErrors && g_wasConstant) { textptr = otextptr; g_scriptPtr--; *inst -= (CON_SETVARVAR - CON_SETVAR); C_GetNextValue(LABEL_DEFINE); } return 0; } case CON_SIN: case CON_COS: C_GetNextVarType(GV_WRITABLE); C_GetNextVar(); return 0; // *** random case CON_DISPLAYRAND: // syntax: displayrand <var> // gets rand (not game rand) into <var> C_GetNextVarType(GV_WRITABLE); break; // *** float access: convert float to int and back case CON_ITOF: case CON_FTOI: // syntax: itof <<var>> SCALE // ftoi <<var>> SCALE C_GetNextVarType(GV_WRITABLE); C_GetNextValue(LABEL_DEFINE); if (*(g_scriptPtr-1)<=0) C_CUSTOMERROR("scale value in integer/float conversion must be greater zero."); return 0; // *** other math case CON_CLAMP: C_GetNextVarType(GV_WRITABLE); C_GetManyVars(2); return 0; case CON_INV: C_GetNextVarType(GV_WRITABLE); return 0; case CON_SQRT: case CON_GETSPRITELINKTYPE: { // syntax sqrt <invar> <outvar> // gets the sqrt of invar into outvar C_GetNextVar(); // target var C_GetNextVarType(GV_WRITABLE); break; } case CON_MULSCALE: case CON_DIVSCALE: C_GetNextVarType(GV_WRITABLE); C_GetManyVars(3); return 0; case CON_DIST: case CON_LDIST: case CON_GETANGLE: case CON_GETINCANGLE: C_GetNextVarType(GV_WRITABLE); C_GetManyVars(2); return 0; case CON_A2XY: C_GetNextVar(); C_GetManyVarsType(GV_WRITABLE, 2); return 0; case CON_AH2XYZ: C_GetManyVars(2); C_GetManyVarsType(GV_WRITABLE, 3); return 0; case CON_FOR: // special-purpose iteration { ofstype offset; instype *tscrptr; int32_t how; C_GetNextVarType(GV_WRITABLE|GV_SIMPLE); // only simple vars allowed C_GetNextLabelName(0); how = hash_find(&h_iter, tlabel); if (how < 0) { C_CUSTOMERROR("unknown iteration type `%s'.", tlabel); return 1; } *g_scriptPtr++ = how; if (how >= ITER_SPRITESOFSECTOR) C_GetNextVar(); offset = g_scriptPtr-script; g_scriptPtr++; //Leave a spot for the location to jump to after completion C_ParseCommand(); tscrptr = (instype *)script+offset; *tscrptr = (g_scriptPtr-script)-offset; // relative offset return 0; } // *** if&while var&varvar case CON_IFVARL: case CON_IFVARLE: case CON_IFVARG: case CON_IFVARGE: case CON_IFVARE: case CON_IFVARN: case CON_IFVARAND: case CON_IFVAROR: case CON_IFVARXOR: case CON_IFVAREITHER: case CON_IFVARBOTH: case CON_WHILEVARN: case CON_WHILEVARL: // --- case CON_IFVARVARL: case CON_IFVARVARLE: case CON_IFVARVARG: case CON_IFVARVARGE: case CON_IFVARVARE: case CON_IFVARVARN: case CON_IFVARVARAND: case CON_IFVARVAROR: case CON_IFVARVARXOR: case CON_IFVARVAREITHER: case CON_IFVARVARBOTH: case CON_WHILEVARVARN: case CON_WHILEVARVARL: // --- case CON_IFHITKEY: case CON_IFHOLDKEY: case CON_IFRND: // vvv if* using current sprite case CON_IFANGDIFFL: case CON_IFSPRITEPAL: case CON_IFHIGHLIGHTED: case CON_IFACTOR: case CON_IFSOUND: case CON_IFPDISTL: case CON_IFPDISTG: /// case CON_IFGAPZL: /// case CON_IFFLOORDISTL: /// case CON_IFCEILINGDISTL: // --- case CON_IFINSIDE: // --- case CON_IFEITHERALT: case CON_IFEITHERCTRL: case CON_IFEITHERSHIFT: case CON_IFAWAYFROMWALL: case CON_IFCANSEE: case CON_IFONWATER: case CON_IFINWATER: case CON_IFOUTSIDE: /// case CON_IFHITSPACE: /// case CON_IFINSPACE: /// case CON_IFINOUTERSPACE: /// case CON_IFCANSEETARGET: case CON_IFNOSOUNDS: case CON_IFIN3DMODE: case CON_IFAIMINGSPRITE: case CON_IFAIMINGWALL: case CON_IFAIMINGSECTOR: case CON_IFINTERACTIVE: { ofstype offset; ofstype lastScriptOfs = (g_scriptPtr-script-1); instype *tscrptr; cs.ifElseAborted = 0; if (tw<=CON_WHILEVARL) // careful! check this against order in m32def.h! { C_GetNextVar(); C_GetNextValue(LABEL_DEFINE); // the number to check against... } else if (tw<=CON_WHILEVARVARL) { instype *inst = (g_scriptPtr-1); const char *otextptr; C_GetNextVar(); otextptr = textptr; g_wasConstant = 0; C_GetNextVar(); if (!g_numCompilerErrors && g_wasConstant) { textptr = otextptr; g_scriptPtr--; *inst -= (CON_IFVARVARL - CON_IFVARL); C_GetNextValue(LABEL_DEFINE); } } else if (tw<=CON_IFPDISTG) { if (tw==CON_IFHIGHLIGHTED) { int32_t id; if (C_GetNextLabelName(1)) return 1; id = GetGamevarID(tlabel, 0); if (!(id==M32_SPRITE_VAR_ID || id==M32_WALL_VAR_ID)) { C_CUSTOMERROR("\"ifhighlighted\" must be followed immediately by \"sprite\" or \"wall\"."); return 1; } *g_scriptPtr++ = id; C_GetNextVar(); } else C_GetNextVar(); } else if (tw<=CON_IFINSIDE) C_GetManyVars(3); // else {} if (C_CheckMalformedBranch(lastScriptOfs)) return 0; offset = (g_scriptPtr-script); g_scriptPtr++; //Leave a spot for the fail location C_ParseCommand(); if (C_CheckEmptyBranch(tw, lastScriptOfs)) return 0; tscrptr = (instype *)script+offset; *tscrptr = (g_scriptPtr-script)-offset; // relative offset if (tw != CON_WHILEVARN && tw != CON_WHILEVARVARN) { j = C_GetKeyword(); if (j == CON_ELSE || j == CON_LEFTBRACE) cs.checkingIfElse++; } return 0; } // *** BUILD functions case CON_TDUPSPRITE: if (cs.currentEvent>=0 && cs.currentEvent != EVENT_ANALYZESPRITES) { C_ReportError(WARNING_OUTSIDEDRAWSPRITE); g_numCompilerWarnings++; } case CON_RESETKEY: case CON_SETKEY: case CON_INSERTSPRITE: case CON_DUPSPRITE: case CON_DELETESPRITE: C_GetNextVar(); break; case CON_LASTWALL: C_GetNextVar(); C_GetNextVarType(GV_WRITABLE); break; case CON_UPDATECURSECTNUM: return 0; case CON_UPDATESECTOR: case CON_UPDATESECTORZ: C_GetManyVars(2); if (tw==CON_UPDATESECTORZ) C_GetNextVar(); C_GetNextVarType(GV_WRITABLE); break; case CON_GETZRANGE: C_GetManyVars(4); C_GetManyVarsType(GV_WRITABLE, 4); C_GetManyVars(2); break; case CON_CALCHYPOTENUSE: C_GetNextVarType(GV_WRITABLE); C_GetManyVars(2); break; case CON_CLIPMOVE: // <retvar>,<x>,<y>,z,<sectnum>, xvect,yvect,walldist,floordist,ceildist,clipmask C_GetManyVarsType(GV_WRITABLE,3); C_GetNextVar(); C_GetNextVarType(GV_WRITABLE); C_GetManyVars(6); break; case CON_LINEINTERSECT: case CON_RAYINTERSECT: // lineintersect x y z x y z x y x y <intx> <inty> <intz> <ret> // rayintersect x y z vx vy vz x y x y <intx> <inty> <intz> <ret> C_GetManyVars(10); C_GetManyVarsType(GV_WRITABLE,4); break; case CON_HITSCAN: case CON_CANSEE: // get the ID of the DEF C_GetManyVars(tw==CON_CANSEE?8:7); C_GetManyVarsType(GV_WRITABLE, tw==CON_CANSEE?1:6); if (tw==CON_HITSCAN) C_GetNextVar(); break; case CON_CANSEESPR: C_GetManyVars(2); C_GetNextVarType(GV_WRITABLE); break; case CON_NEARTAG: case CON_ROTATEPOINT: C_GetManyVars(5); C_GetManyVarsType(GV_WRITABLE, 2); if (tw == CON_NEARTAG) { C_GetManyVarsType(GV_WRITABLE, 2); C_GetManyVars(2); } break; case CON_DRAGPOINT: C_GetManyVars(3); return 0; case CON_GETFLORZOFSLOPE: case CON_GETCEILZOFSLOPE: C_GetManyVars(3); C_GetNextVarType(GV_WRITABLE); return 0; case CON_ALIGNCEILSLOPE: case CON_ALIGNFLORSLOPE: case CON_BSETSPRITE: // was CON_SETSPRITE C_GetManyVars(4); break; case CON_SETFIRSTWALL: case CON_CHANGESPRITESTAT: case CON_CHANGESPRITESECT: case CON_HEADSPRITESTAT: case CON_PREVSPRITESTAT: case CON_NEXTSPRITESTAT: case CON_HEADSPRITESECT: case CON_PREVSPRITESECT: case CON_NEXTSPRITESECT: C_GetManyVars(2); return 0; case CON_SECTOROFWALL: C_GetNextVarType(GV_WRITABLE); C_GetNextVar(); return 0; case CON_FIXREPEATS: C_GetNextVar(); return 0; case CON_GETCLOSESTCOL: C_GetManyVars(3); C_GetNextVarType(GV_WRITABLE); return 0; // *** stuff case CON_SETHIGHLIGHT: // sethighlight <what> <index> <set_or_unset> // if <what>&16384, index&16383 is sprite index, else wall index C_GetManyVars(3); return 0; case CON_SETHIGHLIGHTSECTOR: // sethighlightsector <index> <set_or_unset> C_GetManyVars(2); return 0; case CON_ADDLOGVAR: // syntax: addlogvar <var> // prints the line number in the log file. C_GetNextVar(); return 0; case CON_UPDATEHIGHLIGHT: case CON_UPDATEHIGHLIGHTSECTOR: case CON_ADDLOG: // syntax: addlog // prints the line number in the log file. return 0; case CON_DEBUG: C_GetNextValue(LABEL_DEFINE); return 0; // *** strings case CON_DEFINEQUOTE: if (cs.currentStateIdx >=0 || cs.currentEvent >= 0) { C_CUSTOMERROR("Can define quotes only at top level."); return 1; } case CON_REDEFINEQUOTE: if (tw == CON_DEFINEQUOTE) g_scriptPtr--; C_GetNextValue(LABEL_DEFINE); k = *(g_scriptPtr-1); if (k<0 || k >= MAXQUOTES) { C_CUSTOMERROR("quote number out of range (0 to %d).", MAXQUOTES-1); k = MAXQUOTES; } if (ScriptQuotes[k] == NULL) ScriptQuotes[k] = (char *)Xcalloc(MAXQUOTELEN, sizeof(uint8_t)); if (tw == CON_DEFINEQUOTE) g_scriptPtr--; while (*textptr == ' ' || *textptr == '\t') textptr++; if (tw == CON_REDEFINEQUOTE) { if (ScriptQuoteRedefinitions[g_numQuoteRedefinitions] == NULL) ScriptQuoteRedefinitions[g_numQuoteRedefinitions] = (char *)Xcalloc(MAXQUOTELEN, sizeof(uint8_t)); } i = 0; while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) { // if (*textptr == '%' && *(textptr+1) == 's') // { // initprintf("%s:%d: error: quote text contains string identifier.\n",g_szScriptFileName,g_lineNumber); // g_numCompilerErrors++; // while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) textptr++; // break; // } if (tw == CON_DEFINEQUOTE) *(ScriptQuotes[k]+i) = *textptr; else *(ScriptQuoteRedefinitions[g_numQuoteRedefinitions]+i) = *textptr; textptr++; i++; if (i >= MAXQUOTELEN-1) { C_CUSTOMWARNING("truncating quote text to %d characters.", MAXQUOTELEN-1); while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) textptr++; break; } } if (tw == CON_DEFINEQUOTE) *(ScriptQuotes[k]+i) = '\0'; else { *(ScriptQuoteRedefinitions[g_numQuoteRedefinitions]+i) = '\0'; *g_scriptPtr++ = g_numQuoteRedefinitions; g_numQuoteRedefinitions++; } return 0; case CON_PRINT: case CON_QUOTE: case CON_ERRORINS: case CON_PRINTMESSAGE16: case CON_PRINTMESSAGE256: case CON_PRINTEXT256: case CON_PRINTEXT16: case CON_DRAWLABEL: if (C_GetNextVarOrString()==-1) return 1; if (tw==CON_PRINTMESSAGE256) C_GetManyVars(2); else if (tw >= CON_PRINTEXT256) C_GetManyVars(5); return 0; case CON_GETNUMBER16: case CON_GETNUMBER256: case CON_GETNUMBERFROMUSER: // <<retvar>> "quote" <max> <flags>(1|2|4|8|(16)) C_GetNextVarType(GV_WRITABLE); if (C_GetNextVarOrString()==-1) return 1; C_GetNextVar(); if (tw==CON_GETNUMBERFROMUSER) C_GetNextVar(); return 0; case CON_QSPRINTF: C_GetNextVar(); if (C_GetNextVarOrString()==-1) return 1; j = 0; while (C_GetKeyword() == -1 && j < 32) C_GetNextVar(), j++; *g_scriptPtr++ = -1; //CON_NULLOP + (g_lineNumber<<12); return 0; case CON_QSTRCAT: case CON_QSTRCPY: case CON_QSTRLEN: case CON_QSTRNCAT: case CON_QSUBSTR: C_GetNextVar(); if (C_GetNextVarOrString()==-1) return 1; if (tw==CON_QSTRNCAT) C_GetNextVar(); else if (tw==CON_QSUBSTR) C_GetManyVars(2); return 0; // *** findnear* case CON_FINDNEARSPRITE: case CON_FINDNEARSPRITE3D: case CON_FINDNEARSPRITEZ: { // syntax findnearactor <type> <maxdist> <getvar> // gets the sprite ID of the nearest actor within max dist // that is of <type> into <getvar> // -1 for none found C_GetNextValue(LABEL_DEFINE); // get <type> C_GetNextValue(LABEL_DEFINE); // get maxdist if (tw==CON_FINDNEARSPRITEZ) C_GetNextValue(LABEL_DEFINE); // target var // get the ID of the DEF C_GetNextVarType(GV_WRITABLE); break; } case CON_FINDNEARSPRITEVAR: case CON_FINDNEARSPRITE3DVAR: case CON_FINDNEARSPRITEZVAR: { C_GetNextValue(LABEL_DEFINE); // get <type> // get the ID of the DEF C_GetNextVar(); if (tw==CON_FINDNEARSPRITEZVAR) C_GetNextVar(); // target var // get the ID of the DEF C_GetNextVarType(GV_WRITABLE); break; } case CON_GETTICKS: C_GetNextVarType(GV_WRITABLE); return 0; case CON_GETTIMEDATE: C_GetManyVarsType(GV_WRITABLE, 8); break; case CON_SETASPECT: C_GetNextVar(); // get the ID of the DEF C_GetNextVar(); // get the ID of the DEF return 0; case CON_SPGETLOTAG: case CON_SPGETHITAG: case CON_SECTGETLOTAG: case CON_SECTGETHITAG: case CON_GETTEXTUREFLOOR: case CON_GETTEXTURECEILING: case CON_STOPALLSOUNDS: // no paramaters... return 0; case CON_SETI: C_GetNextVar(); return 0; case CON_SIZEAT: C_GetManyVars(2); // C_GetNextValue(LABEL_DEFINE); // C_GetNextValue(LABEL_DEFINE); break; /// case CON_ANGOFF: case CON_GETSOUNDFLAGS: C_GetNextVar(); C_GetNextVarType(GV_WRITABLE); break; case CON_SOUNDVAR: case CON_STOPSOUNDVAR: case CON_SOUNDONCEVAR: case CON_GLOBALSOUNDVAR: case CON_CSTATOR: case CON_SPRITEPAL: case CON_CACTOR: case CON_CLIPDIST: C_GetNextVar(); return 0; case CON_CSTAT: C_GetNextValue(LABEL_DEFINE); if ((*(g_scriptPtr-1) & 32) && (*(g_scriptPtr-1) & 16)) { i = *(g_scriptPtr-1); *(g_scriptPtr-1) ^= 48; C_CUSTOMWARNING("tried to set cstat %d, using %d instead.", i, *(g_scriptPtr-1)); } return 0; case CON_DRAWLINE16: case CON_DRAWLINE16B: case CON_DRAWLINE16Z: case CON_DRAWCIRCLE16: case CON_DRAWCIRCLE16B: case CON_DRAWCIRCLE16Z: if (cs.parsingEventOfs < 0 && cs.currentStateIdx < 0) { C_ReportError(ERROR_EVENTONLY); g_numCompilerErrors++; } if (tw==CON_DRAWLINE16 || tw==CON_DRAWLINE16B || tw==CON_DRAWCIRCLE16Z) C_GetManyVars(5); else if (tw==CON_DRAWLINE16Z) C_GetManyVars(7); else C_GetManyVars(4); break; case CON_ROTATESPRITE16: case CON_ROTATESPRITE: if (cs.parsingEventOfs < 0 && cs.currentStateIdx < 0) { C_ReportError(ERROR_EVENTONLY); g_numCompilerErrors++; } // syntax: // int32_t x, int32_t y, int32_t z, short a, short tilenum, int8_t shade, char orientation, x1, y1, x2, y2 // myospal adds char pal C_GetManyVars(12); // get the ID of the DEFs break; case CON_ROTATESPRITEA: if (cs.parsingEventOfs < 0 && cs.currentStateIdx < 0) { C_ReportError(ERROR_EVENTONLY); g_numCompilerErrors++; } C_GetManyVars(13); break; case CON_SETGAMEPALETTE: C_GetNextVar(); return 0; } return 0; } /* Anything added with C_AddDefinition() cannot be overwritten in the CONs */ static void C_AddDefinition(const char *lLabel,int32_t lValue, uint8_t lType) { Bstrcpy(label+(g_numLabels*MAXLABELLEN), lLabel); hash_add(&h_labels, label+(g_numLabels*MAXLABELLEN), g_numLabels, 0); labeltype[g_numLabels] = lType; labelval[g_numLabels++] = lValue; g_numDefaultLabels++; } static void C_AddDefaultDefinitions(void) { // events must come first and in correct order C_AddDefinition("EVENT_ENTER3DMODE", EVENT_ENTER3DMODE, LABEL_EVENT); C_AddDefinition("EVENT_ANALYZESPRITES", EVENT_ANALYZESPRITES, LABEL_EVENT); C_AddDefinition("EVENT_INSERTSPRITE2D", EVENT_INSERTSPRITE2D, LABEL_EVENT); C_AddDefinition("EVENT_INSERTSPRITE3D", EVENT_INSERTSPRITE3D, LABEL_EVENT); C_AddDefinition("EVENT_DRAW2DSCREEN", EVENT_DRAW2DSCREEN, LABEL_EVENT); C_AddDefinition("EVENT_DRAW3DSCREEN", EVENT_DRAW3DSCREEN, LABEL_EVENT); C_AddDefinition("EVENT_KEYS2D", EVENT_KEYS2D, LABEL_EVENT); C_AddDefinition("EVENT_KEYS3D", EVENT_KEYS3D, LABEL_EVENT); C_AddDefinition("EVENT_PREKEYS2D", EVENT_PREKEYS2D, LABEL_EVENT); C_AddDefinition("EVENT_PREKEYS3D", EVENT_PREKEYS3D, LABEL_EVENT); C_AddDefinition("EVENT_LINKTAGS", EVENT_LINKTAGS, LABEL_EVENT); C_AddDefinition("EVENT_KEYPRESS", EVENT_KEYPRESS, LABEL_EVENT); C_AddDefinition("EVENT_PREDRAW3DSCREEN", EVENT_PREDRAW3DSCREEN, LABEL_EVENT); C_AddDefinition("EVENT_LOADMAP", EVENT_LOADMAP, LABEL_EVENT); C_AddDefinition("EVENT_SAVEMAP", EVENT_SAVEMAP, LABEL_EVENT); C_AddDefinition("EVENT_PRELOADMAP", EVENT_PRELOADMAP, LABEL_EVENT); C_AddDefinition("EVENT_PRESAVEMAP", EVENT_PRESAVEMAP, LABEL_EVENT); C_AddDefinition("CLIPMASK0", CLIPMASK0, LABEL_DEFINE); C_AddDefinition("CLIPMASK1", CLIPMASK1, LABEL_DEFINE); C_AddDefinition("MAXSPRITES", MAXSPRITES, LABEL_DEFINE); C_AddDefinition("MAXSECTORS", MAXSECTORS, LABEL_DEFINE); C_AddDefinition("MAXWALLS", MAXWALLS, LABEL_DEFINE); C_AddDefinition("MAXTILES", MAXTILES, LABEL_DEFINE); C_AddDefinition("MAXSTATUS", MAXSTATUS, LABEL_DEFINE); C_AddDefinition("MAXSOUNDS", MAXSOUNDS, LABEL_DEFINE); // keys C_AddDefinition("KEY_SPACE", KEYSC_SPACE, LABEL_DEFINE); C_AddDefinition("KEY_A", KEYSC_A, LABEL_DEFINE); C_AddDefinition("KEY_B", KEYSC_B, LABEL_DEFINE); C_AddDefinition("KEY_C", KEYSC_C, LABEL_DEFINE); C_AddDefinition("KEY_D", KEYSC_D, LABEL_DEFINE); C_AddDefinition("KEY_E", KEYSC_E, LABEL_DEFINE); C_AddDefinition("KEY_F", KEYSC_F, LABEL_DEFINE); C_AddDefinition("KEY_G", KEYSC_G, LABEL_DEFINE); C_AddDefinition("KEY_H", KEYSC_H, LABEL_DEFINE); C_AddDefinition("KEY_I", KEYSC_I, LABEL_DEFINE); C_AddDefinition("KEY_J", KEYSC_J, LABEL_DEFINE); C_AddDefinition("KEY_K", KEYSC_K, LABEL_DEFINE); C_AddDefinition("KEY_L", KEYSC_L, LABEL_DEFINE); C_AddDefinition("KEY_M", KEYSC_M, LABEL_DEFINE); C_AddDefinition("KEY_N", KEYSC_N, LABEL_DEFINE); C_AddDefinition("KEY_O", KEYSC_O, LABEL_DEFINE); C_AddDefinition("KEY_P", KEYSC_P, LABEL_DEFINE); C_AddDefinition("KEY_Q", KEYSC_Q, LABEL_DEFINE); C_AddDefinition("KEY_R", KEYSC_R, LABEL_DEFINE); C_AddDefinition("KEY_S", KEYSC_S, LABEL_DEFINE); C_AddDefinition("KEY_T", KEYSC_T, LABEL_DEFINE); C_AddDefinition("KEY_U", KEYSC_U, LABEL_DEFINE); C_AddDefinition("KEY_V", KEYSC_V, LABEL_DEFINE); C_AddDefinition("KEY_W", KEYSC_W, LABEL_DEFINE); C_AddDefinition("KEY_X", KEYSC_X, LABEL_DEFINE); C_AddDefinition("KEY_Y", KEYSC_Y, LABEL_DEFINE); C_AddDefinition("KEY_Z", KEYSC_Z, LABEL_DEFINE); C_AddDefinition("KEY_ENTER", KEYSC_ENTER, LABEL_DEFINE); C_AddDefinition("KEY_BS", KEYSC_BS, LABEL_DEFINE); C_AddDefinition("KEY_TAB", KEYSC_TAB, LABEL_DEFINE); C_AddDefinition("KEY_0", KEYSC_0, LABEL_DEFINE); C_AddDefinition("KEY_1", KEYSC_1, LABEL_DEFINE); C_AddDefinition("KEY_2", KEYSC_2, LABEL_DEFINE); C_AddDefinition("KEY_3", KEYSC_3, LABEL_DEFINE); C_AddDefinition("KEY_4", KEYSC_4, LABEL_DEFINE); C_AddDefinition("KEY_5", KEYSC_5, LABEL_DEFINE); C_AddDefinition("KEY_6", KEYSC_6, LABEL_DEFINE); C_AddDefinition("KEY_7", KEYSC_7, LABEL_DEFINE); C_AddDefinition("KEY_8", KEYSC_8, LABEL_DEFINE); C_AddDefinition("KEY_9", KEYSC_9, LABEL_DEFINE); C_AddDefinition("KEY_DASH", KEYSC_DASH, LABEL_DEFINE); C_AddDefinition("KEY_EQUAL", KEYSC_EQUAL, LABEL_DEFINE); C_AddDefinition("KEY_LBRACK", KEYSC_LBRACK, LABEL_DEFINE); C_AddDefinition("KEY_RBRACK", KEYSC_RBRACK, LABEL_DEFINE); C_AddDefinition("KEY_SEMI", KEYSC_SEMI, LABEL_DEFINE); C_AddDefinition("KEY_QUOTE", KEYSC_QUOTE, LABEL_DEFINE); C_AddDefinition("KEY_BQUOTE", KEYSC_BQUOTE, LABEL_DEFINE); C_AddDefinition("KEY_BSLASH", KEYSC_BSLASH, LABEL_DEFINE); C_AddDefinition("KEY_COMMA", KEYSC_COMMA, LABEL_DEFINE); C_AddDefinition("KEY_PERIOD", KEYSC_PERIOD, LABEL_DEFINE); C_AddDefinition("KEY_SLASH", KEYSC_SLASH, LABEL_DEFINE); C_AddDefinition("KEY_LALT", KEYSC_LALT, LABEL_DEFINE); C_AddDefinition("KEY_LCTRL", KEYSC_LCTRL, LABEL_DEFINE); C_AddDefinition("KEY_LSHIFT", KEYSC_LSHIFT, LABEL_DEFINE); C_AddDefinition("KEY_RALT", KEYSC_RALT, LABEL_DEFINE); C_AddDefinition("KEY_RCTRL", KEYSC_RCTRL, LABEL_DEFINE); C_AddDefinition("KEY_RSHIFT", KEYSC_RSHIFT, LABEL_DEFINE); // some aliases... C_AddDefinition("KEY_KP7", KEYSC_gHOME, LABEL_DEFINE); C_AddDefinition("KEY_KP8", KEYSC_gUP, LABEL_DEFINE); C_AddDefinition("KEY_KP9", KEYSC_gPGUP, LABEL_DEFINE); C_AddDefinition("KEY_KP4", KEYSC_gLEFT, LABEL_DEFINE); C_AddDefinition("KEY_KP5", KEYSC_gKP5, LABEL_DEFINE); C_AddDefinition("KEY_KP6", KEYSC_gRIGHT, LABEL_DEFINE); C_AddDefinition("KEY_KP1", KEYSC_gEND, LABEL_DEFINE); C_AddDefinition("KEY_KP2", KEYSC_gDOWN, LABEL_DEFINE); C_AddDefinition("KEY_KP3", KEYSC_gPGDN, LABEL_DEFINE); C_AddDefinition("KEY_KP0", KEYSC_gINS, LABEL_DEFINE); C_AddDefinition("KEY_KPCOMMA", KEYSC_gDEL, LABEL_DEFINE); C_AddDefinition("KEY_gDEL", KEYSC_gDEL, LABEL_DEFINE); C_AddDefinition("KEY_gDOWN", KEYSC_gDOWN, LABEL_DEFINE); C_AddDefinition("KEY_gEND", KEYSC_gEND, LABEL_DEFINE); C_AddDefinition("KEY_gHOME", KEYSC_gHOME, LABEL_DEFINE); C_AddDefinition("KEY_gINS", KEYSC_gINS, LABEL_DEFINE); C_AddDefinition("KEY_gKP5", KEYSC_gKP5, LABEL_DEFINE); C_AddDefinition("KEY_gLEFT", KEYSC_gLEFT, LABEL_DEFINE); C_AddDefinition("KEY_gMINUS", KEYSC_gMINUS, LABEL_DEFINE); C_AddDefinition("KEY_gPGDN", KEYSC_gPGDN, LABEL_DEFINE); C_AddDefinition("KEY_gPGUP", KEYSC_gPGUP, LABEL_DEFINE); C_AddDefinition("KEY_gPLUS", KEYSC_gPLUS, LABEL_DEFINE); C_AddDefinition("KEY_gRIGHT", KEYSC_gRIGHT, LABEL_DEFINE); C_AddDefinition("KEY_gSLASH", KEYSC_gSLASH, LABEL_DEFINE); C_AddDefinition("KEY_gSTAR", KEYSC_gSTAR, LABEL_DEFINE); C_AddDefinition("KEY_gUP", KEYSC_gUP, LABEL_DEFINE); C_AddDefinition("KEY_SCROLL", KEYSC_SCROLL, LABEL_DEFINE); C_AddDefinition("KEY_HOME", KEYSC_HOME, LABEL_DEFINE); C_AddDefinition("KEY_UP", KEYSC_UP, LABEL_DEFINE); C_AddDefinition("KEY_PGUP", KEYSC_PGUP, LABEL_DEFINE); C_AddDefinition("KEY_LEFT", KEYSC_LEFT, LABEL_DEFINE); C_AddDefinition("KEY_RIGHT", KEYSC_RIGHT, LABEL_DEFINE); C_AddDefinition("KEY_END", KEYSC_END, LABEL_DEFINE); C_AddDefinition("KEY_DOWN", KEYSC_DOWN, LABEL_DEFINE); C_AddDefinition("KEY_PGDN", KEYSC_PGDN, LABEL_DEFINE); C_AddDefinition("KEY_INSERT", KEYSC_INSERT, LABEL_DEFINE); C_AddDefinition("KEY_DELETE", KEYSC_DELETE, LABEL_DEFINE); C_AddDefinition("KEY_F1", KEYSC_F1, LABEL_DEFINE); C_AddDefinition("KEY_F2", KEYSC_F2, LABEL_DEFINE); C_AddDefinition("KEY_F3", KEYSC_F3, LABEL_DEFINE); C_AddDefinition("KEY_F4", KEYSC_F4, LABEL_DEFINE); C_AddDefinition("KEY_F5", KEYSC_F5, LABEL_DEFINE); C_AddDefinition("KEY_F6", KEYSC_F6, LABEL_DEFINE); C_AddDefinition("KEY_F7", KEYSC_F7, LABEL_DEFINE); C_AddDefinition("KEY_F8", KEYSC_F8, LABEL_DEFINE); C_AddDefinition("KEY_F9", KEYSC_F9, LABEL_DEFINE); C_AddDefinition("KEY_F10", KEYSC_F10, LABEL_DEFINE); C_AddDefinition("KEY_F11", KEYSC_F11, LABEL_DEFINE); C_AddDefinition("KEY_F12", KEYSC_F12, LABEL_DEFINE); // end keys // C_AddDefinition("STR_MAPFILENAME",STR_MAPFILENAME, LABEL_DEFINE); // C_AddDefinition("STR_VERSION",STR_VERSION, LABEL_DEFINE); C_AddDefinition("NO",0, LABEL_DEFINE); C_AddDefinition("COLOR_WHITE",31, LABEL_DEFINE); } void C_CompilationInfo(void) { int32_t j, k=0; initprintf(" \n"); initprintf("Compiled code info: (size=%ld*%d bytes)\n", (unsigned long)(g_scriptPtr-script), (int32_t)sizeof(instype)); initprintf(" %d/%d user labels, %d/65536 indirect constants,\n", g_numLabels-g_numDefaultLabels, 65536-g_numDefaultLabels, g_numSavedConstants); initprintf(" %d/%d user variables, %d/%d user arrays\n", g_gameVarCount-g_systemVarCount, MAXGAMEVARS-g_systemVarCount, g_gameArrayCount-g_systemArrayCount, MAXGAMEARRAYS-g_systemArrayCount); for (j=0; j<MAXEVENTS; j++) if (aEventOffsets[j] >= 0) k++; initprintf(" %d states, %d/%d defined events\n", g_stateCount, k,MAXEVENTS); for (k=0, j=MAXQUOTES-1; j>=0; j--) if (ScriptQuotes[j]) k++; if (k || g_numQuoteRedefinitions) initprintf(" %d/%d quotes, %d/%d quote redefinitions\n", k,MAXQUOTES, g_numQuoteRedefinitions,MAXQUOTES); } EDUKE32_STATIC_ASSERT(ARRAY_SIZE(keyw)-1 == CON_END); void C_Compile(const char *filenameortext, int32_t isfilename) { char *mptr = NULL; static char firstime=1; int32_t i,j; int32_t fs=0,fp=0; int32_t startcompiletime; instype *oscriptPtr; int32_t ostateCount = g_stateCount; interactive_compilation = !isfilename; if (firstime) { label = (char *)Xmalloc(label_allocsize * MAXLABELLEN * sizeof(char)); labelval = (int32_t *)Xmalloc(label_allocsize * sizeof(int32_t)); labeltype = (uint8_t *)Xmalloc(label_allocsize * sizeof(uint8_t)); constants = (int32_t *)Xmalloc(constants_allocsize * sizeof(int32_t)); statesinfo = (statesinfo_t *)Xmalloc(statesinfo_allocsize * sizeof(statesinfo_t)); for (i=0; i<MAXEVENTS; i++) { aEventOffsets[i] = -1; aEventEnabled[i] = 0; aEventNumLocals[i] = 0; } C_InitHashes(); Gv_Init(); C_AddDefaultDefinitions(); script = (instype *)Xcalloc(g_scriptSize, sizeof(instype)); g_scriptPtr = script+1; firstime = 0; } if (isfilename) { fs = Bstrlen(filenameortext); mptr = (char *)Xmalloc(fs+1+4); Bmemcpy(mptr, filenameortext, fs+1); fp = kopen4load(mptr, 0 /*g_loadFromGroupOnly*/); if (fp == -1) // JBF: was 0 { if (fs < 4 || Bmemcmp(&mptr[fs-4], ".m32", 4) != 0) { Bstrcat(&mptr[fs], ".m32"); fp = kopen4load(mptr, 0 /*g_loadFromGroupOnly*/); } if (fp == -1) { initprintf("M32 file `%s' not found.\n", mptr); Bfree(mptr); //g_loadFromGroupOnly = 1; return; } } fs = kfilelength(fp); initprintf(" \n"); initprintf("--- Compiling: %s (%d bytes)\n",mptr,fs); Bstrcpy(g_szScriptFileName, mptr); // JBF 20031130: Store currently compiling file name Bfree(mptr); } else { Bsprintf(g_szScriptFileName, "(console)"); // fs = Bstrlen(filenameortext); // initprintf("Compiling: (from console) (%d bytes)\n",fs); } // flushlogwindow = 0; startcompiletime = getticks(); if (isfilename) { mptr = (char *)Xmalloc(fs+1); mptr[fs] = 0; kread(fp, mptr, fs); kclose(fp); start_textptr = textptr = (char *)mptr; } else start_textptr = textptr = filenameortext; g_curkwptr = textptr; ////// g_numCompilerWarnings = g_numCompilerErrors = 0; g_lineNumber = 1; g_totalLines = 0; ////// oscriptPtr = g_scriptPtr; g_didDefineSomething = 0; Bmemcpy(&cs, &cs_default, sizeof(compilerstate_t)); while (C_ParseCommand() == 0); // flushlogwindow = 1; // if (g_numCompilerErrors >= ABORTERRCNT) // initprintf("Too many errors: Aborted\n"); //*script = g_scriptPtr-script; if (mptr) Bfree(mptr); if (g_stateCount > ostateCount) { for (i=0; i<g_gameVarCount; i++) if (aGameVars[i].dwFlags & GAMEVAR_PERBLOCK) { if (aGameVars[i].val.plValues) { aGameVars[i].val.plValues = (int32_t *)Xrealloc(aGameVars[i].val.plValues, (1+MAXEVENTS+g_stateCount)*sizeof(int32_t)); for (j=ostateCount; j<g_stateCount; j++) aGameVars[i].val.plValues[1+MAXEVENTS+j] = aGameVars[i].lDefault; } else { aGameVars[i].val.plValues = (int32_t *)Xmalloc((1+MAXEVENTS+g_stateCount)*sizeof(int32_t)); for (j=0; j<(1+MAXEVENTS+g_stateCount); j++) aGameVars[i].val.plValues[j] = aGameVars[i].lDefault; } } } if (g_numCompilerErrors) { if (!g_didDefineSomething) g_scriptPtr = oscriptPtr; } else { g_totalLines += g_lineNumber; // C_SetScriptSize(g_scriptPtr-script+8); if (isfilename) { int32_t ct = getticks() - startcompiletime; if (ct > 50) initprintf("Script compiled in %dms\n", ct); C_CompilationInfo(); } /// for (i=MAXQUOTES-1; i>=0; i--) /// if (ScriptQuotes[i] == NULL) /// ScriptQuotes[i] = Xcalloc(MAXQUOTELEN,sizeof(uint8_t)); } if (g_numCompilerErrors) { initprintf(" \n"); initprintf("--- Found %d errors", g_numCompilerErrors); if (g_numCompilerWarnings) initprintf(", %d warnings.\n", g_numCompilerWarnings); else initprintf(".\n"); } else if (g_numCompilerWarnings) { initprintf(" \n"); initprintf("--- Found %d warnings.\n", g_numCompilerWarnings); } } void C_ReportError(int32_t iError) { if (Bstrcmp(g_szCurrentBlockName, g_szLastBlockName)) { if (cs.parsingEventOfs >= 0 || cs.currentStateIdx >= 0) initprintf("%s: In %s `%s':\n", g_szScriptFileName, cs.parsingEventOfs >= 0 ? "event":"state", g_szCurrentBlockName); else initprintf("%s: At top level:\n", g_szScriptFileName); Bstrcpy(g_szLastBlockName, g_szCurrentBlockName); } switch (iError) { case ERROR_CLOSEBRACKET: initprintf("%s:%d: error: found more `}' than `{' before `%s'.\n", g_szScriptFileName, g_lineNumber, tempbuf); break; case ERROR_EVENTONLY: initprintf("%s:%d: error: `%s' only valid during events.\n", g_szScriptFileName, g_lineNumber, tempbuf); break; case ERROR_EXPECTEDKEYWORD: initprintf("%s:%d: error: expected a keyword but found `%s'.\n", g_szScriptFileName, g_lineNumber, tempbuf); break; case ERROR_FOUNDWITHIN: initprintf("%s:%d: error: found `%s' within %s.\n", g_szScriptFileName, g_lineNumber, tempbuf, (cs.parsingEventOfs >= 0)?"an event":"a state"); break; case ERROR_ISAKEYWORD: initprintf("%s:%d: error: symbol `%s' is a keyword.\n", g_szScriptFileName, g_lineNumber, tlabel); break; case ERROR_NOENDSWITCH: initprintf("%s:%d: error: did not find `endswitch' before `%s'.\n", g_szScriptFileName, g_lineNumber, tlabel); break; case ERROR_NOTAGAMEDEF: initprintf("%s:%d: error: symbol `%s' is not a game definition.\n", g_szScriptFileName, g_lineNumber, tlabel); break; case ERROR_NOTAGAMEVAR: initprintf("%s:%d: error: symbol `%s' is not a game variable.\n", g_szScriptFileName, g_lineNumber, tlabel); break; case ERROR_NOTAGAMEARRAY: initprintf("%s:%d: error: symbol `%s' is not a game array.\n", g_szScriptFileName, g_lineNumber, tlabel); break; case ERROR_GAMEARRAYBNC: initprintf("%s:%d: error: square brackets for index of game array not closed, expected ] found %c\n", g_szScriptFileName, g_lineNumber, *textptr); break; case ERROR_GAMEARRAYBNO: initprintf("%s:%d: error: square brackets for index of game array not opened, expected [ found %c\n", g_szScriptFileName, g_lineNumber, *textptr); break; case ERROR_INVALIDARRAYWRITE: initprintf("%s:%d: error: arrays can only be written to using `setarray'\n", g_szScriptFileName, g_lineNumber); break; case ERROR_EXPECTEDSIMPLEVAR: initprintf("%s:%d: error: expected a simple gamevar or a constant\n", g_szScriptFileName, g_lineNumber); break; case ERROR_OPENBRACKET: initprintf("%s:%d: error: found more `{' than `}' before `%s'.\n", g_szScriptFileName, g_lineNumber, tempbuf); break; case ERROR_PARAMUNDEFINED: initprintf("%s:%d: error: parameter `%s' is undefined.\n", g_szScriptFileName, g_lineNumber, tempbuf); break; case ERROR_SYMBOLNOTRECOGNIZED: initprintf("%s:%d: error: symbol `%s' is not recognized.\n", g_szScriptFileName, g_lineNumber, tlabel); break; case ERROR_SYNTAXERROR: initprintf("%s:%d: error: syntax error.\n", g_szScriptFileName, g_lineNumber); break; case ERROR_VARREADONLY: initprintf("%s:%d: error: variable `%s' is read-only.\n", g_szScriptFileName, g_lineNumber, tlabel); break; case ERROR_ARRAYREADONLY: initprintf("%s:%d: error: array `%s' is read-only.\n", g_szScriptFileName, g_lineNumber, tlabel); break; case ERROR_VARTYPEMISMATCH: initprintf("%s:%d: error: variable `%s' is of the wrong type.\n", g_szScriptFileName, g_lineNumber, tlabel); break; case ERROR_LABELINUSE: initprintf("%s:%d: error: label `%s' is already in use by a %s.\n", g_szScriptFileName, g_lineNumber, tlabel, def_tw==CON_DEFSTATE?"define":"state"); break; case WARNING_DUPLICATECASE: initprintf("%s:%d: warning: duplicate case ignored.\n", g_szScriptFileName, g_lineNumber); break; case WARNING_DUPLICATEDEFINITION: initprintf("%s:%d: warning: duplicate game definition `%s' ignored.\n", g_szScriptFileName, g_lineNumber, tlabel); break; case WARNING_LABELSONLY: initprintf("%s:%d: warning: expected a label, found a constant.\n", g_szScriptFileName, g_lineNumber); break; case WARNING_NAMEMATCHESVAR: initprintf("%s:%d: warning: symbol `%s' already used for game variable.\n", g_szScriptFileName, g_lineNumber, tlabel); case WARNING_OUTSIDEDRAWSPRITE: initprintf("%s:%d: warning: found `%s' outside of EVENT_ANALYZESPRITES\n", g_szScriptFileName,g_lineNumber,tempbuf); break; } if (iError!=-1) C_PrintErrorPosition(); } void C_PrintErrorPosition() { const char *b = g_curkwptr, *e=textptr; int32_t i, nchars; int32_t osdcols = OSD_GetCols(); if (!(b<e+1)) return; while (*e && !(*e==0x0a || *e==0x0d)) e++; while (b>start_textptr && *b!=0x0a) b--; if (*b==0x0a) b++; nchars = e-b; if (nchars==0 || nchars > osdcols) return; { char *buf = (char *)Xmalloc(nchars+1); Bmemcpy(buf, b, nchars); buf[nchars]=0; for (i=0; i<nchars; i++) if (buf[i]==0x0a || buf[i]==0x0d || buf[i]=='\t') buf[i]=' '; initprintf("%s\n", buf); for (i=0; i<nchars; i++) buf[i]=' '; buf[g_curkwptr-b] = '|'; buf[textptr-b] = '*'; buf[textptr-1-b] = '>'; i = g_curkwptr-b+1; while (i<textptr-1-b) buf[i++] = '-'; buf[nchars]=0; initprintf("%s\n", buf); Bfree(buf); } }