1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2016 EDuke32 developers and contributors
4 
5 This file is part of EDuke32.
6 
7 EDuke32 is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License version 2
9 as published by the Free Software Foundation.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 
15 See the GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21 //-------------------------------------------------------------------------
22 
23 #include "duke3d.h"
24 #include "namesdyn.h"
25 #include "gamedef.h"
26 #include "gameexec.h"
27 #include "savegame.h"
28 #include "common.h"
29 #include "common_game.h"
30 #include "cheats.h"
31 
32 #include "osd.h"
33 #include "crc32.h"
34 
35 #define LINE_NUMBER (g_lineNumber << 12)
36 
37 int32_t g_scriptVersion = 14; // 13 = 1.3D-style CON files, 14 = 1.4/1.5 style CON files
38 
39 char g_scriptFileName[BMAX_PATH] = "(none)";  // file we're currently compiling
40 
41 int32_t g_totalLines, g_lineNumber;
42 uint32_t g_scriptcrc;
43 char g_szBuf[1024];
44 
45 static char g_szCurrentBlockName[256] = "(none)", g_szLastBlockName[256] = "NULL";
46 static int32_t g_checkingIfElse, g_processingState, g_lastKeyword = -1;
47 
48 // The pointer to the start of the case table in a switch statement.
49 // First entry is 'default' code.
50 static intptr_t *g_caseScriptPtr;
51 static int32_t g_labelsOnly = 0;
52 static int32_t g_numBraces = 0;
53 
54 static int32_t C_ParseCommand(int32_t loop);
55 static int32_t C_SetScriptSize(int32_t size);
56 
57 static intptr_t apScriptGameEventEnd[MAXEVENTS];
58 static intptr_t g_parsingActorPtr;
59 static intptr_t g_scriptEventOffset;
60 static char *textptr;
61 
62 int32_t g_errorCnt,g_warningCnt;
63 
C_GetLabelType(int32_t type)64 static char *C_GetLabelType(int32_t type)
65 {
66     int32_t i;
67     char x[64];
68 
69     const char *LabelTypeText[] =
70     {
71         "define",
72         "state",
73         "actor",
74         "action",
75         "ai",
76         "move"
77     };
78 
79     x[0] = 0;
80     for (i=0; i<6; i++)
81     {
82         if (!(type & (1<<i))) continue;
83         if (x[0]) Bstrcat(x, " or ");
84         Bstrcat(x, LabelTypeText[i]);
85     }
86 
87     return Xstrdup(x);
88 }
89 
90 static tokenmap_t const vm_keywords[] =
91 {
92     { "action",                 CON_ACTION },
93     { "actor",                  CON_ACTOR },
94     { "addammo",                CON_ADDAMMO },
95     { "addinventory",           CON_ADDINVENTORY },
96     { "addkills",               CON_ADDKILLS },
97     { "addlog",                 CON_ADDLOGVAR },
98     { "addlogvar",              CON_ADDLOGVAR },
99     { "addphealth",             CON_ADDPHEALTH },
100     { "addstrength",            CON_ADDSTRENGTH },
101     { "addvar",                 CON_ADDVAR },
102     { "addvarvar",              CON_ADDVARVAR },
103     { "addweapon",              CON_ADDWEAPON },
104     { "ai",                     CON_AI },
105     { "betaname",               CON_BETANAME },
106     { "break",                  CON_BREAK },
107     { "cactor",                 CON_CACTOR },
108     { "clipdist",               CON_CLIPDIST },
109     { "count",                  CON_COUNT },
110     { "cstat",                  CON_CSTAT },
111     { "cstator",                CON_CSTATOR },
112     { "debris",                 CON_DEBRIS },
113     { "debug",                  CON_DEBUG },
114     { "define",                 CON_DEFINE },
115     { "definelevelname",        CON_DEFINELEVELNAME },
116     { "definequote",            CON_DEFINEQUOTE },
117     { "defineskillname",        CON_DEFINESKILLNAME },
118     { "definesound",            CON_DEFINESOUND },
119     { "definevolumename",       CON_DEFINEVOLUMENAME },
120     { "deploybias",             CON_DEPLOYBIAS },
121     { "destroyit",              CON_DESTROYIT },
122     { "else",                   CON_ELSE },
123     { "enda",                   CON_ENDA },
124     { "endevent",               CON_ENDEVENT },
125     { "endofgame",              CON_ENDOFGAME },
126     { "ends",                   CON_ENDS },
127     { "fakebubba",              CON_FAKEBUBBA },
128     { "fall",                   CON_FALL },
129     { "feathers",               CON_MAIL },
130     { "gamestartup",            CON_GAMESTARTUP },
131     { "gamevar",                CON_GAMEVAR },
132     { "garybanjo",              CON_GARYBANJO },
133     { "getlastpal",             CON_GETLASTPAL },
134     { "globalsound",            CON_GLOBALSOUND },
135     { "guts",                   CON_GUTS },
136     { "hitradius",              CON_HITRADIUS },
137     { "ifaction",               CON_IFACTION },
138     { "ifactioncount",          CON_IFACTIONCOUNT },
139     { "ifactor",                CON_IFACTOR },
140     { "ifactorhealthg",         CON_IFACTORHEALTHG },
141     { "ifactorhealthl",         CON_IFACTORHEALTHL },
142     { "ifactornotstayput",      CON_IFACTORNOTSTAYPUT },
143     { "ifai",                   CON_IFAI },
144     { "ifangdiffl",             CON_IFANGDIFFL },
145     { "ifawayfromwall",         CON_IFAWAYFROMWALL },
146     { "ifbulletnear",           CON_IFBULLETNEAR },
147     { "ifcansee",               CON_IFCANSEE },
148     { "ifcanseetarget",         CON_IFCANSEETARGET },
149     { "ifcanshoottarget",       CON_IFCANSHOOTTARGET },
150     { "ifceilingdistl",         CON_IFCEILINGDISTL },
151     { "ifcount",                CON_IFCOUNT },
152     { "ifdead",                 CON_IFDEAD },
153     { "iffindnewspot",          CON_IFFINDNEWSPOT },
154     { "iffloordistl",           CON_IFFLOORDISTL },
155     { "ifgapzl",                CON_IFGAPZL },
156     { "ifgotweaponce",          CON_IFGOTWEAPONCE },
157     { "ifhitspace",             CON_IFHITSPACE },
158     { "ifhittruck",             CON_IFHITTRUCK },
159     { "ifhitweapon",            CON_IFHITWEAPON },
160     { "ifinouterspace",         CON_IFINOUTERSPACE },
161     { "ifinspace",              CON_IFINSPACE },
162     { "ifinwater",              CON_IFINWATER },
163     { "ifmotofast",             CON_IFMOTOFAST },
164     { "ifmove",                 CON_IFMOVE },
165     { "ifmultiplayer",          CON_IFMULTIPLAYER },
166     { "ifnocover",              CON_IFNOCOVER },
167     { "ifnosounds",             CON_IFNOSOUNDS },
168     { "ifnotmoving",            CON_IFNOTMOVING },
169     { "ifcoop",                 CON_IFCOOP },
170     { "ifonboat",               CON_IFONBOAT },
171     { "ifonmoto",               CON_IFONMOTO },
172     { "ifonmud",                CON_IFONMUD },
173     { "ifonwater",              CON_IFONWATER },
174     { "ifoutside",              CON_IFOUTSIDE },
175     { "ifp",                    CON_IFP },
176     { "ifpdistg",               CON_IFPDISTG },
177     { "ifpdistl",               CON_IFPDISTL },
178     { "ifpdrunk",               CON_IFPDRUNK },
179     { "ifphealthl",             CON_IFPHEALTHL },
180     { "ifpinventory",           CON_IFPINVENTORY },
181     { "ifrespawn",              CON_IFRESPAWN },
182     { "ifrnd",                  CON_IFRND },
183     { "ifsizedown",             CON_IFSIZEDOWN },
184     { "ifsounddist",            CON_IFSOUNDDIST },
185     { "ifsoundid",              CON_IFSOUNDID },
186     { "ifspawnedby",            CON_IFSPAWNEDBY },
187     { "ifspritepal",            CON_IFSPRITEPAL },
188     { "ifsquished",             CON_IFSQUISHED },
189     { "ifstrength",             CON_IFSTRENGTH },
190     { "iftipcow",               CON_IFTIPCOW },
191     { "ifpupwind",              CON_IFPUPWIND },
192     { "ifvare",                 CON_IFVARE },
193     { "ifvarg",                 CON_IFVARG },
194     { "ifvarl",                 CON_IFVARL },
195     { "ifvarvare",              CON_IFVARVARE },
196     { "ifvarvarg",              CON_IFVARVARG },
197     { "ifvarvarl",              CON_IFVARVARL },
198     { "ifwasweapon",            CON_IFWASWEAPON },
199     { "ifwind",                 CON_IFWIND },
200     { "include",                CON_INCLUDE },
201     { "isdrunk",                CON_ISDRUNK },
202     { "iseat",                  CON_ISEAT },
203     { "killit",                 CON_KILLIT },
204     { "larrybird",              CON_LARRYBIRD },
205     { "leavedroppings",         CON_LEAVEDROPPINGS },
206     { "leavetrax",              CON_LEAVETRAX },
207     { "lotsofglass",            CON_LOTSOFGLASS },
208     { "mail",                   CON_MAIL },
209     { "mamaend",                CON_MAMAEND },
210     { "mamaquake",              CON_MAMAQUAKE },
211     { "mamaspawn",              CON_MAMASPAWN },
212     { "mamatrigger",            CON_MAMATRIGGER },
213     { "mikesnd",                CON_MIKESND },
214     { "money",                  CON_MONEY },
215     { "motoloopsnd",            CON_MOTOLOOPSND },
216     { "move",                   CON_MOVE },
217     { "music",                  CON_MUSIC },
218     { "newpic",                 CON_NEWPIC },
219     { "nullop",                 CON_NULLOP },
220     { "onevent",                CON_ONEVENT },
221     { "operate",                CON_OPERATE },
222     { "palfrom",                CON_PALFROM },
223     { "paper",                  CON_PAPER },
224     { "pkick",                  CON_PKICK },
225     { "pstomp",                 CON_PSTOMP },
226     { "quote",                  CON_QUOTE },
227     { "resetactioncount",       CON_RESETACTIONCOUNT },
228     { "resetcount",             CON_RESETCOUNT },
229     { "resetplayer",            CON_RESETPLAYER },
230     { "respawnhitag",           CON_RESPAWNHITAG },
231     { "rndmove",                CON_RNDMOVE },
232     { "setvar",                 CON_SETVAR },
233     { "setvarvar",              CON_SETVARVAR },
234     { "sizeat",                 CON_SIZEAT },
235     { "sizeto",                 CON_SIZETO },
236     { "slapplayer",             CON_SLAPPLAYER },
237     { "sleeptime",              CON_SLEEPTIME },
238     { "shoot",                  CON_SHOOT },
239     { "smackbubba",             CON_SMACKBUBBA },
240     { "smacksprite",            CON_SMACKSPRITE },
241     { "sound",                  CON_SOUND },
242     { "soundonce",              CON_SOUNDONCE },
243     { "soundtag",               CON_SOUNDTAG },
244     { "soundtagonce",           CON_SOUNDTAGONCE },
245     { "spawn",                  CON_SPAWN },
246     { "spritepal",              CON_SPRITEPAL },
247     { "state",                  CON_STATE },
248     { "stopsound",              CON_STOPSOUND },
249     { "strafeleft",             CON_STRAFELEFT },
250     { "straferight",            CON_STRAFERIGHT },
251     { "strength",               CON_STRENGTH },
252     { "tearitup",               CON_TEARITUP },
253     { "tip",                    CON_TIP },
254     { "tossweapon",             CON_TOSSWEAPON },
255     { "useractor",              CON_USERACTOR },
256     { "wackplayer",             CON_WACKPLAYER },
257     { "{",                      CON_LEFTBRACE },
258     { "}",                      CON_RIGHTBRACE },
259 };
260 
261 static const vec2_t varvartable[] =
262 {
263     { CON_IFVARVARE,         CON_IFVARE },
264     { CON_IFVARVARG,         CON_IFVARG },
265     { CON_IFVARVARL,         CON_IFVARL },
266 
267     { CON_ADDVARVAR,         CON_ADDVAR },
268     { CON_SETVARVAR,         CON_SETVAR },
269 };
270 
271 static inthashtable_t h_varvar = { NULL, INTHASH_SIZE(ARRAY_SIZE(varvartable)) };
272 
273 static inthashtable_t *const inttables[] = {
274     &h_varvar,
275 };
276 
VM_GetKeywordForID(int32_t id)277 char const * VM_GetKeywordForID(int32_t id)
278 {
279     // could be better but this is only called for diagnostics, ayy lmao
280     for (tokenmap_t const & keyword : vm_keywords)
281         if (keyword.val == id)
282             return keyword.token;
283 
284     return "<invalid keyword>";
285 }
286 
287 char *bitptr; // pointer to bitmap of which bytecode positions contain pointers
288 #define BITPTR_SET(x) (bitptr[(x)>>3] |= (1<<((x)&7)))
289 #define BITPTR_CLEAR(x) (bitptr[(x)>>3] &= ~(1<<((x)&7)))
290 #define BITPTR_IS_POINTER(x) (bitptr[(x)>>3] & (1<<((x) &7)))
291 
292 hashtable_t h_gamevars = { MAXGAMEVARS >> 1, NULL };
293 hashtable_t h_labels   = { 11264>>1, NULL };
294 
295 static hashtable_t h_keywords   = { CON_END>>1, NULL };;
296 
297 static hashtable_t * const tables[] = {
298     &h_labels, &h_keywords, &h_gamevars
299 };
300 
301 static hashtable_t * const tables_free [] = {
302     &h_labels, &h_keywords
303 };
304 
305 #define STRUCT_HASH_SETUP(table, labels) do { for (i=0; labels[i].lId >= 0; i++) hash_add(&table, labels[i].name, i, 0); } while (0)
306 
C_InitHashes()307 void C_InitHashes()
308 {
309     uint32_t i;
310 
311     for (auto table : tables)
312         hash_init(table);
313 
314     for (auto table : inttables)
315         inthash_init(table);
316 
317     for (auto &varvar : varvartable)
318         inthash_add(&h_varvar, varvar.x, varvar.y, 0);
319 
320     //inithashnames();
321     initsoundhashnames();
322 
323     for (tokenmap_t const & keyword : vm_keywords)
324         hash_add(&h_keywords, keyword.token, keyword.val, 0);
325 }
326 
327 #undef STRUCT_HASH_SETUP
328 
329 // "magic" number for { and }, overrides line number in compiled code for later detection
330 #define IFELSE_MAGIC 31337
331 static int32_t g_skipBranch;
332 
C_SetScriptSize(int32_t newsize)333 static int32_t C_SetScriptSize(int32_t newsize)
334 {
335     intptr_t const oscript = (intptr_t)apScript;
336     intptr_t *newscript;
337     intptr_t i, j;
338     int32_t osize = g_scriptSize;
339     char *scriptptrs;
340     char *newbitptr;
341 
342     scriptptrs = (char *)Xcalloc(1, g_scriptSize * sizeof(uint8_t));
343 
344     for (i=g_scriptSize-1; i>=0; i--)
345     {
346         if (BITPTR_IS_POINTER(i))
347         {
348             if (EDUKE32_PREDICT_FALSE((intptr_t)apScript[i] < (intptr_t)&apScript[0] || (intptr_t)apScript[i] >= (intptr_t)&apScript[g_scriptSize]))
349             {
350                 g_errorCnt++;
351                 buildprint("Internal compiler error at ", i, " (0x", hex(i), ")\n");
352                 VM_ScriptInfo(&apScript[i], 16);
353             }
354 
355             scriptptrs[i] = 1;
356             apScript[i] -= (intptr_t)&apScript[0];
357         }
358         else scriptptrs[i] = 0;
359     }
360 
361     G_Util_PtrToIdx2(&g_tile[0].execPtr, MAXTILES, sizeof(tiledata_t), apScript, P2I_FWD_NON0);
362     G_Util_PtrToIdx2(&g_tile[0].loadPtr, MAXTILES, sizeof(tiledata_t), apScript, P2I_FWD_NON0);
363 
364     newscript = (intptr_t *)Xrealloc(apScript, newsize * sizeof(intptr_t));
365     newbitptr = (char *)Xcalloc(1,(((newsize+7)>>3)+1) * sizeof(uint8_t));
366 
367     if (newsize >= osize)
368     {
369         Bmemset(&newscript[0]+osize,0,(newsize-osize) * sizeof(uint8_t));
370         Bmemcpy(newbitptr,bitptr,sizeof(uint8_t) *((osize+7)>>3));
371     }
372     else
373         Bmemcpy(newbitptr,bitptr,sizeof(uint8_t) *((newsize+7)>>3));
374 
375     Bfree(bitptr);
376     bitptr = newbitptr;
377     if (apScript != newscript)
378     {
379         buildprint("Relocating compiled code from to 0x", hex((intptr_t)apScript), " to 0x", hex((intptr_t)newscript), "\n");
380         apScript = newscript;
381     }
382 
383     g_scriptSize = newsize;
384     g_scriptPtr = apScript + (intptr_t)g_scriptPtr - oscript;
385 
386     if (g_caseScriptPtr)
387         g_caseScriptPtr = apScript + (intptr_t)g_caseScriptPtr - oscript;
388 
389     for (i=(((newsize>=osize)?osize:newsize))-1; i>=0; i--)
390         if (scriptptrs[i])
391         {
392             j = (intptr_t)apScript[i]+(intptr_t)&apScript[0];
393             apScript[i] = j;
394         }
395 
396     G_Util_PtrToIdx2(&g_tile[0].execPtr, MAXTILES, sizeof(tiledata_t), apScript, P2I_BACK_NON0);
397     G_Util_PtrToIdx2(&g_tile[0].loadPtr, MAXTILES, sizeof(tiledata_t), apScript, P2I_BACK_NON0);
398 
399     Bfree(scriptptrs);
400     return 0;
401 }
402 
ispecial(const char c)403 static inline int32_t ispecial(const char c)
404 {
405     return (c == ' ' || c == 0x0d || c == '(' || c == ')' ||
406         c == ',' || c == ';' || (c == 0x0a /*&& ++g_lineNumber*/));
407 }
408 
C_NextLine(void)409 static inline void C_NextLine(void)
410 {
411     while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
412         textptr++;
413 }
414 
C_SkipSpace(void)415 static inline void C_SkipSpace(void)
416 {
417     while (*textptr == ' ' || *textptr == '\t')
418         textptr++;
419 }
420 
421 static int32_t g_gotComment = 0;
422 
C_SkipComments(void)423 static int32_t C_SkipComments(void)
424 {
425     g_gotComment = 0;
426     do
427     {
428         switch (*textptr)
429         {
430         case '\n':
431             g_lineNumber++;
432             fallthrough__;
433         case ' ':
434         case '\t':
435         case '\r':
436         case 0x1a:
437             textptr++;
438             break;
439         case '/':
440             switch (textptr[1])
441             {
442             case '/': // C++ style comment
443                 if (!(g_errorCnt || g_warningCnt) && g_scriptDebug > 1)
444                     initprintf("%s:%d: debug: got comment.\n",g_scriptFileName,g_lineNumber);
445                 C_NextLine();
446                 g_gotComment = 1;
447                 continue;
448             case '*': // beginning of a C style comment
449                 if (!(g_errorCnt || g_warningCnt) && g_scriptDebug > 1)
450                     initprintf("%s:%d: debug: got start of comment block.\n",g_scriptFileName,g_lineNumber);
451                 do
452                 {
453                     if (*textptr == '\n')
454                         g_lineNumber++;
455                     textptr++;
456                 }
457                 while (*textptr && (textptr[0] != '*' || textptr[1] != '/'));
458 
459                 if (EDUKE32_PREDICT_FALSE(!*textptr))
460                 {
461                     if (!(g_errorCnt || g_warningCnt) && g_scriptDebug)
462                         initprintf("%s:%d: debug: EOF in comment!\n",g_scriptFileName,g_lineNumber);
463                     C_ReportError(-1);
464                     initprintf("%s:%d: error: found `/*' with no `*/'.\n",g_scriptFileName,g_lineNumber);
465                     g_parsingActorPtr = g_processingState = g_numBraces = 0;
466                     g_errorCnt++;
467                     continue;
468                 }
469 
470                 if (!(g_errorCnt || g_warningCnt) && g_scriptDebug > 1)
471                     initprintf("%s:%d: debug: got end of comment block.\n",g_scriptFileName,g_lineNumber);
472 
473                 textptr+=2;
474                 g_gotComment = 1;
475                 continue;
476             default:
477                 C_ReportError(-1);
478                 initprintf("%s:%d: error: malformed comment.\n", g_scriptFileName, g_lineNumber);
479                 C_NextLine();
480                 g_errorCnt++;
481                 continue;
482             }
483             break;
484 
485         default:
486             if (ispecial(*textptr))
487             {
488                 textptr++;
489                 continue;
490             }
491             fallthrough__;
492         case 0: // EOF
493             return ((g_scriptPtr-apScript) > (g_scriptSize-32)) ? C_SetScriptSize(g_scriptSize<<1) : 0;
494         }
495     }
496     while (1);
497 }
498 
GetDefID(char const * label)499 static inline int GetDefID(char const *label) { return hash_find(&h_gamevars, label); }
500 
501 #define LAST_LABEL (label+(g_labelCnt<<6))
isaltok(const char c)502 static inline int32_t isaltok(const char c)
503 {
504     return (isalnum(c) || c == '{' || c == '}' || c == '/' || c == '\\' || c == '*' || c == '-' || c == '_' ||
505             c == '.');
506 }
507 
C_IsLabelChar(const char c,int32_t const i)508 static inline int32_t C_IsLabelChar(const char c, int32_t const i)
509 {
510     return (isalnum(c) || c == '_' || c == '*' || c == '?' || (i > 0 && (c == '+' || c == '-' || c == '.')));
511 }
512 
C_GetLabelNameID(const memberlabel_t * pLabel,hashtable_t const * const table,const char * psz)513 static inline int32_t C_GetLabelNameID(const memberlabel_t *pLabel, hashtable_t const * const table, const char *psz)
514 {
515     // find the label psz in the table pLabel.
516     // returns the ID for the label, or -1
517 
518     int32_t l = hash_findcase(table, psz);
519     return (l >= 0) ? pLabel[l].lId : -1;
520 }
521 
C_GetLabelNameOffset(hashtable_t const * const table,const char * psz)522 static inline int32_t C_GetLabelNameOffset(hashtable_t const * const table, const char *psz)
523 {
524     // find the label psz in the table pLabel.
525     // returns the offset in the array for the label, or -1
526 
527     return hash_findcase(table, psz);
528 }
529 
C_GetNextLabelName(void)530 static void C_GetNextLabelName(void)
531 {
532     int32_t i = 0;
533 
534     C_SkipComments();
535 
536 //    while (ispecial(*textptr) == 0 && *textptr!='['&& *textptr!=']' && *textptr!='\t' && *textptr!='\n' && *textptr!='\r')
537     while (C_IsLabelChar(*textptr, i))
538         label[(g_labelCnt<<6)+(i++)] = *(textptr++);
539 
540     label[(g_labelCnt<<6)+i] = 0;
541 
542     if (!(g_errorCnt|g_warningCnt) && g_scriptDebug > 1)
543         initprintf("%s:%d: debug: label `%s'.\n",g_scriptFileName,g_lineNumber,label+(g_labelCnt<<6));
544 }
545 
scriptWriteValue(int32_t const value)546 static inline void scriptWriteValue(int32_t const value)
547 {
548     BITPTR_CLEAR(g_scriptPtr-apScript);
549     *g_scriptPtr++ = value;
550 }
551 
552 // addresses passed to these functions must be within the block of memory pointed to by apScript
scriptWriteAtOffset(int32_t const value,intptr_t * const addr)553 static inline void scriptWriteAtOffset(int32_t const value, intptr_t * const addr)
554 {
555     BITPTR_CLEAR(addr-apScript);
556     *(addr) = value;
557 }
558 
scriptWritePointer(intptr_t const value,intptr_t * const addr)559 static inline void scriptWritePointer(intptr_t const value, intptr_t * const addr)
560 {
561     BITPTR_SET(addr-apScript);
562     *(addr) = value;
563 }
564 
C_GetKeyword(void)565 static int32_t C_GetKeyword(void)
566 {
567     int32_t i;
568     char *temptextptr;
569 
570     C_SkipComments();
571 
572     temptextptr = textptr;
573 
574     if (*temptextptr == 0) // EOF
575         return -2;
576 
577     while (isaltok(*temptextptr) == 0)
578     {
579         temptextptr++;
580         if (*temptextptr == 0)
581             return 0;
582     }
583 
584     i = 0;
585     while (isaltok(*temptextptr))
586         tempbuf[i++] = *(temptextptr++);
587     tempbuf[i] = 0;
588 
589     return hash_find(&h_keywords,tempbuf);
590 }
591 
C_GetNextKeyword(void)592 static int32_t C_GetNextKeyword(void) //Returns its code #
593 {
594     int32_t i, l;
595 
596     C_SkipComments();
597 
598     if (*textptr == 0) // EOF
599         return -2;
600 
601     l = 0;
602     while (isaltok(*(textptr+l)))
603     {
604         tempbuf[l] = textptr[l];
605         l++;
606     }
607     tempbuf[l] = 0;
608 
609     if (EDUKE32_PREDICT_TRUE((i = hash_find(&h_keywords,tempbuf)) >= 0))
610     {
611         if (i == CON_LEFTBRACE || i == CON_RIGHTBRACE || i == CON_NULLOP)
612             scriptWriteValue(i | (VM_IFELSE_MAGIC<<12));
613         else scriptWriteValue(i | LINE_NUMBER);
614 
615         textptr += l;
616 
617         if (!(g_errorCnt || g_warningCnt) && g_scriptDebug)
618             initprintf("%s:%d: debug: keyword `%s'.\n", g_scriptFileName, g_lineNumber, tempbuf);
619         return i;
620     }
621 
622     textptr += l;
623     g_errorCnt++;
624 
625     if (EDUKE32_PREDICT_FALSE((tempbuf[0] == '{' || tempbuf[0] == '}') && tempbuf[1] != 0))
626     {
627         C_ReportError(-1);
628         initprintf("%s:%d: error: expected whitespace between `%c' and `%s'.\n",g_scriptFileName,g_lineNumber,tempbuf[0],tempbuf+1);
629     }
630     else C_ReportError(ERROR_EXPECTEDKEYWORD);
631 
632     return -1;
633 }
634 
parse_decimal_number(void)635 static int32_t parse_decimal_number(void)  // (textptr)
636 {
637     // decimal constants -- this is finicky business
638     int64_t num = strtoll(textptr, NULL, 10);  // assume long long to be int64_t
639 
640     if (EDUKE32_PREDICT_TRUE(num >= INT32_MIN && num <= INT32_MAX))
641     {
642         // all OK
643     }
644     else if (EDUKE32_PREDICT_FALSE(num > INT32_MAX && num <= UINT32_MAX))
645     {
646         // Number interpreted as uint32, but packed as int32 (on 32-bit archs)
647         // (CON code in the wild exists that does this).  Note that such conversion
648         // is implementation-defined (C99 6.3.1.3) but GCC does the 'expected' thing.
649 #if 0
650         initprintf("%s:%d: warning: number greater than INT32_MAX converted to a negative one.\n",
651                    g_szScriptFileName,g_lineNumber);
652         g_numCompilerWarnings++;
653 #endif
654     }
655     else
656     {
657         // out of range, this is arguably worse
658 
659         initprintf("%s:%d: warning: number out of the range of a 32-bit integer encountered.\n",
660                    g_scriptFileName,g_lineNumber);
661         g_warningCnt++;
662     }
663 
664     return (int32_t)num;
665 }
666 
parse_hex_constant(const char * hexnum)667 static int32_t parse_hex_constant(const char *hexnum)
668 {
669     uint64_t x;
670     sscanf(hexnum, "%" PRIx64 "", &x);
671 
672     if (EDUKE32_PREDICT_FALSE(x > UINT32_MAX))
673     {
674         initprintf(g_scriptFileName, ":", g_lineNumber, ": warning: number 0x", hex(x), " truncated to 32 bits.\n");
675         g_warningCnt++;
676     }
677 
678     return x;
679 }
680 
C_GetNextVarType(int32_t type)681 static void C_GetNextVarType(int32_t type)
682 {
683     int32_t id    = 0;
684     int32_t flags = 0;
685 
686     auto varptr = g_scriptPtr;
687 
688     C_SkipComments();
689 
690     if (!type && !g_labelsOnly && (isdigit(*textptr) || ((*textptr == '-') && (isdigit(*(textptr+1))))))
691     {
692         scriptWriteValue(GV_FLAG_CONSTANT);
693 
694         if (tolower(textptr[1])=='x')  // hex constants
695             scriptWriteValue(parse_hex_constant(textptr+2));
696         else
697             scriptWriteValue(parse_decimal_number());
698 
699         if (!(g_errorCnt || g_warningCnt) && g_scriptDebug)
700             initprintf("%s:%d: debug: constant %ld in place of gamevar.\n", g_scriptFileName, g_lineNumber, (long)(g_scriptPtr[-1]));
701 #if 1
702         while (!ispecial(*textptr) && *textptr != ']') textptr++;
703 #else
704         C_GetNextLabelName();
705 #endif
706         return;
707     }
708     else if (*textptr == '-'/* && !isdigit(*(textptr+1))*/)
709     {
710         if (EDUKE32_PREDICT_FALSE(type))
711         {
712             g_errorCnt++;
713             C_ReportError(ERROR_SYNTAXERROR);
714             C_GetNextLabelName();
715             return;
716         }
717 
718         if (!(g_errorCnt || g_warningCnt) && g_scriptDebug)
719             initprintf("%s:%d: debug: flagging gamevar as negative.\n", g_scriptFileName, g_lineNumber); //,Batol(textptr));
720 
721         flags = GV_FLAG_NEGATIVE;
722         textptr++;
723     }
724 
725     C_GetNextLabelName();
726 
727     if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,LAST_LABEL)>=0))
728     {
729         g_errorCnt++;
730         C_ReportError(ERROR_ISAKEYWORD);
731         return;
732     }
733 
734     C_SkipComments();
735 
736     id=GetDefID(LAST_LABEL);
737     if (id<0)   //gamevar not found
738     {
739         if (EDUKE32_PREDICT_TRUE(!type && !g_labelsOnly))
740         {
741             //try looking for a define instead
742             Bstrcpy(tempbuf,LAST_LABEL);
743             id = hash_find(&h_labels,tempbuf);
744 
745             if (EDUKE32_PREDICT_TRUE(id>=0 && labeltype[id] & LABEL_DEFINE))
746             {
747                 if (!(g_errorCnt || g_warningCnt) && g_scriptDebug)
748                     initprintf("%s:%d: debug: label `%s' in place of gamevar.\n",g_scriptFileName,g_lineNumber,label+(id<<6));
749 
750                 scriptWriteValue(GV_FLAG_CONSTANT);
751                 scriptWriteValue(labelcode[id]);
752                 return;
753             }
754         }
755 
756         g_errorCnt++;
757         C_ReportError(ERROR_NOTAGAMEVAR);
758         return;
759     }
760 
761     if (EDUKE32_PREDICT_FALSE(type == GAMEVAR_READONLY && aGameVars[id].flags & GAMEVAR_READONLY))
762     {
763         g_errorCnt++;
764         C_ReportError(ERROR_VARREADONLY);
765         return;
766     }
767     else if (EDUKE32_PREDICT_FALSE(aGameVars[id].flags & type))
768     {
769         g_errorCnt++;
770         C_ReportError(ERROR_VARTYPEMISMATCH);
771         return;
772     }
773 
774     if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
775         initprintf("%s:%d: debug: gamevar `%s'.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
776 
777     scriptWriteValue(id|flags);
778 }
779 
780 #define C_GetNextVar() C_GetNextVarType(0)
781 
C_GetManyVarsType(int32_t type,int num)782 static FORCE_INLINE void C_GetManyVarsType(int32_t type, int num)
783 {
784     for (; num>0; --num)
785         C_GetNextVarType(type);
786 }
787 
788 #define C_GetManyVars(num) C_GetManyVarsType(0,num)
789 
790 // returns:
791 //  -1 on EOF or wrong type or error
792 //   0 if literal value
793 //   LABEL_* (>0) if that type and matched
794 //
795 // *g_scriptPtr will contain the value OR 0 if wrong type or error
C_GetNextValue(int32_t type)796 static int32_t C_GetNextValue(int32_t type)
797 {
798     C_SkipComments();
799 
800     if (*textptr == 0) // EOF
801         return -1;
802 
803     int32_t l = 0;
804 
805     while (isaltok(*(textptr+l)))
806     {
807         tempbuf[l] = textptr[l];
808         l++;
809     }
810     tempbuf[l] = 0;
811 
812     if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,tempbuf /*label+(g_numLabels<<6)*/)>=0))
813     {
814         g_errorCnt++;
815         C_ReportError(ERROR_ISAKEYWORD);
816         textptr+=l;
817     }
818 
819     int32_t i = hash_find(&h_labels,tempbuf);
820 
821     if (i>=0)
822     {
823         if (EDUKE32_PREDICT_TRUE(labeltype[i] & type))
824         {
825             if (!(g_errorCnt || g_warningCnt) && g_scriptDebug > 1)
826             {
827                 char *gl = C_GetLabelType(labeltype[i]);
828                 initprintf("%s:%d: debug: %s label `%s'.\n",g_scriptFileName,g_lineNumber,gl,label+(i<<6));
829                 Bfree(gl);
830             }
831 
832             BITPTR_CLEAR(g_scriptPtr-apScript);
833             *(g_scriptPtr++) = labelcode[i];
834 
835             textptr += l;
836             return labeltype[i];
837         }
838 
839         BITPTR_CLEAR(g_scriptPtr-apScript);
840         *(g_scriptPtr++) = 0;
841         textptr += l;
842         char *el = C_GetLabelType(type);
843         char *gl = C_GetLabelType(labeltype[i]);
844         C_ReportError(-1);
845         initprintf("%s:%d: warning: expected %s, found %s.\n",g_scriptFileName,g_lineNumber,el,gl);
846         g_warningCnt++;
847         Bfree(el);
848         Bfree(gl);
849         return -1;  // valid label name, but wrong type
850     }
851 
852     if (EDUKE32_PREDICT_FALSE(isdigit(*textptr) == 0 && *textptr != '-'))
853     {
854         C_ReportError(ERROR_PARAMUNDEFINED);
855         g_errorCnt++;
856         BITPTR_CLEAR(g_scriptPtr-apScript);
857         *g_scriptPtr = 0;
858         g_scriptPtr++;
859         textptr+=l;
860         if (!l) textptr++;
861         return -1; // error!
862     }
863 
864     if (EDUKE32_PREDICT_FALSE(isdigit(*textptr) && g_labelsOnly))
865     {
866         C_ReportError(WARNING_LABELSONLY);
867         g_warningCnt++;
868     }
869 
870     i = l-1;
871     do
872     {
873         // FIXME: check for 0-9 A-F for hex
874         if (textptr[0] == '0' && textptr[1] == 'x') break; // kill the warning for hex
875         if (EDUKE32_PREDICT_FALSE(!isdigit(textptr[i--])))
876         {
877             C_ReportError(-1);
878             initprintf("%s:%d: warning: invalid character `%c' in definition!\n",g_scriptFileName,g_lineNumber,textptr[i+1]);
879             g_warningCnt++;
880             break;
881         }
882     }
883     while (i > 0);
884 
885     BITPTR_CLEAR(g_scriptPtr-apScript);
886 
887     if (textptr[0] == '0' && tolower(textptr[1])=='x')
888         *g_scriptPtr = parse_hex_constant(textptr+2);
889     else
890         *g_scriptPtr = parse_decimal_number();
891 
892     if (!(g_errorCnt || g_warningCnt) && g_scriptDebug > 1)
893         initprintf("%s:%d: debug: constant %ld.\n",
894                    g_scriptFileName,g_lineNumber,(long)*g_scriptPtr);
895 
896     g_scriptPtr++;
897 
898     textptr += l;
899 
900     return 0;   // literal value
901 }
902 
C_CheckMalformedBranch(intptr_t lastScriptPtr)903 static int32_t C_CheckMalformedBranch(intptr_t lastScriptPtr)
904 {
905     switch (C_GetKeyword())
906     {
907     case CON_RIGHTBRACE:
908     case CON_ENDA:
909     case CON_ENDS:
910     case CON_ELSE:
911         g_scriptPtr = lastScriptPtr + &apScript[0];
912         g_skipBranch = 1;
913         C_ReportError(-1);
914         g_warningCnt++;
915         initprintf("%s:%d: warning: malformed `%s' branch\n",g_scriptFileName,g_lineNumber,
916                    VM_GetKeywordForID(*(g_scriptPtr) & VM_INSTMASK));
917         return 1;
918     }
919     return 0;
920 }
921 
C_CheckEmptyBranch(int32_t tw,intptr_t lastScriptPtr)922 static int32_t C_CheckEmptyBranch(int32_t tw, intptr_t lastScriptPtr)
923 {
924     // ifrnd and the others actually do something when the condition is executed
925     if ((Bstrncmp(VM_GetKeywordForID(tw), "if", 2) && tw != CON_ELSE) ||
926             tw == CON_IFRND || tw == CON_IFHITWEAPON || tw == CON_IFCANSEE || tw == CON_IFCANSEETARGET ||
927             tw == CON_IFPDISTL || tw == CON_IFPDISTG || tw == CON_IFGOTWEAPONCE)
928     {
929         g_skipBranch = 0;
930         return 0;
931     }
932 
933     if ((*(g_scriptPtr) & VM_INSTMASK) != CON_NULLOP || *(g_scriptPtr)>>12 != IFELSE_MAGIC)
934         g_skipBranch = 0;
935 
936     if (EDUKE32_PREDICT_FALSE(g_skipBranch))
937     {
938         C_ReportError(-1);
939         g_warningCnt++;
940         g_scriptPtr = lastScriptPtr + &apScript[0];
941         initprintf("%s:%d: warning: empty `%s' branch\n",g_scriptFileName,g_lineNumber,
942                    VM_GetKeywordForID(*(g_scriptPtr) & VM_INSTMASK));
943         *(g_scriptPtr) = (CON_NULLOP + (IFELSE_MAGIC<<12));
944         return 1;
945     }
946     return 0;
947 }
948 
C_Include(const char * confile)949 static void C_Include(const char *confile)
950 {
951     int32_t fp = kopen4loadfrommod(confile,g_loadFromGroupOnly);
952 
953     if (EDUKE32_PREDICT_FALSE(fp < 0))
954     {
955         g_errorCnt++;
956         initprintf("%s:%d: error: could not find file `%s'.\n",g_scriptFileName,g_lineNumber,confile);
957         return;
958     }
959 
960     int32_t j = kfilelength(fp);
961 
962     char *mptr = (char *)Xmalloc(j+1);
963 
964     initprintf("Including: %s (%d bytes)\n",confile, j);
965 
966     kread(fp, mptr, j);
967     kclose(fp);
968     g_scriptcrc = Bcrc32(mptr, j, g_scriptcrc);
969     mptr[j] = 0;
970 
971     if (*textptr == '"') // skip past the closing quote if it's there so we don't screw up the next line
972         textptr++;
973 
974     char *origtptr = textptr;
975     char parentScriptFileName[255];
976 
977     Bstrcpy(parentScriptFileName, g_scriptFileName);
978     Bstrcpy(g_scriptFileName, confile);
979 
980     int32_t temp_ScriptLineNumber = g_lineNumber;
981     g_lineNumber = 1;
982 
983     int32_t temp_ifelse_check = g_checkingIfElse;
984     g_checkingIfElse = 0;
985 
986     textptr = mptr;
987 
988     C_SkipComments();
989     C_ParseCommand(1);
990 
991     Bstrcpy(g_scriptFileName, parentScriptFileName);
992 
993     g_totalLines += g_lineNumber;
994     g_lineNumber = temp_ScriptLineNumber;
995     g_checkingIfElse = temp_ifelse_check;
996 
997     textptr = origtptr;
998 
999     Bfree(mptr);
1000 }
1001 
1002 #ifdef _WIN32
check_filename_case(const char * fn)1003 static void check_filename_case(const char *fn)
1004 {
1005     int32_t fp;
1006     if ((fp = kopen4loadfrommod(fn, g_loadFromGroupOnly)) >= 0)
1007         kclose(fp);
1008 }
1009 #else
check_filename_case(const char * fn)1010 static void check_filename_case(const char *fn) { UNREFERENCED_PARAMETER(fn); }
1011 #endif
1012 
G_DoGameStartup(const int32_t * params)1013 void G_DoGameStartup(const int32_t *params)
1014 {
1015     int j = 0;
1016 
1017     ud.const_visibility               = params[j++];
1018     g_impactDamage                    = params[j++];
1019     g_player[0].ps->max_shield_amount = params[j++];
1020     g_player[0].ps->max_player_health = g_player[0].ps->max_shield_amount;
1021     g_maxPlayerHealth                 = g_player[0].ps->max_player_health;
1022     g_startArmorAmount                = params[j++];
1023     g_actorRespawnTime                = params[j++];
1024     g_itemRespawnTime                 = params[j++];
1025     g_playerFriction                  = params[j++];
1026     g_spriteGravity                   = params[j++];
1027     g_rpgRadius                       = params[j++];
1028     g_pipebombRadius                  = params[j++];
1029     g_shrinkerRadius                  = params[j++];
1030     g_tripbombRadius                  = params[j++];
1031     g_morterRadius                    = params[j++];
1032     g_bouncemineRadius                = params[j++];
1033     g_seenineRadius                   = params[j++];
1034 
1035     g_player[0].ps->max_ammo_amount[1] = params[j++];
1036     g_player[0].ps->max_ammo_amount[2] = params[j++];
1037     g_player[0].ps->max_ammo_amount[3] = params[j++];
1038     g_player[0].ps->max_ammo_amount[4] = params[j++];
1039     g_player[0].ps->max_ammo_amount[5] = params[j++];
1040     g_player[0].ps->max_ammo_amount[6] = params[j++];
1041     g_player[0].ps->max_ammo_amount[7] = params[j++];
1042     g_player[0].ps->max_ammo_amount[8] = params[j++];
1043     g_player[0].ps->max_ammo_amount[9] = params[j++];
1044     g_player[0].ps->max_ammo_amount[11] = params[j++];
1045 
1046     if (RR)
1047         g_player[0].ps->max_ammo_amount[12] = params[j++];
1048 
1049     g_damageCameras     = params[j++];
1050     g_numFreezeBounces  = params[j++];
1051     g_freezerSelfDamage = params[j++];
1052     g_deleteQueueSize   = clamp(params[j++], 0, 1024);
1053     g_tripbombLaserMode = params[j++];
1054 
1055     if (RRRA)
1056     {
1057         g_player[0].ps->max_ammo_amount[13] = params[j++];
1058         g_player[0].ps->max_ammo_amount[14] = params[j++];
1059         g_player[0].ps->max_ammo_amount[16] = params[j++];
1060     }
1061 }
1062 
C_DefineMusic(int volumeNum,int levelNum,const char * fileName)1063 void C_DefineMusic(int volumeNum, int levelNum, const char *fileName)
1064 {
1065     Bassert((unsigned)volumeNum < MAXVOLUMES+1);
1066     Bassert((unsigned)levelNum < MAXLEVELS);
1067 
1068     map_t *const pMapInfo = &g_mapInfo[(MAXLEVELS*volumeNum)+levelNum];
1069 
1070     Bfree(pMapInfo->musicfn);
1071     pMapInfo->musicfn = dup_filename(fileName);
1072     check_filename_case(pMapInfo->musicfn);
1073 }
1074 
C_DefineVolumeFlags(int32_t vol,int32_t flags)1075 void C_DefineVolumeFlags(int32_t vol, int32_t flags)
1076 {
1077     Bassert((unsigned)vol < MAXVOLUMES);
1078 
1079     g_volumeFlags[vol] = flags;
1080 }
1081 
C_AllocQuote(int32_t qnum)1082 int32_t C_AllocQuote(int32_t qnum)
1083 {
1084     Bassert((unsigned)qnum < MAXQUOTES);
1085 
1086     if (apStrings[qnum] == NULL)
1087     {
1088         apStrings[qnum] = (char *)Xcalloc(MAXQUOTELEN,sizeof(uint8_t));
1089         return 1;
1090     }
1091 
1092     return 0;
1093 }
1094 
1095 #ifndef EDUKE32_TOUCH_DEVICES
C_ReplaceQuoteSubstring(const size_t q,char const * const query,char const * const replacement)1096 static void C_ReplaceQuoteSubstring(const size_t q, char const * const query, char const * const replacement)
1097 {
1098     size_t querylength = Bstrlen(query);
1099 
1100     for (bssize_t i = MAXQUOTELEN - querylength - 2; i >= 0; i--)
1101         if (Bstrncmp(&apStrings[q][i], query, querylength) == 0)
1102         {
1103             Bmemset(tempbuf, 0, sizeof(tempbuf));
1104             Bstrncpy(tempbuf, apStrings[q], i);
1105             Bstrcat(tempbuf, replacement);
1106             Bstrcat(tempbuf, &apStrings[q][i + querylength]);
1107             Bstrncpy(apStrings[q], tempbuf, MAXQUOTELEN - 1);
1108             i = MAXQUOTELEN - querylength - 2;
1109         }
1110 }
1111 #endif
1112 
C_InitQuotes(void)1113 void C_InitQuotes(void)
1114 {
1115     for (bssize_t i = 0; i < 128; i++) C_AllocQuote(i);
1116 
1117 #ifdef EDUKE32_TOUCH_DEVICES
1118     apStrings[QUOTE_DEAD] = 0;
1119 #else
1120     char const * const OpenGameFunc = gamefunctions[gamefunc_Open];
1121     C_ReplaceQuoteSubstring(QUOTE_DEAD, "SPACE", OpenGameFunc);
1122     C_ReplaceQuoteSubstring(QUOTE_DEAD, "OPEN", OpenGameFunc);
1123     C_ReplaceQuoteSubstring(QUOTE_DEAD, "USE", OpenGameFunc);
1124 #endif
1125 
1126     // most of these are based on Blood, obviously
1127     const char *PlayerObituaries[] =
1128     {
1129         "^02%s^02 beat %s^02 like a cur",
1130         "^02%s^02 broke %s",
1131         "^02%s^02 body bagged %s",
1132         "^02%s^02 boned %s^02 like a fish",
1133         "^02%s^02 castrated %s",
1134         "^02%s^02 creamed %s",
1135         "^02%s^02 crushed %s",
1136         "^02%s^02 destroyed %s",
1137         "^02%s^02 diced %s",
1138         "^02%s^02 disemboweled %s",
1139         "^02%s^02 erased %s",
1140         "^02%s^02 eviscerated %s",
1141         "^02%s^02 flailed %s",
1142         "^02%s^02 flattened %s",
1143         "^02%s^02 gave AnAl MaDnEsS to %s",
1144         "^02%s^02 gave %s^02 Anal Justice",
1145         "^02%s^02 hosed %s",
1146         "^02%s^02 hurt %s^02 real bad",
1147         "^02%s^02 killed %s",
1148         "^02%s^02 made dog meat out of %s",
1149         "^02%s^02 made mincemeat out of %s",
1150         "^02%s^02 manhandled %s",
1151         "^02%s^02 massacred %s",
1152         "^02%s^02 mutilated %s",
1153         "^02%s^02 murdered %s",
1154         "^02%s^02 neutered %s",
1155         "^02%s^02 punted %s",
1156         "^02%s^02 reamed %s",
1157         "^02%s^02 ripped %s^02 a new orifice",
1158         "^02%s^02 rocked %s",
1159         "^02%s^02 sent %s^02 to hell",
1160         "^02%s^02 shredded %s",
1161         "^02%s^02 slashed %s",
1162         "^02%s^02 slaughtered %s",
1163         "^02%s^02 sliced %s",
1164         "^02%s^02 smacked %s around",
1165         "^02%s^02 smashed %s",
1166         "^02%s^02 snuffed %s",
1167         "^02%s^02 sodomized %s",
1168         "^02%s^02 splattered %s",
1169         "^02%s^02 sprayed %s",
1170         "^02%s^02 squashed %s",
1171         "^02%s^02 throttled %s",
1172         "^02%s^02 toasted %s",
1173         "^02%s^02 vented %s",
1174         "^02%s^02 ventilated %s",
1175         "^02%s^02 wasted %s",
1176         "^02%s^02 wrecked %s",
1177     };
1178 
1179     const char *PlayerSelfObituaries[] =
1180     {
1181         "^02%s^02 is excrement",
1182         "^02%s^02 is hamburger",
1183         "^02%s^02 suffered scrotum separation",
1184         "^02%s^02 volunteered for population control",
1185         "^02%s^02 has suicided",
1186         "^02%s^02 bled out",
1187     };
1188 
1189     EDUKE32_STATIC_ASSERT(OBITQUOTEINDEX + ARRAY_SIZE(PlayerObituaries)-1 < MAXQUOTES);
1190     EDUKE32_STATIC_ASSERT(SUICIDEQUOTEINDEX + ARRAY_SIZE(PlayerSelfObituaries)-1 < MAXQUOTES);
1191 
1192     g_numObituaries = ARRAY_SIZE(PlayerObituaries);
1193     for (bssize_t i = g_numObituaries - 1; i >= 0; i--)
1194     {
1195         if (C_AllocQuote(i + OBITQUOTEINDEX))
1196             Bstrcpy(apStrings[i + OBITQUOTEINDEX], PlayerObituaries[i]);
1197     }
1198 
1199     g_numSelfObituaries = ARRAY_SIZE(PlayerSelfObituaries);
1200     for (bssize_t i = g_numSelfObituaries - 1; i >= 0; i--)
1201     {
1202         if (C_AllocQuote(i + SUICIDEQUOTEINDEX))
1203             Bstrcpy(apStrings[i + SUICIDEQUOTEINDEX], PlayerSelfObituaries[i]);
1204     }
1205 }
1206 
C_BitOrNextValue(int32_t * valptr)1207 static inline void C_BitOrNextValue(int32_t *valptr)
1208 {
1209     C_GetNextValue(LABEL_DEFINE);
1210     g_scriptPtr--;
1211     *valptr |= *g_scriptPtr;
1212 }
1213 
C_FinishBitOr(int32_t value)1214 static inline void C_FinishBitOr(int32_t value)
1215 {
1216     BITPTR_CLEAR(g_scriptPtr-apScript);
1217     *g_scriptPtr++ = value;
1218 }
1219 
scriptUpdateOpcodeForVariableType(intptr_t * ins)1220 static void scriptUpdateOpcodeForVariableType(intptr_t *ins)
1221 {
1222     int opcode = -1;
1223 
1224     if (opcode != -1)
1225     {
1226         if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
1227         {
1228             initprintf("%s:%d: %s -> %s for var %s\n", g_scriptFileName, g_lineNumber,
1229                         VM_GetKeywordForID(*ins & VM_INSTMASK), VM_GetKeywordForID(opcode), aGameVars[ins[1] & (MAXGAMEVARS-1)].szLabel);
1230         }
1231 
1232         scriptWriteAtOffset(opcode | LINE_NUMBER, ins);
1233     }
1234 }
1235 
C_ParseCommand(int32_t loop)1236 static int32_t C_ParseCommand(int32_t loop)
1237 {
1238     int32_t i, j=0, k=0, tw;
1239 
1240     do
1241     {
1242         if (EDUKE32_PREDICT_FALSE(g_errorCnt > 63 || (*textptr == '\0') || (*(textptr+1) == '\0') || C_SkipComments()))
1243             return 1;
1244 
1245         if (EDUKE32_PREDICT_FALSE(g_scriptDebug))
1246             C_ReportError(-1);
1247 
1248         switch ((g_lastKeyword = tw = C_GetNextKeyword()))
1249         {
1250         default:
1251         case -1:
1252         case -2:
1253             return 1; //End
1254         case CON_STATE:
1255             if (!g_parsingActorPtr && g_processingState == 0)
1256             {
1257                 C_GetNextLabelName();
1258                 g_scriptPtr--;
1259                 labelcode[g_labelCnt] = g_scriptPtr-apScript;
1260                 labeltype[g_labelCnt] = LABEL_STATE;
1261 
1262                 g_processingState = 1;
1263                 Bsprintf(g_szCurrentBlockName,"%s",label+(g_labelCnt<<6));
1264 
1265                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,label+(g_labelCnt<<6))>=0))
1266                 {
1267                     g_errorCnt++;
1268                     C_ReportError(ERROR_ISAKEYWORD);
1269                     continue;
1270                 }
1271 
1272                 hash_add(&h_labels,label+(g_labelCnt<<6),g_labelCnt,0);
1273                 g_labelCnt++;
1274                 continue;
1275             }
1276 
1277             C_GetNextLabelName();
1278 
1279             if (EDUKE32_PREDICT_FALSE((j = hash_find(&h_labels,label+(g_labelCnt<<6))) < 0))
1280             {
1281                 C_ReportError(-1);
1282                 initprintf("%s:%d: error: state `%s' not found.\n",g_scriptFileName,g_lineNumber,label+(g_labelCnt<<6));
1283                 g_errorCnt++;
1284                 g_scriptPtr++;
1285                 continue;
1286             }
1287 
1288             if (EDUKE32_PREDICT_FALSE((labeltype[j] & LABEL_STATE) != LABEL_STATE))
1289             {
1290                 char *gl = (char *) C_GetLabelType(labeltype[j]);
1291                 C_ReportError(-1);
1292                 initprintf("%s:%d: warning: expected state, found %s.\n", g_scriptFileName, g_lineNumber, gl);
1293                 g_warningCnt++;
1294                 Bfree(gl);
1295                 *(g_scriptPtr-1) = CON_NULLOP; // get rid of the state, leaving a nullop to satisfy if conditions
1296                 BITPTR_CLEAR(g_scriptPtr-apScript-1);
1297                 continue;  // valid label name, but wrong type
1298             }
1299 
1300             if (!(g_errorCnt || g_warningCnt) && g_scriptDebug > 1)
1301                 initprintf("%s:%d: debug: state label `%s'.\n", g_scriptFileName, g_lineNumber, label+(j<<6));
1302             *g_scriptPtr = (intptr_t) (apScript+labelcode[j]);
1303 
1304             // 'state' type labels are always script addresses, as far as I can see
1305             BITPTR_SET(g_scriptPtr-apScript);
1306 
1307             g_scriptPtr++;
1308             continue;
1309 
1310         case CON_ENDS:
1311             if (EDUKE32_PREDICT_FALSE(g_processingState == 0))
1312             {
1313                 C_ReportError(-1);
1314                 initprintf("%s:%d: error: found `ends' without open `state'.\n",g_scriptFileName,g_lineNumber);
1315                 g_errorCnt++;
1316             }
1317             //            else
1318             {
1319                 if (EDUKE32_PREDICT_FALSE(g_numBraces > 0))
1320                 {
1321                     C_ReportError(ERROR_OPENBRACKET);
1322                     g_errorCnt++;
1323                 }
1324                 else if (EDUKE32_PREDICT_FALSE(g_numBraces < 0))
1325                 {
1326                     C_ReportError(ERROR_CLOSEBRACKET);
1327                     g_errorCnt++;
1328                 }
1329 
1330                 g_processingState = 0;
1331                 Bsprintf(g_szCurrentBlockName,"(none)");
1332             }
1333             continue;
1334 
1335         case CON_GAMEVAR:
1336         {
1337             // syntax: gamevar <var1> <initial value> <flags>
1338             // defines var1 and sets initial value.
1339             // flags are used to define usage
1340             // (see top of this files for flags)
1341 
1342             if (EDUKE32_PREDICT_FALSE(isdigit(*textptr) || (*textptr == '-')))
1343             {
1344                 g_errorCnt++;
1345                 C_ReportError(ERROR_SYNTAXERROR);
1346                 C_NextLine();
1347                 continue;
1348             }
1349 
1350             g_scriptPtr--;
1351 
1352             C_GetNextLabelName();
1353 
1354             if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords, LAST_LABEL)>=0))
1355             {
1356                 g_warningCnt++;
1357                 C_ReportError(WARNING_VARMASKSKEYWORD);
1358                 hash_delete(&h_keywords, LAST_LABEL);
1359             }
1360 
1361             int32_t defaultValue = 0;
1362             int32_t varFlags     = 0;
1363 
1364             if (C_GetKeyword() == -1)
1365             {
1366                 C_GetNextValue(LABEL_DEFINE); // get initial value
1367                 defaultValue = *(--g_scriptPtr);
1368 
1369                 j = 0;
1370 
1371                 while (C_GetKeyword() == -1)
1372                     C_BitOrNextValue(&j);
1373 
1374                 C_FinishBitOr(j);
1375                 varFlags = *(--g_scriptPtr);
1376 
1377                 if (EDUKE32_PREDICT_FALSE((*(g_scriptPtr)&GAMEVAR_USER_MASK)==(GAMEVAR_PERPLAYER|GAMEVAR_PERACTOR)))
1378                 {
1379                     g_warningCnt++;
1380                     varFlags ^= GAMEVAR_PERPLAYER;
1381                     C_ReportError(WARNING_BADGAMEVAR);
1382                 }
1383             }
1384 
1385             Gv_NewVar(LAST_LABEL, defaultValue, varFlags);
1386             continue;
1387         }
1388 
1389         case CON_DEFINE:
1390             {
1391                 C_GetNextLabelName();
1392 
1393                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,label+(g_labelCnt<<6))>=0))
1394                 {
1395                     g_errorCnt++;
1396                     C_ReportError(ERROR_ISAKEYWORD);
1397                     continue;
1398                 }
1399 
1400                 C_GetNextValue(LABEL_DEFINE);
1401 
1402                 i = hash_find(&h_labels,label+(g_labelCnt<<6));
1403                 if (i>=0)
1404                 {
1405                     // if (i >= g_numDefaultLabels)
1406 
1407                     if (EDUKE32_PREDICT_FALSE(labelcode[i] != *(g_scriptPtr-1)))
1408                     {
1409                         g_warningCnt++;
1410                         initprintf("%s:%d: warning: ignored redefinition of `%s' to %d (old: %d).\n",g_scriptFileName,
1411                                    g_lineNumber,label+(g_labelCnt<<6), (int32_t)(*(g_scriptPtr-1)), labelcode[i]);
1412                     }
1413                 }
1414                 else
1415                 {
1416                     hash_add(&h_labels,label+(g_labelCnt<<6),g_labelCnt,0);
1417                     labeltype[g_labelCnt] = LABEL_DEFINE;
1418                     labelcode[g_labelCnt++] = *(g_scriptPtr-1);
1419                     //if (*(g_scriptPtr-1) >= 0 && *(g_scriptPtr-1) < MAXTILES && g_dynamicTileMapping)
1420                     //    G_ProcessDynamicTileMapping(label+((g_labelCnt-1)<<6),*(g_scriptPtr-1));
1421                 }
1422                 g_scriptPtr -= 2;
1423                 continue;
1424             }
1425 
1426         case CON_PALFROM:
1427             for (j=3; j>=0; j--)
1428             {
1429                 if (C_GetKeyword() == -1)
1430                     C_GetNextValue(LABEL_DEFINE);
1431                 else break;
1432             }
1433 
1434             while (j>-1)
1435             {
1436                 BITPTR_CLEAR(g_scriptPtr-apScript);
1437                 *g_scriptPtr++ = 0;
1438                 j--;
1439             }
1440             continue;
1441 
1442         case CON_MOVE:
1443             if (g_parsingActorPtr || g_processingState)
1444             {
1445                 if (EDUKE32_PREDICT_FALSE((C_GetNextValue(LABEL_MOVE|LABEL_DEFINE) == 0) && (*(g_scriptPtr-1) != 0) && (*(g_scriptPtr-1) != 1)))
1446                 {
1447                     C_ReportError(-1);
1448                     BITPTR_CLEAR(g_scriptPtr-apScript-1);
1449                     *(g_scriptPtr-1) = 0;
1450                     initprintf("%s:%d: warning: expected a move, found a constant.\n",g_scriptFileName,g_lineNumber);
1451                     g_warningCnt++;
1452                 }
1453 
1454                 j = 0;
1455                 while (C_GetKeyword() == -1)
1456                     C_BitOrNextValue(&j);
1457 
1458                 C_FinishBitOr(j);
1459             }
1460             else
1461             {
1462                 g_scriptPtr--;
1463                 C_GetNextLabelName();
1464                 // Check to see it's already defined
1465 
1466                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,label+(g_labelCnt<<6))>=0))
1467                 {
1468                     g_errorCnt++;
1469                     C_ReportError(ERROR_ISAKEYWORD);
1470                     continue;
1471                 }
1472 
1473                 if (EDUKE32_PREDICT_FALSE((i = hash_find(&h_labels,label+(g_labelCnt<<6))) >= 0))
1474                 {
1475                     g_warningCnt++;
1476                     initprintf("%s:%d: warning: duplicate move `%s' ignored.\n",g_scriptFileName,g_lineNumber,label+(g_labelCnt<<6));
1477                 }
1478                 else
1479                 {
1480                     hash_add(&h_labels,label+(g_labelCnt<<6),g_labelCnt,0);
1481                     labeltype[g_labelCnt] = LABEL_MOVE;
1482                     labelcode[g_labelCnt++] = g_scriptPtr-apScript;
1483                 }
1484 
1485                 for (j=1; j>=0; j--)
1486                 {
1487                     if (C_GetKeyword() != -1) break;
1488                     C_GetNextValue(LABEL_DEFINE);
1489                 }
1490 
1491                 for (k=j; k>=0; k--)
1492                 {
1493                     BITPTR_CLEAR(g_scriptPtr-apScript);
1494                     *g_scriptPtr = 0;
1495                     g_scriptPtr++;
1496                 }
1497             }
1498             continue;
1499 
1500         case CON_MUSIC:
1501             {
1502                 // NOTE: this doesn't get stored in the PCode...
1503 
1504                 // music 1 stalker.mid dethtoll.mid streets.mid watrwld1.mid snake1.mid
1505                 //    thecall.mid ahgeez.mid dethtoll.mid streets.mid watrwld1.mid snake1.mid
1506                 g_scriptPtr--;
1507                 C_GetNextValue(LABEL_DEFINE); // Volume Number (0/4)
1508                 g_scriptPtr--;
1509 
1510                 k = *g_scriptPtr-1;  // 0-based volume number. -1 or MAXVOLUMES: "special"
1511                 if (k == -1)
1512                     k = MAXVOLUMES;
1513 
1514                 if (EDUKE32_PREDICT_FALSE((unsigned)k >= MAXVOLUMES+1)) // if it's not background or special music
1515                 {
1516                     g_errorCnt++;
1517                     C_ReportError(-1);
1518                     initprintf("%s:%d: error: volume number must be between 0 and MAXVOLUMES+1=%d.\n",
1519                                g_scriptFileName, g_lineNumber, MAXVOLUMES+1);
1520                     continue;
1521 
1522                 }
1523 
1524                 i = 0;
1525                 // get the file name...
1526                 while (C_GetKeyword() == -1)
1527                 {
1528                     C_SkipComments();
1529 
1530                     j = 0;
1531                     tempbuf[j] = '/';
1532                     while (isaltok(*(textptr+j)))
1533                     {
1534                         tempbuf[j+1] = textptr[j];
1535                         j++;
1536                     }
1537                     tempbuf[j+1] = '\0';
1538 
1539                     C_DefineMusic(k, i, tempbuf);
1540 
1541                     textptr += j;
1542 
1543                     if (i >= MAXLEVELS)
1544                         break;
1545                     i++;
1546                 }
1547             }
1548             continue;
1549 
1550         case CON_INCLUDE:
1551             g_scriptPtr--;
1552 
1553             C_SkipComments();
1554             while (isaltok(*textptr) == 0)
1555             {
1556                 textptr++;
1557                 if (*textptr == 0) break;
1558             }
1559 
1560             j = 0;
1561             while (isaltok(*textptr))
1562             {
1563                 tempbuf[j] = *(textptr++);
1564                 j++;
1565             }
1566             tempbuf[j] = '\0';
1567 
1568             C_Include(tempbuf);
1569             continue;
1570 
1571         case CON_AI:
1572             if (g_parsingActorPtr || g_processingState)
1573             {
1574                 C_GetNextValue(LABEL_AI);
1575             }
1576             else
1577             {
1578                 g_scriptPtr--;
1579                 C_GetNextLabelName();
1580 
1581                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,label+(g_labelCnt<<6))>=0))
1582                 {
1583                     g_errorCnt++;
1584                     C_ReportError(ERROR_ISAKEYWORD);
1585                     continue;
1586                 }
1587 
1588                 i = hash_find(&h_labels,label+(g_labelCnt<<6));
1589                 if (EDUKE32_PREDICT_FALSE(i>=0))
1590                 {
1591                     g_warningCnt++;
1592                     initprintf("%s:%d: warning: duplicate ai `%s' ignored.\n",g_scriptFileName,g_lineNumber,label+(g_labelCnt<<6));
1593                 }
1594                 else
1595                 {
1596                     labeltype[g_labelCnt] = LABEL_AI;
1597                     hash_add(&h_labels,label+(g_labelCnt<<6),g_labelCnt,0);
1598                     labelcode[g_labelCnt++] = g_scriptPtr-apScript;
1599                 }
1600 
1601                 for (j=0; j<3; j++)
1602                 {
1603                     if (C_GetKeyword() != -1) break;
1604                     if (j == 1)
1605                         C_GetNextValue(LABEL_ACTION);
1606                     else if (j == 2)
1607                     {
1608                         if (EDUKE32_PREDICT_FALSE((C_GetNextValue(LABEL_MOVE|LABEL_DEFINE) == 0) &&
1609                             (*(g_scriptPtr-1) != 0) && (*(g_scriptPtr-1) != 1)))
1610                         {
1611                             C_ReportError(-1);
1612                             BITPTR_CLEAR(g_scriptPtr-apScript-1);
1613                             *(g_scriptPtr-1) = 0;
1614                             initprintf("%s:%d: warning: expected a move, found a constant.\n",g_scriptFileName,g_lineNumber);
1615                             g_warningCnt++;
1616                         }
1617 
1618                         k = 0;
1619                         while (C_GetKeyword() == -1)
1620                             C_BitOrNextValue(&k);
1621 
1622                         C_FinishBitOr(k);
1623                         j = 666;
1624                         break;
1625                     }
1626                 }
1627 
1628                 if (j == 666)
1629                     continue;
1630 
1631                 for (k=j; k<3; k++)
1632                 {
1633                     BITPTR_CLEAR(g_scriptPtr-apScript);
1634                     *g_scriptPtr = 0;
1635                     g_scriptPtr++;
1636                 }
1637             }
1638             continue;
1639 
1640         case CON_ACTION:
1641             if (g_parsingActorPtr || g_processingState)
1642             {
1643                 C_GetNextValue(LABEL_ACTION);
1644             }
1645             else
1646             {
1647                 g_scriptPtr--;
1648                 C_GetNextLabelName();
1649                 // Check to see it's already defined
1650 
1651                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,label+(g_labelCnt<<6))>=0))
1652                 {
1653                     g_errorCnt++;
1654                     C_ReportError(ERROR_ISAKEYWORD);
1655                     continue;
1656                 }
1657 
1658                 i = hash_find(&h_labels,label+(g_labelCnt<<6));
1659                 if (EDUKE32_PREDICT_FALSE(i>=0))
1660                 {
1661                     g_warningCnt++;
1662                     initprintf("%s:%d: warning: duplicate action `%s' ignored.\n",g_scriptFileName,g_lineNumber,label+(g_labelCnt<<6));
1663                 }
1664                 else
1665                 {
1666                     labeltype[g_labelCnt] = LABEL_ACTION;
1667                     labelcode[g_labelCnt] = g_scriptPtr-apScript;
1668                     hash_add(&h_labels,label+(g_labelCnt<<6),g_labelCnt,0);
1669                     g_labelCnt++;
1670                 }
1671 
1672                 for (j=ACTION_PARAM_COUNT-1; j>=0; j--)
1673                 {
1674                     if (C_GetKeyword() != -1) break;
1675                     C_GetNextValue(LABEL_DEFINE);
1676                 }
1677                 for (k=j; k>=0; k--)
1678                 {
1679                     BITPTR_CLEAR(g_scriptPtr-apScript);
1680                     *(g_scriptPtr++) = 0;
1681                 }
1682             }
1683             continue;
1684 
1685         case CON_ACTOR:
1686         case CON_USERACTOR:
1687             if (EDUKE32_PREDICT_FALSE(g_processingState || g_parsingActorPtr))
1688             {
1689                 C_ReportError(ERROR_FOUNDWITHIN);
1690                 g_errorCnt++;
1691             }
1692 
1693             g_numBraces = 0;
1694             g_scriptPtr--;
1695             g_parsingActorPtr = g_scriptPtr - apScript;
1696 
1697             if (tw == CON_USERACTOR)
1698             {
1699                 C_GetNextValue(LABEL_DEFINE);
1700                 g_scriptPtr--;
1701             }
1702 
1703             // save the actor name w/o consuming it
1704             C_SkipComments();
1705             j = 0;
1706             while (isaltok(*(textptr+j)))
1707             {
1708                 g_szCurrentBlockName[j] = textptr[j];
1709                 j++;
1710             }
1711             g_szCurrentBlockName[j] = 0;
1712 
1713             j = hash_find(&h_labels, g_szCurrentBlockName);
1714 
1715             if (j != -1)
1716                 labeltype[j] |= LABEL_ACTOR;
1717 
1718             if (tw == CON_USERACTOR)
1719             {
1720                 j = *g_scriptPtr;
1721 
1722                 if (EDUKE32_PREDICT_FALSE(j >= 3))
1723                 {
1724                     C_ReportError(-1);
1725                     initprintf("%s:%d: warning: invalid useractor type. Must be 0, 1, 2"
1726                                " (notenemy, enemy, enemystayput).\n",
1727                                g_scriptFileName,g_lineNumber);
1728                     g_warningCnt++;
1729                     j = 0;
1730                 }
1731             }
1732 
1733             C_GetNextValue(LABEL_ACTOR);
1734             g_scriptPtr--;
1735 
1736             if (EDUKE32_PREDICT_FALSE((unsigned)*g_scriptPtr >= MAXTILES))
1737             {
1738                 C_ReportError(ERROR_EXCEEDSMAXTILES);
1739                 g_errorCnt++;
1740                 continue;
1741             }
1742 
1743             g_tile[*g_scriptPtr].execPtr = apScript + g_parsingActorPtr;
1744 
1745             if (tw == CON_USERACTOR)
1746             {
1747                 if (j & 1)
1748                     g_tile[*g_scriptPtr].flags |= SFLAG_BADGUY;
1749 
1750                 if (j & 2)
1751                     g_tile[*g_scriptPtr].flags |= (SFLAG_BADGUY|SFLAG_BADGUYSTAYPUT);
1752             }
1753 
1754             for (j=0; j<4; j++)
1755             {
1756                 BITPTR_CLEAR(g_parsingActorPtr+j);
1757                 *((apScript+j)+g_parsingActorPtr) = 0;
1758                 if (j == 3)
1759                 {
1760                     j = 0;
1761                     while (C_GetKeyword() == -1)
1762                         C_BitOrNextValue(&j);
1763 
1764                     C_FinishBitOr(j);
1765                     break;
1766                 }
1767                 else
1768                 {
1769                     if (C_GetKeyword() != -1)
1770                     {
1771                         for (i=4-j; i; i--)
1772                         {
1773                             BITPTR_CLEAR(g_scriptPtr-apScript);
1774                             *(g_scriptPtr++) = 0;
1775                         }
1776                         break;
1777                     }
1778                     switch (j)
1779                     {
1780                     case 0:
1781                         C_GetNextValue(LABEL_DEFINE);
1782                         break;
1783                     case 1:
1784                         C_GetNextValue(LABEL_ACTION);
1785                         break;
1786                     case 2:
1787                         // XXX: LABEL_MOVE|LABEL_DEFINE, what is this shit? compatibility?
1788                         // yep, it sure is :(
1789                         if (EDUKE32_PREDICT_FALSE((C_GetNextValue(LABEL_MOVE|LABEL_DEFINE) == 0) && (*(g_scriptPtr-1) != 0) && (*(g_scriptPtr-1) != 1)))
1790                         {
1791                             C_ReportError(-1);
1792                             BITPTR_CLEAR(g_scriptPtr-apScript-1);
1793                             *(g_scriptPtr-1) = 0;
1794                             initprintf("%s:%d: warning: expected a move, found a constant.\n",g_scriptFileName,g_lineNumber);
1795                             g_warningCnt++;
1796                         }
1797                         break;
1798                     }
1799                     if (*(g_scriptPtr-1) >= (intptr_t)&apScript[0] && *(g_scriptPtr-1) < (intptr_t)&apScript[g_scriptSize])
1800                         BITPTR_SET(g_parsingActorPtr+j);
1801                     else BITPTR_CLEAR(g_parsingActorPtr+j);
1802                     *((apScript+j)+g_parsingActorPtr) = *(g_scriptPtr-1);
1803                 }
1804             }
1805             g_checkingIfElse = 0;
1806             continue;
1807 
1808         case CON_ONEVENT:
1809             if (EDUKE32_PREDICT_FALSE(g_processingState || g_parsingActorPtr))
1810             {
1811                 C_ReportError(ERROR_FOUNDWITHIN);
1812                 g_errorCnt++;
1813             }
1814 
1815             g_numBraces = 0;
1816             g_scriptPtr--;
1817             g_scriptEventOffset = g_parsingActorPtr = g_scriptPtr - apScript;
1818 
1819             C_SkipComments();
1820             j = 0;
1821             while (isaltok(*(textptr+j)))
1822             {
1823                 g_szCurrentBlockName[j] = textptr[j];
1824                 j++;
1825             }
1826             g_szCurrentBlockName[j] = 0;
1827             //        g_labelsOnly = 1;
1828             C_GetNextValue(LABEL_DEFINE);
1829             g_labelsOnly = 0;
1830             g_scriptPtr--;
1831             j= *g_scriptPtr;  // type of event
1832             g_currentEvent = j;
1833             //Bsprintf(g_szBuf,"Adding Event for %d at %lX",j, g_parsingEventPtr);
1834             //AddLog(g_szBuf);
1835             if (EDUKE32_PREDICT_FALSE((unsigned)j > MAXEVENTS-1))
1836             {
1837                 initprintf("%s:%d: error: invalid event ID.\n",g_scriptFileName,g_lineNumber);
1838                 g_errorCnt++;
1839                 continue;
1840             }
1841             // if event has already been declared then store previous script location
1842             apScriptEvents[j] = g_scriptEventOffset;
1843 
1844             g_checkingIfElse = 0;
1845 
1846             continue;
1847 
1848         case CON_CSTAT:
1849             C_GetNextValue(LABEL_DEFINE);
1850 
1851             if (EDUKE32_PREDICT_FALSE(*(g_scriptPtr-1) == 32767))
1852             {
1853                 *(g_scriptPtr-1) = 32768;
1854                 C_ReportError(-1);
1855                 initprintf("%s:%d: warning: tried to set cstat 32767, using 32768 instead.\n",g_scriptFileName,g_lineNumber);
1856                 g_warningCnt++;
1857             }
1858             else if (EDUKE32_PREDICT_FALSE((*(g_scriptPtr-1) & 48) == 48))
1859             {
1860                 i = *(g_scriptPtr-1);
1861                 *(g_scriptPtr-1) ^= 48;
1862                 C_ReportError(-1);
1863                 initprintf("%s:%d: warning: tried to set cstat %d, using %d instead.\n",g_scriptFileName,g_lineNumber,i,(int32_t)(*(g_scriptPtr-1)));
1864                 g_warningCnt++;
1865             }
1866             continue;
1867 
1868         case CON_HITRADIUS:
1869             C_GetNextValue(LABEL_DEFINE);
1870             C_GetNextValue(LABEL_DEFINE);
1871             C_GetNextValue(LABEL_DEFINE);
1872             fallthrough__;
1873         case CON_ADDAMMO:
1874         case CON_ADDWEAPON:
1875         case CON_SIZETO:
1876         case CON_SIZEAT:
1877         case CON_DEBRIS:
1878         case CON_ADDINVENTORY:
1879         case CON_GUTS:
1880             C_GetNextValue(LABEL_DEFINE);
1881             fallthrough__;
1882         case CON_STRENGTH:
1883         case CON_SHOOT:
1884         case CON_ADDPHEALTH:
1885         case CON_SPAWN:
1886         case CON_COUNT:
1887         case CON_ENDOFGAME:
1888         case CON_SPRITEPAL:
1889         case CON_CACTOR:
1890         case CON_MONEY:
1891         case CON_ADDKILLS:
1892         case CON_DEBUG:
1893         case CON_ADDSTRENGTH:
1894         case CON_CSTATOR:
1895         case CON_MAIL:
1896         case CON_PAPER:
1897         case CON_SLEEPTIME:
1898         case CON_CLIPDIST:
1899         case CON_ISDRUNK:
1900         case CON_ISEAT:
1901         case CON_NEWPIC:
1902         case CON_LOTSOFGLASS:
1903         case CON_QUOTE:
1904         case CON_SOUND:
1905         case CON_GLOBALSOUND:
1906         case CON_SOUNDONCE:
1907         case CON_STOPSOUND:
1908             C_GetNextValue(LABEL_DEFINE);
1909             continue;
1910 
1911         case CON_ELSE:
1912             {
1913                 if (EDUKE32_PREDICT_FALSE(!g_checkingIfElse))
1914                 {
1915                     g_scriptPtr--;
1916                     intptr_t *tempscrptr = g_scriptPtr;
1917                     g_warningCnt++;
1918                     C_ReportError(-1);
1919 
1920                     initprintf("%s:%d: warning: found `else' with no `if'.\n", g_scriptFileName, g_lineNumber);
1921 
1922                     if (C_GetKeyword() == CON_LEFTBRACE)
1923                     {
1924                         C_GetNextKeyword();
1925                         g_numBraces++;
1926 
1927                         C_ParseCommand(1);
1928                     }
1929                     else C_ParseCommand(0);
1930 
1931                     g_scriptPtr = tempscrptr;
1932 
1933                     continue;
1934                 }
1935 
1936                 intptr_t const lastScriptPtr = g_scriptPtr - apScript - 1;
1937 
1938                 g_skipBranch = 0;
1939                 g_checkingIfElse--;
1940 
1941                 if (C_CheckMalformedBranch(lastScriptPtr))
1942                     continue;
1943 
1944                 intptr_t const offset = (unsigned) (g_scriptPtr-apScript);
1945 
1946                 g_scriptPtr++; //Leave a spot for the fail location
1947 
1948                 if (!g_gotComment)
1949                     C_ParseCommand(0);
1950 
1951                 if (C_CheckEmptyBranch(tw, lastScriptPtr))
1952                     continue;
1953 
1954                 intptr_t *tempscrptr = (intptr_t *) apScript+offset;
1955                 *tempscrptr = (intptr_t) g_scriptPtr;
1956                 BITPTR_SET(tempscrptr-apScript);
1957 
1958                 continue;
1959             }
1960         case CON_ADDLOGVAR:
1961             g_labelsOnly = 1;
1962             C_GetNextVar();
1963             g_labelsOnly = 0;
1964             continue;
1965         case CON_ADDVAR:
1966         case CON_SETVAR:
1967 setvar:
1968         {
1969             auto ins = &g_scriptPtr[-1];
1970 
1971             C_GetNextVarType(GAMEVAR_READONLY);
1972             C_GetNextValue(LABEL_DEFINE);
1973             // replace instructions with special versions for specific var types
1974             scriptUpdateOpcodeForVariableType(ins);
1975             continue;
1976         }
1977         case CON_ADDVARVAR:
1978         case CON_SETVARVAR:
1979             {
1980 setvarvar:
1981                 auto ins = &g_scriptPtr[-1];
1982                 auto tptr = textptr;
1983                 int const lnum = g_lineNumber;
1984 
1985                 C_GetNextVarType(GAMEVAR_READONLY);
1986                 C_GetNextVar();
1987 
1988                 int const opcode = inthash_find(&h_varvar, *ins & VM_INSTMASK);
1989 
1990                 if (ins[2] == GV_FLAG_CONSTANT && opcode != -1)
1991                 {
1992                     if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
1993                     {
1994                         initprintf("%s:%d: %s -> %s\n", g_scriptFileName, g_lineNumber,
1995                                     VM_GetKeywordForID(*ins & VM_INSTMASK), VM_GetKeywordForID(opcode));
1996                     }
1997 
1998                     tw = opcode;
1999                     scriptWriteAtOffset(opcode | LINE_NUMBER, ins);
2000                     g_scriptPtr = &ins[1];
2001                     textptr = tptr;
2002                     g_lineNumber = lnum;
2003                     goto setvar;
2004                 }
2005 
2006                 continue;
2007             }
2008 
2009         case CON_IFVARVARE:
2010         case CON_IFVARVARG:
2011         case CON_IFVARVARL:
2012             {
2013                 auto const ins = &g_scriptPtr[-1];
2014                 auto const lastScriptPtr = &g_scriptPtr[-1] - apScript;
2015                 auto const lasttextptr = textptr;
2016                 int const lnum = g_lineNumber;
2017 
2018                 g_skipBranch = false;
2019 
2020                 C_GetNextVar();
2021                 auto const var = g_scriptPtr;
2022                 C_GetNextVar();
2023 
2024                 if (*var == GV_FLAG_CONSTANT)
2025                 {
2026                     int const opcode = inthash_find(&h_varvar, tw);
2027 
2028                     if (opcode != -1)
2029                     {
2030                         if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
2031                         {
2032                             initprintf("%s:%d: replacing %s with %s\n", g_scriptFileName, g_lineNumber,
2033                                        VM_GetKeywordForID(*ins & VM_INSTMASK), VM_GetKeywordForID(opcode));
2034                         }
2035 
2036                         scriptWriteAtOffset(opcode | LINE_NUMBER, ins);
2037                         tw = opcode;
2038                         g_scriptPtr = &ins[1];
2039                         textptr = lasttextptr;
2040                         g_lineNumber = lnum;
2041                         goto ifvar;
2042                     }
2043                 }
2044 
2045                 if (C_CheckMalformedBranch(lastScriptPtr))
2046                     continue;
2047 
2048                 auto const offset = g_scriptPtr - apScript;
2049                 g_scriptPtr++; // Leave a spot for the fail location
2050 
2051                 C_ParseCommand(0);
2052 
2053                 if (C_CheckEmptyBranch(tw, lastScriptPtr))
2054                     continue;
2055 
2056                 auto const tempscrptr = apScript + offset;
2057                 scriptWritePointer((intptr_t)g_scriptPtr, tempscrptr);
2058                 continue;
2059             }
2060 
2061         case CON_IFVARE:
2062         case CON_IFVARG:
2063         case CON_IFVARL:
2064             {
2065 ifvar:
2066                 auto const ins = &g_scriptPtr[-1];
2067                 auto const lastScriptPtr = &g_scriptPtr[-1] - apScript;
2068 
2069                 g_skipBranch = false;
2070 
2071                 C_GetNextVar();
2072                 C_GetNextValue(LABEL_DEFINE);
2073 
2074                 if (C_CheckMalformedBranch(lastScriptPtr))
2075                     continue;
2076 
2077                 scriptUpdateOpcodeForVariableType(ins);
2078 
2079                 auto const offset = g_scriptPtr - apScript;
2080                 g_scriptPtr++; //Leave a spot for the fail location
2081 
2082                 C_ParseCommand(0);
2083 
2084                 if (C_CheckEmptyBranch(tw, lastScriptPtr))
2085                     continue;
2086 
2087                 auto const tempscrptr = apScript + offset;
2088                 scriptWritePointer((intptr_t)g_scriptPtr, tempscrptr);
2089 
2090                 j = C_GetKeyword();
2091 
2092                 if (j == CON_ELSE)
2093                     g_checkingIfElse++;
2094 
2095                 continue;
2096             }
2097 
2098         case CON_IFRND:
2099         case CON_IFPDISTL:
2100         case CON_IFPDISTG:
2101         case CON_IFWASWEAPON:
2102         case CON_IFACTIONCOUNT:
2103         case CON_IFCOUNT:
2104         case CON_IFACTOR:
2105         case CON_IFSTRENGTH:
2106         case CON_IFSPAWNEDBY:
2107         case CON_IFGAPZL:
2108         case CON_IFFLOORDISTL:
2109         case CON_IFCEILINGDISTL:
2110         case CON_IFPHEALTHL:
2111         case CON_IFSPRITEPAL:
2112         case CON_IFGOTWEAPONCE:
2113         case CON_IFANGDIFFL:
2114         case CON_IFACTORHEALTHG:
2115         case CON_IFACTORHEALTHL:
2116         case CON_IFSOUNDID:
2117         case CON_IFSOUNDDIST:
2118         case CON_IFAI:
2119         case CON_IFACTION:
2120         case CON_IFMOVE:
2121         case CON_IFP:
2122         case CON_IFPINVENTORY:
2123             {
2124                 intptr_t offset;
2125                 intptr_t lastScriptPtr = (g_scriptPtr-&apScript[0]-1);
2126 
2127                 g_skipBranch = 0;
2128 
2129                 switch (tw)
2130                 {
2131                 case CON_IFAI:
2132                     C_GetNextValue(LABEL_AI);
2133                     break;
2134                 case CON_IFACTION:
2135                     C_GetNextValue(LABEL_ACTION);
2136                     break;
2137                 case CON_IFMOVE:
2138                     if (EDUKE32_PREDICT_FALSE((C_GetNextValue(LABEL_MOVE|LABEL_DEFINE) == 0) && (*(g_scriptPtr-1) != 0) && (*(g_scriptPtr-1) != 1)))
2139                     {
2140                         C_ReportError(-1);
2141                         *(g_scriptPtr-1) = 0;
2142                         initprintf("%s:%d: warning: expected a move, found a constant.\n",g_scriptFileName,g_lineNumber);
2143                         g_warningCnt++;
2144                     }
2145                     break;
2146                 case CON_IFPINVENTORY:
2147                     C_GetNextValue(LABEL_DEFINE);
2148                     C_GetNextValue(LABEL_DEFINE);
2149                     break;
2150                 case CON_IFP:
2151                     j = 0;
2152                     do
2153                         C_BitOrNextValue(&j);
2154                     while (C_GetKeyword() == -1);
2155                     C_FinishBitOr(j);
2156                     break;
2157                 default:
2158                     C_GetNextValue(LABEL_DEFINE);
2159                     break;
2160                 }
2161 
2162                 if (C_CheckMalformedBranch(lastScriptPtr))
2163                     continue;
2164 
2165                 intptr_t *tempscrptr = g_scriptPtr;
2166                 offset = (unsigned)(tempscrptr-apScript);
2167 
2168                 g_scriptPtr++; //Leave a spot for the fail location
2169 
2170                 C_ParseCommand(0);
2171 
2172                 if (C_CheckEmptyBranch(tw, lastScriptPtr))
2173                     continue;
2174 
2175                 tempscrptr = (intptr_t *)apScript+offset;
2176                 *tempscrptr = (intptr_t) g_scriptPtr;
2177                 BITPTR_SET(tempscrptr-apScript);
2178 
2179                 j = C_GetKeyword();
2180 
2181                 if (j == CON_ELSE || j == CON_LEFTBRACE)
2182                     g_checkingIfElse++;
2183 
2184                 continue;
2185             }
2186 
2187         case CON_IFONWATER:
2188         case CON_IFINWATER:
2189         case CON_IFACTORNOTSTAYPUT:
2190         case CON_IFCANSEE:
2191         case CON_IFHITWEAPON:
2192         case CON_IFSQUISHED:
2193         case CON_IFDEAD:
2194         case CON_IFCANSHOOTTARGET:
2195         case CON_IFHITSPACE:
2196         case CON_IFOUTSIDE:
2197         case CON_IFMULTIPLAYER:
2198         case CON_IFINSPACE:
2199         case CON_IFBULLETNEAR:
2200         case CON_IFRESPAWN:
2201         case CON_IFINOUTERSPACE:
2202         case CON_IFNOTMOVING:
2203         case CON_IFAWAYFROMWALL:
2204         case CON_IFCANSEETARGET:
2205         case CON_IFNOSOUNDS:
2206         case CON_IFNOCOVER:
2207         case CON_IFHITTRUCK:
2208         case CON_IFTIPCOW:
2209         case CON_IFONMUD:
2210         case CON_IFCOOP:
2211         case CON_IFMOTOFAST:
2212         case CON_IFWIND:
2213         case CON_IFONMOTO:
2214         case CON_IFONBOAT:
2215         case CON_IFSIZEDOWN:
2216         case CON_IFFINDNEWSPOT:
2217         case CON_IFPUPWIND:
2218             {
2219                 intptr_t offset;
2220                 intptr_t lastScriptPtr = (g_scriptPtr-&apScript[0]-1);
2221 
2222                 g_skipBranch = 0;
2223 
2224                 if (C_CheckMalformedBranch(lastScriptPtr))
2225                     continue;
2226 
2227                 intptr_t *tempscrptr = g_scriptPtr;
2228                 offset = (unsigned)(tempscrptr-apScript);
2229 
2230                 g_scriptPtr++; //Leave a spot for the fail location
2231 
2232                 C_ParseCommand(0);
2233 
2234                 if (C_CheckEmptyBranch(tw, lastScriptPtr))
2235                     continue;
2236 
2237                 tempscrptr = (intptr_t *)apScript+offset;
2238                 *tempscrptr = (intptr_t) g_scriptPtr;
2239                 BITPTR_SET(tempscrptr-apScript);
2240 
2241                 j = C_GetKeyword();
2242 
2243                 if (j == CON_ELSE || j == CON_LEFTBRACE)
2244                     g_checkingIfElse++;
2245 
2246                 continue;
2247             }
2248 
2249         case CON_LEFTBRACE:
2250             if (EDUKE32_PREDICT_FALSE(!(g_processingState || g_parsingActorPtr || g_scriptEventOffset)))
2251             {
2252                 g_errorCnt++;
2253                 C_ReportError(ERROR_SYNTAXERROR);
2254             }
2255             g_numBraces++;
2256 
2257             C_ParseCommand(1);
2258             continue;
2259 
2260         case CON_RIGHTBRACE:
2261             g_numBraces--;
2262 
2263             if ((*(g_scriptPtr-2)>>12) == (IFELSE_MAGIC) &&
2264                 ((*(g_scriptPtr-2) & VM_INSTMASK) == CON_LEFTBRACE)) // rewrite "{ }" into "nullop"
2265             {
2266                 //            initprintf("%s:%d: rewriting empty braces '{ }' as 'nullop' from right\n",g_szScriptFileName,g_lineNumber);
2267                 *(g_scriptPtr-2) = CON_NULLOP + (IFELSE_MAGIC<<12);
2268                 g_scriptPtr -= 2;
2269 
2270                 if (C_GetKeyword() != CON_ELSE && (*(g_scriptPtr-2) & VM_INSTMASK) != CON_ELSE)
2271                     g_skipBranch = 1;
2272                 else g_skipBranch = 0;
2273 
2274                 j = C_GetKeyword();
2275 
2276                 if (g_checkingIfElse && j != CON_ELSE)
2277                     g_checkingIfElse--;
2278 
2279                 return 1;
2280             }
2281 
2282             if (EDUKE32_PREDICT_FALSE(g_numBraces < 0))
2283             {
2284                 C_ReportError(-1);
2285                 initprintf("%s:%d: error: found more `}' than `{'.\n",g_scriptFileName,g_lineNumber);
2286                 g_errorCnt++;
2287             }
2288 
2289             if (g_checkingIfElse && j != CON_ELSE)
2290                 g_checkingIfElse--;
2291 
2292             return 1;
2293 
2294         case CON_BETANAME:
2295             g_scriptPtr--;
2296             j = 0;
2297             C_NextLine();
2298             continue;
2299 
2300         case CON_DEFINEVOLUMENAME:
2301             g_scriptPtr--;
2302 
2303             C_GetNextValue(LABEL_DEFINE);
2304             g_scriptPtr--;
2305             j = *g_scriptPtr;
2306 
2307             C_SkipSpace();
2308 
2309             if (EDUKE32_PREDICT_FALSE((unsigned)j > MAXVOLUMES-1))
2310             {
2311                 initprintf("%s:%d: error: volume number exceeds maximum volume count.\n",
2312                     g_scriptFileName,g_lineNumber);
2313                 g_errorCnt++;
2314                 C_NextLine();
2315                 continue;
2316             }
2317 
2318             i = 0;
2319 
2320             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
2321             {
2322                 g_volumeNames[j][i] = *textptr;
2323                 textptr++,i++;
2324                 if (EDUKE32_PREDICT_FALSE(i >= (signed)sizeof(g_volumeNames[j])-1))
2325                 {
2326                     initprintf("%s:%d: warning: truncating volume name to %d characters.\n",
2327                         g_scriptFileName,g_lineNumber,(int32_t)sizeof(g_volumeNames[j])-1);
2328                     g_warningCnt++;
2329                     C_NextLine();
2330                     break;
2331                 }
2332             }
2333             g_volumeCnt = j+1;
2334             g_volumeNames[j][i] = '\0';
2335             continue;
2336 
2337         case CON_DEFINESKILLNAME:
2338             g_scriptPtr--;
2339 
2340             C_GetNextValue(LABEL_DEFINE);
2341             g_scriptPtr--;
2342             j = *g_scriptPtr;
2343 
2344             C_SkipSpace();
2345 
2346             if (EDUKE32_PREDICT_FALSE((unsigned)j >= MAXSKILLS))
2347             {
2348                 initprintf("%s:%d: error: skill number exceeds maximum skill count %d.\n",
2349                            g_scriptFileName,g_lineNumber, MAXSKILLS);
2350                 g_errorCnt++;
2351                 C_NextLine();
2352                 continue;
2353             }
2354 
2355             i = 0;
2356 
2357             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
2358             {
2359                 g_skillNames[j][i] = *textptr;
2360                 textptr++,i++;
2361                 if (EDUKE32_PREDICT_FALSE(i >= (signed)sizeof(g_skillNames[j])-1))
2362                 {
2363                     initprintf("%s:%d: warning: truncating skill name to %d characters.\n",
2364                         g_scriptFileName,g_lineNumber,(int32_t)sizeof(g_skillNames[j])-1);
2365                     g_warningCnt++;
2366                     C_NextLine();
2367                     break;
2368                 }
2369             }
2370 
2371             g_skillNames[j][i] = '\0';
2372 
2373             for (i=0; i<MAXSKILLS; i++)
2374                 if (g_skillNames[i][0] == 0)
2375                     break;
2376             g_skillCnt = i;
2377 
2378             continue;
2379 
2380         case CON_DEFINELEVELNAME:
2381             g_scriptPtr--;
2382             C_GetNextValue(LABEL_DEFINE);
2383             g_scriptPtr--;
2384             j = *g_scriptPtr;
2385             C_GetNextValue(LABEL_DEFINE);
2386             g_scriptPtr--;
2387             k = *g_scriptPtr;
2388             C_SkipComments();
2389 
2390             if (EDUKE32_PREDICT_FALSE((unsigned)j > MAXVOLUMES-1))
2391             {
2392                 initprintf("%s:%d: error: volume number exceeds maximum volume count.\n",g_scriptFileName,g_lineNumber);
2393                 g_errorCnt++;
2394                 C_NextLine();
2395                 continue;
2396             }
2397             if (EDUKE32_PREDICT_FALSE((unsigned)k > MAXLEVELS-1))
2398             {
2399                 initprintf("%s:%d: error: level number exceeds maximum number of levels per episode.\n",g_scriptFileName,g_lineNumber);
2400                 g_errorCnt++;
2401                 C_NextLine();
2402                 continue;
2403             }
2404 
2405             i = 0;
2406 
2407             tempbuf[i] = '/';
2408 
2409             while (*textptr != ' ' && *textptr != '\t' && *textptr != 0x0a)
2410             {
2411                 tempbuf[i+1] = *textptr;
2412                 textptr++,i++;
2413                 if (EDUKE32_PREDICT_FALSE(i >= BMAX_PATH))
2414                 {
2415                     initprintf("%s:%d: error: level file name exceeds limit of %d characters.\n",g_scriptFileName,g_lineNumber,BMAX_PATH);
2416                     g_errorCnt++;
2417                     C_SkipSpace();
2418                     break;
2419                 }
2420             }
2421             tempbuf[i+1] = '\0';
2422 
2423             Bcorrectfilename(tempbuf,0);
2424 
2425             if (g_mapInfo[j *MAXLEVELS+k].filename == NULL)
2426                 g_mapInfo[j *MAXLEVELS+k].filename = (char *)Xcalloc(Bstrlen(tempbuf)+1,sizeof(uint8_t));
2427             else if ((Bstrlen(tempbuf)+1) > sizeof(g_mapInfo[j*MAXLEVELS+k].filename))
2428                 g_mapInfo[j *MAXLEVELS+k].filename = (char *)Xrealloc(g_mapInfo[j*MAXLEVELS+k].filename,(Bstrlen(tempbuf)+1));
2429 
2430             Bstrcpy(g_mapInfo[j*MAXLEVELS+k].filename,tempbuf);
2431 
2432             C_SkipComments();
2433 
2434             g_mapInfo[j *MAXLEVELS+k].partime =
2435                 (((*(textptr+0)-'0')*10+(*(textptr+1)-'0'))*REALGAMETICSPERSEC*60)+
2436                 (((*(textptr+3)-'0')*10+(*(textptr+4)-'0'))*REALGAMETICSPERSEC);
2437 
2438             textptr += 5;
2439             C_SkipSpace();
2440 
2441             // cheap hack, 0.99 doesn't have the 3D Realms time
2442             if (*(textptr+2) == ':')
2443             {
2444                 g_mapInfo[j *MAXLEVELS+k].designertime =
2445                     (((*(textptr+0)-'0')*10+(*(textptr+1)-'0'))*REALGAMETICSPERSEC*60)+
2446                     (((*(textptr+3)-'0')*10+(*(textptr+4)-'0'))*REALGAMETICSPERSEC);
2447 
2448                 textptr += 5;
2449                 C_SkipSpace();
2450             }
2451             else if (g_scriptVersion == 10) g_scriptVersion = 9;
2452 
2453             i = 0;
2454 
2455             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
2456             {
2457                 tempbuf[i] = *textptr;
2458                 textptr++,i++;
2459                 if (EDUKE32_PREDICT_FALSE(i >= 32))
2460                 {
2461                     initprintf("%s:%d: warning: truncating level name to %d characters.\n",
2462                         g_scriptFileName,g_lineNumber,32);
2463                     g_warningCnt++;
2464                     C_NextLine();
2465                     break;
2466                 }
2467             }
2468 
2469             tempbuf[i] = '\0';
2470 
2471             if (g_mapInfo[j*MAXLEVELS+k].name == NULL)
2472                 g_mapInfo[j*MAXLEVELS+k].name = (char *)Xcalloc(Bstrlen(tempbuf)+1,sizeof(uint8_t));
2473             else if ((Bstrlen(tempbuf)+1) > sizeof(g_mapInfo[j*MAXLEVELS+k].name))
2474                 g_mapInfo[j *MAXLEVELS+k].name = (char *)Xrealloc(g_mapInfo[j*MAXLEVELS+k].name,(Bstrlen(tempbuf)+1));
2475 
2476             /*         initprintf("level name string len: %d\n",Bstrlen(tempbuf)); */
2477 
2478             Bstrcpy(g_mapInfo[j*MAXLEVELS+k].name,tempbuf);
2479 
2480             continue;
2481 
2482         case CON_DEFINEQUOTE:
2483             g_scriptPtr--;
2484 
2485             C_GetNextValue(LABEL_DEFINE);
2486 
2487             k = *(g_scriptPtr-1);
2488 
2489             if (EDUKE32_PREDICT_FALSE((unsigned)k >= MAXQUOTES))
2490             {
2491                 initprintf("%s:%d: error: quote number exceeds limit of %d.\n",g_scriptFileName,g_lineNumber,MAXQUOTES);
2492                 g_errorCnt++;
2493             }
2494             else
2495             {
2496                 C_AllocQuote(k);
2497             }
2498 
2499             g_scriptPtr--;
2500 
2501             i = 0;
2502 
2503             C_SkipSpace();
2504 
2505             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
2506             {
2507                 /*
2508                 if (*textptr == '%' && *(textptr+1) == 's')
2509                 {
2510                 initprintf("%s:%d: error: quote text contains string identifier.\n",g_szScriptFileName,g_lineNumber);
2511                 g_numCompilerErrors++;
2512                 while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) textptr++;
2513                 break;
2514                 }
2515                 */
2516                 *(apStrings[k]+i) = *textptr;
2517 
2518                 textptr++,i++;
2519                 if (EDUKE32_PREDICT_FALSE(i >= MAXQUOTELEN-1))
2520                 {
2521                     initprintf("%s:%d: warning: truncating quote text to %d characters.\n",g_scriptFileName,g_lineNumber,MAXQUOTELEN-1);
2522                     g_warningCnt++;
2523                     C_NextLine();
2524                     break;
2525                 }
2526             }
2527 
2528             if ((unsigned)k < MAXQUOTES)
2529                 *(apStrings[k]+i) = '\0';
2530             continue;
2531 
2532         case CON_DEFINESOUND:
2533             g_scriptPtr--;
2534             C_GetNextValue(LABEL_DEFINE);
2535 
2536             // Ideally we could keep the value of i from C_GetNextValue() instead of having to hash_find() again.
2537             // This depends on tempbuf remaining in place after C_GetNextValue():
2538             j = hash_find(&h_labels,tempbuf);
2539 
2540             k = *(g_scriptPtr-1);
2541             if (EDUKE32_PREDICT_FALSE((unsigned)k >= MAXSOUNDS-1))
2542             {
2543                 initprintf("%s:%d: error: index exceeds sound limit of %d.\n",g_scriptFileName,g_lineNumber, MAXSOUNDS-1);
2544                 g_errorCnt++;
2545                 k = MAXSOUNDS-1;
2546             }
2547             g_scriptPtr--;
2548             i = 0;
2549             C_SkipComments();
2550 
2551             if (g_sounds[k].filename == NULL)
2552                 g_sounds[k].filename = (char *)Xcalloc(BMAX_PATH,sizeof(uint8_t));
2553 
2554             if (*textptr == '\"')
2555             {
2556                 textptr++;
2557                 while (*textptr && *textptr != '\"')
2558                 {
2559                     g_sounds[k].filename[i++] = *textptr++;
2560                     if (EDUKE32_PREDICT_FALSE(i >= BMAX_PATH-1))
2561                     {
2562                         initprintf("%s:%d: error: sound filename exceeds limit of %d characters.\n",g_scriptFileName,g_lineNumber,BMAX_PATH-1);
2563                         g_errorCnt++;
2564                         C_SkipComments();
2565                         break;
2566                     }
2567                 }
2568                 textptr++;
2569             }
2570             else while (*textptr != ' ' && *textptr != '\t' && *textptr != '\r' && *textptr != '\n')
2571             {
2572                 g_sounds[k].filename[i++] = *textptr++;
2573                 if (EDUKE32_PREDICT_FALSE(i >= BMAX_PATH-1))
2574                 {
2575                     initprintf("%s:%d: error: sound filename exceeds limit of %d characters.\n",g_scriptFileName,g_lineNumber,BMAX_PATH-1);
2576                     g_errorCnt++;
2577                     C_SkipComments();
2578                     break;
2579                 }
2580             }
2581             g_sounds[k].filename[i] = '\0';
2582 
2583             check_filename_case(g_sounds[k].filename);
2584 
2585             C_GetNextValue(LABEL_DEFINE);
2586             g_sounds[k].ps = *(g_scriptPtr-1);
2587             C_GetNextValue(LABEL_DEFINE);
2588             g_sounds[k].pe = *(g_scriptPtr-1);
2589             C_GetNextValue(LABEL_DEFINE);
2590             g_sounds[k].pr = *(g_scriptPtr-1);
2591 
2592             C_GetNextValue(LABEL_DEFINE);
2593             g_sounds[k].m = *(g_scriptPtr-1) & ~SF_ONEINST_INTERNAL;
2594             if (*(g_scriptPtr-1) & SF_LOOP)
2595                 g_sounds[k].m |= SF_ONEINST_INTERNAL;
2596 
2597             C_GetNextValue(LABEL_DEFINE);
2598             g_sounds[k].vo = *(g_scriptPtr-1);
2599             g_scriptPtr -= 5;
2600 
2601             g_sounds[k].volume = fix16_one;
2602 
2603             if (k > g_highestSoundIdx)
2604                 g_highestSoundIdx = k;
2605             continue;
2606 
2607         case CON_ENDEVENT:
2608 
2609             if (EDUKE32_PREDICT_FALSE(!g_scriptEventOffset))
2610             {
2611                 C_ReportError(-1);
2612                 initprintf("%s:%d: error: found `endevent' without open `onevent'.\n",g_scriptFileName,g_lineNumber);
2613                 g_errorCnt++;
2614             }
2615             if (EDUKE32_PREDICT_FALSE(g_numBraces != 0))
2616             {
2617                 C_ReportError(g_numBraces > 0 ? ERROR_OPENBRACKET : ERROR_CLOSEBRACKET);
2618                 g_errorCnt++;
2619             }
2620 
2621             g_scriptEventOffset = g_parsingActorPtr = 0;
2622             g_currentEvent = -1;
2623             Bsprintf(g_szCurrentBlockName,"(none)");
2624             continue;
2625 
2626         case CON_ENDA:
2627             if (EDUKE32_PREDICT_FALSE(!g_parsingActorPtr || g_scriptEventOffset))
2628             {
2629                 C_ReportError(-1);
2630                 initprintf("%s:%d: error: found `enda' without open `actor'.\n",g_scriptFileName,g_lineNumber);
2631                 g_errorCnt++;
2632                 g_scriptEventOffset = 0;
2633             }
2634             if (EDUKE32_PREDICT_FALSE(g_numBraces != 0))
2635             {
2636                 C_ReportError(g_numBraces > 0 ? ERROR_OPENBRACKET : ERROR_CLOSEBRACKET);
2637                 g_errorCnt++;
2638             }
2639             g_parsingActorPtr = 0;
2640             Bsprintf(g_szCurrentBlockName,"(none)");
2641             continue;
2642 
2643         case CON_BREAK:
2644             continue;
2645 
2646         case CON_FALL:
2647         case CON_TIP:
2648             //        case 21:
2649         case CON_KILLIT:
2650         case CON_RESETACTIONCOUNT:
2651         case CON_PSTOMP:
2652         case CON_RESETPLAYER:
2653         case CON_RESETCOUNT:
2654         case CON_WACKPLAYER:
2655         case CON_OPERATE:
2656         case CON_RESPAWNHITAG:
2657         case CON_GETLASTPAL:
2658         case CON_PKICK:
2659         case CON_MIKESND:
2660         case CON_TOSSWEAPON:
2661         case CON_DESTROYIT:
2662         case CON_LARRYBIRD:
2663         case CON_STRAFELEFT:
2664         case CON_STRAFERIGHT:
2665         case CON_SLAPPLAYER:
2666         case CON_TEARITUP:
2667         case CON_SMACKBUBBA:
2668         case CON_SOUNDTAGONCE:
2669         case CON_SOUNDTAG:
2670         case CON_SMACKSPRITE:
2671         case CON_FAKEBUBBA:
2672         case CON_MAMATRIGGER:
2673         case CON_MAMASPAWN:
2674         case CON_MAMAQUAKE:
2675         case CON_MAMAEND:
2676         case CON_GARYBANJO:
2677         case CON_MOTOLOOPSND:
2678         case CON_RNDMOVE:
2679         case CON_LEAVETRAX:
2680         case CON_LEAVEDROPPINGS:
2681         case CON_DEPLOYBIAS:
2682             continue;
2683 
2684         case CON_NULLOP:
2685             if (EDUKE32_PREDICT_FALSE(C_GetKeyword() != CON_ELSE))
2686             {
2687                 C_ReportError(-1);
2688                 g_warningCnt++;
2689                 initprintf("%s:%d: warning: `nullop' found without `else'\n",g_scriptFileName,g_lineNumber);
2690                 g_scriptPtr--;
2691                 g_skipBranch = 1;
2692             }
2693             continue;
2694 
2695         case CON_GAMESTARTUP:
2696             {
2697                 int32_t params[34];
2698 
2699                 g_scriptPtr--;
2700                 for (j = 0; j < 34; j++)
2701                 {
2702                     C_GetNextValue(LABEL_DEFINE);
2703                     g_scriptPtr--;
2704                     params[j] = *g_scriptPtr;
2705 
2706                     if (j != 29 && j != 30) continue;
2707 
2708                     if (C_GetKeyword() != -1)
2709                     {
2710                         /*if (j == 12)
2711                             g_scriptVersion = 10;
2712                         else if (j == 21)
2713                             g_scriptVersion = 11;
2714                         else if (j == 25)
2715                             g_scriptVersion = 13;
2716                         else if (j == 29)
2717                             g_scriptVersion = 14;*/
2718                         break;
2719                     }
2720                     /*else
2721                         g_scriptVersion = 16;*/
2722                 }
2723 
2724                 /*
2725                 v1.3d                   v1.5
2726                 DEFAULTVISIBILITY       DEFAULTVISIBILITY
2727                 GENERICIMPACTDAMAGE     GENERICIMPACTDAMAGE
2728                 MAXPLAYERHEALTH         MAXPLAYERHEALTH
2729                 STARTARMORHEALTH        STARTARMORHEALTH
2730                 RESPAWNACTORTIME        RESPAWNACTORTIME
2731                 RESPAWNITEMTIME         RESPAWNITEMTIME
2732                 RUNNINGSPEED            RUNNINGSPEED
2733                 RPGBLASTRADIUS          GRAVITATIONALCONSTANT
2734                 PIPEBOMBRADIUS          RPGBLASTRADIUS
2735                 SHRINKERBLASTRADIUS     PIPEBOMBRADIUS
2736                 TRIPBOMBBLASTRADIUS     SHRINKERBLASTRADIUS
2737                 MORTERBLASTRADIUS       TRIPBOMBBLASTRADIUS
2738                 BOUNCEMINEBLASTRADIUS   MORTERBLASTRADIUS
2739                 SEENINEBLASTRADIUS      BOUNCEMINEBLASTRADIUS
2740                 MAXPISTOLAMMO           SEENINEBLASTRADIUS
2741                 MAXSHOTGUNAMMO          MAXPISTOLAMMO
2742                 MAXCHAINGUNAMMO         MAXSHOTGUNAMMO
2743                 MAXRPGAMMO              MAXCHAINGUNAMMO
2744                 MAXHANDBOMBAMMO         MAXRPGAMMO
2745                 MAXSHRINKERAMMO         MAXHANDBOMBAMMO
2746                 MAXDEVISTATORAMMO       MAXSHRINKERAMMO
2747                 MAXTRIPBOMBAMMO         MAXDEVISTATORAMMO
2748                 MAXFREEZEAMMO           MAXTRIPBOMBAMMO
2749                 CAMERASDESTRUCTABLE     MAXFREEZEAMMO
2750                 NUMFREEZEBOUNCES        MAXGROWAMMO
2751                 FREEZERHURTOWNER        CAMERASDESTRUCTABLE
2752                 NUMFREEZEBOUNCES
2753                 FREEZERHURTOWNER
2754                 QSIZE
2755                 TRIPBOMBLASERMODE
2756                 */
2757 
2758                 G_DoGameStartup(params);
2759             }
2760             continue;
2761         }
2762     }
2763     while (loop);
2764 
2765     return 0;
2766 }
2767 
C_ScriptVersionString(int32_t version)2768 static char const * C_ScriptVersionString(int32_t version)
2769 {
2770     switch (version)
2771     {
2772     case 9:
2773         return ", v0.99 compatibility mode";
2774     case 10:
2775         return ", v1.0 compatibility mode";
2776     case 11:
2777         return ", v1.1 compatibility mode";
2778     case 13:
2779         return ", v1.3D compatibility mode";
2780     default:
2781         return "";
2782     }
2783 }
2784 
C_PrintStats(void)2785 void C_PrintStats(void)
2786 {
2787     initprintf("%d/%d labels\n", g_labelCnt,
2788         (int32_t) min((MAXSECTORS * sizeof(sectortype)/sizeof(int32_t)),
2789             MAXSPRITES * sizeof(spritetype)/(1<<6)));
2790 
2791     int i, j;
2792 
2793     for (i=MAXQUOTES-1, j=0; i>=0; i--)
2794     {
2795         if (apStrings[i])
2796             j++;
2797     }
2798 
2799     if (j) initprintf("%d strings, ", j);
2800 
2801     for (i=MAXTILES-1, j=0; i>=0; i--)
2802     {
2803         if (g_tile[i].execPtr)
2804             j++;
2805     }
2806     if (j) initprintf("%d actors", j);
2807 
2808     initprintf("\n");
2809 }
2810 
C_Compile(const char * fileName)2811 void C_Compile(const char *fileName)
2812 {
2813     Bmemset(apScriptEvents, 0, sizeof(apScriptEvents));
2814     Bmemset(apScriptGameEventEnd, 0, sizeof(apScriptGameEventEnd));
2815 
2816     for (int i=0; i<MAXTILES; i++)
2817     {
2818         Bmemset(&g_tile[i], 0, sizeof(tiledata_t));
2819         g_actorMinMs[i] = 1e308;
2820     }
2821 
2822     C_InitHashes();
2823     Gv_Init();
2824 
2825 #ifdef USE_OPENGL
2826     if (REALITY)
2827     {
2828         if (RT_PrepareScript())
2829             G_GameExit("Script error");
2830         return;
2831     }
2832 #endif
2833 
2834     int kFile = kopen4loadfrommod(fileName,g_loadFromGroupOnly);
2835 
2836     if (kFile == -1) // JBF: was 0
2837     {
2838         if (g_loadFromGroupOnly == 1 || numgroupfiles == 0)
2839         {
2840             char const *gf = G_GrpFile();
2841             Bsprintf(tempbuf,"Required game data was not found.  A valid copy of \"%s\" or other compatible data is needed to run EDuke32.\n\n"
2842                      "You must copy \"%s\" to your game directory before continuing!", gf, gf);
2843             G_GameExit(tempbuf);
2844         }
2845         else
2846         {
2847             Bsprintf(tempbuf,"CON file `%s' missing.", fileName);
2848             G_GameExit(tempbuf);
2849         }
2850 
2851         //g_loadFromGroupOnly = 1;
2852         return; //Not there
2853     }
2854 
2855     int const kFileLen = kfilelength(kFile);
2856 
2857     initprintf("Compiling: %s (%d bytes)\n", fileName, kFileLen);
2858 
2859     g_logFlushWindow = 0;
2860 
2861     uint32_t const startcompiletime = timerGetTicks();
2862 
2863     char * mptr = (char *)Xmalloc(kFileLen+1);
2864     mptr[kFileLen] = 0;
2865 
2866     textptr = (char *) mptr;
2867     kread(kFile,(char *)textptr,kFileLen);
2868     kclose(kFile);
2869 
2870     g_scriptcrc = Bcrc32(NULL, 0, 0L);
2871     g_scriptcrc = Bcrc32(textptr, kFileLen, g_scriptcrc);
2872 
2873     Bfree(apScript);
2874 
2875     apScript = (intptr_t *)Xcalloc(1, g_scriptSize * sizeof(intptr_t));
2876     bitptr   = (char *)Xcalloc(1, (((g_scriptSize + 7) >> 3) + 1) * sizeof(uint8_t));
2877     //    initprintf("script: %d, bitptr: %d\n",script,bitptr);
2878 
2879     g_labelCnt        = 0;
2880     g_defaultLabelCnt = 0;
2881     g_scriptPtr       = apScript + 3;  // move permits constants 0 and 1; moveptr[1] would be script[2] (reachable?)
2882     g_warningCnt      = 0;
2883     g_errorCnt        = 0;
2884     g_lineNumber      = 1;
2885     g_totalLines      = 0;
2886 
2887     Bstrcpy(g_scriptFileName, fileName);
2888 
2889     C_ParseCommand(1);
2890 
2891     for (char * m : g_scriptModules)
2892     {
2893         C_Include(m);
2894         free(m);
2895     }
2896     g_scriptModules.clear();
2897 
2898     g_logFlushWindow = 1;
2899 
2900     if (g_errorCnt > 63)
2901         initprintf("fatal error: too many errors: Aborted\n");
2902 
2903     //*script = (intptr_t) g_scriptPtr;
2904 
2905     DO_FREE_AND_NULL(mptr);
2906 
2907     if (g_warningCnt || g_errorCnt)
2908         initprintf("Found %d warning(s), %d error(s).\n", g_warningCnt, g_errorCnt);
2909 
2910     if (g_errorCnt)
2911     {
2912         Bsprintf(buf, "Error compiling CON files.");
2913         G_GameExit(buf);
2914     }
2915 
2916     g_totalLines += g_lineNumber;
2917 
2918     C_SetScriptSize(g_scriptPtr-apScript+8);
2919 
2920     initprintf("Script compiled in %dms, %ld bytes%s\n", timerGetTicks() - startcompiletime,
2921                 (unsigned long)(g_scriptPtr-apScript), C_ScriptVersionString(g_scriptVersion));
2922 
2923     for (auto *i : tables_free)
2924         hash_free(i);
2925 
2926     //freehashnames();
2927     freesoundhashnames();
2928 
2929     if (g_scriptDebug)
2930         C_PrintStats();
2931 
2932     C_InitQuotes();
2933 }
2934 
C_ReportError(int32_t iError)2935 void C_ReportError(int32_t iError)
2936 {
2937     if (Bstrcmp(g_szCurrentBlockName,g_szLastBlockName))
2938     {
2939         if (g_scriptEventOffset || g_processingState || g_parsingActorPtr)
2940             initprintf("%s: In %s `%s':\n",g_scriptFileName,g_scriptEventOffset?"event":g_parsingActorPtr?"actor":"state",g_szCurrentBlockName);
2941         else initprintf("%s: At top level:\n",g_scriptFileName);
2942         Bstrcpy(g_szLastBlockName,g_szCurrentBlockName);
2943     }
2944     switch (iError)
2945     {
2946     case ERROR_CLOSEBRACKET:
2947         initprintf("%s:%d: error: found more `}' than `{' before `%s'.\n",g_scriptFileName,g_lineNumber,tempbuf);
2948         break;
2949     case ERROR_EXCEEDSMAXTILES:
2950         initprintf("%s:%d: error: `%s' value exceeds MAXTILES.  Maximum is %d.\n",g_scriptFileName,g_lineNumber,tempbuf,MAXTILES-1);
2951         break;
2952     case ERROR_EXPECTEDKEYWORD:
2953         initprintf("%s:%d: error: expected a keyword but found `%s'.\n",g_scriptFileName,g_lineNumber,tempbuf);
2954         break;
2955     case ERROR_FOUNDWITHIN:
2956         initprintf("%s:%d: error: found `%s' within %s.\n",g_scriptFileName,g_lineNumber,tempbuf,g_parsingActorPtr?"an actor":"a state");
2957         break;
2958     case ERROR_ISAKEYWORD:
2959         initprintf("%s:%d: error: symbol `%s' is a keyword.\n",g_scriptFileName,g_lineNumber,label+(g_labelCnt<<6));
2960         break;
2961     case ERROR_OPENBRACKET:
2962         initprintf("%s:%d: error: found more `{' than `}' before `%s'.\n",g_scriptFileName,g_lineNumber,tempbuf);
2963         break;
2964     case ERROR_NOTAGAMEVAR:
2965         initprintf("%s:%d: error: symbol `%s' is not a variable.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
2966         break;
2967     case ERROR_PARAMUNDEFINED:
2968         initprintf("%s:%d: error: parameter `%s' is undefined.\n",g_scriptFileName,g_lineNumber,tempbuf);
2969         break;
2970     case ERROR_SYNTAXERROR:
2971         initprintf("%s:%d: error: syntax error.\n",g_scriptFileName,g_lineNumber);
2972         break;
2973     case ERROR_VARREADONLY:
2974         initprintf("%s:%d: error: variable `%s' is read-only.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
2975         break;
2976     case ERROR_VARTYPEMISMATCH:
2977         initprintf("%s:%d: error: variable `%s' is of the wrong type.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
2978         break;
2979     case WARNING_BADGAMEVAR:
2980         initprintf("%s:%d: warning: variable `%s' should be either per-player OR per-actor, not both.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
2981         break;
2982     case WARNING_DUPLICATEDEFINITION:
2983         initprintf("%s:%d: warning: duplicate definition `%s' ignored.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
2984         break;
2985     case WARNING_LABELSONLY:
2986         initprintf("%s:%d: warning: expected a label, found a constant.\n",g_scriptFileName,g_lineNumber);
2987         break;
2988     case WARNING_VARMASKSKEYWORD:
2989         initprintf("%s:%d: warning: variable `%s' masks keyword.\n", g_scriptFileName, g_lineNumber, LAST_LABEL);
2990         break;
2991     }
2992 }
2993 
2994