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 "gamedef.h"
24 
25 #include "cheats.h"
26 #include "common.h"
27 #include "common_game.h"
28 #include "crc32.h"
29 #include "duke3d.h"
30 #include "gameexec.h"
31 #include "gamestructures.h"
32 #include "kplib.h"
33 #include "namesdyn.h"
34 #include "osd.h"
35 #include "savegame.h"
36 #include "vfs.h"
37 
38 #include "microprofile.h"
39 
40 #if MICROPROFILE_ENABLED != 0
41 MicroProfileToken g_eventTokens[MAXEVENTS];
42 MicroProfileToken g_eventCounterTokens[MAXEVENTS];
43 MicroProfileToken g_actorTokens[MAXTILES];
44 MicroProfileToken g_statnumTokens[MAXSTATUS];
45 #if 0
46 MicroProfileToken g_instTokens[CON_END];
47 #endif
48 #endif
49 
50 #define LINE_NUMBER (g_lineNumber << 12)
51 
52 int32_t g_scriptVersion = 13; // 13 = 1.3D-style CON files, 14 = 1.4/1.5 style CON files
53 
54 char g_scriptFileName[BMAX_PATH] = "(none)";  // file we're currently compiling
55 
56 int32_t g_totalLines;
57 int32_t g_lineNumber;
58 char g_szBuf[1024];
59 
60 static char *textptr;
61 
62 static char g_szCurrentBlockName[64] = "(none)";
63 static char g_szLastBlockName[64] = "NULL";
64 
65 static bool g_checkingCase;
66 static bool g_dynamicSoundMapping;
67 static bool g_dynamicTileMapping;
68 static bool g_labelsOnly;
69 static bool g_processingState;
70 static bool g_skipBranch;
71 
72 static int g_checkingIfElse;
73 static int g_checkingSwitch;
74 static int g_lastKeyword = -1;
75 static int g_numBraces;
76 static int g_numCases;
77 
78 static intptr_t apScriptGameEventEnd[MAXEVENTS];
79 static intptr_t g_scriptActorOffset;
80 static intptr_t g_scriptEventBreakOffset;
81 static intptr_t g_scriptEventChainOffset;
82 static intptr_t g_scriptEventOffset;
83 
84 // The pointer to the start of the case table in a switch statement.
85 // First entry is 'default' code.
86 static intptr_t *g_caseTablePtr;
87 
88 static bool C_ParseCommand(bool loop = false);
89 static void C_SetScriptSize(int32_t newsize);
90 
91 int32_t g_errorCnt;
92 int32_t g_warningCnt;
93 int32_t g_numXStrings;
94 
C_GetLabelType(int const type)95 static char *C_GetLabelType(int const type)
96 {
97     static tokenmap_t const LabelType[] =
98     {
99         { "action", LABEL_ACTION },
100         { "actor",  LABEL_ACTOR },
101         { "ai",     LABEL_AI },
102         { "define", LABEL_DEFINE },
103         { "event",  LABEL_EVENT },
104         { "move",   LABEL_MOVE },
105         { "state",  LABEL_STATE },
106     };
107 
108     char x[64] = {};
109 
110     for (auto &label : LabelType)
111     {
112         if ((type & label.val) != label.val)
113             continue;
114 
115         if (x[0]) Bstrcat(x, " or ");
116         Bstrcat(x, label.token);
117 
118         if (type == label.val)
119             break;
120     }
121 
122     return Xstrdup(x);
123 }
124 
125 static hashtable_t h_keywords   = { CON_END>>1, NULL };
126 static hashtable_t h_iter       = { ITER_END>>1, NULL };
127 
128 static hashtable_t *const tables[] = {
129     &h_arrays,
130     &h_gamevars,
131     &h_iter,
132     &h_keywords,
133     &h_labels,
134 };
135 
136 static hashtable_t *const tables_free[] = {
137     &h_iter,
138     &h_keywords,
139 };
140 
141 static tokenmap_t const vm_keywords[] =
142 {
143     { "action",                 CON_ACTION },
144     { "activate",               CON_ACTIVATE },
145     { "activatebysector",       CON_ACTIVATEBYSECTOR },
146     { "activatecheat",          CON_ACTIVATECHEAT },
147     { "actor",                  CON_ACTOR },
148     { "actorsound",             CON_ACTORSOUND },
149     { "addammo",                CON_ADDAMMO },
150     { "addinventory",           CON_ADDINVENTORY },
151     { "addkills",               CON_ADDKILLS },
152     { "addlog",                 CON_ADDLOGVAR },
153     { "addlogvar",              CON_ADDLOGVAR },
154     { "addphealth",             CON_ADDPHEALTH },
155     { "addstrength",            CON_ADDSTRENGTH },
156     { "addvar",                 CON_ADDVAR },
157     { "addvarvar",              CON_ADDVARVAR },
158     { "addweapon",              CON_ADDWEAPON },
159     { "addweaponvar",           CON_ADDWEAPON },
160     { "ai",                     CON_AI },
161     { "andvar",                 CON_ANDVAR },
162     { "andvarvar",              CON_ANDVARVAR },
163     { "angoff",                 CON_ANGOFF },
164     { "angoffvar",              CON_ANGOFF },
165     { "appendevent",            CON_APPENDEVENT },
166     { "betaname",               CON_BETANAME },
167     { "break",                  CON_BREAK },
168     { "cactor",                 CON_CACTOR },
169     { "calchypotenuse",         CON_CALCHYPOTENUSE },
170     { "cansee",                 CON_CANSEE },
171     { "canseespr",              CON_CANSEESPR },
172     { "capia",                  CON_CAPIA },
173     { "capis",                  CON_CAPIS },
174     { "case",                   CON_CASE },
175     { "changespritesect",       CON_CHANGESPRITESECT },
176     { "changespritestat",       CON_CHANGESPRITESTAT },
177     { "cheatkeys",              CON_CHEATKEYS },
178     { "checkactivatormotion",   CON_CHECKACTIVATORMOTION },
179     { "checkavailinven",        CON_CHECKAVAILINVEN },
180     { "checkavailweapon",       CON_CHECKAVAILWEAPON },
181     { "clamp",                  CON_CLAMP },
182     { "clearmapstate",          CON_CLEARMAPSTATE },
183     { "clipdist",               CON_CLIPDIST },
184     { "clipmove",               CON_CLIPMOVE },
185     { "clipmovenoslide",        CON_CLIPMOVENOSLIDE },
186     { "cmenu",                  CON_CMENU },
187     { "copy",                   CON_COPY },
188     { "cos",                    CON_COS },
189     { "count",                  CON_COUNT },
190     { "cstat",                  CON_CSTAT },
191     { "cstator",                CON_CSTATOR },
192     { "damageeventtile",        CON_DAMAGEEVENTTILE },
193     { "damageeventtilerange",   CON_DAMAGEEVENTTILERANGE },
194     { "debris",                 CON_DEBRIS },
195     { "debug",                  CON_DEBUG },
196     { "default",                CON_DEFAULT },
197     { "define",                 CON_DEFINE },
198     { "definecheat",            CON_DEFINECHEAT },
199     { "definecheatdescription", CON_DEFINECHEATDESCRIPTION },
200     { "definegamefuncname",     CON_DEFINEGAMEFUNCNAME },
201     { "definegametype",         CON_DEFINEGAMETYPE },
202     { "definelevelname",        CON_DEFINELEVELNAME },
203     { "defineprojectile",       CON_DEFINEPROJECTILE },
204     { "definequote",            CON_DEFINEQUOTE },
205     { "defineskillname",        CON_DEFINESKILLNAME },
206     { "definesound",            CON_DEFINESOUND },
207     { "definevolumeflags",      CON_DEFINEVOLUMEFLAGS },
208     { "definevolumename",       CON_DEFINEVOLUMENAME },
209     { "defstate",               CON_DEFSTATE },
210     { "digitalnumber",          CON_DIGITALNUMBER },
211     { "digitalnumberz",         CON_DIGITALNUMBERZ },
212     { "displayrand",            CON_DISPLAYRAND },
213     { "displayrandvar",         CON_DISPLAYRANDVAR },
214     { "displayrandvarvar",      CON_DISPLAYRANDVARVAR },
215     { "dist",                   CON_DIST },
216     { "divr",                   CON_DIVR },
217     { "divrd",                  CON_DIVVARVAR }, // div round toward zero -- alias until proven otherwise
218     { "divru",                  CON_DIVRU },
219     { "divscale",               CON_DIVSCALE },
220     { "divvar",                 CON_DIVVAR },
221     { "divvarvar",              CON_DIVVARVAR },
222     { "dragpoint",              CON_DRAGPOINT },
223     { "drawline256",            CON_DRAWLINE256 },
224     { "drawlinergb",            CON_DRAWLINERGB },
225     { "dynamicremap",           CON_DYNAMICREMAP },
226     { "dynamicsoundremap",      CON_DYNAMICSOUNDREMAP },
227     { "echo",                   CON_ECHO },
228     { "else",                   CON_ELSE },
229     { "enda",                   CON_ENDA },
230     { "endevent",               CON_ENDEVENT },
231     { "endofgame",              CON_ENDOFGAME },
232     { "endoflevel",             CON_ENDOFLEVEL },
233     { "ends",                   CON_ENDS },
234     { "endswitch",              CON_ENDSWITCH },
235     { "enhanced",               CON_ENHANCED },
236     { "eqspawnvar",             CON_EQSPAWN },
237     { "eshootvar",              CON_ESHOOT },
238     { "espawnvar",              CON_ESPAWN },
239     { "eventloadactor",         CON_EVENTLOADACTOR },
240     { "ezshootvar",             CON_EZSHOOT },
241     { "fall",                   CON_FALL },
242     { "findnearactor3dvar",     CON_FINDNEARACTOR3D },
243     { "findnearactorvar",       CON_FINDNEARACTOR },
244     { "findnearactorzvar",      CON_FINDNEARACTORZ },
245     { "findnearsprite3dvar",    CON_FINDNEARSPRITE3D },
246     { "findnearspritevar",      CON_FINDNEARSPRITE },
247     { "findnearspritezvar",     CON_FINDNEARSPRITEZ },
248     { "findotherplayer",        CON_FINDOTHERPLAYER },
249     { "findplayer",             CON_FINDPLAYER },
250     { "flash",                  CON_FLASH },
251     { "for",                    CON_FOR },
252     { "gamearray",              CON_GAMEARRAY },
253     { "gamestartup",            CON_GAMESTARTUP },
254     { "gametext",               CON_GAMETEXT },
255     { "gametextz",              CON_GAMETEXTZ },
256     { "gamevar",                CON_GAMEVAR },
257     { "getactor",               CON_GETACTOR },
258     { "getactorangle",          CON_GETACTORANGLE },
259     { "getactorvar",            CON_GETACTORVAR },
260     { "getangle",               CON_GETANGLE },
261     { "getangletotarget",       CON_GETANGLETOTARGET },
262     { "getarraysequence",       CON_GETARRAYSEQUENCE },
263     { "getarraysize",           CON_GETARRAYSIZE },
264     { "getceilzofslope",        CON_GETCEILZOFSLOPE },
265     { "getclosestcol",          CON_GETCLOSESTCOL },
266     { "getcurraddress",         CON_GETCURRADDRESS },
267     { "getflorzofslope",        CON_GETFLORZOFSLOPE },
268     { "getgamefuncbind",        CON_GETGAMEFUNCBIND },
269     { "getincangle",            CON_GETINCANGLE },
270     { "getinput",               CON_GETINPUT },
271     { "getkeyname",             CON_GETKEYNAME },
272     { "getlastpal",             CON_GETLASTPAL },
273     { "getmusicposition",       CON_GETMUSICPOSITION },
274     { "getplayer",              CON_GETPLAYER },
275     { "getplayerangle",         CON_GETPLAYERANGLE },
276     { "getplayervar",           CON_GETPLAYERVAR },
277     { "getpname",               CON_GETPNAME },
278     { "getprojectile",          CON_GETPROJECTILE },
279     { "getsector",              CON_GETSECTOR },
280     { "gettextureceiling",      CON_GETTEXTURECEILING },
281     { "gettexturefloor",        CON_GETTEXTUREFLOOR },
282     { "getthisprojectile",      CON_GETTHISPROJECTILE },
283     { "getticks",               CON_GETTICKS },
284     { "gettiledata",            CON_GETTILEDATA }, // OldMP compat.
285     { "gettimedate",            CON_GETTIMEDATE },
286     { "gettspr",                CON_GETTSPR },
287     { "getuserdef",             CON_GETUSERDEF },
288     { "getwall",                CON_GETWALL },
289     { "getzrange",              CON_GETZRANGE },
290     { "globalsound",            CON_GLOBALSOUND },
291     { "globalsoundvar",         CON_GLOBALSOUND },
292     { "gmaxammo",               CON_GMAXAMMO },
293     { "guniqhudid",             CON_GUNIQHUDID },
294     { "guts",                   CON_GUTS },
295     { "headspritesect",         CON_HEADSPRITESECT },
296     { "headspritestat",         CON_HEADSPRITESTAT },
297     { "hitradius",              CON_HITRADIUS },
298     { "hitradiusvar",           CON_HITRADIUS },
299     { "hitscan",                CON_HITSCAN },
300     { "ifaction",               CON_IFACTION },
301     { "ifactioncount",          CON_IFACTIONCOUNT },
302     { "ifactor",                CON_IFACTOR },
303     { "ifactornotstayput",      CON_IFACTORNOTSTAYPUT },
304     { "ifactorsound",           CON_IFACTORSOUND },
305     { "ifai",                   CON_IFAI },
306     { "ifangdiffl",             CON_IFANGDIFFL },
307     { "ifawayfromwall",         CON_IFAWAYFROMWALL },
308     { "ifbulletnear",           CON_IFBULLETNEAR },
309     { "ifcansee",               CON_IFCANSEE },
310     { "ifcanseetarget",         CON_IFCANSEETARGET },
311     { "ifcanshoottarget",       CON_IFCANSHOOTTARGET },
312     { "ifceilingdistl",         CON_IFCEILINGDISTL },
313     { "ifclient",               CON_IFCLIENT },
314     { "ifcount",                CON_IFCOUNT },
315     { "ifcutscene",             CON_IFCUTSCENE },
316     { "ifdead",                 CON_IFDEAD },
317     { "iffloordistl",           CON_IFFLOORDISTL },
318     { "ifgapzl",                CON_IFGAPZL },
319     { "ifgotweaponce",          CON_IFGOTWEAPONCE },
320     { "ifhitspace",             CON_IFHITSPACE },
321     { "ifhitweapon",            CON_IFHITWEAPON },
322     { "ifinouterspace",         CON_IFINOUTERSPACE },
323     { "ifinspace",              CON_IFINSPACE },
324     { "ifinwater",              CON_IFINWATER },
325     { "ifmove",                 CON_IFMOVE },
326     { "ifmultiplayer",          CON_IFMULTIPLAYER },
327     { "ifnosounds",             CON_IFNOSOUNDS },
328     { "ifnotmoving",            CON_IFNOTMOVING },
329     { "ifonwater",              CON_IFONWATER },
330     { "ifoutside",              CON_IFOUTSIDE },
331     { "ifp",                    CON_IFP },
332     { "ifpdistg",               CON_IFPDISTG },
333     { "ifpdistl",               CON_IFPDISTL },
334     { "ifphealthl",             CON_IFPHEALTHL },
335     { "ifpinventory",           CON_IFPINVENTORY },
336     { "ifplaybackon",           CON_IFPLAYBACKON },
337     { "ifplayersl",             CON_IFPLAYERSL },
338     { "ifrespawn",              CON_IFRESPAWN },
339     { "ifrnd",                  CON_IFRND },
340     { "ifserver",               CON_IFSERVER },
341     { "ifsound",                CON_IFSOUND },
342     { "ifspawnedby",            CON_IFSPAWNEDBY },
343     { "ifspritepal",            CON_IFSPRITEPAL },
344     { "ifsquished",             CON_IFSQUISHED },
345     { "ifstrength",             CON_IFSTRENGTH },
346     { "ifvara",                 CON_IFVARA },
347     { "ifvarae",                CON_IFVARAE },
348     { "ifvarand",               CON_IFVARAND },
349     { "ifvarb",                 CON_IFVARB },
350     { "ifvarbe",                CON_IFVARBE },
351     { "ifvarboth",              CON_IFVARBOTH },
352     { "ifvare",                 CON_IFVARE },
353     { "ifvareither",            CON_IFVAREITHER },
354     { "ifvarg",                 CON_IFVARG },
355     { "ifvarge",                CON_IFVARGE },
356     { "ifvarl",                 CON_IFVARL },
357     { "ifvarle",                CON_IFVARLE },
358     { "ifvarn",                 CON_IFVARN },
359     { "ifvaror",                CON_IFVAROR },
360     { "ifvarvara",              CON_IFVARVARA },
361     { "ifvarvarae",             CON_IFVARVARAE },
362     { "ifvarvarand",            CON_IFVARVARAND },
363     { "ifvarvarb",              CON_IFVARVARB },
364     { "ifvarvarbe",             CON_IFVARVARBE },
365     { "ifvarvarboth",           CON_IFVARVARBOTH },
366     { "ifvarvare",              CON_IFVARVARE },
367     { "ifvarvareither",         CON_IFVARVAREITHER },
368     { "ifvarvarg",              CON_IFVARVARG },
369     { "ifvarvarge",             CON_IFVARVARGE },
370     { "ifvarvarl",              CON_IFVARVARL },
371     { "ifvarvarle",             CON_IFVARVARLE },
372     { "ifvarvarn",              CON_IFVARVARN },
373     { "ifvarvaror",             CON_IFVARVAROR },
374     { "ifvarvarxor",            CON_IFVARVARXOR },
375     { "ifvarxor",               CON_IFVARXOR },
376     { "ifwasweapon",            CON_IFWASWEAPON },
377     { "include",                CON_INCLUDE },
378     { "includedefault",         CON_INCLUDEDEFAULT },
379     { "inittimer",              CON_INITTIMER },
380     { "insertspriteq",          CON_INSERTSPRITEQ },
381     { "inv",                    CON_INV },
382     { "jump",                   CON_JUMP },
383     { "killit",                 CON_KILLIT },
384     { "klabs",                  CON_KLABS },
385     { "ldist",                  CON_LDIST },
386     { "lineintersect",          CON_LINEINTERSECT },
387     { "loadmapstate",           CON_LOADMAPSTATE },
388     { "lockplayer",             CON_LOCKPLAYER },
389     { "lotsofglass",            CON_LOTSOFGLASS },
390     { "mail",                   CON_MAIL },
391     { "mikesnd",                CON_MIKESND },
392     { "minitext",               CON_MINITEXT },
393     { "modvar",                 CON_MODVAR },
394     { "modvarvar",              CON_MODVARVAR },
395     { "money",                  CON_MONEY },
396     { "move",                   CON_MOVE },
397     { "movesector",             CON_MOVESECTOR },
398     { "movesprite",             CON_MOVESPRITE },
399     { "mulscale",               CON_MULSCALE },
400     { "mulvar",                 CON_MULVAR },
401     { "mulvarvar",              CON_MULVARVAR },
402     { "music",                  CON_MUSIC },
403     { "myos",                   CON_MYOS },
404     { "myospal",                CON_MYOSPAL },
405     { "myospalx",               CON_MYOSPALX },
406     { "myosx",                  CON_MYOSX },
407     { "neartag",                CON_NEARTAG },
408     { "nextsectorneighborz",    CON_NEXTSECTORNEIGHBORZ },
409     { "nextspritesect",         CON_NEXTSPRITESECT },
410     { "nextspritestat",         CON_NEXTSPRITESTAT },
411     { "nullop",                 CON_NULLOP },
412     { "onevent",                CON_ONEVENT },
413     { "operate",                CON_OPERATE },
414     { "operateactivators",      CON_OPERATEACTIVATORS },
415     { "operatemasterswitches",  CON_OPERATEMASTERSWITCHES },
416     { "operaterespawns",        CON_OPERATERESPAWNS },
417     { "operatesectors",         CON_OPERATESECTORS },
418     { "orvar",                  CON_ORVAR },
419     { "orvarvar",               CON_ORVARVAR },
420     { "palfrom",                CON_PALFROM },
421     { "paper",                  CON_PAPER },
422     { "pkick",                  CON_PKICK },
423     { "precache",               CON_PRECACHE },
424     { "prevspritesect",         CON_PREVSPRITESECT },
425     { "prevspritestat",         CON_PREVSPRITESTAT },
426     { "preloadtrackslotforswap", CON_PRELOADTRACKSLOTFORSWAP },
427     { "pstomp",                 CON_PSTOMP },
428     { "qgetsysstr",             CON_QGETSYSSTR },
429     { "qspawnvar",              CON_QSPAWN },
430     { "qsprintf",               CON_QSPRINTF },
431     { "qstrcat",                CON_QSTRCAT },
432     { "qstrcmp",                CON_QSTRCMP },
433     { "qstrcpy",                CON_QSTRCPY },
434     { "qstrdim",                CON_QSTRDIM },
435     { "qstrlen",                CON_QSTRLEN },
436     { "qstrncat",               CON_QSTRNCAT },
437     { "qsubstr",                CON_QSUBSTR },
438     { "quake",                  CON_QUAKE },
439     { "quote",                  CON_QUOTE },
440     { "randvar",                CON_RANDVAR },
441     { "randvarvar",             CON_RANDVARVAR },
442     { "rayintersect",           CON_RAYINTERSECT },
443     { "readarrayfromfile",      CON_READARRAYFROMFILE },
444     { "readgamevar",            CON_READGAMEVAR },
445     { "redefinequote",          CON_REDEFINEQUOTE },
446     { "resetactioncount",       CON_RESETACTIONCOUNT },
447     { "resetcount",             CON_RESETCOUNT },
448     { "resetplayer",            CON_RESETPLAYER },
449     { "resetplayerflags",       CON_RESETPLAYERFLAGS },
450     { "resizearray",            CON_RESIZEARRAY },
451     { "respawnhitag",           CON_RESPAWNHITAG },
452     { "return",                 CON_RETURN },
453     { "rotatepoint",            CON_ROTATEPOINT },
454     { "rotatesprite",           CON_ROTATESPRITE },
455     { "rotatesprite16",         CON_ROTATESPRITE16 },
456     { "rotatespritea",          CON_ROTATESPRITEA },
457     { "save",                   CON_SAVE },
458     { "savegamevar",            CON_SAVEGAMEVAR },
459     { "savemapstate",           CON_SAVEMAPSTATE },
460     { "savenn",                 CON_SAVENN },
461     { "scalevar",               CON_SCALEVAR },
462     { "screenpal",              CON_SCREENPAL },
463     { "screensound",            CON_SCREENSOUND },
464     { "screentext",             CON_SCREENTEXT },
465     { "scriptsize",             CON_SCRIPTSIZE },
466     { "sectclearinterpolation", CON_SECTCLEARINTERPOLATION },
467     { "sectgethitag",           CON_SECTGETHITAG },
468     { "sectgetlotag",           CON_SECTGETLOTAG },
469     { "sectorofwall",           CON_SECTOROFWALL },
470     { "sectsetinterpolation",   CON_SECTSETINTERPOLATION },
471     { "setactor",               CON_SETACTOR },
472     { "setactorangle",          CON_SETACTORANGLE },
473     { "setactorsoundpitch",     CON_SETACTORSOUNDPITCH },
474     { "setactorvar",            CON_SETACTORVAR },
475     { "setarray",               CON_SETARRAY },
476     { "setarraysequence",       CON_SETARRAYSEQUENCE },
477     { "setaspect",              CON_SETASPECT },
478     { "setcfgname",             CON_SETCFGNAME },
479     { "setdefname",             CON_SETDEFNAME },
480     { "setgamename",            CON_SETGAMENAME },
481     { "setgamepalette",         CON_SETGAMEPALETTE },
482     { "setinput",               CON_SETINPUT },
483     { "setmusicposition",       CON_SETMUSICPOSITION },
484     { "setplayer",              CON_SETPLAYER },
485     { "setplayerangle",         CON_SETPLAYERANGLE },
486     { "setplayervar",           CON_SETPLAYERVAR },
487     { "setprojectile",          CON_SETPROJECTILE },
488     { "setsector",              CON_SETSECTOR },
489     { "setsprite",              CON_SETSPRITE },
490     { "setthisprojectile",      CON_SETTHISPROJECTILE },
491     { "settiledata",            CON_SETTILEDATA },
492     { "settspr",                CON_SETTSPR },
493     { "setuserdef",             CON_SETUSERDEF },
494     { "setvar",                 CON_SETVAR },
495     { "setvarvar",              CON_SETVARVAR },
496     { "setwall",                CON_SETWALL },
497     { "shadeto",                CON_SHADETO },
498     { "shiftvarl",              CON_SHIFTVARL },
499     { "shiftvarr",              CON_SHIFTVARR },
500     { "shiftvarvarl",           CON_SHIFTVARVARL },
501     { "shiftvarvarr",           CON_SHIFTVARVARR },
502     { "shootvar",               CON_SHOOT },
503     { "showview",               CON_SHOWVIEW },
504     { "showviewunbiased",       CON_SHOWVIEWUNBIASED },
505     { "showviewq16",            CON_SHOWVIEWQ16 },
506     { "showviewq16unbiased",    CON_SHOWVIEWQ16UNBIASED },
507     { "sin",                    CON_SIN },
508     { "sizeat",                 CON_SIZEAT },
509     { "sizeto",                 CON_SIZETO },
510     { "sleeptime",              CON_SLEEPTIME },
511     { "smaxammo",               CON_SMAXAMMO },
512     { "sound",                  CON_SOUND },
513     { "soundonce",              CON_SOUNDONCE },
514     { "soundoncevar",           CON_SOUNDONCE },
515     { "soundvar",               CON_SOUND },
516     { "spawn",                  CON_SPAWN },
517     { "spawnceilingglass",      CON_SPAWNCEILINGGLASS },
518     { "spawnwallstainedglass",  CON_SPAWNWALLSTAINEDGLASS },
519     { "spawnwallglass",         CON_SPAWNWALLGLASS },
520     { "spgethitag",             CON_SPGETHITAG },
521     { "spgetlotag",             CON_SPGETLOTAG },
522     { "spriteflags",            CON_SPRITEFLAGS },
523     { "spritenopal",            CON_SPRITENOPAL },
524     { "spritenoshade",          CON_SPRITENOSHADE },
525     { "spritenvg",              CON_SPRITENVG },
526     { "spritepal",              CON_SPRITEPAL },
527     { "spriteshadow",           CON_SPRITESHADOW },
528     { "sqrt",                   CON_SQRT },
529     { "ssp",                    CON_SSP },
530     { "startcutscene",          CON_STARTCUTSCENE },
531     { "startlevel",             CON_STARTLEVEL },
532     { "startscreen",            CON_STARTSCREEN },
533     { "starttrack",             CON_STARTTRACK },
534     { "starttrackslot",         CON_STARTTRACKSLOT },
535     { "starttrackvar",          CON_STARTTRACK },
536     { "state",                  CON_STATE },
537     { "stopactorsound",         CON_STOPACTORSOUND },
538     { "stopallmusic",           CON_STOPALLMUSIC },
539     { "stopallsounds",          CON_STOPALLSOUNDS },
540     { "stopsound",              CON_STOPSOUND },
541     { "stopsoundvar",           CON_STOPSOUND },
542     { "strength",               CON_STRENGTH },
543     { "subvar",                 CON_SUBVAR },
544     { "subvarvar",              CON_SUBVARVAR },
545     { "switch",                 CON_SWITCH },
546     { "swaparrays",             CON_SWAPARRAYS },
547     { "swaptrackslot",          CON_SWAPTRACKSLOT },
548     { "time",                   CON_TIME },
549     { "tip",                    CON_TIP },
550     { "tossweapon",             CON_TOSSWEAPON },
551     { "undefinecheat",          CON_UNDEFINECHEAT },
552     { "undefinegamefunc",       CON_UNDEFINEGAMEFUNC },
553     { "undefinelevel",          CON_UNDEFINELEVEL },
554     { "undefineskill",          CON_UNDEFINESKILL },
555     { "undefinevolume",         CON_UNDEFINEVOLUME },
556     { "updatesector",           CON_UPDATESECTOR },
557     { "updatesectorz",          CON_UPDATESECTORZ },
558     { "updatesectorneighbor",  CON_UPDATESECTORNEIGHBOR },
559     { "updatesectorneighborz", CON_UPDATESECTORNEIGHBORZ },
560     { "useractor",              CON_USERACTOR },
561     { "userquote",              CON_USERQUOTE },
562     { "wackplayer",             CON_WACKPLAYER },
563     { "whilevarl",              CON_WHILEVARL },
564     { "whilevarn",              CON_WHILEVARN },
565     { "whilevarvarl",           CON_WHILEVARVARL },
566     { "whilevarvarn",           CON_WHILEVARVARN },
567     { "writearraytofile",       CON_WRITEARRAYTOFILE },
568     { "xorvar",                 CON_XORVAR },
569     { "xorvarvar",              CON_XORVARVAR },
570     { "zshootvar",              CON_ZSHOOT },
571     { "{",                      CON_LEFTBRACE },
572     { "}",                      CON_RIGHTBRACE },
573 
574     { "#define",                CON_DEFINE },
575     { "#include",               CON_INCLUDE },
576     { "al",                     CON_ADDLOGVAR },
577     { "var",                    CON_GAMEVAR },
578     { "array",                  CON_GAMEARRAY },
579     { "shiftl",                 CON_SHIFTVARVARL },
580     { "shiftr",                 CON_SHIFTVARVARR },
581     { "rand",                   CON_RANDVARVAR },
582     { "set",                    CON_SETVARVAR },
583     { "add",                    CON_ADDVARVAR },
584     { "sub",                    CON_SUBVARVAR },
585     { "mul",                    CON_MULVARVAR },
586     { "div",                    CON_DIVVARVAR },
587     { "mod",                    CON_MODVARVAR },
588     { "and",                    CON_ANDVARVAR },
589     { "or",                     CON_ORVARVAR },
590     { "xor",                    CON_XORVARVAR },
591     { "ifa",                    CON_IFVARVARA },
592     { "ifae",                   CON_IFVARVARAE },
593     { "ifb",                    CON_IFVARVARB },
594     { "ifbe",                   CON_IFVARVARBE },
595     { "ifl",                    CON_IFVARVARL },
596     { "ifle",                   CON_IFVARVARLE },
597     { "ifg",                    CON_IFVARVARG },
598     { "ifge",                   CON_IFVARVARGE },
599     { "ife",                    CON_IFVARVARE },
600     { "ifn",                    CON_IFVARVARN },
601     { "ifand",                  CON_IFVARVARAND },
602     { "ifor",                   CON_IFVARVAROR },
603     { "ifxor",                  CON_IFVARVARXOR },
604     { "ifeither",               CON_IFVARVAREITHER },
605     { "ifboth",                 CON_IFVARVARBOTH },
606     { "whilen",                 CON_WHILEVARVARN },
607     { "whilel",                 CON_WHILEVARVARL },
608     { "abs",                    CON_KLABS },
609 
610     { "getp",                   CON_GETPLAYER },
611     { "getpv",                  CON_GETPLAYERVAR },
612     { "gets",                   CON_GETSECTOR },
613     { "geta",                   CON_GETACTOR },
614     { "getav",                  CON_GETACTORVAR },
615     { "getw",                   CON_GETWALL },
616     { "getu",                   CON_GETUSERDEF },
617     { "geti",                   CON_GETINPUT },
618     { "getarrayseq",            CON_GETARRAYSEQUENCE },
619 
620     { "setp",                   CON_SETPLAYER },
621     { "setpv",                  CON_SETPLAYERVAR },
622     { "sets",                   CON_SETSECTOR },
623     { "seta",                   CON_SETACTOR },
624     { "setav",                  CON_SETACTORVAR },
625     { "setw",                   CON_SETWALL },
626     { "setu",                   CON_SETUSERDEF },
627     { "seti",                   CON_SETINPUT },
628     { "setarrayseq",            CON_SETARRAYSEQUENCE },
629 
630     { "string",                 CON_DEFINEQUOTE },
631     { "print",                  CON_QUOTE },
632 
633     { "dc",                     CON_DEFINECHEAT },
634     { "dcd",                    CON_DEFINECHEATDESCRIPTION },
635     { "udc",                    CON_UNDEFINECHEAT },
636     { "ck",                     CON_CHEATKEYS },
637 
638     { "qputs",                  CON_REDEFINEQUOTE },
639 
640     { "espawn",                 CON_ESPAWN },
641     { "qspawn",                 CON_QSPAWN },
642     { "eqspawn",                CON_EQSPAWN },
643 
644     { "eshoot",                 CON_ESHOOT },
645     { "zshoot",                 CON_ZSHOOT },
646     { "ezshoot",                CON_EZSHOOT },
647     { "shoot",                  CON_SHOOT },
648 
649     { "findnearactor",          CON_FINDNEARACTOR },
650     { "findnearactor3d",        CON_FINDNEARACTOR3D },
651     { "findnearactorz",         CON_FINDNEARACTORZ },
652 
653     { "findnearsprite",         CON_FINDNEARSPRITE },
654     { "findnearsprite3d",       CON_FINDNEARSPRITE3D },
655     { "findnearspritez",        CON_FINDNEARSPRITEZ },
656 };
657 
658 static const vec2_t varvartable[] =
659 {
660     { CON_IFVARVARA,         CON_IFVARA },
661     { CON_IFVARVARAE,        CON_IFVARAE },
662     { CON_IFVARVARAND,       CON_IFVARAND },
663     { CON_IFVARVARB,         CON_IFVARB },
664     { CON_IFVARVARBE,        CON_IFVARBE },
665     { CON_IFVARVARBOTH,      CON_IFVARBOTH },
666     { CON_IFVARVARE,         CON_IFVARE },
667     { CON_IFVARVAREITHER,    CON_IFVAREITHER },
668     { CON_IFVARVARG,         CON_IFVARG },
669     { CON_IFVARVARGE,        CON_IFVARGE },
670     { CON_IFVARVARL,         CON_IFVARL },
671     { CON_IFVARVARLE,        CON_IFVARLE },
672     { CON_IFVARVARN,         CON_IFVARN },
673     { CON_IFVARVAROR,        CON_IFVAROR },
674     { CON_IFVARVARXOR,       CON_IFVARXOR },
675 
676     { CON_ADDVARVAR,         CON_ADDVAR },
677     { CON_ANDVARVAR,         CON_ANDVAR },
678     { CON_DISPLAYRANDVARVAR, CON_DISPLAYRANDVAR },
679     { CON_DIVVARVAR,         CON_DIVVAR },
680     { CON_MODVARVAR,         CON_MODVAR },
681     { CON_MULVARVAR,         CON_MULVAR },
682     { CON_ORVARVAR,          CON_ORVAR },
683     { CON_RANDVARVAR,        CON_RANDVAR },
684     { CON_SETVARVAR,         CON_SETVAR },
685     { CON_SHIFTVARVARL,      CON_SHIFTVARL },
686     { CON_SHIFTVARVARR,      CON_SHIFTVARR },
687     { CON_SUBVARVAR,         CON_SUBVAR },
688     { CON_WHILEVARVARL,      CON_WHILEVARL },
689     { CON_WHILEVARVARN,      CON_WHILEVARN },
690     { CON_XORVARVAR,         CON_XORVAR },
691 };
692 
693 static const vec2_t globalvartable[] =
694 {
695     { CON_SETVAR,         CON_SETVAR_GLOBAL },
696 #ifdef CON_DISCRETE_VAR_ACCESS
697     { CON_IFVARA,         CON_IFVARA_GLOBAL },
698     { CON_IFVARAE,        CON_IFVARAE_GLOBAL },
699     { CON_IFVARAND,       CON_IFVARAND_GLOBAL },
700     { CON_IFVARB,         CON_IFVARB_GLOBAL },
701     { CON_IFVARBE,        CON_IFVARBE_GLOBAL },
702     { CON_IFVARBOTH,      CON_IFVARBOTH_GLOBAL },
703     { CON_IFVARE,         CON_IFVARE_GLOBAL },
704     { CON_IFVAREITHER,    CON_IFVAREITHER_GLOBAL },
705     { CON_IFVARG,         CON_IFVARG_GLOBAL },
706     { CON_IFVARGE,        CON_IFVARGE_GLOBAL },
707     { CON_IFVARL,         CON_IFVARL_GLOBAL },
708     { CON_IFVARLE,        CON_IFVARLE_GLOBAL },
709     { CON_IFVARN,         CON_IFVARN_GLOBAL },
710     { CON_IFVAROR,        CON_IFVAROR_GLOBAL },
711     { CON_IFVARXOR,       CON_IFVARXOR_GLOBAL },
712     { CON_WHILEVARL,      CON_WHILEVARL_GLOBAL },
713     { CON_WHILEVARN,      CON_WHILEVARN_GLOBAL },
714 
715     { CON_ADDVAR,         CON_ADDVAR_GLOBAL },
716     { CON_ANDVAR,         CON_ANDVAR_GLOBAL },
717     { CON_DIVVAR,         CON_DIVVAR_GLOBAL },
718     { CON_MODVAR,         CON_MODVAR_GLOBAL },
719     { CON_MULVAR,         CON_MULVAR_GLOBAL },
720     { CON_ORVAR,          CON_ORVAR_GLOBAL },
721     { CON_RANDVAR,        CON_RANDVAR_GLOBAL },
722     { CON_SHIFTVARL,      CON_SHIFTVARL_GLOBAL },
723     { CON_SHIFTVARR,      CON_SHIFTVARR_GLOBAL },
724     { CON_SUBVAR,         CON_SUBVAR_GLOBAL },
725     { CON_XORVAR,         CON_XORVAR_GLOBAL },
726 #endif
727 };
728 
729 static const vec2_t playervartable[] =
730 {
731     { CON_SETVAR,         CON_SETVAR_PLAYER },
732 #ifdef CON_DISCRETE_VAR_ACCESS
733     { CON_IFVARA,         CON_IFVARA_PLAYER },
734     { CON_IFVARAE,        CON_IFVARAE_PLAYER },
735     { CON_IFVARAND,       CON_IFVARAND_PLAYER },
736     { CON_IFVARB,         CON_IFVARB_PLAYER },
737     { CON_IFVARBE,        CON_IFVARBE_PLAYER },
738     { CON_IFVARBOTH,      CON_IFVARBOTH_PLAYER },
739     { CON_IFVARE,         CON_IFVARE_PLAYER },
740     { CON_IFVAREITHER,    CON_IFVAREITHER_PLAYER },
741     { CON_IFVARG,         CON_IFVARG_PLAYER },
742     { CON_IFVARGE,        CON_IFVARGE_PLAYER },
743     { CON_IFVARL,         CON_IFVARL_PLAYER },
744     { CON_IFVARLE,        CON_IFVARLE_PLAYER },
745     { CON_IFVARN,         CON_IFVARN_PLAYER },
746     { CON_IFVAROR,        CON_IFVAROR_PLAYER },
747     { CON_IFVARXOR,       CON_IFVARXOR_PLAYER },
748     { CON_WHILEVARL,      CON_WHILEVARL_PLAYER },
749     { CON_WHILEVARN,      CON_WHILEVARN_PLAYER },
750 
751     { CON_ADDVAR,         CON_ADDVAR_PLAYER },
752     { CON_ANDVAR,         CON_ANDVAR_PLAYER },
753     { CON_DIVVAR,         CON_DIVVAR_PLAYER },
754     { CON_MODVAR,         CON_MODVAR_PLAYER },
755     { CON_MULVAR,         CON_MULVAR_PLAYER },
756     { CON_ORVAR,          CON_ORVAR_PLAYER },
757     { CON_RANDVAR,        CON_RANDVAR_PLAYER },
758     { CON_SHIFTVARL,      CON_SHIFTVARL_PLAYER },
759     { CON_SHIFTVARR,      CON_SHIFTVARR_PLAYER },
760     { CON_SUBVAR,         CON_SUBVAR_PLAYER },
761     { CON_XORVAR,         CON_XORVAR_PLAYER },
762 #endif
763 };
764 
765 static const vec2_t actorvartable[] =
766 {
767     { CON_SETVAR,         CON_SETVAR_ACTOR },
768 #ifdef CON_DISCRETE_VAR_ACCESS
769     { CON_IFVARA,         CON_IFVARA_ACTOR },
770     { CON_IFVARAE,        CON_IFVARAE_ACTOR },
771     { CON_IFVARAND,       CON_IFVARAND_ACTOR },
772     { CON_IFVARB,         CON_IFVARB_ACTOR },
773     { CON_IFVARBE,        CON_IFVARBE_ACTOR },
774     { CON_IFVARBOTH,      CON_IFVARBOTH_ACTOR },
775     { CON_IFVARE,         CON_IFVARE_ACTOR },
776     { CON_IFVAREITHER,    CON_IFVAREITHER_ACTOR },
777     { CON_IFVARG,         CON_IFVARG_ACTOR },
778     { CON_IFVARGE,        CON_IFVARGE_ACTOR },
779     { CON_IFVARL,         CON_IFVARL_ACTOR },
780     { CON_IFVARLE,        CON_IFVARLE_ACTOR },
781     { CON_IFVARN,         CON_IFVARN_ACTOR },
782     { CON_IFVAROR,        CON_IFVAROR_ACTOR },
783     { CON_IFVARXOR,       CON_IFVARXOR_ACTOR },
784     { CON_WHILEVARL,      CON_WHILEVARL_ACTOR },
785     { CON_WHILEVARN,      CON_WHILEVARN_ACTOR },
786 
787     { CON_ADDVAR,         CON_ADDVAR_ACTOR },
788     { CON_ANDVAR,         CON_ANDVAR_ACTOR },
789     { CON_DIVVAR,         CON_DIVVAR_ACTOR },
790     { CON_MODVAR,         CON_MODVAR_ACTOR },
791     { CON_MULVAR,         CON_MULVAR_ACTOR },
792     { CON_ORVAR,          CON_ORVAR_ACTOR },
793     { CON_RANDVAR,        CON_RANDVAR_ACTOR },
794     { CON_SHIFTVARL,      CON_SHIFTVARL_ACTOR },
795     { CON_SHIFTVARR,      CON_SHIFTVARR_ACTOR },
796     { CON_SUBVAR,         CON_SUBVAR_ACTOR },
797     { CON_XORVAR,         CON_XORVAR_ACTOR },
798 #endif
799 };
800 
801 static inthashtable_t h_varvar = { NULL, INTHASH_SIZE(ARRAY_SIZE(varvartable)) };
802 static inthashtable_t h_globalvar = { NULL, INTHASH_SIZE(ARRAY_SIZE(globalvartable)) };
803 static inthashtable_t h_playervar = { NULL, INTHASH_SIZE(ARRAY_SIZE(playervartable)) };
804 static inthashtable_t h_actorvar = { NULL, INTHASH_SIZE(ARRAY_SIZE(actorvartable)) };
805 
806 static inthashtable_t *const inttables[] = {
807     &h_varvar,
808     &h_globalvar,
809     &h_playervar,
810     &h_actorvar,
811 };
812 
813 
814 const tokenmap_t iter_tokens [] =
815 {
816     { "allsprites",       ITER_ALLSPRITES },
817     { "allspritesbystat", ITER_ALLSPRITESBYSTAT },
818     { "allspritesbysect", ITER_ALLSPRITESBYSECT },
819     { "allsectors",       ITER_ALLSECTORS },
820     { "allwalls",         ITER_ALLWALLS },
821     { "activelights",     ITER_ACTIVELIGHTS },
822     { "drawnsprites",     ITER_DRAWNSPRITES },
823     { "spritesofsector",  ITER_SPRITESOFSECTOR },
824     { "spritesofstatus",  ITER_SPRITESOFSTATUS },
825     { "loopofwall",       ITER_LOOPOFWALL },
826     { "wallsofsector",    ITER_WALLSOFSECTOR },
827     { "range",            ITER_RANGE },
828     // vvv alternatives go here vvv
829     { "lights",           ITER_ACTIVELIGHTS },
830     { "sprofsec",         ITER_SPRITESOFSECTOR },
831     { "sprofstat",        ITER_SPRITESOFSTATUS },
832     { "walofsec",         ITER_WALLSOFSECTOR },
833 };
834 
835 // some keywords generate different opcodes depending on the context the keyword is used in
836 // keywords_for_private_opcodes[] resolves those opcodes to the publicly facing keyword that can generate them
837 static const tokenmap_t keywords_for_private_opcodes[] =
838 {
839     { "getactor",  CON_GETSPRITEEXT },
840     { "getactor",  CON_GETACTORSTRUCT },
841     { "getactor",  CON_GETSPRITESTRUCT },
842 
843     { "setactor",  CON_SETSPRITEEXT },
844     { "setactor",  CON_SETACTORSTRUCT },
845     { "setactor",  CON_SETSPRITESTRUCT },
846 
847     { "getwall",   CON_GETWALLSTRUCT },
848     { "setwall",   CON_SETWALLSTRUCT },
849 
850     { "getsector", CON_GETSECTORSTRUCT },
851     { "setsector", CON_SETSECTORSTRUCT },
852 
853     { "getplayer", CON_GETPLAYERSTRUCT },
854     { "setplayer", CON_SETPLAYERSTRUCT },
855 };
856 
VM_GetKeywordForID(int32_t id)857 char const *VM_GetKeywordForID(int32_t id)
858 {
859     // could be better, but this is used strictly for diagnostic warning and error messages
860     for (tokenmap_t const & keyword : vm_keywords)
861         if (keyword.val == id)
862             return keyword.token;
863 
864     for (tokenmap_t const & keyword : keywords_for_private_opcodes)
865         if (keyword.val == id)
866             return keyword.token;
867 
868     return "<unknown instruction>";
869 }
870 
871 // KEEPINSYNC with enum GameEvent_t
872 const char *EventNames[MAXEVENTS] =
873 {
874     "EVENT_INIT",
875     "EVENT_ENTERLEVEL",
876     "EVENT_RESETWEAPONS",
877     "EVENT_RESETINVENTORY",
878     "EVENT_HOLSTER",
879     "EVENT_LOOKLEFT",
880     "EVENT_LOOKRIGHT",
881     "EVENT_SOARUP",
882     "EVENT_SOARDOWN",
883     "EVENT_CROUCH",
884     "EVENT_JUMP",
885     "EVENT_RETURNTOCENTER",
886     "EVENT_LOOKUP",
887     "EVENT_LOOKDOWN",
888     "EVENT_AIMUP",
889     "EVENT_FIRE",
890     "EVENT_CHANGEWEAPON",
891     "EVENT_GETSHOTRANGE",
892     "EVENT_GETAUTOAIMANGLE",
893     "EVENT_GETLOADTILE",
894     "EVENT_CHEATGETSTEROIDS",
895     "EVENT_CHEATGETHEAT",
896     "EVENT_CHEATGETBOOT",
897     "EVENT_CHEATGETSHIELD",
898     "EVENT_CHEATGETSCUBA",
899     "EVENT_CHEATGETHOLODUKE",
900     "EVENT_CHEATGETJETPACK",
901     "EVENT_CHEATGETFIRSTAID",
902     "EVENT_QUICKKICK",
903     "EVENT_INVENTORY",
904     "EVENT_USENIGHTVISION",
905     "EVENT_USESTEROIDS",
906     "EVENT_INVENTORYLEFT",
907     "EVENT_INVENTORYRIGHT",
908     "EVENT_HOLODUKEON",
909     "EVENT_HOLODUKEOFF",
910     "EVENT_USEMEDKIT",
911     "EVENT_USEJETPACK",
912     "EVENT_TURNAROUND",
913     "EVENT_DISPLAYWEAPON",
914     "EVENT_FIREWEAPON",
915     "EVENT_SELECTWEAPON",
916     "EVENT_MOVEFORWARD",
917     "EVENT_MOVEBACKWARD",
918     "EVENT_TURNLEFT",
919     "EVENT_TURNRIGHT",
920     "EVENT_STRAFELEFT",
921     "EVENT_STRAFERIGHT",
922     "EVENT_WEAPKEY1",
923     "EVENT_WEAPKEY2",
924     "EVENT_WEAPKEY3",
925     "EVENT_WEAPKEY4",
926     "EVENT_WEAPKEY5",
927     "EVENT_WEAPKEY6",
928     "EVENT_WEAPKEY7",
929     "EVENT_WEAPKEY8",
930     "EVENT_WEAPKEY9",
931     "EVENT_WEAPKEY10",
932     "EVENT_DRAWWEAPON",
933     "EVENT_DISPLAYCROSSHAIR",
934     "EVENT_DISPLAYREST",
935     "EVENT_DISPLAYSBAR",
936     "EVENT_RESETPLAYER",
937     "EVENT_INCURDAMAGE",
938     "EVENT_AIMDOWN",
939     "EVENT_GAME",
940     "EVENT_PREVIOUSWEAPON",
941     "EVENT_NEXTWEAPON",
942     "EVENT_SWIMUP",
943     "EVENT_SWIMDOWN",
944     "EVENT_GETMENUTILE",
945     "EVENT_SPAWN",
946     "EVENT_LOGO",
947     "EVENT_EGS",
948     "EVENT_DOFIRE",
949     "EVENT_PRESSEDFIRE",
950     "EVENT_USE",
951     "EVENT_PROCESSINPUT",
952     "EVENT_FAKEDOMOVETHINGS",
953     "EVENT_DISPLAYROOMS",
954     "EVENT_KILLIT",
955     "EVENT_LOADACTOR",
956     "EVENT_DISPLAYBONUSSCREEN",
957     "EVENT_DISPLAYMENU",
958     "EVENT_DISPLAYMENUREST",
959     "EVENT_DISPLAYLOADINGSCREEN",
960     "EVENT_ANIMATESPRITES",
961     "EVENT_NEWGAME",
962     "EVENT_SOUND",
963     "EVENT_CHECKTOUCHDAMAGE",
964     "EVENT_CHECKFLOORDAMAGE",
965     "EVENT_LOADGAME",
966     "EVENT_SAVEGAME",
967     "EVENT_PREGAME",
968     "EVENT_CHANGEMENU",
969     "EVENT_DAMAGEHPLANE",
970     "EVENT_ACTIVATECHEAT",
971     "EVENT_DISPLAYINACTIVEMENU",
972     "EVENT_DISPLAYINACTIVEMENUREST",
973     "EVENT_CUTSCENE",
974     "EVENT_DISPLAYCURSOR",
975     "EVENT_DISPLAYLEVELSTATS",
976     "EVENT_DISPLAYCAMERAOSD",
977     "EVENT_DISPLAYROOMSCAMERA",
978     "EVENT_DISPLAYSTART",
979     "EVENT_WORLD",
980     "EVENT_PREWORLD",
981     "EVENT_PRELEVEL",
982     "EVENT_DISPLAYSPIT",
983     "EVENT_DISPLAYFIST",
984     "EVENT_DISPLAYKNEE",
985     "EVENT_DISPLAYKNUCKLES",
986     "EVENT_DISPLAYSCUBA",
987     "EVENT_DISPLAYTIP",
988     "EVENT_DISPLAYACCESS",
989     "EVENT_MOVESECTOR",
990     "EVENT_MOVEEFFECTORS",
991     "EVENT_DISPLAYOVERHEADMAPTEXT",
992     "EVENT_PRELOADGAME",
993     "EVENT_POSTSAVEGAME",
994     "EVENT_PRECUTSCENE",
995     "EVENT_SKIPCUTSCENE",
996     "EVENT_SCREEN",
997     "EVENT_DISPLAYROOMSEND",
998     "EVENT_DISPLAYEND",
999     "EVENT_OPENMENUSOUND",
1000     "EVENT_RECOGSOUND",
1001     "EVENT_UPDATESCREENAREA",
1002     "EVENT_DISPLAYBORDER",
1003     "EVENT_SETDEFAULTS",
1004     "EVENT_MAINMENUSCREEN",
1005     "EVENT_NEWGAMESCREEN",
1006     "EVENT_ENDLEVELSCREEN",
1007     "EVENT_EXITGAMESCREEN",
1008     "EVENT_EXITPROGRAMSCREEN",
1009     "EVENT_ALTFIRE",
1010     "EVENT_ALTWEAPON",
1011     "EVENT_DISPLAYOVERHEADMAPPLAYER",
1012     "EVENT_MENUCURSORLEFT",
1013     "EVENT_MENUCURSORRIGHT",
1014     "EVENT_MENUCURSORSHADE",
1015     "EVENT_MENUSHADESELECTED",
1016     "EVENT_PLAYLEVELMUSICSLOT",
1017     "EVENT_CONTINUELEVELMUSICSLOT",
1018     "EVENT_DISPLAYPOINTER",
1019     "EVENT_LASTWEAPON",
1020     "EVENT_DAMAGESPRITE",
1021     "EVENT_POSTDAMAGESPRITE",
1022     "EVENT_DAMAGEWALL",
1023     "EVENT_DAMAGEFLOOR",
1024     "EVENT_DAMAGECEILING",
1025     "EVENT_DISPLAYROOMSCAMERATILE",
1026     "EVENT_RESETGOTPICS",
1027     "EVENT_VALIDATESTART",
1028     "EVENT_NEWGAMECUSTOM",
1029     "EVENT_INITCOMPLETE",
1030     "EVENT_CAPIR",
1031 };
1032 
1033 uint8_t *bitptr; // pointer to bitmap of which bytecode positions contain pointers
1034 
1035 #define BITPTR_SET(x) bitmap_set(bitptr, x)
1036 #define BITPTR_CLEAR(x) bitmap_clear(bitptr, x)
1037 #define BITPTR_IS_POINTER(x) bitmap_test(bitptr, x)
1038 
1039 hashtable_t h_arrays   = { MAXGAMEARRAYS >> 1, NULL };
1040 hashtable_t h_gamevars = { MAXGAMEVARS >> 1, NULL };
1041 hashtable_t h_labels   = { MAXLABELS >> 1, NULL };
1042 
C_SetScriptSize(int32_t newsize)1043 static void C_SetScriptSize(int32_t newsize)
1044 {
1045     for (int i = 0; i < g_scriptSize - 1; ++i)
1046     {
1047         if (BITPTR_IS_POINTER(i))
1048         {
1049             if (EDUKE32_PREDICT_FALSE(apScript[i] < (intptr_t)apScript || apScript[i] > (intptr_t)g_scriptPtr))
1050             {
1051                 g_errorCnt++;
1052                 buildprint("Internal compiler error at ", i, " (0x", hex(i), ")\n");
1053                 VM_ScriptInfo(&apScript[i], 16);
1054             }
1055             else
1056                 apScript[i] -= (intptr_t)apScript;
1057         }
1058     }
1059 
1060     G_Util_PtrToIdx2(&g_tile[0].execPtr, MAXTILES, sizeof(tiledata_t), apScript, P2I_FWD_NON0);
1061     G_Util_PtrToIdx2(&g_tile[0].loadPtr, MAXTILES, sizeof(tiledata_t), apScript, P2I_FWD_NON0);
1062 
1063     size_t old_bitptr_size = (((g_scriptSize + 7) >> 3) + 1) * sizeof(uint8_t);
1064     size_t new_bitptr_size = (((newsize + 7) >> 3) + 1) * sizeof(uint8_t);
1065 
1066     auto newscript = (intptr_t *)Xrealloc(apScript, newsize * sizeof(intptr_t));
1067     bitptr = (uint8_t *)Xrealloc(bitptr, new_bitptr_size);
1068 
1069     if (newsize > g_scriptSize)
1070     {
1071         Bmemset(&newscript[g_scriptSize], 0, (newsize - g_scriptSize) * sizeof(intptr_t));
1072         Bmemset(&bitptr[old_bitptr_size], 0, new_bitptr_size - old_bitptr_size);
1073     }
1074 
1075     if (apScript != newscript)
1076     {
1077         buildprint("Relocated compiled code from 0x", hex((intptr_t)apScript), " to 0x", hex((intptr_t)newscript), "\n");
1078         g_scriptPtr = g_scriptPtr - apScript + newscript;
1079         apScript    = newscript;
1080     }
1081 
1082     int const smallestSize = min(g_scriptSize, newsize);
1083 
1084     for (int i = 0; i < smallestSize - 1; ++i)
1085     {
1086         if (BITPTR_IS_POINTER(i))
1087             apScript[i] += (intptr_t)apScript;
1088     }
1089 
1090     g_scriptSize = newsize;
1091 
1092     G_Util_PtrToIdx2(&g_tile[0].execPtr, MAXTILES, sizeof(tiledata_t), apScript, P2I_BACK_NON0);
1093     G_Util_PtrToIdx2(&g_tile[0].loadPtr, MAXTILES, sizeof(tiledata_t), apScript, P2I_BACK_NON0);
1094 }
1095 
ispecial(const char c)1096 static inline bool ispecial(const char c)
1097 {
1098     return (c == ' ' || c == 0x0d || c == '(' || c == ')' ||
1099         c == ',' || c == ';' || (c == 0x0a /*&& ++g_lineNumber*/));
1100 }
1101 
scriptSkipLine(void)1102 static inline void scriptSkipLine(void)
1103 {
1104     while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
1105         textptr++;
1106 }
1107 
scriptSkipSpaces(void)1108 static inline void scriptSkipSpaces(void)
1109 {
1110     while (*textptr == ' ' || *textptr == '\t')
1111         textptr++;
1112 }
1113 
C_SkipComments(void)1114 static void C_SkipComments(void)
1115 {
1116     do
1117     {
1118         switch (*textptr)
1119         {
1120         case '\n':
1121             g_lineNumber++;
1122             fallthrough__;
1123         case ' ':
1124         case '\t':
1125         case '\r':
1126         case 0x1a:
1127             textptr++;
1128             break;
1129         case '/':
1130             switch (textptr[1])
1131             {
1132             case '/': // C++ style comment
1133                 if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
1134                     initprintf("%s:%d: debug: got comment.\n",g_scriptFileName,g_lineNumber);
1135                 scriptSkipLine();
1136                 continue;
1137             case '*': // beginning of a C style comment
1138                 if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
1139                     initprintf("%s:%d: debug: got start of comment block.\n",g_scriptFileName,g_lineNumber);
1140                 do
1141                 {
1142                     if (*textptr == '\n')
1143                         g_lineNumber++;
1144                     textptr++;
1145                 }
1146                 while (*textptr && (textptr[0] != '*' || textptr[1] != '/'));
1147 
1148                 if (EDUKE32_PREDICT_FALSE(!*textptr))
1149                 {
1150                     if (!(g_errorCnt || g_warningCnt) && g_scriptDebug)
1151                         initprintf("%s:%d: debug: EOF in comment!\n",g_scriptFileName,g_lineNumber);
1152                     C_ReportError(-1);
1153                     initprintf("%s:%d: error: found `/*' with no `*/'.\n",g_scriptFileName,g_lineNumber);
1154                     g_scriptActorOffset = g_numBraces = g_processingState = 0;
1155                     g_errorCnt++;
1156                     continue;
1157                 }
1158 
1159                 if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
1160                     initprintf("%s:%d: debug: got end of comment block.\n",g_scriptFileName,g_lineNumber);
1161 
1162                 textptr+=2;
1163                 continue;
1164             default:
1165                 C_ReportError(-1);
1166                 initprintf("%s:%d: error: malformed comment.\n", g_scriptFileName, g_lineNumber);
1167                 scriptSkipLine();
1168                 g_errorCnt++;
1169                 continue;
1170             }
1171             break;
1172 
1173         default:
1174             if (ispecial(*textptr))
1175             {
1176                 textptr++;
1177                 continue;
1178             }
1179             fallthrough__;
1180         case 0: // EOF
1181             return;
1182         }
1183     }
1184     while (1);
1185 }
1186 
GetDefID(char const * label)1187 static inline int GetDefID(char const *label) { return hash_find(&h_gamevars, label); }
GetADefID(char const * label)1188 static inline int GetADefID(char const *label) { return hash_find(&h_arrays, label); }
1189 
1190 #define LAST_LABEL (label+(g_labelCnt<<6))
isaltok(const char c)1191 static inline bool isaltok(const char c)
1192 {
1193     return (isalnum(c) || c == '{' || c == '}' || c == '/' || c == '\\' || c == '*' || c == '-' || c == '_' ||
1194             c == '.');
1195 }
1196 
C_IsLabelChar(const char c,int32_t const i)1197 static inline bool C_IsLabelChar(const char c, int32_t const i)
1198 {
1199     return (isalnum(c) || c == '_' || c == '*' || c == '?' || (i > 0 && (c == '+' || c == '-')));
1200 }
1201 
C_GetLabelNameID(memberlabel_t const * pLabel,hashtable_t const * const table,const char * psz)1202 static inline int32_t C_GetLabelNameID(memberlabel_t const *pLabel, hashtable_t const * const table, const char *psz)
1203 {
1204     // find the label psz in the table pLabel.
1205     // returns the ID for the label, or -1
1206 
1207     int const l = hash_findcase(table, psz);
1208     return (l >= 0) ? pLabel[l].lId : -1;
1209 }
1210 
C_GetLabelNameOffset(hashtable_t const * const table,const char * psz)1211 static inline int32_t C_GetLabelNameOffset(hashtable_t const * const table, const char *psz)
1212 {
1213     // find the label psz in the table pLabel.
1214     // returns the offset in the array for the label, or -1
1215 
1216     return hash_findcase(table, psz);
1217 }
1218 
C_GetNextLabelName(void)1219 static void C_GetNextLabelName(void)
1220 {
1221     int32_t i = 0;
1222 
1223     if (EDUKE32_PREDICT_FALSE(g_labelCnt >= MAXLABELS))
1224     {
1225         g_errorCnt++;
1226         C_ReportError(ERROR_TOOMANYLABELS);
1227         G_GameExit("Error: too many labels defined!");
1228         return;
1229     }
1230 
1231     C_SkipComments();
1232 
1233 //    while (ispecial(*textptr) == 0 && *textptr!='['&& *textptr!=']' && *textptr!='\t' && *textptr!='\n' && *textptr!='\r')
1234     while (C_IsLabelChar(*textptr, i))
1235     {
1236         if (i < (1<<6)-1)
1237             label[(g_labelCnt<<6) + (i++)] = *textptr;
1238         textptr++;
1239     }
1240 
1241     label[(g_labelCnt<<6)+i] = 0;
1242 
1243     if (!(g_errorCnt|g_warningCnt) && g_scriptDebug > 1)
1244         initprintf("%s:%d: debug: label `%s'.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
1245 }
1246 
scriptWriteValue(int32_t const value)1247 static inline void scriptWriteValue(int32_t const value)
1248 {
1249     BITPTR_CLEAR(g_scriptPtr-apScript);
1250     *g_scriptPtr++ = value;
1251 }
1252 
1253 // 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)1254 static inline void scriptWriteAtOffset(int32_t const value, intptr_t * const addr)
1255 {
1256     BITPTR_CLEAR(addr-apScript);
1257     *(addr) = value;
1258 }
1259 
scriptWritePointer(intptr_t const value,intptr_t * const addr)1260 static inline void scriptWritePointer(intptr_t const value, intptr_t * const addr)
1261 {
1262     BITPTR_SET(addr-apScript);
1263     *(addr) = value;
1264 }
1265 
C_GetNextGameArrayName(void)1266 static int32_t C_GetNextGameArrayName(void)
1267 {
1268     C_GetNextLabelName();
1269     int32_t const i = GetADefID(LAST_LABEL);
1270     if (EDUKE32_PREDICT_FALSE(i < 0))
1271     {
1272         g_errorCnt++;
1273         C_ReportError(ERROR_NOTAGAMEARRAY);
1274         return -1;
1275     }
1276 
1277     scriptWriteValue(i);
1278     return i;
1279 }
1280 
C_GetKeyword(void)1281 static int C_GetKeyword(void)
1282 {
1283     C_SkipComments();
1284 
1285     char *temptextptr = textptr;
1286 
1287     if (*temptextptr == 0) // EOF
1288         return -2;
1289 
1290     while (isaltok(*temptextptr) == 0)
1291     {
1292         temptextptr++;
1293         if (*temptextptr == 0)
1294             return 0;
1295     }
1296 
1297     int i = 0;
1298 
1299     while (isaltok(*temptextptr))
1300         tempbuf[i++] = *(temptextptr++);
1301     tempbuf[i] = 0;
1302 
1303     return hash_find(&h_keywords,tempbuf);
1304 }
1305 
C_GetNextKeyword(void)1306 static int C_GetNextKeyword(void) //Returns its code #
1307 {
1308     C_SkipComments();
1309 
1310     if (*textptr == 0) // EOF
1311         return -2;
1312 
1313     int l = 0;
1314     while (isaltok(*(textptr+l)))
1315     {
1316         tempbuf[l] = textptr[l];
1317         l++;
1318     }
1319     tempbuf[l] = 0;
1320 
1321     int i;
1322     if (EDUKE32_PREDICT_TRUE((i = hash_find(&h_keywords,tempbuf)) >= 0))
1323     {
1324         if (i == CON_LEFTBRACE || i == CON_RIGHTBRACE || i == CON_NULLOP)
1325             scriptWriteValue(i | (VM_IFELSE_MAGIC<<12));
1326         else scriptWriteValue(i | LINE_NUMBER);
1327 
1328         textptr += l;
1329         if (!(g_errorCnt || g_warningCnt) && g_scriptDebug)
1330             initprintf("%s:%d: debug: keyword `%s'.\n", g_scriptFileName, g_lineNumber, tempbuf);
1331         return i;
1332     }
1333 
1334     textptr += l;
1335     g_errorCnt++;
1336 
1337     if (EDUKE32_PREDICT_FALSE((tempbuf[0] == '{' || tempbuf[0] == '}') && tempbuf[1] != 0))
1338     {
1339         C_ReportError(-1);
1340         initprintf("%s:%d: error: expected whitespace between `%c' and `%s'.\n",g_scriptFileName,g_lineNumber,tempbuf[0],tempbuf+1);
1341     }
1342     else C_ReportError(ERROR_EXPECTEDKEYWORD);
1343 
1344     return -1;
1345 }
1346 
parse_decimal_number(void)1347 static int32_t parse_decimal_number(void)  // (textptr)
1348 {
1349     // decimal constants -- this is finicky business
1350     int64_t num = strtoll(textptr, NULL, 10);  // assume long long to be int64_t
1351 
1352     if (EDUKE32_PREDICT_TRUE(num >= INT32_MIN && num <= INT32_MAX))
1353     {
1354         // all OK
1355     }
1356     else if (EDUKE32_PREDICT_FALSE(num > INT32_MAX && num <= UINT32_MAX))
1357     {
1358         // Number interpreted as uint32, but packed as int32 (on 32-bit archs)
1359         // (CON code in the wild exists that does this).  Note that such conversion
1360         // is implementation-defined (C99 6.3.1.3) but GCC does the 'expected' thing.
1361 #if 0
1362         initprintf("%s:%d: warning: number greater than INT32_MAX converted to a negative one.\n",
1363                    g_szScriptFileName,g_lineNumber);
1364         g_numCompilerWarnings++;
1365 #endif
1366     }
1367     else
1368     {
1369         // out of range, this is arguably worse
1370 
1371         initprintf("%s:%d: warning: number out of the range of a 32-bit integer encountered.\n",
1372                    g_scriptFileName,g_lineNumber);
1373         g_warningCnt++;
1374     }
1375 
1376     return (int32_t)num;
1377 }
1378 
parse_hex_constant(const char * hexnum)1379 static int32_t parse_hex_constant(const char *hexnum)
1380 {
1381     uint64_t x;
1382     sscanf(hexnum, "%" PRIx64 "", &x);
1383 
1384     if (EDUKE32_PREDICT_FALSE(x > UINT32_MAX))
1385     {
1386         initprintf(g_scriptFileName, ":", g_lineNumber, ": warning: number 0x", hex(x), " truncated to 32 bits.\n");
1387         g_warningCnt++;
1388     }
1389 
1390     return x;
1391 }
1392 
C_GetNextVarType(int32_t type)1393 static void C_GetNextVarType(int32_t type)
1394 {
1395     int32_t id    = 0;
1396     int32_t flags = 0;
1397 
1398     auto varptr = g_scriptPtr;
1399 
1400     C_SkipComments();
1401 
1402     if (!type && !g_labelsOnly && (isdigit(*textptr) || ((*textptr == '-') && (isdigit(*(textptr+1))))))
1403     {
1404         scriptWriteValue(GV_FLAG_CONSTANT);
1405 
1406         if (tolower(textptr[1])=='x')  // hex constants
1407             scriptWriteValue(parse_hex_constant(textptr+2));
1408         else
1409             scriptWriteValue(parse_decimal_number());
1410 
1411         if (!(g_errorCnt || g_warningCnt) && g_scriptDebug)
1412             initprintf("%s:%d: debug: constant %ld in place of gamevar.\n", g_scriptFileName, g_lineNumber, (long)(g_scriptPtr[-1]));
1413 #if 1
1414         while (!ispecial(*textptr) && *textptr != ']') textptr++;
1415 #else
1416         C_GetNextLabelName();
1417 #endif
1418         return;
1419     }
1420     else if (*textptr == '-'/* && !isdigit(*(textptr+1))*/)
1421     {
1422         if (EDUKE32_PREDICT_FALSE(type))
1423         {
1424             g_errorCnt++;
1425             C_ReportError(ERROR_SYNTAXERROR);
1426             C_GetNextLabelName();
1427             return;
1428         }
1429 
1430         if (!(g_errorCnt || g_warningCnt) && g_scriptDebug)
1431             initprintf("%s:%d: debug: flagging gamevar as negative.\n", g_scriptFileName, g_lineNumber); //,Batol(textptr));
1432 
1433         flags = GV_FLAG_NEGATIVE;
1434         textptr++;
1435     }
1436 
1437     C_GetNextLabelName();
1438 
1439     if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,LAST_LABEL)>=0))
1440     {
1441         g_errorCnt++;
1442         C_ReportError(ERROR_ISAKEYWORD);
1443         return;
1444     }
1445 
1446     C_SkipComments();
1447 
1448     if (*textptr == '[' || *textptr == '.')     //read of array as a gamevar
1449     {
1450         flags |= GV_FLAG_ARRAY;
1451         if (*textptr != '.') textptr++;
1452         id=GetADefID(LAST_LABEL);
1453 
1454         if (id < 0)
1455         {
1456             id=GetDefID(LAST_LABEL);
1457             if ((unsigned) (id - g_structVarIDs) >= NUMQUICKSTRUCTS)
1458                 id = -1;
1459 
1460             if (EDUKE32_PREDICT_FALSE(id < 0))
1461             {
1462                 g_errorCnt++;
1463                 C_ReportError(ERROR_NOTAGAMEARRAY);
1464                 return;
1465             }
1466 
1467             flags &= ~GV_FLAG_ARRAY; // not an array
1468             flags |= GV_FLAG_STRUCT;
1469         }
1470 
1471         scriptWriteValue(id|flags);
1472 
1473         if ((flags & GV_FLAG_STRUCT) && id - g_structVarIDs == STRUCT_USERDEF)
1474         {
1475             // userdef doesn't really have an array index
1476             while (*textptr != '.')
1477             {
1478                 if (*textptr == 0xa || *textptr == 0)
1479                     break;
1480 
1481                 textptr++;
1482             }
1483 
1484             scriptWriteValue(0); // help out the VM by inserting a dummy index
1485         }
1486         else
1487         {
1488             // allow "[]" or "." but not "[."
1489             if (*textptr == ']' || (*textptr == '.' && textptr[-1] != '['))
1490             {
1491                 scriptWriteValue(g_thisActorVarID);
1492             }
1493             else
1494                 C_GetNextVarType(0);
1495 
1496             C_SkipComments();
1497         }
1498 
1499         if (EDUKE32_PREDICT_FALSE(*textptr != ']' && *textptr != '.'))
1500         {
1501             g_errorCnt++;
1502             C_ReportError(ERROR_GAMEARRAYBNC);
1503             return;
1504         }
1505 
1506         if (*textptr != '.') textptr++;
1507 
1508         //writing arrays in this way is not supported because it would require too many changes to other code
1509 
1510         if (EDUKE32_PREDICT_FALSE(type))
1511         {
1512             g_errorCnt++;
1513             C_ReportError(ERROR_INVALIDARRAYWRITE);
1514             return;
1515         }
1516 
1517         if (flags & GV_FLAG_STRUCT)
1518         {
1519             while (*textptr != '.')
1520             {
1521                 if (*textptr == 0xa || !*textptr)
1522                     break;
1523 
1524                 textptr++;
1525             }
1526 
1527             if (EDUKE32_PREDICT_FALSE(*textptr != '.'))
1528             {
1529                 g_errorCnt++;
1530                 C_ReportError(ERROR_SYNTAXERROR);
1531                 return;
1532             }
1533             textptr++;
1534             /// now pointing at 'xxx'
1535             C_GetNextLabelName();
1536             /*initprintf("found xxx label of \"%s\"\n",   label+(g_numLabels<<6));*/
1537 
1538             int32_t labelNum = -1;
1539 
1540             switch (id - g_structVarIDs)
1541             {
1542                 case STRUCT_SPRITE:         labelNum = C_GetLabelNameOffset(&h_actor,      Bstrtolower(LAST_LABEL)); break;
1543                 case STRUCT_SECTOR:         labelNum = C_GetLabelNameOffset(&h_sector,     Bstrtolower(LAST_LABEL)); break;
1544                 case STRUCT_WALL:           labelNum = C_GetLabelNameOffset(&h_wall,       Bstrtolower(LAST_LABEL)); break;
1545                 case STRUCT_PLAYER:         labelNum = C_GetLabelNameOffset(&h_player,     Bstrtolower(LAST_LABEL)); break;
1546                 case STRUCT_TSPR:           labelNum = C_GetLabelNameOffset(&h_tsprite,    Bstrtolower(LAST_LABEL)); break;
1547                 case STRUCT_PROJECTILE:
1548                 case STRUCT_THISPROJECTILE: labelNum = C_GetLabelNameOffset(&h_projectile, Bstrtolower(LAST_LABEL)); break;
1549                 case STRUCT_USERDEF:        labelNum = C_GetLabelNameOffset(&h_userdef,    Bstrtolower(LAST_LABEL)); break;
1550                 case STRUCT_INPUT:          labelNum = C_GetLabelNameOffset(&h_input,      Bstrtolower(LAST_LABEL)); break;
1551                 case STRUCT_TILEDATA:       labelNum = C_GetLabelNameOffset(&h_tiledata,   Bstrtolower(LAST_LABEL)); break;
1552                 case STRUCT_PALDATA:        labelNum = C_GetLabelNameOffset(&h_paldata,    Bstrtolower(LAST_LABEL)); break;
1553 
1554                 case STRUCT_ACTORVAR:
1555                 case STRUCT_PLAYERVAR:      labelNum = GetDefID(LAST_LABEL); break;
1556             }
1557 
1558             if (labelNum == -1)
1559             {
1560                 g_errorCnt++;
1561                 C_ReportError(ERROR_NOTAMEMBER);
1562                 return;
1563             }
1564 
1565             switch (id - g_structVarIDs)
1566             {
1567             case STRUCT_SPRITE:
1568                 {
1569                     auto const &label = ActorLabels[labelNum];
1570 
1571                     scriptWriteValue(label.lId);
1572 
1573                     Bassert((*varptr & (MAXGAMEVARS-1)) == g_structVarIDs + STRUCT_SPRITE);
1574 
1575                     if (label.flags & LABEL_HASPARM2)
1576                         C_GetNextVarType(0);
1577                     else if (label.offset != -1 && (label.flags & LABEL_READFUNC) == 0)
1578                     {
1579                         if (labelNum >= ACTOR_SPRITEEXT_BEGIN)
1580                             *varptr = (*varptr & ~(MAXGAMEVARS-1)) + g_structVarIDs + STRUCT_SPRITEEXT_INTERNAL__;
1581                         else if (labelNum >= ACTOR_STRUCT_BEGIN)
1582                             *varptr = (*varptr & ~(MAXGAMEVARS-1)) + g_structVarIDs + STRUCT_ACTOR_INTERNAL__;
1583                         else
1584                             *varptr = (*varptr & ~(MAXGAMEVARS-1)) + g_structVarIDs + STRUCT_SPRITE_INTERNAL__;
1585                     }
1586                 }
1587 
1588                 break;
1589             case STRUCT_SECTOR:
1590                 {
1591                     auto const &label = SectorLabels[labelNum];
1592 
1593                     scriptWriteValue(label.lId);
1594 
1595                     Bassert((*varptr & (MAXGAMEVARS-1)) == g_structVarIDs + STRUCT_SECTOR);
1596 
1597                     if (label.offset != -1 && (label.flags & LABEL_READFUNC) == 0)
1598                         *varptr = (*varptr & ~(MAXGAMEVARS-1)) + g_structVarIDs + STRUCT_SECTOR_INTERNAL__;
1599                 }
1600                 break;
1601             case STRUCT_WALL:
1602                 {
1603                     auto const &label = WallLabels[labelNum];
1604 
1605                     scriptWriteValue(label.lId);
1606 
1607                     Bassert((*varptr & (MAXGAMEVARS-1)) == g_structVarIDs + STRUCT_WALL);
1608 
1609                     if (label.offset != -1 && (label.flags & LABEL_READFUNC) == 0)
1610                         *varptr = (*varptr & ~(MAXGAMEVARS-1)) + g_structVarIDs + STRUCT_WALL_INTERNAL__;
1611                 }
1612                 break;
1613             case STRUCT_PLAYER:
1614                 {
1615                     auto const &label = PlayerLabels[labelNum];
1616 
1617                     scriptWriteValue(label.lId);
1618 
1619                     Bassert((*varptr & (MAXGAMEVARS-1)) == g_structVarIDs + STRUCT_PLAYER);
1620 
1621                     if (label.flags & LABEL_HASPARM2)
1622                         C_GetNextVarType(0);
1623                     else if (label.offset != -1 && (label.flags & LABEL_READFUNC) == 0)
1624                         *varptr = (*varptr & ~(MAXGAMEVARS-1)) + g_structVarIDs + STRUCT_PLAYER_INTERNAL__;
1625                 }
1626                 break;
1627             case STRUCT_ACTORVAR:
1628             case STRUCT_PLAYERVAR:
1629                 scriptWriteValue(labelNum);
1630                 break;
1631             case STRUCT_TSPR:
1632                 scriptWriteValue(TsprLabels[labelNum].lId);
1633                 break;
1634             case STRUCT_PROJECTILE:
1635             case STRUCT_THISPROJECTILE:
1636                 scriptWriteValue(ProjectileLabels[labelNum].lId);
1637                 break;
1638             case STRUCT_USERDEF:
1639                 scriptWriteValue(UserdefsLabels[labelNum].lId);
1640 
1641                 if (UserdefsLabels[labelNum].flags & LABEL_HASPARM2)
1642                     C_GetNextVarType(0);
1643                 break;
1644             case STRUCT_INPUT:
1645                 scriptWriteValue(InputLabels[labelNum].lId);
1646                 break;
1647             case STRUCT_TILEDATA:
1648                 scriptWriteValue(TileDataLabels[labelNum].lId);
1649                 break;
1650             case STRUCT_PALDATA:
1651                 scriptWriteValue(PalDataLabels[labelNum].lId);
1652                 break;
1653             }
1654         }
1655         return;
1656     }
1657 
1658     id=GetDefID(LAST_LABEL);
1659     if (id<0)   //gamevar not found
1660     {
1661         if (EDUKE32_PREDICT_TRUE(!type && !g_labelsOnly))
1662         {
1663             //try looking for a define instead
1664             Bstrcpy(tempbuf,LAST_LABEL);
1665             id = hash_find(&h_labels,tempbuf);
1666 
1667             if (EDUKE32_PREDICT_TRUE(id>=0 && labeltype[id] & LABEL_DEFINE))
1668             {
1669                 if (!(g_errorCnt || g_warningCnt) && g_scriptDebug)
1670                     initprintf("%s:%d: debug: label `%s' in place of gamevar.\n",g_scriptFileName,g_lineNumber,label+(id<<6));
1671 
1672                 scriptWriteValue(GV_FLAG_CONSTANT);
1673                 scriptWriteValue(labelcode[id]);
1674                 return;
1675             }
1676         }
1677 
1678         g_errorCnt++;
1679         C_ReportError(ERROR_NOTAGAMEVAR);
1680         return;
1681     }
1682 
1683     if (EDUKE32_PREDICT_FALSE(type == GAMEVAR_READONLY && aGameVars[id].flags & GAMEVAR_READONLY))
1684     {
1685         g_errorCnt++;
1686         C_ReportError(ERROR_VARREADONLY);
1687         return;
1688     }
1689     else if (EDUKE32_PREDICT_FALSE(aGameVars[id].flags & type))
1690     {
1691         g_errorCnt++;
1692         C_ReportError(ERROR_VARTYPEMISMATCH);
1693         return;
1694     }
1695 
1696     if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
1697         initprintf("%s:%d: debug: gamevar `%s'.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
1698 
1699     scriptWriteValue(id|flags);
1700 }
1701 
1702 #define C_GetNextVar() C_GetNextVarType(0)
1703 
C_GetManyVarsType(int32_t type,int num)1704 static FORCE_INLINE void C_GetManyVarsType(int32_t type, int num)
1705 {
1706     for (; num>0; --num)
1707         C_GetNextVarType(type);
1708 }
1709 
1710 #define C_GetManyVars(num) C_GetManyVarsType(0,num)
1711 
1712 // returns:
1713 //  -1 on EOF or wrong type or error
1714 //   0 if literal value
1715 //   LABEL_* (>0) if that type and matched
1716 //
1717 // *g_scriptPtr will contain the value OR 0 if wrong type or error
C_GetNextValue(int32_t type)1718 static int32_t C_GetNextValue(int32_t type)
1719 {
1720     C_SkipComments();
1721 
1722     if (*textptr == 0) // EOF
1723         return -1;
1724 
1725     int32_t l = 0;
1726 
1727     while (isaltok(*(textptr+l)))
1728     {
1729         tempbuf[l] = textptr[l];
1730         l++;
1731     }
1732     tempbuf[l] = 0;
1733 
1734     if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,tempbuf /*label+(g_numLabels<<6)*/)>=0))
1735     {
1736         g_errorCnt++;
1737         C_ReportError(ERROR_ISAKEYWORD);
1738         textptr+=l;
1739     }
1740 
1741     int32_t i = hash_find(&h_labels,tempbuf);
1742 
1743     if (i>=0)
1744     {
1745         if (EDUKE32_PREDICT_TRUE(labeltype[i] & type))
1746         {
1747             if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
1748             {
1749                 char *gl = C_GetLabelType(labeltype[i]);
1750                 initprintf("%s:%d: debug: %s label `%s'.\n",g_scriptFileName,g_lineNumber,gl,label+(i<<6));
1751                 Xfree(gl);
1752             }
1753 
1754             scriptWriteValue(labelcode[i]);
1755 
1756             textptr += l;
1757             return labeltype[i];
1758         }
1759 
1760         scriptWriteValue(0);
1761         textptr += l;
1762         char * const el = C_GetLabelType(type);
1763         char * const gl = C_GetLabelType(labeltype[i]);
1764         C_ReportError(-1);
1765         initprintf("%s:%d: warning: expected %s, found %s.\n",g_scriptFileName,g_lineNumber,el,gl);
1766         g_warningCnt++;
1767         Xfree(el);
1768         Xfree(gl);
1769         return -1;  // valid label name, but wrong type
1770     }
1771 
1772     if (EDUKE32_PREDICT_FALSE(isdigit(*textptr) == 0 && *textptr != '-'))
1773     {
1774         C_ReportError(ERROR_PARAMUNDEFINED);
1775         g_errorCnt++;
1776         scriptWriteValue(0);
1777         textptr+=l;
1778         if (!l) textptr++;
1779         return -1; // error!
1780     }
1781 
1782     if (EDUKE32_PREDICT_FALSE(isdigit(*textptr) && g_labelsOnly))
1783     {
1784         C_ReportError(WARNING_LABELSONLY);
1785         g_warningCnt++;
1786     }
1787 
1788     i = l-1;
1789     do
1790     {
1791         // FIXME: check for 0-9 A-F for hex
1792         if (textptr[0] == '0' && textptr[1] == 'x') break; // kill the warning for hex
1793         if (EDUKE32_PREDICT_FALSE(!isdigit(textptr[i--])))
1794         {
1795             C_ReportError(-1);
1796             initprintf("%s:%d: warning: invalid character `%c' in definition!\n",g_scriptFileName,g_lineNumber,textptr[i+1]);
1797             g_warningCnt++;
1798             break;
1799         }
1800     }
1801     while (i > 0);
1802 
1803     if (textptr[0] == '0' && tolower(textptr[1])=='x')
1804         scriptWriteValue(parse_hex_constant(textptr+2));
1805     else
1806         scriptWriteValue(parse_decimal_number());
1807 
1808     if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
1809         initprintf("%s:%d: debug: constant %ld.\n", g_scriptFileName, g_lineNumber, (long)g_scriptPtr[-1]);
1810 
1811     textptr += l;
1812 
1813     return 0;   // literal value
1814 }
1815 
C_GetStructureIndexes(bool const labelsonly,hashtable_t const * const table)1816 static int C_GetStructureIndexes(bool const labelsonly, hashtable_t const * const table)
1817 {
1818     C_SkipComments();
1819 
1820     if (EDUKE32_PREDICT_FALSE(*textptr != '[' && *textptr != '.'))
1821     {
1822         g_errorCnt++;
1823         C_ReportError(ERROR_SYNTAXERROR);
1824         return -1;
1825     }
1826 
1827     if (*textptr != '.') textptr++;
1828 
1829     C_SkipComments();
1830 
1831     if (*textptr == ']' || *textptr == '.')
1832     {
1833         scriptWriteValue(g_thisActorVarID);
1834     }
1835     else
1836     {
1837         g_labelsOnly = labelsonly;
1838         C_GetNextVar();
1839         g_labelsOnly = 0;
1840     }
1841 
1842     if (*textptr != '.') textptr++;
1843 
1844     C_SkipComments();
1845 
1846     // now get name of .xxx
1847 
1848     if (EDUKE32_PREDICT_FALSE(*textptr++ != '.'))
1849     {
1850         g_errorCnt++;
1851         C_ReportError(ERROR_SYNTAXERROR);
1852         return -1;
1853     }
1854 
1855     if (!table)
1856         return 0;
1857 
1858     // .xxx
1859 
1860     C_GetNextLabelName();
1861 
1862     int const labelNum = C_GetLabelNameOffset(table, Bstrtolower(LAST_LABEL));
1863 
1864     if (EDUKE32_PREDICT_FALSE(labelNum == -1))
1865     {
1866         g_errorCnt++;
1867         C_ReportError(ERROR_NOTAMEMBER);
1868         return -1;
1869     }
1870 
1871     return labelNum;
1872 }
1873 
1874 #ifdef CURRENTLY_UNUSED
C_IntPow2(int32_t const v)1875 static FORCE_INLINE bool C_IntPow2(int32_t const v)
1876 {
1877     return ((v!=0) && (v&(v-1))==0);
1878 }
1879 
C_Pow2IntLogBase2(int32_t const v)1880 static inline uint32_t C_Pow2IntLogBase2(int32_t const v)
1881 {
1882     static constexpr uint32_t b[] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 };
1883 
1884     uint32_t r = (v & b[0]) != 0;
1885 
1886     for (int i = 0; i < ARRAY_SSIZE(b); ++i)
1887         r |= ((v & b[i]) != 0) << i;
1888 
1889     return r;
1890 }
1891 #endif
1892 
C_CheckMalformedBranch(intptr_t lastScriptPtr)1893 static bool C_CheckMalformedBranch(intptr_t lastScriptPtr)
1894 {
1895     switch (C_GetKeyword())
1896     {
1897     case CON_RIGHTBRACE:
1898     case CON_ENDA:
1899     case CON_ENDEVENT:
1900     case CON_ENDS:
1901     case CON_ELSE:
1902         g_scriptPtr = lastScriptPtr + apScript;
1903         g_skipBranch = true;
1904         C_ReportError(-1);
1905         g_warningCnt++;
1906         initprintf("%s:%d: warning: malformed `%s' branch\n",g_scriptFileName,g_lineNumber,
1907                    VM_GetKeywordForID(*(g_scriptPtr) & VM_INSTMASK));
1908         return true;
1909     }
1910     return false;
1911 }
1912 
C_CheckEmptyBranch(int tw,intptr_t lastScriptPtr)1913 static bool C_CheckEmptyBranch(int tw, intptr_t lastScriptPtr)
1914 {
1915     // ifrnd and the others actually do something when the condition is executed
1916     if ((Bstrncmp(VM_GetKeywordForID(tw), "if", 2) && tw != CON_ELSE) ||
1917             tw == CON_IFRND || tw == CON_IFHITWEAPON || tw == CON_IFCANSEE || tw == CON_IFCANSEETARGET ||
1918             tw == CON_IFPDISTL || tw == CON_IFPDISTG || tw == CON_IFGOTWEAPONCE)
1919     {
1920         g_skipBranch = false;
1921         return false;
1922     }
1923 
1924     if ((*(g_scriptPtr) & VM_INSTMASK) != CON_NULLOP || *(g_scriptPtr)>>12 != VM_IFELSE_MAGIC)
1925         g_skipBranch = false;
1926 
1927     if (EDUKE32_PREDICT_FALSE(g_skipBranch))
1928     {
1929         C_ReportError(-1);
1930         g_warningCnt++;
1931         g_scriptPtr = lastScriptPtr + apScript;
1932         initprintf("%s:%d: warning: empty `%s' branch\n",g_scriptFileName,g_lineNumber,
1933                    VM_GetKeywordForID(*(g_scriptPtr) & VM_INSTMASK));
1934         scriptWriteAtOffset(CON_NULLOP | (VM_IFELSE_MAGIC<<12), g_scriptPtr);
1935         return true;
1936     }
1937 
1938     return false;
1939 }
1940 
C_CountCaseStatements()1941 static int C_CountCaseStatements()
1942 {
1943     char *const    temptextptr      = textptr;
1944     int const      backupLineNumber = g_lineNumber;
1945     int const      backupNumCases   = g_numCases;
1946     intptr_t const casePtrOffset    = g_caseTablePtr - apScript;
1947     intptr_t const scriptPtrOffset  = g_scriptPtr - apScript;
1948 
1949     g_numCases = 0;
1950     g_caseTablePtr = NULL;
1951     C_ParseCommand(true);
1952 
1953     // since we processed the endswitch, we need to re-increment g_checkingSwitch
1954     g_checkingSwitch++;
1955 
1956     int const numCases = g_numCases;
1957 
1958     textptr        = temptextptr;
1959     g_lineNumber   = backupLineNumber;
1960     g_numCases     = backupNumCases;
1961     g_caseTablePtr = apScript + casePtrOffset;
1962     g_scriptPtr    = apScript + scriptPtrOffset;
1963 
1964     return numCases;
1965 }
1966 
C_Include(const char * confile)1967 static void C_Include(const char *confile)
1968 {
1969     buildvfs_kfd fp = kopen4loadfrommod(confile, g_loadFromGroupOnly);
1970 
1971     if (EDUKE32_PREDICT_FALSE(fp == buildvfs_kfd_invalid))
1972     {
1973         g_errorCnt++;
1974         initprintf("%s:%d: error: could not find file `%s'.\n",g_scriptFileName,g_lineNumber,confile);
1975         return;
1976     }
1977 
1978     int32_t const len = kfilelength(fp);
1979     char *mptr = (char *)Xmalloc(len+1);
1980 
1981     initprintf("Including: %s (%d bytes)\n",confile, len);
1982 
1983     kread(fp, mptr, len);
1984     kclose(fp);
1985 
1986     mptr[len] = 0;
1987 
1988     if (*textptr == '"') // skip past the closing quote if it's there so we don't screw up the next line
1989         textptr++;
1990 
1991     char * const origtptr = textptr;
1992     char parentScriptFileName[BMAX_PATH];
1993 
1994     Bstrcpy(parentScriptFileName, g_scriptFileName);
1995     Bstrcpy(g_scriptFileName, confile);
1996 
1997     int const temp_ScriptLineNumber = g_lineNumber;
1998     g_lineNumber = 1;
1999 
2000     int const temp_ifelse_check = g_checkingIfElse;
2001     g_checkingIfElse = 0;
2002 
2003     textptr = mptr;
2004 
2005     C_SkipComments();
2006     C_ParseCommand(true);
2007 
2008     Bstrcpy(g_scriptFileName, parentScriptFileName);
2009 
2010     g_totalLines += g_lineNumber;
2011     g_lineNumber = temp_ScriptLineNumber;
2012     g_checkingIfElse = temp_ifelse_check;
2013 
2014     textptr = origtptr;
2015 
2016     Xfree(mptr);
2017 }
2018 
2019 #ifdef _WIN32
check_filename_case(const char * fn)2020 static void check_filename_case(const char *fn)
2021 {
2022         static char buf[BMAX_PATH];
2023 
2024         // .zip isn't case sensitive, and calling kopen4load on files in .zips is slow
2025         Bstrcpy(buf, fn);
2026         kzfindfilestart(buf);
2027 
2028         if (!kzfindfile(buf))
2029         {
2030             buildvfs_kfd fp;
2031             if ((fp = kopen4loadfrommod(fn, g_loadFromGroupOnly)) != buildvfs_kfd_invalid)
2032                 kclose(fp);
2033         }
2034 }
2035 #else
check_filename_case(const char * fn)2036 static void check_filename_case(const char *fn) { UNREFERENCED_PARAMETER(fn); }
2037 #endif
2038 
G_DoGameStartup(const int32_t * params)2039 void G_DoGameStartup(const int32_t *params)
2040 {
2041     auto &p0 = *g_player[0].ps;
2042     int j = 0;
2043 
2044     ud.const_visibility  = params[j++];
2045     g_impactDamage       = params[j++];
2046 
2047     p0.max_shield_amount = params[j];
2048     p0.max_player_health = params[j];
2049     g_maxPlayerHealth    = params[j++];
2050 
2051     g_startArmorAmount   = params[j++];
2052     g_actorRespawnTime   = params[j++];
2053     g_itemRespawnTime    = (g_scriptVersion >= 11) ? params[j++] : g_actorRespawnTime;
2054 
2055     if (g_scriptVersion >= 11)
2056         g_playerFriction = params[j++];
2057 
2058     if (g_scriptVersion >= 14)
2059         g_spriteGravity = params[j++];
2060 
2061     if (g_scriptVersion >= 11)
2062     {
2063         g_rpgRadius        = params[j++];
2064         g_pipebombRadius   = params[j++];
2065         g_shrinkerRadius   = params[j++];
2066         g_tripbombRadius   = params[j++];
2067         g_morterRadius     = params[j++];
2068         g_bouncemineRadius = params[j++];
2069         g_seenineRadius    = params[j++];
2070     }
2071 
2072     p0.max_ammo_amount[PISTOL_WEAPON]     = params[j++];
2073     p0.max_ammo_amount[SHOTGUN_WEAPON]    = params[j++];
2074     p0.max_ammo_amount[CHAINGUN_WEAPON]   = params[j++];
2075     p0.max_ammo_amount[RPG_WEAPON]        = params[j++];
2076     p0.max_ammo_amount[HANDBOMB_WEAPON]   = params[j++];
2077     p0.max_ammo_amount[SHRINKER_WEAPON]   = params[j++];
2078     p0.max_ammo_amount[DEVISTATOR_WEAPON] = params[j++];
2079     p0.max_ammo_amount[TRIPBOMB_WEAPON]   = params[j++];
2080 
2081     if (g_scriptVersion >= 13)
2082     {
2083         p0.max_ammo_amount[FREEZE_WEAPON] = params[j++];
2084 
2085         if (g_scriptVersion >= 14)
2086             p0.max_ammo_amount[GROW_WEAPON] = params[j++];
2087 
2088         g_damageCameras     = params[j++];
2089         g_numFreezeBounces  = params[j++];
2090         g_freezerSelfDamage = params[j++];
2091 
2092         if (g_scriptVersion >= 14)
2093         {
2094             g_deleteQueueSize   = clamp(params[j++], 0, ARRAY_SSIZE(SpriteDeletionQueue));
2095             g_tripbombLaserMode = params[j++];
2096         }
2097 
2098         if (g_scriptVersion >= 16)
2099             p0.max_ammo_amount[FLAMETHROWER_WEAPON] = params[j++];
2100     }
2101 }
2102 
C_DefineMusic(int volumeNum,int levelNum,const char * fileName)2103 void C_DefineMusic(int volumeNum, int levelNum, const char *fileName)
2104 {
2105     Bassert((unsigned)volumeNum < MAXVOLUMES+1);
2106     Bassert((unsigned)levelNum < MAXLEVELS);
2107 
2108     if (strcmp(fileName, "/.") == 0)
2109         return;
2110 
2111     map_t *const pMapInfo = &g_mapInfo[(MAXLEVELS*volumeNum)+levelNum];
2112 
2113     Xfree(pMapInfo->musicfn);
2114     pMapInfo->musicfn = dup_filename(fileName);
2115     check_filename_case(pMapInfo->musicfn);
2116 }
2117 
C_DefineVolumeFlags(int32_t vol,int32_t flags)2118 void C_DefineVolumeFlags(int32_t vol, int32_t flags)
2119 {
2120     Bassert((unsigned)vol < MAXVOLUMES);
2121 
2122     g_volumeFlags[vol] = flags;
2123 }
2124 
C_UndefineVolume(int32_t vol)2125 void C_UndefineVolume(int32_t vol)
2126 {
2127     Bassert((unsigned)vol < MAXVOLUMES);
2128 
2129     for (bssize_t i = 0; i < MAXLEVELS; i++)
2130         C_UndefineLevel(vol, i);
2131 
2132     g_volumeNames[vol][0] = '\0';
2133 
2134     g_volumeCnt = 0;
2135     for (bssize_t i = MAXVOLUMES-1; i >= 0; i--)
2136     {
2137         if (g_volumeNames[i][0])
2138         {
2139             g_volumeCnt = i+1;
2140             break;
2141         }
2142     }
2143 }
2144 
C_UndefineSkill(int32_t skill)2145 void C_UndefineSkill(int32_t skill)
2146 {
2147     Bassert((unsigned)skill < MAXSKILLS);
2148 
2149     g_skillNames[skill][0] = '\0';
2150 
2151     g_skillCnt = 0;
2152     for (bssize_t i = MAXSKILLS-1; i >= 0; i--)
2153     {
2154         if (g_skillNames[i][0])
2155         {
2156             g_skillCnt = i+1;
2157             break;
2158         }
2159     }
2160 }
2161 
C_UndefineLevel(int32_t vol,int32_t lev)2162 void C_UndefineLevel(int32_t vol, int32_t lev)
2163 {
2164     Bassert((unsigned)vol < MAXVOLUMES);
2165     Bassert((unsigned)lev < MAXLEVELS);
2166 
2167     map_t *const map = &g_mapInfo[(MAXLEVELS*vol)+lev];
2168 
2169     DO_FREE_AND_NULL(map->filename);
2170     DO_FREE_AND_NULL(map->name);
2171     map->partime = 0;
2172     map->designertime = 0;
2173 }
2174 
C_SetDefName(const char * name)2175 static int32_t C_SetDefName(const char *name)
2176 {
2177     clearDefNamePtr();
2178     g_defNamePtr = dup_filename(name);
2179     if (g_defNamePtr)
2180         initprintf("Using DEF file: %s.\n", g_defNamePtr);
2181     return (g_defNamePtr==NULL);
2182 }
2183 
2184 defaultprojectile_t DefaultProjectile;
2185 
2186 EDUKE32_STATIC_ASSERT(sizeof(projectile_t) == sizeof(DefaultProjectile));
2187 
C_AllocProjectile(int32_t j)2188 void C_AllocProjectile(int32_t j)
2189 {
2190     g_tile[j].proj = (projectile_t *)Xrealloc(g_tile[j].proj, 2 * sizeof(projectile_t));
2191     g_tile[j].defproj = g_tile[j].proj + 1;
2192 }
2193 
C_FreeProjectile(int32_t j)2194 void C_FreeProjectile(int32_t j)
2195 {
2196     DO_FREE_AND_NULL(g_tile[j].proj);
2197     g_tile[j].defproj = NULL;
2198 }
2199 
2200 
C_DefineProjectile(int32_t j,int32_t what,int32_t val)2201 static void C_DefineProjectile(int32_t j, int32_t what, int32_t val)
2202 {
2203     if (g_tile[j].proj == NULL)
2204     {
2205         C_AllocProjectile(j);
2206         *g_tile[j].proj = DefaultProjectile;
2207     }
2208 
2209     projectile_t * const proj = g_tile[j].proj;
2210 
2211     switch (what)
2212     {
2213         case PROJ_WORKSLIKE:   proj->workslike  = val; break;
2214         case PROJ_SPAWNS:      proj->spawns     = val; break;
2215         case PROJ_SXREPEAT:    proj->sxrepeat   = val; break;
2216         case PROJ_SYREPEAT:    proj->syrepeat   = val; break;
2217         case PROJ_SOUND:       proj->sound      = val; break;
2218         case PROJ_ISOUND:      proj->isound     = val; break;
2219         case PROJ_VEL:         proj->vel        = val; break;
2220         case PROJ_EXTRA:       proj->extra      = val; break;
2221         case PROJ_DECAL:       proj->decal      = val; break;
2222         case PROJ_TRAIL:       proj->trail      = val; break;
2223         case PROJ_TXREPEAT:    proj->txrepeat   = val; break;
2224         case PROJ_TYREPEAT:    proj->tyrepeat   = val; break;
2225         case PROJ_TOFFSET:     proj->toffset    = val; break;
2226         case PROJ_TNUM:        proj->tnum       = val; break;
2227         case PROJ_DROP:        proj->drop       = val; break;
2228         case PROJ_CSTAT:       proj->cstat      = val; break;
2229         case PROJ_CLIPDIST:    proj->clipdist   = val; break;
2230         case PROJ_SHADE:       proj->shade      = val; break;
2231         case PROJ_XREPEAT:     proj->xrepeat    = val; break;
2232         case PROJ_YREPEAT:     proj->yrepeat    = val; break;
2233         case PROJ_PAL:         proj->pal        = val; break;
2234         case PROJ_EXTRA_RAND:  proj->extra_rand = val; break;
2235         case PROJ_HITRADIUS:   proj->hitradius  = val; break;
2236         case PROJ_MOVECNT:     proj->movecnt    = val; break;
2237         case PROJ_OFFSET:      proj->offset     = val; break;
2238         case PROJ_BOUNCES:     proj->bounces    = val; break;
2239         case PROJ_BSOUND:      proj->bsound     = val; break;
2240         case PROJ_RANGE:       proj->range      = val; break;
2241         case PROJ_FLASH_COLOR: proj->flashcolor = val; break;
2242         case PROJ_USERDATA:    proj->userdata   = val; break;
2243         default: break;
2244     }
2245 
2246     *g_tile[j].defproj = *proj;
2247 
2248     g_tile[j].flags |= SFLAG_PROJECTILE;
2249 }
2250 
C_AllocQuote(int32_t qnum)2251 int32_t C_AllocQuote(int32_t qnum)
2252 {
2253     Bassert((unsigned)qnum < MAXQUOTES);
2254 
2255     if (apStrings[qnum] == NULL)
2256     {
2257         apStrings[qnum] = (char *)Xcalloc(MAXQUOTELEN,sizeof(uint8_t));
2258         return 1;
2259     }
2260 
2261     return 0;
2262 }
2263 
2264 #ifndef EDUKE32_TOUCH_DEVICES
C_ReplaceQuoteSubstring(const size_t q,char const * const query,char const * const replacement)2265 static void C_ReplaceQuoteSubstring(const size_t q, char const * const query, char const * const replacement)
2266 {
2267     size_t querylength = Bstrlen(query);
2268 
2269     for (bssize_t i = MAXQUOTELEN - querylength - 2; i >= 0; i--)
2270         if (Bstrncmp(&apStrings[q][i], query, querylength) == 0)
2271         {
2272             Bmemset(tempbuf, 0, sizeof(tempbuf));
2273             Bstrncpy(tempbuf, apStrings[q], i);
2274             Bstrcat(tempbuf, replacement);
2275             Bstrcat(tempbuf, &apStrings[q][i + querylength]);
2276             Bstrncpy(apStrings[q], tempbuf, MAXQUOTELEN - 1);
2277             i = MAXQUOTELEN - querylength - 2;
2278         }
2279 }
2280 #endif
2281 
C_InitQuotes(void)2282 void C_InitQuotes(void)
2283 {
2284     for (int i = 0; i < 128; i++) C_AllocQuote(i);
2285 
2286 #ifdef EDUKE32_TOUCH_DEVICES
2287     apStrings[QUOTE_DEAD] = 0;
2288 #else
2289     char const * const OpenGameFunc = gamefunctions[gamefunc_Open];
2290     C_ReplaceQuoteSubstring(QUOTE_DEAD, "SPACE", OpenGameFunc);
2291     C_ReplaceQuoteSubstring(QUOTE_DEAD, "OPEN", OpenGameFunc);
2292     C_ReplaceQuoteSubstring(QUOTE_DEAD, "USE", OpenGameFunc);
2293 #endif
2294 
2295     // most of these are based on Blood, obviously
2296     const char *PlayerObituaries[] =
2297     {
2298         "^02%s^02 beat %s^02 like a cur",
2299         "^02%s^02 broke %s",
2300         "^02%s^02 body bagged %s",
2301         "^02%s^02 boned %s^02 like a fish",
2302         "^02%s^02 castrated %s",
2303         "^02%s^02 creamed %s",
2304         "^02%s^02 crushed %s",
2305         "^02%s^02 destroyed %s",
2306         "^02%s^02 diced %s",
2307         "^02%s^02 disemboweled %s",
2308         "^02%s^02 erased %s",
2309         "^02%s^02 eviscerated %s",
2310         "^02%s^02 flailed %s",
2311         "^02%s^02 flattened %s",
2312         "^02%s^02 gave AnAl MaDnEsS to %s",
2313         "^02%s^02 gave %s^02 Anal Justice",
2314         "^02%s^02 hosed %s",
2315         "^02%s^02 hurt %s^02 real bad",
2316         "^02%s^02 killed %s",
2317         "^02%s^02 made dog meat out of %s",
2318         "^02%s^02 made mincemeat out of %s",
2319         "^02%s^02 manhandled %s",
2320         "^02%s^02 massacred %s",
2321         "^02%s^02 mutilated %s",
2322         "^02%s^02 murdered %s",
2323         "^02%s^02 neutered %s",
2324         "^02%s^02 punted %s",
2325         "^02%s^02 reamed %s",
2326         "^02%s^02 ripped %s^02 a new orifice",
2327         "^02%s^02 rocked %s",
2328         "^02%s^02 sent %s^02 to hell",
2329         "^02%s^02 shredded %s",
2330         "^02%s^02 slashed %s",
2331         "^02%s^02 slaughtered %s",
2332         "^02%s^02 sliced %s",
2333         "^02%s^02 smacked %s around",
2334         "^02%s^02 smashed %s",
2335         "^02%s^02 snuffed %s",
2336         "^02%s^02 sodomized %s",
2337         "^02%s^02 splattered %s",
2338         "^02%s^02 sprayed %s",
2339         "^02%s^02 squashed %s",
2340         "^02%s^02 throttled %s",
2341         "^02%s^02 toasted %s",
2342         "^02%s^02 vented %s",
2343         "^02%s^02 ventilated %s",
2344         "^02%s^02 wasted %s",
2345         "^02%s^02 wrecked %s",
2346     };
2347 
2348     const char *PlayerSelfObituaries[] =
2349     {
2350         "^02%s^02 is excrement",
2351         "^02%s^02 is hamburger",
2352         "^02%s^02 suffered scrotum separation",
2353         "^02%s^02 volunteered for population control",
2354         "^02%s^02 has suicided",
2355         "^02%s^02 bled out",
2356     };
2357 
2358     EDUKE32_STATIC_ASSERT(OBITQUOTEINDEX + ARRAY_SIZE(PlayerObituaries)-1 < MAXQUOTES);
2359     EDUKE32_STATIC_ASSERT(SUICIDEQUOTEINDEX + ARRAY_SIZE(PlayerSelfObituaries)-1 < MAXQUOTES);
2360 
2361     g_numObituaries = ARRAY_SIZE(PlayerObituaries);
2362     for (bssize_t i = g_numObituaries - 1; i >= 0; i--)
2363     {
2364         if (C_AllocQuote(i + OBITQUOTEINDEX))
2365             Bstrcpy(apStrings[i + OBITQUOTEINDEX], PlayerObituaries[i]);
2366     }
2367 
2368     g_numSelfObituaries = ARRAY_SIZE(PlayerSelfObituaries);
2369     for (bssize_t i = g_numSelfObituaries - 1; i >= 0; i--)
2370     {
2371         if (C_AllocQuote(i + SUICIDEQUOTEINDEX))
2372             Bstrcpy(apStrings[i + SUICIDEQUOTEINDEX], PlayerSelfObituaries[i]);
2373     }
2374 }
2375 
C_SetCfgName(const char * cfgname)2376 static void C_SetCfgName(const char *cfgname)
2377 {
2378     if (Bstrcmp(g_setupFileName, cfgname) == 0) // no need to do anything if name is the same
2379         return;
2380 
2381     if (Bstrcmp(g_setupFileName, SETUPFILENAME) != 0) // set to something else via -cfg
2382         return;
2383 
2384     ud_setup_t const config = ud.setup;
2385 #ifdef POLYMER
2386     int const renderMode = glrendmode;
2387 #endif
2388 
2389     if (!buildvfs_isdir(g_modDir))
2390     {
2391         if (buildvfs_mkdir(g_modDir, S_IRWXU) != 0)
2392         {
2393             OSD_Printf("Failed to create directory \"%s\"!\n", g_modDir);
2394             return;
2395         }
2396         else
2397             OSD_Printf("Created configuration file directory %s\n", g_modDir);
2398     }
2399 
2400     // XXX: Back up 'cfgname' as it may be the global 'tempbuf'.
2401     char *temp = Xstrdup(cfgname);
2402 
2403     CONFIG_WriteSetup(1);
2404 
2405     if (g_modDir[0] != '/')
2406         Bsnprintf(g_setupFileName, sizeof(g_setupFileName), "%s/%s", g_modDir, temp);
2407     else
2408         Bstrncpyz(g_setupFileName, temp, sizeof(g_setupFileName));
2409 
2410     DO_FREE_AND_NULL(temp);
2411 
2412     initprintf("Using config file \"%s\".\n", g_setupFileName);
2413 
2414     CONFIG_ReadSetup();
2415 
2416     ud.setup = config;
2417 #ifdef POLYMER
2418     glrendmode = renderMode;
2419 #endif
2420 }
2421 
C_BitOrNextValue(int32_t * valptr)2422 static inline void C_BitOrNextValue(int32_t *valptr)
2423 {
2424     C_GetNextValue(LABEL_DEFINE);
2425     g_scriptPtr--;
2426     *valptr |= *g_scriptPtr;
2427 }
2428 
C_FinishBitOr(int32_t value)2429 static inline void C_FinishBitOr(int32_t value)
2430 {
2431     scriptWriteValue(value);
2432 }
2433 
C_FillEventBreakStackWithJump(intptr_t * breakPtr,intptr_t destination)2434 static void C_FillEventBreakStackWithJump(intptr_t *breakPtr, intptr_t destination)
2435 {
2436     while (breakPtr)
2437     {
2438         breakPtr = apScript + (intptr_t)breakPtr;
2439         intptr_t const tempPtr = *breakPtr;
2440         scriptWriteAtOffset(destination, breakPtr);
2441         breakPtr = (intptr_t *)tempPtr;
2442     }
2443 }
2444 
scriptUpdateOpcodeForVariableType(intptr_t * ins)2445 static void scriptUpdateOpcodeForVariableType(intptr_t *ins)
2446 {
2447     int opcode = -1;
2448 
2449     if (ins[1] < MAXGAMEVARS)
2450     {
2451         switch (aGameVars[ins[1] & (MAXGAMEVARS - 1)].flags & (GAMEVAR_USER_MASK | GAMEVAR_PTR_MASK))
2452         {
2453             case 0:
2454                 opcode = inthash_find(&h_globalvar, *ins & VM_INSTMASK);
2455                 break;
2456             case GAMEVAR_PERACTOR:
2457                 opcode = inthash_find(&h_actorvar, *ins & VM_INSTMASK);
2458                 break;
2459             case GAMEVAR_PERPLAYER:
2460                 opcode = inthash_find(&h_playervar, *ins & VM_INSTMASK);
2461                 break;
2462         }
2463     }
2464 
2465     if (opcode != -1)
2466     {
2467         if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
2468         {
2469             initprintf("%s:%d: %s -> %s for var %s\n", g_scriptFileName, g_lineNumber,
2470                         VM_GetKeywordForID(*ins & VM_INSTMASK), VM_GetKeywordForID(opcode), aGameVars[ins[1] & (MAXGAMEVARS-1)].szLabel);
2471         }
2472 
2473         scriptWriteAtOffset(opcode | LINE_NUMBER, ins);
2474     }
2475 }
2476 
C_ParseCommand(bool loop)2477 static bool C_ParseCommand(bool loop /*= false*/)
2478 {
2479     int32_t i, j=0, k=0, tw;
2480 
2481     do
2482     {
2483         if (EDUKE32_PREDICT_FALSE(g_errorCnt > 63 || (*textptr == '\0') || (*(textptr+1) == '\0')))
2484             return 1;
2485 
2486         if ((g_scriptPtr - apScript) > (g_scriptSize - 4096) && g_caseTablePtr == NULL)
2487             C_SetScriptSize(g_scriptSize << 1);
2488 
2489         if (EDUKE32_PREDICT_FALSE(g_scriptDebug))
2490             C_ReportError(-1);
2491 
2492         int const otw = g_lastKeyword;
2493 
2494         C_SkipComments();
2495 
2496         switch ((g_lastKeyword = tw = C_GetNextKeyword()))
2497         {
2498         default:
2499         case -1:
2500         case -2:
2501             return 1; //End
2502         case CON_DEFSTATE:
2503             if (EDUKE32_PREDICT_FALSE(g_processingState || g_scriptActorOffset))
2504             {
2505                 C_ReportError(ERROR_FOUNDWITHIN);
2506                 g_errorCnt++;
2507                 continue;
2508             }
2509             goto DO_DEFSTATE;
2510         case CON_STATE:
2511             if (!g_scriptActorOffset && g_processingState == 0)
2512             {
2513 DO_DEFSTATE:
2514                 C_GetNextLabelName();
2515                 g_scriptPtr--;
2516                 labelcode[g_labelCnt] = g_scriptPtr-apScript;
2517                 labeltype[g_labelCnt] = LABEL_STATE;
2518 
2519                 g_processingState = 1;
2520                 Bsprintf(g_szCurrentBlockName,"%s",LAST_LABEL);
2521 
2522                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,LAST_LABEL)>=0))
2523                 {
2524                     g_errorCnt++;
2525                     C_ReportError(ERROR_ISAKEYWORD);
2526                     continue;
2527                 }
2528 
2529                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_gamevars,LAST_LABEL)>=0))
2530                 {
2531                     g_warningCnt++;
2532                     C_ReportError(WARNING_NAMEMATCHESVAR);
2533                 }
2534 
2535                 hash_add(&h_labels,LAST_LABEL,g_labelCnt,0);
2536                 g_labelCnt++;
2537                 continue;
2538             }
2539 
2540             C_GetNextLabelName();
2541 
2542             if (EDUKE32_PREDICT_FALSE((j = hash_find(&h_labels,LAST_LABEL)) < 0))
2543             {
2544                 C_ReportError(-1);
2545                 initprintf("%s:%d: error: state `%s' not found.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
2546                 g_errorCnt++;
2547                 g_scriptPtr++;
2548                 continue;
2549             }
2550 
2551             if (EDUKE32_PREDICT_FALSE((labeltype[j] & LABEL_STATE) != LABEL_STATE))
2552             {
2553                 char *gl = (char *) C_GetLabelType(labeltype[j]);
2554                 C_ReportError(-1);
2555                 initprintf("%s:%d: warning: expected state, found %s.\n", g_scriptFileName, g_lineNumber, gl);
2556                 g_warningCnt++;
2557                 Xfree(gl);
2558                 scriptWriteAtOffset(CON_NULLOP, &g_scriptPtr[-1]); // get rid of the state, leaving a nullop to satisfy if conditions
2559                 continue;  // valid label name, but wrong type
2560             }
2561 
2562             if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
2563                 initprintf("%s:%d: debug: state label `%s'.\n", g_scriptFileName, g_lineNumber, label+(j<<6));
2564 
2565             // 'state' type labels are always script addresses, as far as I can see
2566             scriptWritePointer((intptr_t)(apScript+labelcode[j]), g_scriptPtr++);
2567             continue;
2568 
2569         case CON_ENDS:
2570             if (EDUKE32_PREDICT_FALSE(g_processingState == 0))
2571             {
2572                 C_ReportError(-1);
2573                 initprintf("%s:%d: error: found `ends' without open `state'.\n",g_scriptFileName,g_lineNumber);
2574                 g_errorCnt++;
2575             }
2576 
2577             if (EDUKE32_PREDICT_FALSE(g_numBraces > 0))
2578             {
2579                 C_ReportError(ERROR_NOTTOPLEVEL);
2580                 g_errorCnt++;
2581             }
2582 
2583             if (EDUKE32_PREDICT_FALSE(g_checkingSwitch > 0))
2584             {
2585                 C_ReportError(ERROR_NOENDSWITCH);
2586                 g_errorCnt++;
2587 
2588                 g_checkingSwitch = 0; // can't be checking anymore...
2589             }
2590 
2591             g_processingState = 0;
2592             Bsprintf(g_szCurrentBlockName,"(none)");
2593             continue;
2594 
2595         case CON_GETPROJECTILE:
2596         case CON_GETTHISPROJECTILE:
2597         case CON_SETPROJECTILE:
2598         case CON_SETTHISPROJECTILE:
2599             {
2600                 int const labelNum = C_GetStructureIndexes(tw == CON_SETTHISPROJECTILE || tw == CON_GETTHISPROJECTILE, &h_projectile);
2601 
2602                 if (labelNum == -1)
2603                     continue;
2604 
2605                 scriptWriteValue(ProjectileLabels[labelNum].lId);
2606 
2607                 switch (tw)
2608                 {
2609                 case CON_SETPROJECTILE:
2610                 case CON_SETTHISPROJECTILE:
2611                     C_GetNextVar();
2612                     break;
2613                 default:
2614                     C_GetNextVarType(GAMEVAR_READONLY);
2615                     break;
2616                 }
2617                 continue;
2618             }
2619 
2620         case CON_GAMEVAR:
2621         {
2622             // syntax: gamevar <var1> <initial value> <flags>
2623             // defines var1 and sets initial value.
2624             // flags are used to define usage
2625             // (see top of this files for flags)
2626 
2627             //Skip comments before calling the check in order to align the textptr onto the label
2628             C_SkipComments();
2629             if (EDUKE32_PREDICT_FALSE(isdigit(*textptr) || (*textptr == '-')))
2630             {
2631                 g_errorCnt++;
2632                 C_ReportError(ERROR_SYNTAXERROR);
2633                 scriptSkipLine();
2634                 continue;
2635             }
2636 
2637             g_scriptPtr--;
2638 
2639             C_GetNextLabelName();
2640 
2641             if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords, LAST_LABEL)>=0))
2642             {
2643                 g_warningCnt++;
2644                 C_ReportError(WARNING_VARMASKSKEYWORD);
2645                 hash_delete(&h_keywords, LAST_LABEL);
2646             }
2647 
2648             int32_t defaultValue = 0;
2649             int32_t varFlags     = 0;
2650 
2651             if (C_GetKeyword() == -1)
2652             {
2653                 C_GetNextValue(LABEL_DEFINE); // get initial value
2654                 defaultValue = *(--g_scriptPtr);
2655 
2656                 j = 0;
2657 
2658                 while (C_GetKeyword() == -1)
2659                     C_BitOrNextValue(&j);
2660 
2661                 C_FinishBitOr(j);
2662                 varFlags = *(--g_scriptPtr);
2663 
2664                 if (EDUKE32_PREDICT_FALSE((*(g_scriptPtr)&GAMEVAR_USER_MASK)==(GAMEVAR_PERPLAYER|GAMEVAR_PERACTOR)))
2665                 {
2666                     g_warningCnt++;
2667                     varFlags ^= GAMEVAR_PERPLAYER;
2668                     C_ReportError(WARNING_BADGAMEVAR);
2669                 }
2670             }
2671 
2672             Gv_NewVar(LAST_LABEL, defaultValue, varFlags);
2673             continue;
2674         }
2675 
2676         case CON_GAMEARRAY:
2677         {
2678             //Skip comments before calling the check in order to align the textptr onto the label
2679             C_SkipComments();
2680             if (EDUKE32_PREDICT_FALSE(isdigit(*textptr) || (*textptr == '-')))
2681             {
2682                 g_errorCnt++;
2683                 C_ReportError(ERROR_SYNTAXERROR);
2684                 scriptSkipLine();
2685                 continue;
2686             }
2687             C_GetNextLabelName();
2688 
2689             if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,LAST_LABEL)>=0))
2690             {
2691                 g_warningCnt++;
2692                 C_ReportError(WARNING_ARRAYMASKSKEYWORD);
2693                 hash_delete(&h_keywords, LAST_LABEL);
2694             }
2695 
2696             i = hash_find(&h_gamevars,LAST_LABEL);
2697             if (EDUKE32_PREDICT_FALSE(i>=0))
2698             {
2699                 g_warningCnt++;
2700                 C_ReportError(WARNING_NAMEMATCHESVAR);
2701             }
2702 
2703             C_GetNextValue(LABEL_DEFINE);
2704 
2705             char const * const arrayName = LAST_LABEL;
2706             int32_t arrayFlags = 0;
2707 
2708             while (C_GetKeyword() == -1)
2709                 C_BitOrNextValue(&arrayFlags);
2710 
2711             C_FinishBitOr(arrayFlags);
2712 
2713             arrayFlags = g_scriptPtr[-1];
2714             g_scriptPtr--;
2715 
2716             Gv_NewArray(arrayName, NULL, g_scriptPtr[-1], arrayFlags);
2717 
2718             g_scriptPtr -= 2; // no need to save in script...
2719             continue;
2720         }
2721 
2722         case CON_DEFINE:
2723             {
2724                 C_GetNextLabelName();
2725 
2726                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,LAST_LABEL)>=0))
2727                 {
2728                     g_errorCnt++;
2729                     C_ReportError(ERROR_ISAKEYWORD);
2730                     continue;
2731                 }
2732 
2733                 i = hash_find(&h_gamevars,LAST_LABEL);
2734                 if (EDUKE32_PREDICT_FALSE(i>=0))
2735                 {
2736                     g_warningCnt++;
2737                     C_ReportError(WARNING_NAMEMATCHESVAR);
2738                 }
2739 
2740                 C_GetNextValue(LABEL_DEFINE);
2741 
2742                 i = hash_find(&h_labels,LAST_LABEL);
2743                 if (i>=0)
2744                 {
2745                     // if (i >= g_numDefaultLabels)
2746 
2747                     if (EDUKE32_PREDICT_FALSE(labelcode[i] != g_scriptPtr[-1]))
2748                     {
2749                         g_warningCnt++;
2750                         initprintf("%s:%d: warning: ignored redefinition of `%s' to %d (old: %d).\n",g_scriptFileName,
2751                                    g_lineNumber,LAST_LABEL, (int32_t)(g_scriptPtr[-1]), labelcode[i]);
2752                     }
2753                 }
2754                 else
2755                 {
2756                     hash_add(&h_labels,LAST_LABEL,g_labelCnt,0);
2757                     labeltype[g_labelCnt] = LABEL_DEFINE;
2758                     labelcode[g_labelCnt++] = g_scriptPtr[-1];
2759                     if (g_scriptPtr[-1] >= 0 && g_scriptPtr[-1] < MAXTILES && g_dynamicTileMapping)
2760                         G_ProcessDynamicTileMapping(label+((g_labelCnt-1)<<6),g_scriptPtr[-1]);
2761                 }
2762                 g_scriptPtr -= 2;
2763                 continue;
2764             }
2765 
2766         case CON_PALFROM:
2767             for (j=3; j>=0; j--)
2768             {
2769                 if (C_GetKeyword() == -1)
2770                     C_GetNextValue(LABEL_DEFINE);
2771                 else break;
2772             }
2773 
2774             while (j>-1)
2775             {
2776                 scriptWriteValue(0);
2777                 j--;
2778             }
2779             continue;
2780 
2781         case CON_MOVE:
2782             if (g_scriptActorOffset || g_processingState)
2783             {
2784                 if (EDUKE32_PREDICT_FALSE((C_GetNextValue(LABEL_MOVE|LABEL_DEFINE) == 0) && (g_scriptPtr[-1] != 0) && (g_scriptPtr[-1] != 1)))
2785                 {
2786                     C_ReportError(-1);
2787                     scriptWriteAtOffset(0, &g_scriptPtr[-1]);
2788                     initprintf("%s:%d: warning: expected a move, found a constant.\n",g_scriptFileName,g_lineNumber);
2789                     g_warningCnt++;
2790                 }
2791 
2792                 j = 0;
2793                 while (C_GetKeyword() == -1)
2794                     C_BitOrNextValue(&j);
2795 
2796                 C_FinishBitOr(j);
2797             }
2798             else
2799             {
2800                 g_scriptPtr--;
2801                 scriptWriteValue(CON_MOVE);
2802 
2803                 C_GetNextLabelName();
2804                 // Check to see it's already defined
2805 
2806                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,LAST_LABEL)>=0))
2807                 {
2808                     g_errorCnt++;
2809                     C_ReportError(ERROR_ISAKEYWORD);
2810                     continue;
2811                 }
2812 
2813                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_gamevars,LAST_LABEL)>=0))
2814                 {
2815                     g_warningCnt++;
2816                     C_ReportError(WARNING_NAMEMATCHESVAR);
2817                 }
2818 
2819                 if (EDUKE32_PREDICT_FALSE((i = hash_find(&h_labels,LAST_LABEL)) >= 0))
2820                 {
2821                     g_warningCnt++;
2822                     initprintf("%s:%d: warning: duplicate move `%s' ignored.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
2823                 }
2824                 else
2825                 {
2826                     hash_add(&h_labels,LAST_LABEL,g_labelCnt,0);
2827                     labeltype[g_labelCnt] = LABEL_MOVE;
2828                     labelcode[g_labelCnt++] = g_scriptPtr-apScript;
2829                 }
2830 
2831                 for (j=1; j>=0; j--)
2832                 {
2833                     if (C_GetKeyword() != -1) break;
2834                     C_GetNextValue(LABEL_DEFINE);
2835                 }
2836 
2837                 for (k=j; k>=0; k--)
2838                     scriptWriteValue(0);
2839 
2840                 scriptWriteValue(CON_END);
2841             }
2842             continue;
2843 
2844         case CON_MUSIC:
2845             {
2846                 // NOTE: this doesn't get stored in the PCode...
2847 
2848                 // music 1 stalker.mid dethtoll.mid streets.mid watrwld1.mid snake1.mid
2849                 //    thecall.mid ahgeez.mid dethtoll.mid streets.mid watrwld1.mid snake1.mid
2850                 g_scriptPtr--;
2851                 C_GetNextValue(LABEL_DEFINE); // Volume Number (0/4)
2852                 g_scriptPtr--;
2853 
2854                 k = *g_scriptPtr-1;  // 0-based volume number. -1 or MAXVOLUMES: "special"
2855                 if (k == -1)
2856                     k = MAXVOLUMES;
2857 
2858                 if (EDUKE32_PREDICT_FALSE((unsigned)k >= MAXVOLUMES+1)) // if it's not background or special music
2859                 {
2860                     g_errorCnt++;
2861                     C_ReportError(-1);
2862                     initprintf("%s:%d: error: volume number must be between 0 and MAXVOLUMES+1=%d.\n",
2863                                g_scriptFileName, g_lineNumber, MAXVOLUMES+1);
2864                     continue;
2865 
2866                 }
2867 
2868                 i = 0;
2869                 // get the file name...
2870                 while (C_GetKeyword() == -1)
2871                 {
2872                     C_SkipComments();
2873 
2874                     j = 0;
2875                     tempbuf[j] = '/';
2876                     while (isaltok(*(textptr+j)))
2877                     {
2878                         tempbuf[j+1] = textptr[j];
2879                         j++;
2880                     }
2881                     tempbuf[j+1] = '\0';
2882 
2883                     C_DefineMusic(k, i, tempbuf);
2884 
2885                     textptr += j;
2886 
2887                     if (i >= MAXLEVELS)
2888                         break;
2889                     i++;
2890                 }
2891             }
2892             continue;
2893 
2894         case CON_INCLUDE:
2895             g_scriptPtr--;
2896 
2897             C_SkipComments();
2898 
2899             j = 0;
2900             while (isaltok(*textptr))
2901             {
2902                 tempbuf[j] = *(textptr++);
2903                 j++;
2904             }
2905             tempbuf[j] = '\0';
2906 
2907             C_Include(tempbuf);
2908             continue;
2909 
2910         case CON_INCLUDEDEFAULT:
2911             C_SkipComments();
2912             C_Include(G_DefaultConFile());
2913             continue;
2914 
2915         case CON_AI:
2916             if (g_scriptActorOffset || g_processingState)
2917             {
2918                 C_GetNextValue(LABEL_AI);
2919             }
2920             else
2921             {
2922                 g_scriptPtr--;
2923                 scriptWriteValue(CON_AI);
2924 
2925                 C_GetNextLabelName();
2926 
2927                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,LAST_LABEL)>=0))
2928                 {
2929                     g_errorCnt++;
2930                     C_ReportError(ERROR_ISAKEYWORD);
2931                     continue;
2932                 }
2933 
2934                 i = hash_find(&h_gamevars,LAST_LABEL);
2935                 if (EDUKE32_PREDICT_FALSE(i>=0))
2936                 {
2937                     g_warningCnt++;
2938                     C_ReportError(WARNING_NAMEMATCHESVAR);
2939                 }
2940 
2941                 i = hash_find(&h_labels,LAST_LABEL);
2942                 if (EDUKE32_PREDICT_FALSE(i>=0))
2943                 {
2944                     g_warningCnt++;
2945                     initprintf("%s:%d: warning: duplicate ai `%s' ignored.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
2946                 }
2947                 else
2948                 {
2949                     labeltype[g_labelCnt] = LABEL_AI;
2950                     hash_add(&h_labels,LAST_LABEL,g_labelCnt,0);
2951                     labelcode[g_labelCnt++] = g_scriptPtr-apScript;
2952                 }
2953 
2954                 for (j=0; j<3; j++)
2955                 {
2956                     if (C_GetKeyword() != -1) break;
2957                     if (j == 1)
2958                         C_GetNextValue(LABEL_ACTION);
2959                     else if (j == 2)
2960                     {
2961                         if (EDUKE32_PREDICT_FALSE((C_GetNextValue(LABEL_MOVE|LABEL_DEFINE) == 0) &&
2962                             (g_scriptPtr[-1] != 0) && (g_scriptPtr[-1] != 1)))
2963                         {
2964                             C_ReportError(-1);
2965                             scriptWriteAtOffset(0, &g_scriptPtr[-1]);
2966                             initprintf("%s:%d: warning: expected a move, found a constant.\n",g_scriptFileName,g_lineNumber);
2967                             g_warningCnt++;
2968                         }
2969 
2970                         k = 0;
2971                         while (C_GetKeyword() == -1)
2972                             C_BitOrNextValue(&k);
2973 
2974                         C_FinishBitOr(k);
2975                     }
2976                 }
2977 
2978                 for (k=j; k<3; k++)
2979                     scriptWriteValue(0);
2980 
2981                 scriptWriteValue(CON_END);
2982             }
2983             continue;
2984 
2985         case CON_ACTION:
2986             if (g_scriptActorOffset || g_processingState)
2987             {
2988                 C_GetNextValue(LABEL_ACTION);
2989             }
2990             else
2991             {
2992                 g_scriptPtr--;
2993                 scriptWriteValue(CON_ACTION);
2994 
2995                 C_GetNextLabelName();
2996                 // Check to see it's already defined
2997 
2998                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,LAST_LABEL)>=0))
2999                 {
3000                     g_errorCnt++;
3001                     C_ReportError(ERROR_ISAKEYWORD);
3002                     continue;
3003                 }
3004 
3005                 i = hash_find(&h_gamevars,LAST_LABEL);
3006                 if (EDUKE32_PREDICT_FALSE(i>=0))
3007                 {
3008                     g_warningCnt++;
3009                     C_ReportError(WARNING_NAMEMATCHESVAR);
3010                 }
3011 
3012                 i = hash_find(&h_labels,LAST_LABEL);
3013                 if (EDUKE32_PREDICT_FALSE(i>=0))
3014                 {
3015                     g_warningCnt++;
3016                     initprintf("%s:%d: warning: duplicate action `%s' ignored.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
3017                 }
3018                 else
3019                 {
3020                     labeltype[g_labelCnt] = LABEL_ACTION;
3021                     labelcode[g_labelCnt] = g_scriptPtr-apScript;
3022                     hash_add(&h_labels,LAST_LABEL,g_labelCnt,0);
3023                     g_labelCnt++;
3024                 }
3025 
3026                 for (j=ACTION_PARAM_COUNT-1; j>=0; j--)
3027                 {
3028                     if (C_GetKeyword() != -1) break;
3029                     C_GetNextValue(LABEL_DEFINE);
3030                 }
3031                 for (k=j; k>=0; k--)
3032                     scriptWriteValue(0);
3033 
3034                 scriptWriteValue(CON_END);
3035             }
3036             continue;
3037 
3038         case CON_ACTOR:
3039         case CON_USERACTOR:
3040         case CON_EVENTLOADACTOR:
3041             if (EDUKE32_PREDICT_FALSE(g_processingState || g_scriptActorOffset))
3042             {
3043                 C_ReportError(ERROR_FOUNDWITHIN);
3044                 g_errorCnt++;
3045             }
3046 
3047             g_numBraces = 0;
3048             g_scriptPtr--;
3049             g_scriptActorOffset = g_scriptPtr - apScript;
3050 
3051             if (tw == CON_USERACTOR)
3052             {
3053                 C_GetNextValue(LABEL_DEFINE);
3054                 g_scriptPtr--;
3055             }
3056 
3057             // save the actor name w/o consuming it
3058             C_SkipComments();
3059             j = 0;
3060             while (isaltok(*(textptr+j)))
3061             {
3062                 g_szCurrentBlockName[j] = textptr[j];
3063                 j++;
3064             }
3065             g_szCurrentBlockName[j] = 0;
3066 
3067             j = hash_find(&h_labels, g_szCurrentBlockName);
3068 
3069             if (j != -1)
3070                 labeltype[j] |= LABEL_ACTOR;
3071 
3072             if (tw == CON_USERACTOR)
3073             {
3074                 j = *g_scriptPtr;
3075 
3076                 if (EDUKE32_PREDICT_FALSE(j > 6 || (j&3)==3))
3077                 {
3078                     C_ReportError(-1);
3079                     initprintf("%s:%d: warning: invalid useractor type. Must be 0, 1, 2"
3080                                " (notenemy, enemy, enemystayput) or have 4 added (\"doesn't move\").\n",
3081                                g_scriptFileName,g_lineNumber);
3082                     g_warningCnt++;
3083                     j = 0;
3084                 }
3085             }
3086 
3087             C_GetNextValue(LABEL_ACTOR);
3088             g_scriptPtr--;
3089 
3090             if (EDUKE32_PREDICT_FALSE((unsigned)*g_scriptPtr >= MAXTILES))
3091             {
3092                 C_ReportError(ERROR_EXCEEDSMAXTILES);
3093                 g_errorCnt++;
3094                 continue;
3095             }
3096 
3097             if (tw == CON_EVENTLOADACTOR)
3098             {
3099                 g_tile[*g_scriptPtr].loadPtr = apScript + g_scriptActorOffset;
3100                 g_checkingIfElse = 0;
3101                 continue;
3102             }
3103 
3104             g_tile[*g_scriptPtr].execPtr = apScript + g_scriptActorOffset;
3105 
3106             if (tw == CON_USERACTOR)
3107             {
3108                 if (j & 1) g_tile[*g_scriptPtr].flags |= SFLAG_BADGUY;
3109                 if (j & 2) g_tile[*g_scriptPtr].flags |= (SFLAG_BADGUY|SFLAG_BADGUYSTAYPUT);
3110                 if (j & 4) g_tile[*g_scriptPtr].flags |= SFLAG_ROTFIXED;
3111             }
3112 
3113             for (j=0; j<4; j++)
3114             {
3115                 scriptWriteAtOffset(0, apScript+g_scriptActorOffset+j);
3116                 if (j == 3)
3117                 {
3118                     j = 0;
3119                     while (C_GetKeyword() == -1)
3120                         C_BitOrNextValue(&j);
3121 
3122                     C_FinishBitOr(j);
3123                     break;
3124                 }
3125                 else
3126                 {
3127                     if (C_GetKeyword() != -1)
3128                     {
3129                         for (i=4-j; i; i--)
3130                         {
3131                             scriptWriteValue(0);
3132                         }
3133                         break;
3134                     }
3135                     switch (j)
3136                     {
3137                     case 0:
3138                         C_GetNextValue(LABEL_DEFINE);
3139                         break;
3140                     case 1:
3141                         C_GetNextValue(LABEL_ACTION);
3142                         break;
3143                     case 2:
3144                         // XXX: LABEL_MOVE|LABEL_DEFINE, what is this shit? compatibility?
3145                         // yep, it sure is :(
3146                         if (EDUKE32_PREDICT_FALSE((C_GetNextValue(LABEL_MOVE|LABEL_DEFINE) == 0) && (g_scriptPtr[-1] != 0) && (g_scriptPtr[-1] != 1)))
3147                         {
3148                             C_ReportError(-1);
3149                             scriptWriteAtOffset(0, &g_scriptPtr[-1]);
3150                             initprintf("%s:%d: warning: expected a move, found a constant.\n",g_scriptFileName,g_lineNumber);
3151                             g_warningCnt++;
3152                         }
3153                         break;
3154                     }
3155 
3156                     if (g_scriptPtr[-1] >= (intptr_t)apScript && g_scriptPtr[-1] < (intptr_t)&apScript[g_scriptSize])
3157                         scriptWritePointer(g_scriptPtr[-1], apScript + g_scriptActorOffset + j);
3158                     else
3159                         scriptWriteAtOffset(g_scriptPtr[-1], apScript + g_scriptActorOffset + j);
3160                 }
3161             }
3162             g_checkingIfElse = 0;
3163             continue;
3164 
3165         case CON_ONEVENT:
3166         case CON_APPENDEVENT:
3167             if (EDUKE32_PREDICT_FALSE(g_processingState || g_scriptActorOffset))
3168             {
3169                 C_ReportError(ERROR_FOUNDWITHIN);
3170                 g_errorCnt++;
3171             }
3172 
3173             g_numBraces = 0;
3174             g_scriptPtr--;
3175             g_scriptEventOffset = g_scriptActorOffset = g_scriptPtr - apScript;
3176 
3177             C_SkipComments();
3178             j = 0;
3179             while (isaltok(*(textptr+j)))
3180             {
3181                 g_szCurrentBlockName[j] = textptr[j];
3182                 j++;
3183             }
3184             g_szCurrentBlockName[j] = 0;
3185             //        g_labelsOnly = 1;
3186             C_GetNextValue(LABEL_EVENT);
3187             g_labelsOnly = 0;
3188             g_scriptPtr--;
3189             j= *g_scriptPtr;  // type of event
3190             g_currentEvent = j;
3191             //Bsprintf(g_szBuf,"Adding Event for %d at %lX",j, g_parsingEventPtr);
3192             //AddLog(g_szBuf);
3193             if (EDUKE32_PREDICT_FALSE((unsigned)j > MAXEVENTS-1))
3194             {
3195                 initprintf("%s:%d: error: invalid event ID.\n",g_scriptFileName,g_lineNumber);
3196                 g_errorCnt++;
3197                 continue;
3198             }
3199             // if event has already been declared then store previous script location
3200             if (!apScriptEvents[j])
3201             {
3202                 apScriptEvents[j] = g_scriptEventOffset;
3203             }
3204             else if (tw == CON_ONEVENT)
3205             {
3206                 g_scriptEventChainOffset = apScriptEvents[j];
3207                 apScriptEvents[j] = g_scriptEventOffset;
3208             }
3209             else // if (tw == CON_APPENDEVENT)
3210             {
3211                 auto previous_event_end = apScript + apScriptGameEventEnd[j];
3212                 scriptWriteAtOffset(CON_JUMP | LINE_NUMBER, previous_event_end++);
3213                 scriptWriteAtOffset(GV_FLAG_CONSTANT, previous_event_end++);
3214                 C_FillEventBreakStackWithJump((intptr_t *)*previous_event_end, g_scriptEventOffset);
3215                 scriptWriteAtOffset(g_scriptEventOffset, previous_event_end++);
3216             }
3217 
3218             g_checkingIfElse = 0;
3219 
3220             continue;
3221 
3222         case CON_QSPRINTF:
3223             C_GetManyVars(2);
3224 
3225             j = 0;
3226 
3227             while (C_GetKeyword() == -1 && j < 32)
3228                 C_GetNextVar(), j++;
3229 
3230             scriptWriteValue(CON_NULLOP | LINE_NUMBER);
3231             continue;
3232 
3233         case CON_CSTAT:
3234             C_GetNextValue(LABEL_DEFINE);
3235 
3236             if (EDUKE32_PREDICT_FALSE(g_scriptPtr[-1] == 32767))
3237             {
3238                 g_scriptPtr[-1] = 32768;
3239                 C_ReportError(-1);
3240                 initprintf("%s:%d: warning: tried to set cstat 32767, using 32768 instead.\n",g_scriptFileName,g_lineNumber);
3241                 g_warningCnt++;
3242             }
3243             else if (EDUKE32_PREDICT_FALSE((g_scriptPtr[-1] & 48) == 48))
3244             {
3245                 i = g_scriptPtr[-1];
3246                 g_scriptPtr[-1] ^= 48;
3247                 C_ReportError(-1);
3248                 initprintf("%s:%d: warning: tried to set cstat %d, using %d instead.\n",g_scriptFileName,g_lineNumber,i,(int32_t)(g_scriptPtr[-1]));
3249                 g_warningCnt++;
3250             }
3251             continue;
3252 
3253         case CON_SCREENPAL:
3254             C_GetManyVars(4);
3255             continue;
3256 
3257         case CON_HITRADIUS:
3258         case CON_DRAWLINE256:
3259             C_GetManyVars(5);
3260             continue;
3261 
3262         case CON_DRAWLINERGB:
3263             C_GetManyVars(6);
3264             continue;
3265 
3266         case CON_ADDAMMO:
3267         case CON_ADDINVENTORY:
3268         case CON_DEBRIS:
3269         case CON_GUTS:
3270         case CON_SIZEAT:
3271         case CON_SIZETO:
3272             C_GetNextValue(LABEL_DEFINE);
3273             fallthrough__;
3274         case CON_ADDKILLS:
3275         case CON_ADDPHEALTH:
3276         case CON_ADDSTRENGTH:
3277         case CON_CACTOR:
3278         case CON_CLIPDIST:
3279         case CON_COUNT:
3280         case CON_CSTATOR:
3281         case CON_DEBUG:
3282         case CON_ENDOFGAME:
3283         case CON_ENDOFLEVEL:
3284         case CON_LOTSOFGLASS:
3285         case CON_MAIL:
3286         case CON_MONEY:
3287         case CON_PAPER:
3288         case CON_SAVE:
3289         case CON_SAVENN:
3290         case CON_SLEEPTIME:
3291         case CON_SPAWN:
3292         case CON_SPRITEPAL:
3293         case CON_STRENGTH:
3294             C_GetNextValue(LABEL_DEFINE);
3295             continue;
3296 
3297         case CON_QUOTE:
3298             C_GetNextValue(LABEL_DEFINE);
3299             if (EDUKE32_PREDICT_FALSE(((unsigned)g_scriptPtr[-1] >= MAXQUOTES) || apStrings[g_scriptPtr[-1]] == NULL))
3300             {
3301                 g_errorCnt++;
3302                 C_ReportError(-1);
3303                 initprintf("%s:%d: error: invalid quote\n", g_scriptFileName, g_lineNumber);
3304             }
3305             continue;
3306 
3307         case CON_ELSE:
3308             {
3309                 if (EDUKE32_PREDICT_FALSE(!g_checkingIfElse))
3310                 {
3311                     g_scriptPtr--;
3312                     auto const tempscrptr = g_scriptPtr;
3313                     g_warningCnt++;
3314                     C_ReportError(-1);
3315 
3316                     initprintf("%s:%d: warning: found `else' with no `if'\n", g_scriptFileName, g_lineNumber);
3317 
3318                     if (C_GetKeyword() == CON_LEFTBRACE)
3319                     {
3320                         C_GetNextKeyword();
3321                         g_numBraces++;
3322 
3323                         C_ParseCommand(true);
3324                     }
3325                     else C_ParseCommand();
3326 
3327                     g_scriptPtr = tempscrptr;
3328 
3329                     continue;
3330                 }
3331 
3332                 intptr_t const lastScriptPtr = &g_scriptPtr[-1] - apScript;
3333 
3334                 g_skipBranch = false;
3335                 g_checkingIfElse--;
3336 
3337                 if (C_CheckMalformedBranch(lastScriptPtr))
3338                     continue;
3339 
3340                 intptr_t const offset = (unsigned) (g_scriptPtr-apScript);
3341 
3342                 g_scriptPtr++; //Leave a spot for the fail location
3343 
3344                 C_ParseCommand();
3345 
3346                 if (C_CheckEmptyBranch(tw, lastScriptPtr))
3347                     continue;
3348 
3349                 auto const tempscrptr = (intptr_t *) apScript+offset;
3350                 scriptWritePointer((intptr_t)g_scriptPtr, tempscrptr);
3351 
3352                 continue;
3353             }
3354 
3355         case CON_SETSECTOR:
3356             {
3357                 intptr_t * const ins = &g_scriptPtr[-1];
3358                 int const labelNum = C_GetStructureIndexes(1, &h_sector);
3359 
3360                 if (labelNum == -1)
3361                     continue;
3362 
3363                 Bassert((*ins & VM_INSTMASK) == CON_SETSECTOR);
3364 
3365                 auto const &label = SectorLabels[labelNum];
3366 
3367                 if (label.offset != -1 && (label.flags & LABEL_WRITEFUNC) == 0)
3368                     *ins = CON_SETSECTORSTRUCT | LINE_NUMBER;
3369 
3370                 scriptWriteValue(label.lId);
3371 
3372                 C_GetNextVar();
3373                 continue;
3374             }
3375 
3376         case CON_GETSECTOR:
3377             {
3378                 intptr_t * const ins = &g_scriptPtr[-1];
3379                 int const labelNum = C_GetStructureIndexes(1, &h_sector);
3380 
3381                 if (labelNum == -1)
3382                     continue;
3383 
3384                 Bassert((*ins & VM_INSTMASK) == CON_GETSECTOR);
3385 
3386                 auto const &label = SectorLabels[labelNum];
3387 
3388                 if (label.offset != -1 && (label.flags & LABEL_READFUNC) == 0)
3389                     *ins = CON_GETSECTORSTRUCT | LINE_NUMBER;
3390 
3391                 scriptWriteValue(label.lId);
3392 
3393                 C_GetNextVarType(GAMEVAR_READONLY);
3394                 continue;
3395             }
3396 
3397 
3398         case CON_FINDNEARACTOR3D:
3399         case CON_FINDNEARACTOR:
3400         case CON_FINDNEARACTORZ:
3401         case CON_FINDNEARSPRITE3D:
3402         case CON_FINDNEARSPRITE:
3403         case CON_FINDNEARSPRITEZ:
3404             {
3405                 C_GetNextValue(LABEL_DEFINE); // get <type>
3406 
3407                 // get the ID of the DEF
3408                 C_GetNextVar();
3409                 switch (tw)
3410                 {
3411                 case CON_FINDNEARACTORZ:
3412                 case CON_FINDNEARSPRITEZ:
3413                     C_GetNextVar();
3414                 default:
3415                     break;
3416                 }
3417                 // target var
3418                 // get the ID of the DEF
3419                 C_GetNextVarType(GAMEVAR_READONLY);
3420                 continue;
3421             }
3422 
3423         case CON_SETWALL:
3424             {
3425                 intptr_t * const ins = &g_scriptPtr[-1];
3426                 int const labelNum = C_GetStructureIndexes(1, &h_wall);
3427 
3428                 if (labelNum == -1)
3429                     continue;
3430 
3431                 Bassert((*ins & VM_INSTMASK) == CON_SETWALL);
3432 
3433                 auto const &label = WallLabels[labelNum];
3434 
3435                 if (label.offset != -1 && (label.flags & LABEL_WRITEFUNC) == 0)
3436                     *ins = CON_SETWALLSTRUCT | LINE_NUMBER;
3437 
3438                 scriptWriteValue(label.lId);
3439 
3440                 C_GetNextVar();
3441                 continue;
3442             }
3443 
3444         case CON_GETWALL:
3445             {
3446                 intptr_t * const ins = &g_scriptPtr[-1];
3447                 int const labelNum = C_GetStructureIndexes(1, &h_wall);
3448 
3449                 if (labelNum == -1)
3450                     continue;
3451 
3452                 Bassert((*ins & VM_INSTMASK) == CON_GETWALL);
3453 
3454                 auto const &label = WallLabels[labelNum];
3455 
3456                 if (label.offset != -1 && (label.flags & LABEL_READFUNC) == 0)
3457                     *ins = CON_GETWALLSTRUCT | LINE_NUMBER;
3458 
3459                 scriptWriteValue(label.lId);
3460 
3461                 C_GetNextVarType(GAMEVAR_READONLY);
3462                 continue;
3463             }
3464 
3465         case CON_SETPLAYER:
3466             {
3467                 intptr_t * const ins = &g_scriptPtr[-1];
3468                 int const labelNum = C_GetStructureIndexes(1, &h_player);
3469 
3470                 if (labelNum == -1)
3471                     continue;
3472 
3473                 Bassert((*ins & VM_INSTMASK) == CON_SETPLAYER);
3474 
3475                 auto const &label = PlayerLabels[labelNum];
3476 
3477                 if (label.offset != -1 && (label.flags & (LABEL_WRITEFUNC|LABEL_HASPARM2)) == 0)
3478                     *ins = CON_SETPLAYERSTRUCT | LINE_NUMBER;
3479 
3480                 scriptWriteValue(label.lId);
3481 
3482                 if (label.flags & LABEL_HASPARM2)
3483                     C_GetNextVar();
3484 
3485                 C_GetNextVar();
3486                 continue;
3487             }
3488 
3489         case CON_GETPLAYER:
3490             {
3491                 intptr_t * const ins = &g_scriptPtr[-1];
3492                 int const labelNum = C_GetStructureIndexes(1, &h_player);
3493 
3494                 if (labelNum == -1)
3495                     continue;
3496 
3497                 Bassert((*ins & VM_INSTMASK) == CON_GETPLAYER);
3498 
3499                 auto const &label = PlayerLabels[labelNum];
3500 
3501                 if (label.offset != -1 && (label.flags & (LABEL_READFUNC|LABEL_HASPARM2)) == 0)
3502                     *ins = CON_GETPLAYERSTRUCT | LINE_NUMBER;
3503 
3504                 scriptWriteValue(label.lId);
3505 
3506                 if (label.flags & LABEL_HASPARM2)
3507                     C_GetNextVar();
3508 
3509                 C_GetNextVarType(GAMEVAR_READONLY);
3510                 continue;
3511             }
3512 
3513         case CON_SETINPUT:
3514         case CON_GETINPUT:
3515             {
3516                 int const labelNum = C_GetStructureIndexes(1, &h_input);
3517 
3518                 if (labelNum == -1)
3519                     continue;
3520 
3521                 scriptWriteValue(InputLabels[labelNum].lId);
3522 
3523                 C_GetNextVarType(tw == CON_GETINPUT ? GAMEVAR_READONLY : 0);
3524                 continue;
3525             }
3526 
3527         case CON_SETTILEDATA:
3528         case CON_GETTILEDATA:
3529         {
3530             int const labelNum = C_GetStructureIndexes(0, &h_tiledata);
3531 
3532             if (labelNum == -1)
3533                 continue;
3534 
3535             scriptWriteValue(TileDataLabels[labelNum].lId);
3536 
3537             C_GetNextVarType((tw == CON_GETTILEDATA) ? GAMEVAR_READONLY : 0);
3538             continue;
3539         }
3540 
3541         case CON_SETUSERDEF:
3542         case CON_GETUSERDEF:
3543             {
3544                 // now get name of .xxx
3545                 while (*textptr != '.')
3546                 {
3547                     if (*textptr == 0xa || !*textptr)
3548                         break;
3549 
3550                     textptr++;
3551                 }
3552 
3553                 if (EDUKE32_PREDICT_FALSE(*textptr!='.'))
3554                 {
3555                     g_errorCnt++;
3556                     C_ReportError(ERROR_SYNTAXERROR);
3557                     continue;
3558                 }
3559                 textptr++;
3560                 C_GetNextLabelName();
3561 
3562                 int const labelNum=C_GetLabelNameID(UserdefsLabels,&h_userdef,Bstrtolower(LAST_LABEL));
3563 
3564                 if (EDUKE32_PREDICT_FALSE(labelNum == -1))
3565                 {
3566                     g_errorCnt++;
3567                     C_ReportError(ERROR_NOTAMEMBER);
3568                     continue;
3569                 }
3570                 scriptWriteValue(labelNum);
3571 
3572                 if (UserdefsLabels[labelNum].flags & LABEL_HASPARM2)
3573                     C_GetNextVar();
3574 
3575                 C_GetNextVarType((tw == CON_GETUSERDEF) ? GAMEVAR_READONLY : 0);
3576                 continue;
3577             }
3578 
3579         case CON_SETACTOR:
3580             {
3581                 intptr_t * const ins = &g_scriptPtr[-1];
3582                 int const labelNum = C_GetStructureIndexes(1, &h_actor);
3583 
3584                 if (labelNum == -1)
3585                     continue;
3586 
3587                 Bassert((*ins & VM_INSTMASK) == CON_SETACTOR);
3588 
3589                 auto const &label = ActorLabels[labelNum];
3590 
3591                 if (label.offset != -1 && (label.flags & (LABEL_WRITEFUNC|LABEL_HASPARM2)) == 0)
3592                 {
3593                     if (labelNum >= ACTOR_SPRITEEXT_BEGIN)
3594                         *ins = CON_SETSPRITEEXT | LINE_NUMBER;
3595                     else if (labelNum >= ACTOR_STRUCT_BEGIN)
3596                         *ins = CON_SETACTORSTRUCT | LINE_NUMBER;
3597                     else
3598                         *ins = CON_SETSPRITESTRUCT | LINE_NUMBER;
3599                 }
3600 
3601                 scriptWriteValue(label.lId);
3602 
3603                 if (label.flags & LABEL_HASPARM2)
3604                     C_GetNextVar();
3605 
3606                 C_GetNextVar();
3607                 continue;
3608             }
3609 
3610         case CON_GETACTOR:
3611             {
3612                 intptr_t * const ins = &g_scriptPtr[-1];
3613                 int const labelNum = C_GetStructureIndexes(1, &h_actor);
3614 
3615                 if (labelNum == -1)
3616                     continue;
3617 
3618                 Bassert((*ins & VM_INSTMASK) == CON_GETACTOR);
3619 
3620                 auto const &label = ActorLabels[labelNum];
3621 
3622                 if (label.offset != -1 && (label.flags & (LABEL_READFUNC|LABEL_HASPARM2)) == 0)
3623                 {
3624                     if (labelNum >= ACTOR_SPRITEEXT_BEGIN)
3625                         *ins = CON_GETSPRITEEXT | LINE_NUMBER;
3626                     else if (labelNum >= ACTOR_STRUCT_BEGIN)
3627                         *ins = CON_GETACTORSTRUCT | LINE_NUMBER;
3628                     else
3629                         *ins = CON_GETSPRITESTRUCT | LINE_NUMBER;
3630                 }
3631 
3632                 scriptWriteValue(label.lId);
3633 
3634                 if (label.flags & LABEL_HASPARM2)
3635                     C_GetNextVar();
3636 
3637                 C_GetNextVarType(GAMEVAR_READONLY);
3638                 continue;
3639             }
3640 
3641         case CON_GETTSPR:
3642         case CON_SETTSPR:
3643             {
3644 #if 0
3645                 if (unlikely(g_currentEvent != EVENT_ANIMATESPRITES))
3646                 {
3647                     C_ReportError(-1);
3648                     initprintf("%s:%d: warning: found `%s' outside of EVENT_ANIMATESPRITES\n",g_szScriptFileName,g_lineNumber,tempbuf);
3649                     g_numCompilerWarnings++;
3650                 }
3651 #endif
3652                 int const labelNum = C_GetStructureIndexes(1, &h_tsprite);
3653 
3654                 if (labelNum == -1)
3655                     continue;
3656 
3657                 scriptWriteValue(TsprLabels[labelNum].lId);
3658 
3659                 C_GetNextVarType((tw == CON_GETTSPR) ? GAMEVAR_READONLY : 0);
3660                 continue;
3661             }
3662         case CON_ADDLOGVAR:
3663             g_labelsOnly = 1;
3664             C_GetNextVar();
3665             g_labelsOnly = 0;
3666             continue;
3667 
3668         case CON_COS:
3669         case CON_DIVR:
3670         case CON_DIVRU:
3671         case CON_HEADSPRITESECT:
3672         case CON_HEADSPRITESTAT:
3673         case CON_NEXTSPRITESECT:
3674         case CON_NEXTSPRITESTAT:
3675         case CON_PREVSPRITESECT:
3676         case CON_PREVSPRITESTAT:
3677         case CON_QSTRLEN:
3678         case CON_SECTOROFWALL:
3679         case CON_SIN:
3680             C_GetNextVarType(GAMEVAR_READONLY);
3681             fallthrough__;
3682         case CON_ACTIVATECHEAT:
3683         case CON_ANGOFF:
3684         case CON_CAPIA:
3685         case CON_CHECKACTIVATORMOTION:
3686         case CON_CHECKAVAILINVEN:
3687         case CON_CHECKAVAILWEAPON:
3688         case CON_CLEARMAPSTATE:
3689         case CON_CMENU:
3690         case CON_ECHO:
3691         case CON_EQSPAWN:
3692         case CON_ESHOOT:
3693         case CON_ESPAWN:
3694         case CON_GLOBALSOUND:
3695         case CON_GUNIQHUDID:
3696         case CON_INITTIMER:
3697         case CON_JUMP:
3698         case CON_LOCKPLAYER:
3699         case CON_MOVESECTOR:
3700         case CON_OPERATEMASTERSWITCHES:
3701         case CON_OPERATERESPAWNS:
3702         case CON_QSPAWN:
3703         case CON_QUAKE:
3704         case CON_RESETPLAYERFLAGS:
3705         case CON_SAVEGAMEVAR:
3706         case CON_SCREENSOUND:
3707         case CON_SECTCLEARINTERPOLATION:
3708         case CON_SECTSETINTERPOLATION:
3709         case CON_SETACTORANGLE:
3710         case CON_SETGAMEPALETTE:
3711         case CON_SETMUSICPOSITION:
3712         case CON_SETPLAYERANGLE:
3713         case CON_SHOOT:
3714         case CON_SOUNDONCE:
3715         case CON_SOUND:
3716         case CON_STARTCUTSCENE:
3717         case CON_STARTTRACK:
3718         case CON_STOPSOUND:
3719         case CON_TIME:
3720         case CON_USERQUOTE:
3721             C_GetNextVar();
3722             continue;
3723 
3724         case CON_SQRT:
3725             C_GetNextVar();
3726             fallthrough__;
3727         case CON_DISPLAYRAND:
3728         case CON_FINDOTHERPLAYER:
3729         case CON_FINDPLAYER:
3730         case CON_GETACTORANGLE:
3731         case CON_GETANGLETOTARGET:
3732         case CON_GETCURRADDRESS:
3733         case CON_GETMUSICPOSITION:
3734         case CON_GETPLAYERANGLE:
3735         case CON_GETTICKS:
3736         case CON_INV:
3737         case CON_KLABS:
3738         case CON_READGAMEVAR:
3739             C_GetNextVarType(GAMEVAR_READONLY);
3740             continue;
3741 
3742         case CON_CALCHYPOTENUSE:
3743         case CON_CLAMP:
3744         case CON_GETCLOSESTCOL:
3745             C_GetNextVarType(GAMEVAR_READONLY);
3746             fallthrough__;
3747         case CON_ACTORSOUND:
3748         case CON_CAPIS:
3749         case CON_CHANGESPRITESECT:
3750         case CON_CHANGESPRITESTAT:
3751         case CON_EZSHOOT:
3752         case CON_GETPNAME:
3753         case CON_PRELOADTRACKSLOTFORSWAP:
3754         case CON_QGETSYSSTR:
3755         case CON_QSTRCAT:
3756         case CON_QSTRCPY:
3757         case CON_SPAWNCEILINGGLASS:
3758         case CON_SPAWNWALLGLASS:
3759         case CON_SPAWNWALLSTAINEDGLASS:
3760         case CON_STARTLEVEL:
3761         case CON_STARTTRACKSLOT:
3762         case CON_STOPACTORSOUND:
3763         case CON_SWAPTRACKSLOT:
3764         case CON_ZSHOOT:
3765         case CON_GETGAMEFUNCBIND:
3766             C_GetManyVars(2);
3767             continue;
3768 
3769         case CON_ENHANCED:
3770             g_scriptPtr--;
3771             C_GetNextValue(LABEL_DEFINE);
3772             g_scriptPtr--;
3773             if (EDUKE32_PREDICT_FALSE(*g_scriptPtr > BYTEVERSION_EDUKE32))
3774             {
3775                 g_warningCnt++;
3776                 initprintf("%s:%d: warning: need build %d, found build %d\n",g_scriptFileName,g_lineNumber,k,BYTEVERSION_EDUKE32);
3777             }
3778             continue;
3779 
3780         case CON_DYNAMICREMAP:
3781             g_scriptPtr--;
3782             if (EDUKE32_PREDICT_FALSE(g_dynamicTileMapping))
3783             {
3784                 initprintf("%s:%d: warning: duplicate dynamicremap statement\n",g_scriptFileName,g_lineNumber);
3785                 g_warningCnt++;
3786             }
3787 #ifdef DYNTILEREMAP_ENABLE
3788 #ifdef DEBUGGINGAIDS
3789                 else
3790                     initprintf("Using dynamic tile remapping\n");
3791 #endif
3792             g_dynamicTileMapping = 1;
3793 #else
3794             else
3795             {
3796                 initprintf("%s:%d: warning: dynamic tile remapping is disabled in this build\n",g_scriptFileName,g_lineNumber);
3797                 g_warningCnt++;
3798             }
3799 #endif
3800             continue;
3801 
3802         case CON_DYNAMICSOUNDREMAP:
3803             g_scriptPtr--;
3804             if (EDUKE32_PREDICT_FALSE(g_dynamicSoundMapping))
3805             {
3806                 initprintf("%s:%d: warning: duplicate dynamicsoundremap statement\n",g_scriptFileName,g_lineNumber);
3807                 g_warningCnt++;
3808             }
3809             else
3810 #ifdef DYNSOUNDREMAP_ENABLE
3811 #ifdef DEBUGGINGAIDS
3812                 initprintf("Using dynamic sound remapping\n");
3813 #endif
3814 
3815             g_dynamicSoundMapping = 1;
3816 #else
3817             {
3818                 initprintf("%s:%d: warning: dynamic sound remapping is disabled in this build\n",g_scriptFileName,g_lineNumber);
3819                 g_warningCnt++;
3820             }
3821 #endif
3822             continue;
3823 
3824         case CON_ADDVAR:
3825         case CON_ANDVAR:
3826         case CON_DISPLAYRANDVAR:
3827         case CON_DIVVAR:
3828         case CON_MODVAR:
3829         case CON_MULVAR:
3830         case CON_ORVAR:
3831         case CON_RANDVAR:
3832         case CON_SETVAR:
3833         case CON_SHIFTVARL:
3834         case CON_SHIFTVARR:
3835         case CON_SUBVAR:
3836         case CON_XORVAR:
3837 setvar:
3838         {
3839             auto ins = &g_scriptPtr[-1];
3840 
3841             C_GetNextVarType(GAMEVAR_READONLY);
3842             C_GetNextValue(LABEL_DEFINE);
3843 
3844             // replace divides and multiplies by 0 with an error asking if the user is stupid
3845             if (ins[2] == 0 && (tw == CON_MODVAR || tw == CON_MULVAR || tw == CON_DIVVAR))
3846             {
3847                 g_errorCnt++;
3848                 C_ReportError(-1);
3849                 initprintf("%s:%d: error: divide or multiply by zero! What are you doing?\n", g_scriptFileName, g_lineNumber);
3850                 continue;
3851             }
3852             else if (tw == CON_DIVVAR || tw == CON_MULVAR)
3853             {
3854                 auto const i = ins[2];
3855                 // replace multiplies or divides by 1 with nullop
3856                 if (i == 1)
3857                 {
3858                     int constexpr const opcode = CON_NULLOP;
3859 
3860                     if (!g_errorCnt && !g_warningCnt && g_scriptDebug > 1)
3861                     {
3862                         initprintf("%s:%d: %s -> %s\n", g_scriptFileName, g_lineNumber,
3863                                    VM_GetKeywordForID(tw), VM_GetKeywordForID(opcode));
3864                     }
3865 
3866                     scriptWriteAtOffset(opcode | LINE_NUMBER, ins);
3867                     g_scriptPtr = &ins[1];
3868                 }
3869                 // replace multiplies or divides by -1 with inversion
3870                 else if (i == -1)
3871                 {
3872                     int constexpr const opcode = CON_INV;
3873 
3874                     if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
3875                     {
3876                         initprintf("%s:%d: %s -> %s\n", g_scriptFileName, g_lineNumber,
3877                                    VM_GetKeywordForID(tw), VM_GetKeywordForID(opcode));
3878                     }
3879 
3880                     scriptWriteAtOffset(opcode | LINE_NUMBER, ins);
3881                     g_scriptPtr--;
3882                 }
3883             }
3884             // replace instructions with special versions for specific var types
3885             scriptUpdateOpcodeForVariableType(ins);
3886             continue;
3887         }
3888 
3889         case CON_ADDVARVAR:
3890         case CON_ANDVARVAR:
3891         case CON_DISPLAYRANDVARVAR:
3892         case CON_DIVVARVAR:
3893         case CON_MODVARVAR:
3894         case CON_MULVARVAR:
3895         case CON_ORVARVAR:
3896         case CON_RANDVARVAR:
3897         case CON_SETVARVAR:
3898         case CON_SHIFTVARVARL:
3899         case CON_SHIFTVARVARR:
3900         case CON_SUBVARVAR:
3901         case CON_XORVARVAR:
3902             {
3903 setvarvar:
3904                 auto ins = &g_scriptPtr[-1];
3905                 auto tptr = textptr;
3906                 int const lnum = g_lineNumber;
3907 
3908                 C_GetNextVarType(GAMEVAR_READONLY);
3909                 C_GetNextVar();
3910 
3911                 int const opcode = inthash_find(&h_varvar, *ins & VM_INSTMASK);
3912 
3913                 if (ins[2] == GV_FLAG_CONSTANT && opcode != -1)
3914                 {
3915                     if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
3916                     {
3917                         initprintf("%s:%d: %s -> %s\n", g_scriptFileName, g_lineNumber,
3918                                     VM_GetKeywordForID(*ins & VM_INSTMASK), VM_GetKeywordForID(opcode));
3919                     }
3920 
3921                     tw = opcode;
3922                     scriptWriteAtOffset(opcode | LINE_NUMBER, ins);
3923                     g_scriptPtr = &ins[1];
3924                     textptr = tptr;
3925                     g_lineNumber = lnum;
3926                     goto setvar;
3927                 }
3928 
3929                 continue;
3930             }
3931 
3932         case CON_GETACTORVAR:
3933         case CON_GETPLAYERVAR:
3934         case CON_SETACTORVAR:
3935         case CON_SETPLAYERVAR:
3936             {
3937                 // syntax [gs]etactorvar[<var>].<varx> <VAR>
3938                 // gets the value of the per-actor variable varx into VAR
3939 
3940                 if (C_GetStructureIndexes(1, NULL) == -1)
3941                     continue;
3942 
3943                 if (g_scriptPtr[-1] == g_thisActorVarID) // convert to "setvarvar"
3944                 {
3945                     g_scriptPtr--;
3946                     g_scriptPtr[-1]=CON_SETVARVAR;
3947                     if (tw == CON_SETACTORVAR || tw == CON_SETPLAYERVAR)
3948                     {
3949                         tw = inthash_find(&h_varvar, tw);
3950                         goto setvarvar;
3951                     }
3952                     else
3953                     {
3954                         g_scriptPtr++;
3955                         C_GetNextVar();
3956                         g_scriptPtr-=2;
3957                         C_GetNextVarType(GAMEVAR_READONLY);
3958                         g_scriptPtr++;
3959                     }
3960                     continue;
3961                 }
3962 
3963                 /// now pointing at 'xxx'
3964 
3965                 C_GetNextLabelName();
3966 
3967                 if (EDUKE32_PREDICT_FALSE(hash_find(&h_keywords,LAST_LABEL)>=0))
3968                 {
3969                     g_errorCnt++;
3970                     C_ReportError(ERROR_ISAKEYWORD);
3971                     continue;
3972                 }
3973 
3974                 i=GetDefID(LAST_LABEL);
3975 
3976                 if (EDUKE32_PREDICT_FALSE(i<0))
3977                 {
3978                     g_errorCnt++;
3979                     C_ReportError(ERROR_NOTAGAMEVAR);
3980                     continue;
3981                 }
3982                 if (EDUKE32_PREDICT_FALSE(aGameVars[i].flags & GAMEVAR_READONLY))
3983                 {
3984                     g_errorCnt++;
3985                     C_ReportError(ERROR_VARREADONLY);
3986                     continue;
3987                 }
3988 
3989                 switch (tw)
3990                 {
3991                 case CON_SETACTORVAR:
3992                     if (EDUKE32_PREDICT_FALSE(!(aGameVars[i].flags & GAMEVAR_PERACTOR)))
3993                     {
3994                         g_errorCnt++;
3995                         C_ReportError(-1);
3996                         initprintf("%s:%d: error: variable `%s' is not per-actor.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
3997                         continue;
3998                     }
3999                     break;
4000                 case CON_SETPLAYERVAR:
4001                     if (EDUKE32_PREDICT_FALSE(!(aGameVars[i].flags & GAMEVAR_PERPLAYER)))
4002                     {
4003                         g_errorCnt++;
4004                         C_ReportError(-1);
4005                         initprintf("%s:%d: error: variable `%s' is not per-player.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
4006                         continue;
4007                     }
4008                     break;
4009                 }
4010 
4011                 scriptWriteValue(i); // the ID of the DEF (offset into array...)
4012 
4013                 switch (tw)
4014                 {
4015                 case CON_GETACTORVAR:
4016                 case CON_GETPLAYERVAR:
4017                     C_GetNextVarType(GAMEVAR_READONLY);
4018                     break;
4019                 default:
4020                     C_GetNextVar();
4021                     break;
4022                 }
4023                 continue;
4024             }
4025 
4026         case CON_WRITEARRAYTOFILE:
4027         case CON_READARRAYFROMFILE:
4028             i = C_GetNextGameArrayName();
4029             if (EDUKE32_PREDICT_FALSE(i < 0))
4030                 return 1;
4031 
4032             C_GetNextValue(LABEL_DEFINE);
4033             continue;
4034 
4035         case CON_COPY:
4036             i = C_GetNextGameArrayName();
4037             if (EDUKE32_PREDICT_FALSE(i < 0))
4038                 return 1;
4039 
4040             C_SkipComments();
4041 
4042             if (EDUKE32_PREDICT_FALSE(*textptr != '['))
4043             {
4044                 g_errorCnt++;
4045                 C_ReportError(ERROR_GAMEARRAYBNO);
4046                 return 1;
4047             }
4048             textptr++;
4049             C_GetNextVar();
4050             C_SkipComments();
4051             if (EDUKE32_PREDICT_FALSE(*textptr != ']'))
4052             {
4053                 g_errorCnt++;
4054                 C_ReportError(ERROR_GAMEARRAYBNC);
4055                 return 1;
4056             }
4057             textptr++;
4058             fallthrough__;
4059         case CON_SETARRAY:
4060             i = C_GetNextGameArrayName();
4061             if (EDUKE32_PREDICT_FALSE(i < 0))
4062                 return 1;
4063 
4064             if (EDUKE32_PREDICT_FALSE(aGameArrays[i].flags & GAMEARRAY_READONLY))
4065             {
4066                 C_ReportError(ERROR_ARRAYREADONLY);
4067                 g_errorCnt++;
4068                 return 1;
4069             }
4070 
4071             C_SkipComments();
4072             if (EDUKE32_PREDICT_FALSE(*textptr != '['))
4073             {
4074                 g_errorCnt++;
4075                 C_ReportError(ERROR_GAMEARRAYBNO);
4076                 return 1;
4077             }
4078             textptr++;
4079             C_GetNextVar();
4080             C_SkipComments();
4081             if (EDUKE32_PREDICT_FALSE(*textptr != ']'))
4082             {
4083                 g_errorCnt++;
4084                 C_ReportError(ERROR_GAMEARRAYBNC);
4085                 return 1;
4086             }
4087             textptr++;
4088             C_GetNextVar();
4089             continue;
4090 
4091         case CON_GETARRAYSIZE:
4092             i = C_GetNextGameArrayName();
4093             if (EDUKE32_PREDICT_FALSE(i < 0))
4094                 return 1;
4095             C_SkipComments();
4096             C_GetNextVarType(GAMEVAR_READONLY);
4097             continue;
4098 
4099         case CON_GETARRAYSEQUENCE:
4100         case CON_SETARRAYSEQUENCE:
4101         {
4102             i = C_GetNextGameArrayName();
4103             if (EDUKE32_PREDICT_FALSE(i < 0))
4104                 return 1;
4105             C_SkipComments();
4106 
4107             auto pSize = g_scriptPtr++;
4108 
4109             for (j = 0; j < MAX_ARRAYRANGE_VALUES; ++j)
4110             {
4111                 if (C_GetKeyword() != -1)
4112                     break;
4113 
4114                 C_GetNextVarType(tw == CON_GETARRAYSEQUENCE ? GAMEVAR_READONLY : 0);
4115             }
4116 
4117             scriptWriteAtOffset(j, pSize);
4118             continue;
4119         }
4120 
4121         case CON_RESIZEARRAY:
4122             i = C_GetNextGameArrayName();
4123             if (EDUKE32_PREDICT_FALSE(i < 0))
4124                 return 1;
4125 
4126             if (aGameArrays[i].flags & (GAMEARRAY_READONLY|GAMEARRAY_SYSTEM))
4127             {
4128                 g_errorCnt++;
4129                 C_ReportError(-1);
4130                 initprintf("%s:%d: error: can't resize system array `%s'.\n", g_scriptFileName, g_lineNumber, LAST_LABEL);
4131                 return 1;
4132             }
4133 
4134             C_SkipComments();
4135             C_GetNextVarType(0);
4136             continue;
4137 
4138         case CON_SWAPARRAYS:
4139             i = C_GetNextGameArrayName();
4140             if (EDUKE32_PREDICT_FALSE(i < 0))
4141                 return 1;
4142 
4143             if (aGameArrays[i].flags & (GAMEARRAY_READONLY|GAMEARRAY_SYSTEM|GAMEARRAY_VARSIZE))
4144             {
4145                 g_errorCnt++;
4146                 C_ReportError(-1);
4147                 initprintf("%s:%d: error: can't swap system array `%s'.\n", g_scriptFileName, g_lineNumber, LAST_LABEL);
4148                 return 1;
4149             }
4150 
4151             C_SkipComments();
4152 
4153             tw = C_GetNextGameArrayName();
4154             if (EDUKE32_PREDICT_FALSE(tw < 0))
4155                 return 1;
4156 
4157             if (aGameArrays[tw].flags & (GAMEARRAY_READONLY|GAMEARRAY_SYSTEM|GAMEARRAY_VARSIZE))
4158             {
4159                 g_errorCnt++;
4160                 C_ReportError(-1);
4161                 initprintf("%s:%d: error: can't swap system array `%s'.\n", g_scriptFileName, g_lineNumber, LAST_LABEL);
4162                 return 1;
4163             }
4164 
4165             if ((aGameArrays[i].flags & GAMEARRAY_STORAGE_MASK) != (aGameArrays[tw].flags & GAMEARRAY_STORAGE_MASK))
4166             {
4167                 g_errorCnt++;
4168                 C_ReportError(-1);
4169                 initprintf("%s:%d: error: can't swap arrays of different storage classes.\n", g_scriptFileName, g_lineNumber);
4170                 return 1;
4171             }
4172 
4173             continue;
4174 
4175         case CON_ACTIVATEBYSECTOR:
4176         case CON_ADDWEAPON:
4177         case CON_DIST:
4178         case CON_DIVSCALE:
4179         case CON_GETANGLE:
4180         case CON_GETINCANGLE:
4181         case CON_GMAXAMMO:
4182         case CON_LDIST:
4183         case CON_MULSCALE:
4184         case CON_OPERATEACTIVATORS:
4185         case CON_OPERATESECTORS:
4186         case CON_SCALEVAR:
4187         case CON_SETASPECT:
4188         case CON_SMAXAMMO:
4189         case CON_SSP:
4190             // get the ID of the DEF
4191             switch (tw)
4192             {
4193             case CON_DIST:
4194             case CON_DIVSCALE:
4195             case CON_GETANGLE:
4196             case CON_GETINCANGLE:
4197             case CON_LDIST:
4198             case CON_MULSCALE:
4199             case CON_SCALEVAR:
4200                 C_GetNextVarType(GAMEVAR_READONLY);
4201                 break;
4202             default:
4203                 C_GetNextVar();
4204                 break;
4205             }
4206 
4207             // get the ID of the DEF
4208             if (tw == CON_GMAXAMMO)
4209                 C_GetNextVarType(GAMEVAR_READONLY);
4210             else C_GetNextVar();
4211 
4212             switch (tw)
4213             {
4214             case CON_DIST:
4215             case CON_GETANGLE:
4216             case CON_GETINCANGLE:
4217             case CON_LDIST:
4218                 C_GetNextVar();
4219                 break;
4220             case CON_DIVSCALE:
4221             case CON_MULSCALE:
4222             case CON_SCALEVAR:
4223                 C_GetManyVars(2);
4224                 break;
4225             }
4226             continue;
4227 
4228         case CON_FLASH:
4229         case CON_SAVEMAPSTATE:
4230         case CON_LOADMAPSTATE:
4231             if (tw != CON_FLASH)
4232             {
4233                 if (EDUKE32_PREDICT_FALSE(g_currentEvent == EVENT_ANIMATESPRITES))
4234                 {
4235                     initprintf("%s:%d: warning: found `%s' inside EVENT_ANIMATESPRITES\n",
4236                                g_scriptFileName,g_lineNumber,tempbuf);
4237                     g_warningCnt++;
4238                 }
4239             }
4240             continue;
4241 
4242         case CON_ACTIVATE:
4243             g_scriptPtr[-1] = CON_OPERATEACTIVATORS | LINE_NUMBER;
4244             C_GetNextValue(LABEL_DEFINE);
4245             scriptWriteValue(0);
4246             continue;
4247 
4248         case CON_GETFLORZOFSLOPE:
4249         case CON_GETCEILZOFSLOPE:
4250         case CON_UPDATESECTORZ:
4251         case CON_UPDATESECTORNEIGHBORZ:
4252             C_GetManyVars(3);
4253             C_GetNextVarType(GAMEVAR_READONLY);
4254             continue;
4255 
4256         case CON_DEFINEPROJECTILE:
4257             {
4258                 int32_t y, z;
4259 
4260                 if (EDUKE32_PREDICT_FALSE(g_processingState || g_scriptActorOffset))
4261                 {
4262                     C_ReportError(ERROR_FOUNDWITHIN);
4263                     g_errorCnt++;
4264                 }
4265 
4266                 g_scriptPtr--;
4267 
4268                 C_GetNextValue(LABEL_DEFINE);
4269                 j = g_scriptPtr[-1];
4270                 g_scriptPtr--;
4271 
4272                 C_GetNextValue(LABEL_DEFINE);
4273                 y = g_scriptPtr[-1];
4274                 g_scriptPtr--;
4275 
4276                 C_GetNextValue(LABEL_DEFINE);
4277                 z = g_scriptPtr[-1];
4278                 g_scriptPtr--;
4279 
4280                 if (EDUKE32_PREDICT_FALSE((unsigned)j >= MAXTILES))
4281                 {
4282                     C_ReportError(ERROR_EXCEEDSMAXTILES);
4283                     g_errorCnt++;
4284                     continue;
4285                 }
4286 
4287                 C_DefineProjectile(j, y, z);
4288                 continue;
4289             }
4290 
4291         case CON_DAMAGEEVENTTILE:
4292         {
4293             if (EDUKE32_PREDICT_FALSE(g_processingState || g_scriptActorOffset))
4294             {
4295                 C_ReportError(ERROR_FOUNDWITHIN);
4296                 g_errorCnt++;
4297             }
4298 
4299             g_scriptPtr--;
4300 
4301             C_GetNextValue(LABEL_DEFINE);
4302             j = g_scriptPtr[-1];
4303             g_scriptPtr--;
4304 
4305             if (EDUKE32_PREDICT_FALSE((unsigned)j >= MAXTILES))
4306             {
4307                 C_ReportError(ERROR_EXCEEDSMAXTILES);
4308                 g_errorCnt++;
4309                 continue;
4310             }
4311 
4312             g_tile[j].flags |= SFLAG_DAMAGEEVENT;
4313 
4314             continue;
4315         }
4316 
4317         case CON_DAMAGEEVENTTILERANGE:
4318         {
4319             if (EDUKE32_PREDICT_FALSE(g_processingState || g_scriptActorOffset))
4320             {
4321                 C_ReportError(ERROR_FOUNDWITHIN);
4322                 g_errorCnt++;
4323             }
4324 
4325             g_scriptPtr--;
4326 
4327             C_GetNextValue(LABEL_DEFINE);
4328             i = g_scriptPtr[-1];
4329             g_scriptPtr--;
4330 
4331             C_GetNextValue(LABEL_DEFINE);
4332             j = g_scriptPtr[-1];
4333             g_scriptPtr--;
4334 
4335             if (EDUKE32_PREDICT_FALSE((unsigned)i >= MAXTILES || (unsigned)j >= MAXTILES))
4336             {
4337                 C_ReportError(ERROR_EXCEEDSMAXTILES);
4338                 g_errorCnt++;
4339                 continue;
4340             }
4341 
4342             for (tiledata_t * t = g_tile + i, * t_end = g_tile + j; t <= t_end; ++t)
4343                 t->flags |= SFLAG_DAMAGEEVENT;
4344 
4345             continue;
4346         }
4347 
4348         case CON_SPRITEFLAGS:
4349             if (!g_scriptActorOffset && g_processingState == 0)
4350             {
4351                 g_scriptPtr--;
4352                 auto tmpscrptr = g_scriptPtr;
4353 
4354                 C_GetNextValue(LABEL_DEFINE);
4355                 j = g_scriptPtr[-1];
4356 
4357                 int32_t flags = 0;
4358                 do
4359                     C_BitOrNextValue(&flags);
4360                 while (C_GetKeyword() == -1);
4361 
4362                 g_scriptPtr = tmpscrptr;
4363 
4364                 if (EDUKE32_PREDICT_FALSE((unsigned)j >= MAXTILES))
4365                 {
4366                     C_ReportError(ERROR_EXCEEDSMAXTILES);
4367                     g_errorCnt++;
4368                     continue;
4369                 }
4370 
4371                 g_tile[j].flags = flags;
4372             }
4373             else C_GetNextVar();
4374             continue;
4375 
4376         case CON_PRECACHE:
4377         case CON_SPRITENOPAL:
4378         case CON_SPRITENOSHADE:
4379         case CON_SPRITENVG:
4380         case CON_SPRITESHADOW:
4381             if (EDUKE32_PREDICT_FALSE(g_processingState || g_scriptActorOffset))
4382             {
4383                 C_ReportError(ERROR_FOUNDWITHIN);
4384                 g_errorCnt++;
4385                 scriptSkipLine();
4386                 continue;
4387             }
4388 
4389             g_scriptPtr--;
4390 
4391             C_GetNextValue(LABEL_DEFINE);
4392             g_scriptPtr--;
4393             j = *g_scriptPtr;
4394 
4395             if (EDUKE32_PREDICT_FALSE((unsigned)j >= MAXTILES))
4396             {
4397                 C_ReportError(ERROR_EXCEEDSMAXTILES);
4398                 g_errorCnt++;
4399                 scriptSkipLine();
4400                 continue;
4401             }
4402 
4403             switch (tw)
4404             {
4405             case CON_SPRITESHADOW:
4406                 g_tile[*g_scriptPtr].flags |= SFLAG_SHADOW;
4407                 break;
4408             case CON_SPRITENVG:
4409                 g_tile[*g_scriptPtr].flags |= SFLAG_NVG;
4410                 break;
4411             case CON_SPRITENOSHADE:
4412                 g_tile[*g_scriptPtr].flags |= SFLAG_NOSHADE;
4413                 break;
4414             case CON_SPRITENOPAL:
4415                 g_tile[*g_scriptPtr].flags |= SFLAG_NOPAL;
4416                 break;
4417             case CON_PRECACHE:
4418                 C_GetNextValue(LABEL_DEFINE);
4419                 g_scriptPtr--;
4420                 i = *g_scriptPtr;
4421                 if (EDUKE32_PREDICT_FALSE((unsigned)i >= MAXTILES))
4422                 {
4423                     C_ReportError(ERROR_EXCEEDSMAXTILES);
4424                     g_errorCnt++;
4425                     scriptSkipLine();
4426                     continue;
4427                 }
4428                 g_tile[j].cacherange = i;
4429 
4430                 C_GetNextValue(LABEL_DEFINE);
4431                 g_scriptPtr--;
4432                 if (*g_scriptPtr)
4433                     g_tile[j].flags |= SFLAG_CACHE;
4434 
4435                 break;
4436             }
4437             continue;
4438 
4439         case CON_IFACTORSOUND:
4440         case CON_IFVARVARA:
4441         case CON_IFVARVARAE:
4442         case CON_IFVARVARAND:
4443         case CON_IFVARVARB:
4444         case CON_IFVARVARBE:
4445         case CON_IFVARVARBOTH:
4446         case CON_IFVARVARE:
4447         case CON_IFVARVAREITHER:
4448         case CON_IFVARVARG:
4449         case CON_IFVARVARGE:
4450         case CON_IFVARVARL:
4451         case CON_IFVARVARLE:
4452         case CON_IFVARVARN:
4453         case CON_IFVARVAROR:
4454         case CON_IFVARVARXOR:
4455         case CON_WHILEVARVARL:
4456         case CON_WHILEVARVARN:
4457             {
4458                 auto const ins = &g_scriptPtr[-1];
4459                 auto const lastScriptPtr = &g_scriptPtr[-1] - apScript;
4460                 auto const lasttextptr = textptr;
4461                 int const lnum = g_lineNumber;
4462 
4463                 g_skipBranch = false;
4464 
4465                 C_GetNextVar();
4466                 auto const var = g_scriptPtr;
4467                 C_GetNextVar();
4468 
4469                 if (*var == GV_FLAG_CONSTANT)
4470                 {
4471                     int const opcode = inthash_find(&h_varvar, tw);
4472 
4473                     if (opcode != -1)
4474                     {
4475                         if (g_scriptDebug > 1 && !g_errorCnt && !g_warningCnt)
4476                         {
4477                             initprintf("%s:%d: replacing %s with %s\n", g_scriptFileName, g_lineNumber,
4478                                        VM_GetKeywordForID(*ins & VM_INSTMASK), VM_GetKeywordForID(opcode));
4479                         }
4480 
4481                         scriptWriteAtOffset(opcode | LINE_NUMBER, ins);
4482                         tw = opcode;
4483                         g_scriptPtr = &ins[1];
4484                         textptr = lasttextptr;
4485                         g_lineNumber = lnum;
4486                         goto ifvar;
4487                     }
4488                 }
4489 
4490                 if (C_CheckMalformedBranch(lastScriptPtr))
4491                     continue;
4492 
4493                 auto const offset = g_scriptPtr - apScript;
4494                 g_scriptPtr++; // Leave a spot for the fail location
4495 
4496                 C_ParseCommand();
4497 
4498                 if (C_CheckEmptyBranch(tw, lastScriptPtr))
4499                     continue;
4500 
4501                 auto const tempscrptr = apScript + offset;
4502                 scriptWritePointer((intptr_t)g_scriptPtr, tempscrptr);
4503 
4504                 if (tw != CON_WHILEVARVARN && tw != CON_WHILEVARVARL)
4505                 {
4506                     j = C_GetKeyword();
4507 
4508                     if (j == CON_ELSE)
4509                         g_checkingIfElse++;
4510                 }
4511                 continue;
4512             }
4513 
4514         case CON_IFVARA:
4515         case CON_IFVARAE:
4516         case CON_IFVARAND:
4517         case CON_IFVARB:
4518         case CON_IFVARBE:
4519         case CON_IFVARBOTH:
4520         case CON_IFVARE:
4521         case CON_IFVAREITHER:
4522         case CON_IFVARG:
4523         case CON_IFVARGE:
4524         case CON_IFVARL:
4525         case CON_IFVARLE:
4526         case CON_IFVARN:
4527         case CON_IFVAROR:
4528         case CON_IFVARXOR:
4529         case CON_WHILEVARL:
4530         case CON_WHILEVARN:
4531             {
4532 ifvar:
4533                 auto const ins = &g_scriptPtr[-1];
4534                 auto const lastScriptPtr = &g_scriptPtr[-1] - apScript;
4535 
4536                 g_skipBranch = false;
4537 
4538                 C_GetNextVar();
4539                 C_GetNextValue(LABEL_DEFINE);
4540 
4541                 if (C_CheckMalformedBranch(lastScriptPtr))
4542                     continue;
4543 
4544                 scriptUpdateOpcodeForVariableType(ins);
4545 
4546                 auto const offset = g_scriptPtr - apScript;
4547                 g_scriptPtr++; //Leave a spot for the fail location
4548 
4549                 C_ParseCommand();
4550 
4551                 if (C_CheckEmptyBranch(tw, lastScriptPtr))
4552                     continue;
4553 
4554                 auto const tempscrptr = apScript + offset;
4555                 scriptWritePointer((intptr_t)g_scriptPtr, tempscrptr);
4556 
4557                 if (tw != CON_WHILEVARN && tw != CON_WHILEVARL)
4558                 {
4559                     j = C_GetKeyword();
4560 
4561                     if (j == CON_ELSE)
4562                         g_checkingIfElse++;
4563                 }
4564 
4565                 continue;
4566             }
4567 
4568         case CON_FOR:  // special-purpose iteration
4569         {
4570             C_GetNextVarType(GAMEVAR_READONLY);
4571             C_GetNextLabelName();
4572 
4573             int const iterType = hash_find(&h_iter, LAST_LABEL);
4574 
4575             if (EDUKE32_PREDICT_FALSE(iterType < 0))
4576             {
4577                 C_CUSTOMERROR("unknown iteration type `%s'.", LAST_LABEL);
4578                 return 1;
4579             }
4580 
4581             scriptWriteValue(iterType);
4582 
4583             if (iterType >= ITER_SPRITESOFSECTOR)
4584                 C_GetNextVar();
4585 
4586             intptr_t const offset = g_scriptPtr-apScript;
4587             g_scriptPtr++; //Leave a spot for the location to jump to after completion
4588 
4589             C_ParseCommand();
4590 
4591             // write relative offset
4592             auto const tscrptr = (intptr_t *) apScript+offset;
4593             scriptWriteAtOffset((g_scriptPtr-apScript)-offset, tscrptr);
4594             continue;
4595         }
4596 
4597         case CON_ROTATESPRITE16:
4598         case CON_ROTATESPRITE:
4599             if (EDUKE32_PREDICT_FALSE(!g_scriptEventOffset && g_processingState == 0))
4600             {
4601                 C_ReportError(ERROR_EVENTONLY);
4602                 g_errorCnt++;
4603             }
4604 
4605             // syntax:
4606             // int32_t x, int32_t y, int32_t z, short a, short tilenum, int8_t shade, char orientation, x1, y1, x2, y2
4607             // myospal adds char pal
4608 
4609             // get the ID of the DEFs
4610 
4611             C_GetManyVars(12);
4612             continue;
4613 
4614         case CON_ROTATESPRITEA:
4615             if (EDUKE32_PREDICT_FALSE(!g_scriptEventOffset && g_processingState == 0))
4616             {
4617                 C_ReportError(ERROR_EVENTONLY);
4618                 g_errorCnt++;
4619             }
4620 
4621             C_GetManyVars(13);
4622             continue;
4623 
4624         case CON_SHOWVIEW:
4625         case CON_SHOWVIEWUNBIASED:
4626         case CON_SHOWVIEWQ16:
4627         case CON_SHOWVIEWQ16UNBIASED:
4628             if (EDUKE32_PREDICT_FALSE(!g_scriptEventOffset && g_processingState == 0))
4629             {
4630                 C_ReportError(ERROR_EVENTONLY);
4631                 g_errorCnt++;
4632             }
4633 
4634             C_GetManyVars(10);
4635             continue;
4636 
4637         case CON_GETZRANGE:
4638             C_GetManyVars(4);
4639             C_GetManyVarsType(GAMEVAR_READONLY,4);
4640             C_GetManyVars(2);
4641             continue;
4642 
4643         case CON_CLIPMOVE:
4644         case CON_CLIPMOVENOSLIDE:
4645             // <retvar>,<x>,<y>,z,<sectnum>, xvect,yvect,walldist,floordist,ceildist,clipmask
4646             C_GetManyVarsType(GAMEVAR_READONLY,3);
4647             C_GetNextVar();
4648             C_GetNextVarType(GAMEVAR_READONLY);
4649             C_GetManyVars(6);
4650             continue;
4651 
4652         case CON_LINEINTERSECT:
4653         case CON_RAYINTERSECT:
4654             // lineintersect x y z  x y z  x y  x y  <intx> <inty> <intz> <ret>
4655             // rayintersect x y z  vx vy vz  x y  x y  <intx> <inty> <intz> <ret>
4656             C_GetManyVars(10);
4657             C_GetManyVarsType(GAMEVAR_READONLY,4);
4658             continue;
4659 
4660         case CON_HITSCAN:
4661         case CON_CANSEE:
4662             // get the ID of the DEF
4663             C_GetManyVars(tw==CON_CANSEE?8:7);
4664             C_GetManyVarsType(GAMEVAR_READONLY,tw==CON_CANSEE?1:6);
4665             if (tw==CON_HITSCAN) C_GetNextVar();
4666             continue;
4667 
4668         case CON_CANSEESPR:
4669         case CON_UPDATESECTOR:
4670         case CON_UPDATESECTORNEIGHBOR:
4671         case CON_QSTRCMP:
4672             C_GetManyVars(2);
4673             C_GetNextVarType(GAMEVAR_READONLY);
4674             continue;
4675 
4676         case CON_NEARTAG:
4677             C_GetManyVars(5);
4678             C_GetManyVarsType(GAMEVAR_READONLY,4);
4679             C_GetManyVars(2);
4680             continue;
4681 
4682         case CON_ROTATEPOINT:
4683             C_GetManyVars(5);
4684             C_GetManyVarsType(GAMEVAR_READONLY,2);
4685             continue;
4686 
4687         case CON_GETTIMEDATE:
4688             C_GetManyVarsType(GAMEVAR_READONLY,8);
4689             continue;
4690 
4691         case CON_MOVESPRITE:
4692             C_GetManyVars(5);
4693             C_GetNextVarType(GAMEVAR_READONLY);
4694             continue;
4695 
4696         case CON_DIGITALNUMBER:
4697         case CON_DIGITALNUMBERZ:
4698         case CON_GAMETEXT:
4699         case CON_GAMETEXTZ:
4700         case CON_MINITEXT:
4701         case CON_SCREENTEXT:
4702             if (EDUKE32_PREDICT_FALSE(!g_scriptEventOffset && g_processingState == 0))
4703             {
4704                 C_ReportError(ERROR_EVENTONLY);
4705                 g_errorCnt++;
4706             }
4707 
4708             switch (tw)
4709             {
4710             case CON_SCREENTEXT:
4711                 C_GetManyVars(8);
4712                 fallthrough__;
4713             case CON_GAMETEXTZ:
4714             case CON_DIGITALNUMBERZ:
4715                 C_GetManyVars(1);
4716                 fallthrough__;
4717             case CON_GAMETEXT:
4718             case CON_DIGITALNUMBER:
4719                 C_GetManyVars(6);
4720                 fallthrough__;
4721             default:
4722                 C_GetManyVars(5);
4723                 break;
4724             }
4725             continue;
4726 
4727         case CON_MYOS:
4728         case CON_MYOSPAL:
4729         case CON_MYOSX:
4730         case CON_MYOSPALX:
4731             if (EDUKE32_PREDICT_FALSE(!g_scriptEventOffset && g_processingState == 0))
4732             {
4733                 C_ReportError(ERROR_EVENTONLY);
4734                 g_errorCnt++;
4735             }
4736 
4737             // syntax:
4738             // int32_t x, int32_t y, short tilenum, int8_t shade, char orientation
4739             // myospal adds char pal
4740 
4741             C_GetManyVars(5);
4742             if (tw==CON_MYOSPAL || tw==CON_MYOSPALX)
4743             {
4744                 // Parse: pal
4745 
4746                 // get the ID of the DEF
4747                 C_GetNextVar();
4748             }
4749             continue;
4750 
4751         case CON_SWITCH:
4752             {
4753                 g_checkingSwitch++; // allow nesting (if other things work)
4754                 C_GetNextVar();
4755 
4756                 intptr_t const tempoffset = (unsigned)(g_scriptPtr-apScript);
4757 
4758                 scriptWriteValue(0); // leave spot for end location (for after processing)
4759                 scriptWriteValue(0); // count of case statements
4760 
4761                 auto const backupCaseScriptPtr = g_caseTablePtr;
4762                 g_caseTablePtr=g_scriptPtr;        // the first case's pointer.
4763 
4764                 int const backupNumCases = g_numCases;
4765 
4766                 scriptWriteValue(0); // leave spot for 'default' location (null if none)
4767 
4768 //                temptextptr=textptr;
4769                 // probably does not allow nesting...
4770 
4771                 j=C_CountCaseStatements();
4772                 //        initprintf("Done Counting Case Statements for switch %d: found %d.\n", g_checkingSwitch,j);
4773                 g_scriptPtr+=j*2;
4774                 C_SkipComments();
4775                 g_scriptPtr-=j*2; // allocate buffer for the table
4776                 auto tempscrptr = (intptr_t *)(apScript+tempoffset);
4777 
4778                 //AddLog(g_szBuf);
4779 
4780                 if (j<0)
4781                 {
4782                     return 1;
4783                 }
4784 
4785                 if (tempscrptr)
4786                 {
4787                     // save count of cases
4788                     scriptWriteAtOffset(j, &tempscrptr[1]);
4789                 }
4790                 else
4791                 {
4792                     //Bsprintf(g_szBuf,"ERROR::%s %d",__FILE__,__LINE__);
4793                     //AddLog(g_szBuf);
4794                 }
4795 
4796                 while (j--)
4797                 {
4798                     // leave room for statements
4799 
4800                     scriptWriteValue(0); // value check
4801                     scriptWriteValue(0); // code offset
4802                     C_SkipComments();
4803                 }
4804 
4805                 g_numCases=0;
4806                 C_ParseCommand(true);
4807                 tempscrptr = (intptr_t *)(apScript+tempoffset);
4808 
4809                 //Bsprintf(g_szBuf,"SWITCHXX: '%.22s'",textptr);
4810                 //AddLog(g_szBuf);
4811                 // done processing switch.  clean up.
4812                 if (g_checkingSwitch<1)
4813                 {
4814                     //    Bsprintf(g_szBuf,"ERROR::%s %d: g_checkingSwitch=%d",__FILE__,__LINE__, g_checkingSwitch);
4815                     //    AddLog(g_szBuf);
4816                 }
4817                 g_numCases=0;
4818 
4819                 if (tempscrptr)
4820                 {
4821                     for (i = 3; i < 3 + tempscrptr[1] * 2 - 2; i += 2)  // sort them
4822                     {
4823                         intptr_t t = tempscrptr[i];
4824                         int n = i;
4825 
4826                         for (j = i + 2; j < 3 + tempscrptr[1] * 2; j += 2)
4827                         {
4828                             if (tempscrptr[j] < t)
4829                             {
4830                                 t = tempscrptr[j];
4831                                 n = j;
4832                             }
4833                         }
4834 
4835                         if (n != i)
4836                         {
4837                             swapptr(&tempscrptr[i],   &tempscrptr[n]);
4838                             swapptr(&tempscrptr[i+1], &tempscrptr[n+1]);
4839                         }
4840                     }
4841                     //            for (j=3;j<3+tempscrptr[1]*2;j+=2)initprintf("%5d %8x\n",tempscrptr[j],tempscrptr[j+1]);
4842 
4843                     // save 'end' location
4844                     scriptWriteAtOffset((intptr_t)g_scriptPtr - (intptr_t)apScript, tempscrptr);
4845                 }
4846                 else
4847                 {
4848                     //Bsprintf(g_szBuf,"ERROR::%s %d",__FILE__,__LINE__);
4849                     //AddLog(g_szBuf);
4850                 }
4851                 g_caseTablePtr=backupCaseScriptPtr;
4852                 g_numCases=backupNumCases;
4853                 //AddLog("End of Switch statement");
4854             }
4855             continue;
4856 
4857         case CON_CASE:
4858         case CON_DEFAULT:
4859             {
4860                 if (EDUKE32_PREDICT_FALSE(g_checkingSwitch < 1))
4861                 {
4862                     g_errorCnt++;
4863                     C_ReportError(-1);
4864                     initprintf("%s:%d: error: found `%s' statement when not in switch\n", g_scriptFileName,
4865                                g_lineNumber, tw == CON_CASE ? "case" : "default");
4866                     g_scriptPtr--;
4867                     return 1;
4868                 }
4869 
4870                 intptr_t tempoffset = 0;
4871                 intptr_t *tempscrptr = g_scriptPtr;
4872 
4873                 g_checkingCase = true;
4874 repeatcase:
4875                 g_scriptPtr--;
4876 
4877                 C_SkipComments();
4878 
4879                 if (tw == CON_CASE)
4880                 {
4881                     g_numCases++;
4882                     C_GetNextValue(LABEL_ANY);
4883                     j= *(--g_scriptPtr);
4884                 }
4885 
4886                 C_SkipComments();
4887 
4888                 if (*textptr == ':')
4889                     textptr++;
4890 
4891                 C_SkipComments();
4892 
4893                 if (g_caseTablePtr)
4894                 {
4895                     if (tw == CON_DEFAULT)
4896                     {
4897                         if (EDUKE32_PREDICT_FALSE(g_caseTablePtr[0] != 0))
4898                         {
4899                             // duplicate default statement
4900                             g_errorCnt++;
4901                             C_ReportError(-1);
4902                             initprintf("%s:%d: error: multiple `default' statements found in switch\n", g_scriptFileName, g_lineNumber);
4903                         }
4904                         g_caseTablePtr[0]=(intptr_t) (g_scriptPtr-apScript);   // save offset
4905                     }
4906                     else
4907                     {
4908                         for (i=(g_numCases/2)-1; i>=0; i--)
4909                             if (EDUKE32_PREDICT_FALSE(g_caseTablePtr[i*2+1]==j))
4910                             {
4911                                 g_warningCnt++;
4912                                 C_ReportError(WARNING_DUPLICATECASE);
4913                                 break;
4914                             }
4915                         g_caseTablePtr[g_numCases++]=j;
4916                         g_caseTablePtr[g_numCases]=(intptr_t) ((intptr_t *) g_scriptPtr-apScript);
4917                     }
4918                 }
4919 
4920                 j = C_GetKeyword();
4921 
4922                 if (j == CON_CASE || j == CON_DEFAULT)
4923                 {
4924                     //AddLog("Found Repeat Case");
4925                     C_GetNextKeyword();    // eat keyword
4926                     tw = j;
4927                     goto repeatcase;
4928                 }
4929 
4930                 tempoffset = (unsigned)(tempscrptr-apScript);
4931 
4932                 while (C_ParseCommand() == 0)
4933                 {
4934                     j = C_GetKeyword();
4935 
4936                     if (j == CON_CASE || j == CON_DEFAULT)
4937                     {
4938                         C_GetNextKeyword();    // eat keyword
4939                         tempscrptr = (intptr_t *)(apScript+tempoffset);
4940                         tw = j;
4941                         goto repeatcase;
4942                     }
4943                 }
4944 
4945                 continue;
4946             }
4947 
4948         case CON_ENDSWITCH:
4949             //AddLog("End Switch");
4950             if (g_caseTablePtr)
4951             {
4952                 if (EDUKE32_PREDICT_FALSE(g_checkingCase))
4953                 {
4954                     g_errorCnt++;
4955                     C_ReportError(-1);
4956                     initprintf("%s:%d: error: found `endswitch' before `break' or `return'\n", g_scriptFileName, g_lineNumber);
4957                 }
4958             }
4959 
4960             if (EDUKE32_PREDICT_FALSE(--g_checkingSwitch < 0))
4961             {
4962                 g_errorCnt++;
4963                 C_ReportError(-1);
4964                 initprintf("%s:%d: error: found `endswitch' without matching `switch'\n", g_scriptFileName, g_lineNumber);
4965             }
4966             return 1;      // end of block
4967 
4968         case CON_DRAGPOINT:
4969         case CON_GETKEYNAME:
4970         case CON_QSTRNCAT:
4971         case CON_SETACTORSOUNDPITCH:
4972             C_GetManyVars(3);
4973             continue;
4974 
4975         case CON_QSTRDIM:
4976             C_GetManyVarsType(GAMEVAR_READONLY, 2);
4977             C_GetManyVars(16);
4978             continue;
4979 
4980         case CON_QSUBSTR:
4981         case CON_SETSPRITE:
4982         case CON_NEXTSECTORNEIGHBORZ:
4983             C_GetManyVars(4);
4984             continue;
4985 
4986         case CON_IFACTION:
4987         case CON_IFACTIONCOUNT:
4988         case CON_IFACTOR:
4989         case CON_IFAI:
4990         case CON_IFANGDIFFL:
4991         case CON_IFCEILINGDISTL:
4992         case CON_IFCOUNT:
4993         case CON_IFCUTSCENE:
4994         case CON_IFFLOORDISTL:
4995         case CON_IFGAPZL:
4996         case CON_IFGOTWEAPONCE:
4997         case CON_IFMOVE:
4998         case CON_IFP:
4999         case CON_IFPDISTG:
5000         case CON_IFPDISTL:
5001         case CON_IFPHEALTHL:
5002         case CON_IFPINVENTORY:
5003         case CON_IFPLAYERSL:
5004         case CON_IFRND:
5005         case CON_IFSOUND:
5006         case CON_IFSPAWNEDBY:
5007         case CON_IFSPRITEPAL:
5008         case CON_IFSTRENGTH:
5009         case CON_IFWASWEAPON:
5010             {
5011                 auto const lastScriptPtr = &g_scriptPtr[-1] - apScript;
5012 
5013                 g_skipBranch = false;
5014 
5015                 switch (tw)
5016                 {
5017                 case CON_IFCUTSCENE:
5018                     C_GetNextVar();
5019                     break;
5020                 case CON_IFAI:
5021                     C_GetNextValue(LABEL_AI);
5022                     break;
5023                 case CON_IFACTION:
5024                     C_GetNextValue(LABEL_ACTION);
5025                     break;
5026                 case CON_IFMOVE:
5027                     if (EDUKE32_PREDICT_FALSE((C_GetNextValue(LABEL_MOVE|LABEL_DEFINE) == 0) && (g_scriptPtr[-1] != 0) && (g_scriptPtr[-1] != 1)))
5028                     {
5029                         C_ReportError(-1);
5030                         g_scriptPtr[-1] = 0;
5031                         initprintf("%s:%d: warning: expected a move, found a constant.\n",g_scriptFileName,g_lineNumber);
5032                         g_warningCnt++;
5033                     }
5034                     break;
5035                 case CON_IFPINVENTORY:
5036                     C_GetNextValue(LABEL_DEFINE);
5037                     C_GetNextValue(LABEL_DEFINE);
5038                     break;
5039                 case CON_IFP:
5040                     j = 0;
5041                     do
5042                         C_BitOrNextValue(&j);
5043                     while (C_GetKeyword() == -1);
5044                     C_FinishBitOr(j);
5045                     break;
5046                 case CON_IFSOUND:
5047                 case CON_IFACTORSOUND:
5048                 default:
5049                     C_GetNextValue(LABEL_DEFINE);
5050                     break;
5051                 }
5052 
5053                 if (C_CheckMalformedBranch(lastScriptPtr))
5054                     continue;
5055 
5056                 intptr_t const offset = (unsigned)(g_scriptPtr-apScript);
5057 
5058                 g_scriptPtr++; //Leave a spot for the fail location
5059 
5060                 C_ParseCommand();
5061 
5062                 if (C_CheckEmptyBranch(tw, lastScriptPtr))
5063                     continue;
5064 
5065                 auto const tempscrptr = (intptr_t *)apScript+offset;
5066                 scriptWritePointer((intptr_t)g_scriptPtr, tempscrptr);
5067 
5068                 j = C_GetKeyword();
5069 
5070                 if (j == CON_ELSE)
5071                     g_checkingIfElse++;
5072 
5073                 continue;
5074             }
5075 
5076         case CON_IFACTORNOTSTAYPUT:
5077         case CON_IFAWAYFROMWALL:
5078         case CON_IFBULLETNEAR:
5079         case CON_IFCANSEE:
5080         case CON_IFCANSEETARGET:
5081         case CON_IFCANSHOOTTARGET:
5082         case CON_IFCLIENT:
5083         case CON_IFDEAD:
5084         case CON_IFHITSPACE:
5085         case CON_IFHITWEAPON:
5086         case CON_IFINOUTERSPACE:
5087         case CON_IFINSPACE:
5088         case CON_IFINWATER:
5089         case CON_IFMULTIPLAYER:
5090         case CON_IFNOSOUNDS:
5091         case CON_IFNOTMOVING:
5092         case CON_IFONWATER:
5093         case CON_IFOUTSIDE:
5094         case CON_IFPLAYBACKON:
5095         case CON_IFRESPAWN:
5096         case CON_IFSERVER:
5097         case CON_IFSQUISHED:
5098             {
5099                 auto const lastScriptPtr = &g_scriptPtr[-1] - apScript;
5100 
5101                 g_skipBranch = false;
5102 
5103                 if (C_CheckMalformedBranch(lastScriptPtr))
5104                     continue;
5105 
5106                 intptr_t const offset = (unsigned)(g_scriptPtr-apScript);
5107 
5108                 g_scriptPtr++; //Leave a spot for the fail location
5109 
5110                 C_ParseCommand();
5111 
5112                 if (C_CheckEmptyBranch(tw, lastScriptPtr))
5113                     continue;
5114 
5115                 auto const tempscrptr = (intptr_t *)apScript+offset;
5116                 scriptWritePointer((intptr_t)g_scriptPtr, tempscrptr);
5117 
5118                 j = C_GetKeyword();
5119 
5120                 if (j == CON_ELSE)
5121                     g_checkingIfElse++;
5122 
5123                 continue;
5124             }
5125 
5126         case CON_LEFTBRACE:
5127             if (EDUKE32_PREDICT_FALSE(!(g_processingState || g_scriptActorOffset || g_scriptEventOffset)))
5128             {
5129                 g_errorCnt++;
5130                 C_ReportError(ERROR_SYNTAXERROR);
5131             }
5132             g_numBraces++;
5133 
5134             C_ParseCommand(true);
5135             continue;
5136 
5137         case CON_RIGHTBRACE:
5138             g_numBraces--;
5139 
5140             if ((g_scriptPtr[-2]>>12) == (VM_IFELSE_MAGIC) &&
5141                 ((g_scriptPtr[-2] & VM_INSTMASK) == CON_LEFTBRACE)) // rewrite "{ }" into "nullop"
5142             {
5143                 //            initprintf("%s:%d: rewriting empty braces '{ }' as 'nullop' from right\n",g_szScriptFileName,g_lineNumber);
5144                 g_scriptPtr[-2] = CON_NULLOP | (VM_IFELSE_MAGIC<<12);
5145                 g_scriptPtr -= 2;
5146 
5147                 if (C_GetKeyword() != CON_ELSE && (g_scriptPtr[-2] & VM_INSTMASK) != CON_ELSE)
5148                     g_skipBranch = true;
5149                 else g_skipBranch = false;
5150 
5151                 j = C_GetKeyword();
5152 
5153                 if (g_checkingIfElse && j != CON_ELSE)
5154                     g_checkingIfElse--;
5155 
5156                 return 1;
5157             }
5158 
5159             if (EDUKE32_PREDICT_FALSE(g_numBraces < 0))
5160             {
5161                 if (g_checkingSwitch)
5162                 {
5163                     C_ReportError(ERROR_NOENDSWITCH);
5164                 }
5165 
5166                 C_ReportError(-1);
5167                 initprintf("%s:%d: error: found more `}' than `{'.\n",g_scriptFileName,g_lineNumber);
5168                 g_errorCnt++;
5169             }
5170 
5171             if (g_checkingIfElse && j != CON_ELSE)
5172                 g_checkingIfElse--;
5173 
5174             return 1;
5175 
5176         case CON_BETANAME:
5177             g_scriptPtr--;
5178             j = 0;
5179             scriptSkipLine();
5180             continue;
5181 
5182 
5183         case CON_UNDEFINELEVEL:
5184             g_scriptPtr--;
5185             C_GetNextValue(LABEL_DEFINE);
5186             g_scriptPtr--;
5187             j = *g_scriptPtr;
5188             C_GetNextValue(LABEL_DEFINE);
5189             g_scriptPtr--;
5190             k = *g_scriptPtr;
5191 
5192             if (EDUKE32_PREDICT_FALSE((unsigned)j > MAXVOLUMES-1))
5193             {
5194                 initprintf("%s:%d: error: volume number exceeds maximum volume count.\n",g_scriptFileName,g_lineNumber);
5195                 g_errorCnt++;
5196                 scriptSkipLine();
5197                 continue;
5198             }
5199             if (EDUKE32_PREDICT_FALSE((unsigned)k > MAXLEVELS-1))
5200             {
5201                 initprintf("%s:%d: error: level number exceeds maximum number of levels per episode.\n",g_scriptFileName,g_lineNumber);
5202                 g_errorCnt++;
5203                 scriptSkipLine();
5204                 continue;
5205             }
5206 
5207             C_UndefineLevel(j, k);
5208             continue;
5209 
5210         case CON_UNDEFINESKILL:
5211             g_scriptPtr--;
5212 
5213             C_GetNextValue(LABEL_DEFINE);
5214             g_scriptPtr--;
5215             j = *g_scriptPtr;
5216 
5217             if (EDUKE32_PREDICT_FALSE((unsigned)j >= MAXSKILLS))
5218             {
5219                 initprintf("%s:%d: error: skill number exceeds maximum skill count %d.\n",
5220                            g_scriptFileName,g_lineNumber, MAXSKILLS);
5221                 g_errorCnt++;
5222                 scriptSkipLine();
5223                 continue;
5224             }
5225 
5226             C_UndefineSkill(j);
5227             continue;
5228 
5229         case CON_UNDEFINEVOLUME:
5230             g_scriptPtr--;
5231 
5232             C_GetNextValue(LABEL_DEFINE);
5233             g_scriptPtr--;
5234             j = *g_scriptPtr;
5235 
5236             if (EDUKE32_PREDICT_FALSE((unsigned)j > MAXVOLUMES-1))
5237             {
5238                 initprintf("%s:%d: error: volume number exceeds maximum volume count.\n",
5239                     g_scriptFileName,g_lineNumber);
5240                 g_errorCnt++;
5241                 scriptSkipLine();
5242                 continue;
5243             }
5244 
5245             C_UndefineVolume(j);
5246             continue;
5247 
5248         case CON_DEFINEVOLUMENAME:
5249             g_scriptPtr--;
5250 
5251             C_GetNextValue(LABEL_DEFINE);
5252             g_scriptPtr--;
5253             j = *g_scriptPtr;
5254 
5255             scriptSkipSpaces();
5256 
5257             if (EDUKE32_PREDICT_FALSE((unsigned)j > MAXVOLUMES-1))
5258             {
5259                 initprintf("%s:%d: error: volume number exceeds maximum volume count.\n",
5260                     g_scriptFileName,g_lineNumber);
5261                 g_errorCnt++;
5262                 scriptSkipLine();
5263                 continue;
5264             }
5265 
5266             i = 0;
5267 
5268             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
5269             {
5270                 g_volumeNames[j][i] = *textptr;
5271                 textptr++,i++;
5272                 if (EDUKE32_PREDICT_FALSE(i >= (signed)sizeof(g_volumeNames[j])))
5273                 {
5274                     initprintf("%s:%d: warning: truncating volume name to %d characters.\n",
5275                         g_scriptFileName,g_lineNumber,(int32_t)sizeof(g_volumeNames[j])-1);
5276                     i--;
5277                     g_warningCnt++;
5278                     scriptSkipLine();
5279                     break;
5280                 }
5281             }
5282             g_volumeCnt = j+1;
5283             g_volumeNames[j][i] = '\0';
5284             continue;
5285 
5286         case CON_DEFINEVOLUMEFLAGS:
5287             g_scriptPtr--;
5288             C_GetNextValue(LABEL_DEFINE);
5289             g_scriptPtr--;
5290             j = *g_scriptPtr;
5291             C_GetNextValue(LABEL_DEFINE);
5292             g_scriptPtr--;
5293             k = *g_scriptPtr;
5294 
5295             if (EDUKE32_PREDICT_FALSE((unsigned)j > MAXVOLUMES-1))
5296             {
5297                 initprintf("%s:%d: error: volume number exceeds maximum volume count.\n",g_scriptFileName,g_lineNumber);
5298                 g_errorCnt++;
5299                 scriptSkipLine();
5300                 continue;
5301             }
5302 
5303             C_DefineVolumeFlags(j, k);
5304             continue;
5305 
5306         case CON_DEFINEGAMEFUNCNAME:
5307             g_scriptPtr--;
5308             C_GetNextValue(LABEL_DEFINE);
5309             g_scriptPtr--;
5310             j = *g_scriptPtr;
5311 
5312             scriptSkipSpaces();
5313 
5314             if (EDUKE32_PREDICT_FALSE((unsigned)j > NUMGAMEFUNCTIONS-1))
5315             {
5316                 initprintf("%s:%d: error: function number exceeds number of game functions.\n",
5317                     g_scriptFileName,g_lineNumber);
5318                 g_errorCnt++;
5319                 scriptSkipLine();
5320                 continue;
5321             }
5322 
5323             i = 0;
5324 
5325             hash_delete(&h_gamefuncs, gamefunctions[j]);
5326 
5327             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
5328             {
5329                 gamefunctions[j][i] = *textptr;
5330                 textptr++,i++;
5331                 if (EDUKE32_PREDICT_FALSE(i >= MAXGAMEFUNCLEN))
5332                 {
5333                     initprintf("%s:%d: warning: truncating function name to %d characters.\n",
5334                         g_scriptFileName,g_lineNumber, MAXGAMEFUNCLEN-1);
5335                     i--;
5336                     g_warningCnt++;
5337                     scriptSkipLine();
5338                     break;
5339                 }
5340                 if (EDUKE32_PREDICT_FALSE(*textptr != 0x0a && *textptr != 0x0d && ispecial(*textptr)))
5341                 {
5342                     initprintf("%s:%d: warning: invalid character in function name.\n",
5343                         g_scriptFileName,g_lineNumber);
5344                     g_warningCnt++;
5345                     scriptSkipLine();
5346                     break;
5347                 }
5348             }
5349             gamefunctions[j][i] = '\0';
5350             hash_add(&h_gamefuncs,gamefunctions[j],j,0);
5351 
5352             continue;
5353 
5354         case CON_UNDEFINEGAMEFUNC:
5355             g_scriptPtr--;
5356             C_GetNextValue(LABEL_DEFINE);
5357             g_scriptPtr--;
5358             j = *g_scriptPtr;
5359 
5360             if (EDUKE32_PREDICT_FALSE((unsigned)j > NUMGAMEFUNCTIONS-1))
5361             {
5362                 initprintf("%s:%d: error: function number exceeds number of game functions.\n",
5363                     g_scriptFileName,g_lineNumber);
5364                 g_errorCnt++;
5365                 scriptSkipLine();
5366                 continue;
5367             }
5368 
5369             hash_delete(&h_gamefuncs, gamefunctions[j]);
5370 
5371             gamefunctions[j][0] = '\0';
5372 
5373             continue;
5374 
5375         case CON_DEFINESKILLNAME:
5376             g_scriptPtr--;
5377 
5378             C_GetNextValue(LABEL_DEFINE);
5379             g_scriptPtr--;
5380             j = *g_scriptPtr;
5381 
5382             scriptSkipSpaces();
5383 
5384             if (EDUKE32_PREDICT_FALSE((unsigned)j >= MAXSKILLS))
5385             {
5386                 initprintf("%s:%d: error: skill number exceeds maximum skill count %d.\n",
5387                            g_scriptFileName,g_lineNumber, MAXSKILLS);
5388                 g_errorCnt++;
5389                 scriptSkipLine();
5390                 continue;
5391             }
5392 
5393             i = 0;
5394 
5395             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
5396             {
5397                 g_skillNames[j][i] = *textptr;
5398                 textptr++,i++;
5399                 if (EDUKE32_PREDICT_FALSE(i >= (signed)sizeof(g_skillNames[j])))
5400                 {
5401                     initprintf("%s:%d: warning: truncating skill name to %d characters.\n",
5402                         g_scriptFileName,g_lineNumber,(int32_t)sizeof(g_skillNames[j])-1);
5403                     i--;
5404                     g_warningCnt++;
5405                     scriptSkipLine();
5406                     break;
5407                 }
5408             }
5409 
5410             if (EDUKE32_PREDICT_FALSE(i == 0))
5411             {
5412                 initprintf("%s:%d: warning: empty skill name.\n",
5413                     g_scriptFileName,g_lineNumber);
5414                 g_skillNames[j][i++] = ' ';
5415             }
5416 
5417             g_skillNames[j][i] = '\0';
5418 
5419             for (i=0; i<MAXSKILLS; i++)
5420                 if (g_skillNames[i][0] == 0)
5421                     break;
5422             g_skillCnt = i;
5423 
5424             continue;
5425 
5426         case CON_SETGAMENAME:
5427             {
5428                 char gamename[32];
5429                 g_scriptPtr--;
5430 
5431                 C_SkipComments();
5432 
5433                 i = 0;
5434 
5435                 while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
5436                 {
5437                     gamename[i] = *textptr;
5438                     textptr++,i++;
5439                     if (EDUKE32_PREDICT_FALSE(i >= (signed)sizeof(gamename)))
5440                     {
5441                         initprintf("%s:%d: warning: truncating game name to %d characters.\n",
5442                             g_scriptFileName,g_lineNumber,(int32_t)sizeof(gamename)-1);
5443                         i--;
5444                         g_warningCnt++;
5445                         scriptSkipLine();
5446                         break;
5447                     }
5448                 }
5449                 gamename[i] = '\0';
5450                 g_gameNamePtr = Xstrdup(gamename);
5451                 G_UpdateAppTitle();
5452             }
5453             continue;
5454 
5455         case CON_SETDEFNAME:
5456             {
5457                 g_scriptPtr--;
5458                 C_SkipComments();
5459 
5460                 j = 0;
5461                 while (isaltok(*textptr))
5462                 {
5463                     tempbuf[j] = *(textptr++);
5464                     j++;
5465                 }
5466                 tempbuf[j] = '\0';
5467 
5468                 C_SetDefName(tempbuf);
5469             }
5470             continue;
5471 
5472         case CON_SETCFGNAME:
5473             {
5474                 g_scriptPtr--;
5475                 C_SkipComments();
5476 
5477                 j = 0;
5478                 while (isaltok(*textptr))
5479                 {
5480                     tempbuf[j] = *(textptr++);
5481                     j++;
5482                 }
5483                 tempbuf[j] = '\0';
5484 
5485                 C_SetCfgName(tempbuf);
5486             }
5487             continue;
5488 
5489         case CON_DEFINEGAMETYPE:
5490             g_scriptPtr--;
5491             C_GetNextValue(LABEL_DEFINE);
5492             g_scriptPtr--;
5493             j = *g_scriptPtr;
5494 
5495             C_GetNextValue(LABEL_DEFINE);
5496             g_scriptPtr--;
5497             g_gametypeFlags[j] = *g_scriptPtr;
5498 
5499             C_SkipComments();
5500 
5501             if (EDUKE32_PREDICT_FALSE((unsigned)j > MAXGAMETYPES-1))
5502             {
5503                 initprintf("%s:%d: error: gametype number exceeds maximum gametype count.\n",g_scriptFileName,g_lineNumber);
5504                 g_errorCnt++;
5505                 scriptSkipLine();
5506                 continue;
5507             }
5508             g_gametypeCnt = j+1;
5509 
5510             i = 0;
5511 
5512             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
5513             {
5514                 g_gametypeNames[j][i] = *textptr;
5515                 textptr++,i++;
5516                 if (EDUKE32_PREDICT_FALSE(i >= (signed)sizeof(g_gametypeNames[j])))
5517                 {
5518                     initprintf("%s:%d: warning: truncating gametype name to %d characters.\n",
5519                         g_scriptFileName,g_lineNumber,(int32_t)sizeof(g_gametypeNames[j])-1);
5520                     i--;
5521                     g_warningCnt++;
5522                     scriptSkipLine();
5523                     break;
5524                 }
5525             }
5526             g_gametypeNames[j][i] = '\0';
5527             continue;
5528 
5529         case CON_DEFINELEVELNAME:
5530             g_scriptPtr--;
5531             C_GetNextValue(LABEL_DEFINE);
5532             g_scriptPtr--;
5533             j = *g_scriptPtr;
5534             C_GetNextValue(LABEL_DEFINE);
5535             g_scriptPtr--;
5536             k = *g_scriptPtr;
5537             C_SkipComments();
5538 
5539             if (EDUKE32_PREDICT_FALSE((unsigned)j > MAXVOLUMES-1))
5540             {
5541                 initprintf("%s:%d: error: volume number exceeds maximum volume count.\n",g_scriptFileName,g_lineNumber);
5542                 g_errorCnt++;
5543                 scriptSkipLine();
5544                 continue;
5545             }
5546             if (EDUKE32_PREDICT_FALSE((unsigned)k > MAXLEVELS-1))
5547             {
5548                 initprintf("%s:%d: error: level number exceeds maximum number of levels per episode.\n",g_scriptFileName,g_lineNumber);
5549                 g_errorCnt++;
5550                 scriptSkipLine();
5551                 continue;
5552             }
5553 
5554             i = 0;
5555 
5556             tempbuf[i] = '/';
5557 
5558             while (*textptr != ' ' && *textptr != '\t' && *textptr != 0x0a)
5559             {
5560                 tempbuf[i+1] = *textptr;
5561                 textptr++,i++;
5562                 if (EDUKE32_PREDICT_FALSE(i >= BMAX_PATH))
5563                 {
5564                     initprintf("%s:%d: error: level file name exceeds limit of %d characters.\n",g_scriptFileName,g_lineNumber,BMAX_PATH);
5565                     g_errorCnt++;
5566                     scriptSkipSpaces();
5567                     break;
5568                 }
5569             }
5570             tempbuf[i+1] = '\0';
5571 
5572             Bcorrectfilename(tempbuf,0);
5573 
5574             if (g_mapInfo[j *MAXLEVELS+k].filename == NULL)
5575                 g_mapInfo[j *MAXLEVELS+k].filename = (char *)Xcalloc(Bstrlen(tempbuf)+1,sizeof(uint8_t));
5576             else if ((Bstrlen(tempbuf)+1) > sizeof(g_mapInfo[j*MAXLEVELS+k].filename))
5577                 g_mapInfo[j *MAXLEVELS+k].filename = (char *)Xrealloc(g_mapInfo[j*MAXLEVELS+k].filename,(Bstrlen(tempbuf)+1));
5578 
5579             Bstrcpy(g_mapInfo[j*MAXLEVELS+k].filename,tempbuf);
5580 
5581             C_SkipComments();
5582 
5583             g_mapInfo[j *MAXLEVELS+k].partime =
5584                 (((*(textptr+0)-'0')*10+(*(textptr+1)-'0'))*REALGAMETICSPERSEC*60)+
5585                 (((*(textptr+3)-'0')*10+(*(textptr+4)-'0'))*REALGAMETICSPERSEC);
5586 
5587             textptr += 5;
5588             scriptSkipSpaces();
5589 
5590             // cheap hack, 0.99 doesn't have the 3D Realms time
5591             if (*(textptr+2) == ':')
5592             {
5593                 g_mapInfo[j *MAXLEVELS+k].designertime =
5594                     (((*(textptr+0)-'0')*10+(*(textptr+1)-'0'))*REALGAMETICSPERSEC*60)+
5595                     (((*(textptr+3)-'0')*10+(*(textptr+4)-'0'))*REALGAMETICSPERSEC);
5596 
5597                 textptr += 5;
5598                 scriptSkipSpaces();
5599             }
5600             else if (g_scriptVersion == 10) g_scriptVersion = 9;
5601 
5602             i = 0;
5603 
5604             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
5605             {
5606                 tempbuf[i] = *textptr;
5607                 textptr++,i++;
5608                 if (EDUKE32_PREDICT_FALSE(i >= 32))
5609                 {
5610                     initprintf("%s:%d: warning: truncating level name to %d characters.\n",
5611                         g_scriptFileName,g_lineNumber,31);
5612                     i--;
5613                     g_warningCnt++;
5614                     scriptSkipLine();
5615                     break;
5616                 }
5617             }
5618 
5619             tempbuf[i] = '\0';
5620 
5621             if (g_mapInfo[j*MAXLEVELS+k].name == NULL)
5622                 g_mapInfo[j*MAXLEVELS+k].name = (char *)Xcalloc(Bstrlen(tempbuf)+1,sizeof(uint8_t));
5623             else if ((Bstrlen(tempbuf)+1) > sizeof(g_mapInfo[j*MAXLEVELS+k].name))
5624                 g_mapInfo[j *MAXLEVELS+k].name = (char *)Xrealloc(g_mapInfo[j*MAXLEVELS+k].name,(Bstrlen(tempbuf)+1));
5625 
5626             /*         initprintf("level name string len: %d\n",Bstrlen(tempbuf)); */
5627 
5628             Bstrcpy(g_mapInfo[j*MAXLEVELS+k].name,tempbuf);
5629 
5630             continue;
5631 
5632         case CON_DEFINEQUOTE:
5633         case CON_REDEFINEQUOTE:
5634             if (tw == CON_DEFINEQUOTE)
5635             {
5636                 g_scriptPtr--;
5637             }
5638 
5639             C_GetNextValue(LABEL_DEFINE);
5640 
5641             k = g_scriptPtr[-1];
5642 
5643             if (EDUKE32_PREDICT_FALSE((unsigned)k >= MAXQUOTES))
5644             {
5645                 initprintf("%s:%d: error: quote number exceeds limit of %d.\n",g_scriptFileName,g_lineNumber,MAXQUOTES);
5646                 g_errorCnt++;
5647                 scriptSkipLine();
5648                 continue;
5649             }
5650             else
5651             {
5652                 C_AllocQuote(k);
5653             }
5654 
5655             if (tw == CON_DEFINEQUOTE)
5656                 g_scriptPtr--;
5657 
5658             i = 0;
5659 
5660             scriptSkipSpaces();
5661 
5662             if (tw == CON_REDEFINEQUOTE)
5663             {
5664                 if (apXStrings[g_numXStrings] == NULL)
5665                     apXStrings[g_numXStrings] = (char *)Xcalloc(MAXQUOTELEN,sizeof(uint8_t));
5666             }
5667 
5668             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
5669             {
5670                 /*
5671                 if (*textptr == '%' && *(textptr+1) == 's')
5672                 {
5673                 initprintf("%s:%d: error: quote text contains string identifier.\n",g_szScriptFileName,g_lineNumber);
5674                 g_numCompilerErrors++;
5675                 while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) textptr++;
5676                 break;
5677                 }
5678                 */
5679                 if (tw == CON_DEFINEQUOTE)
5680                     *(apStrings[k]+i) = *textptr;
5681                 else
5682                     *(apXStrings[g_numXStrings]+i) = *textptr;
5683                 textptr++,i++;
5684                 if (EDUKE32_PREDICT_FALSE(i >= MAXQUOTELEN))
5685                 {
5686                     initprintf("%s:%d: warning: truncating quote text to %d characters.\n",g_scriptFileName,g_lineNumber,MAXQUOTELEN-1);
5687                     i--;
5688                     g_warningCnt++;
5689                     scriptSkipLine();
5690                     break;
5691                 }
5692             }
5693 
5694             if (tw == CON_DEFINEQUOTE)
5695             {
5696                 if ((unsigned)k < MAXQUOTES)
5697                     *(apStrings[k]+i) = '\0';
5698             }
5699             else
5700             {
5701                 *(apXStrings[g_numXStrings]+i) = '\0';
5702                 scriptWriteValue(g_numXStrings++);
5703             }
5704             continue;
5705 
5706         case CON_DEFINECHEATDESCRIPTION:
5707             g_scriptPtr--;
5708 
5709             C_GetNextValue(LABEL_DEFINE);
5710 
5711             k = g_scriptPtr[-1];
5712 
5713             if (EDUKE32_PREDICT_FALSE((unsigned)k >= NUMCHEATS))
5714             {
5715                 initprintf("%s:%d: error: cheat number exceeds limit of %d.\n",g_scriptFileName,g_lineNumber,NUMCHEATS);
5716                 g_errorCnt++;
5717                 scriptSkipLine();
5718                 continue;
5719             }
5720 
5721             g_scriptPtr--;
5722 
5723             i = 0;
5724 
5725             scriptSkipSpaces();
5726 
5727             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
5728             {
5729                 *(CheatDescriptions[k]+i) = *textptr;
5730                 textptr++,i++;
5731                 if (EDUKE32_PREDICT_FALSE(i >= MAXCHEATDESC))
5732                 {
5733                     initprintf("%s:%d: warning: truncating cheat text to %d characters.\n",g_scriptFileName,g_lineNumber,MAXCHEATDESC-1);
5734                     i--;
5735                     g_warningCnt++;
5736                     scriptSkipLine();
5737                     break;
5738                 }
5739             }
5740 
5741             *(CheatDescriptions[k]+i) = '\0';
5742             continue;
5743 
5744         case CON_CHEATKEYS:
5745             g_scriptPtr--;
5746             C_GetNextValue(LABEL_DEFINE);
5747             CheatKeys[0] = g_scriptPtr[-1];
5748             C_GetNextValue(LABEL_DEFINE);
5749             CheatKeys[1] = g_scriptPtr[-1];
5750             g_scriptPtr -= 2;
5751             continue;
5752 
5753         case CON_UNDEFINECHEAT:
5754             g_scriptPtr--;
5755 
5756             C_GetNextValue(LABEL_DEFINE);
5757             g_scriptPtr--;
5758             j = *g_scriptPtr;
5759 
5760             if (EDUKE32_PREDICT_FALSE((unsigned)j >= NUMCHEATS))
5761             {
5762                 initprintf("%s:%d: error: cheat undefinition attempts to undefine nonexistent cheat.\n",g_scriptFileName,g_lineNumber);
5763                 g_errorCnt++;
5764                 scriptSkipLine();
5765                 continue;
5766             }
5767 
5768             CheatStrings[j][0] = '\0';
5769             continue;
5770 
5771         case CON_DEFINECHEAT:
5772             g_scriptPtr--;
5773             C_GetNextValue(LABEL_DEFINE);
5774             k = g_scriptPtr[-1];
5775 
5776             if (EDUKE32_PREDICT_FALSE((unsigned)k >= NUMCHEATS))
5777             {
5778                 initprintf("%s:%d: error: cheat redefinition attempts to redefine nonexistent cheat.\n",g_scriptFileName,g_lineNumber);
5779                 g_errorCnt++;
5780                 scriptSkipLine();
5781                 continue;
5782             }
5783             g_scriptPtr--;
5784             i = 0;
5785             scriptSkipSpaces();
5786             while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0 && *textptr != ' ')
5787             {
5788                 CheatStrings[k][i] = Btolower(*textptr);
5789                 textptr++,i++;
5790                 if (EDUKE32_PREDICT_FALSE(i >= (signed)sizeof(CheatStrings[k])))
5791                 {
5792                     initprintf("%s:%d: warning: truncating cheat string to %d characters.\n",
5793                         g_scriptFileName,g_lineNumber,(signed)sizeof(CheatStrings[k])-1);
5794                     i--;
5795                     g_warningCnt++;
5796                     scriptSkipLine();
5797                     break;
5798                 }
5799             }
5800             CheatStrings[k][i] = '\0';
5801             continue;
5802 
5803         case CON_DEFINESOUND:
5804             g_scriptPtr--;
5805             C_GetNextValue(LABEL_DEFINE);
5806 
5807             // Ideally we could keep the value of i from C_GetNextValue() instead of having to hash_find() again.
5808             // This depends on tempbuf remaining in place after C_GetNextValue():
5809             j = hash_find(&h_labels,tempbuf);
5810 
5811             k = g_scriptPtr[-1];
5812             if (EDUKE32_PREDICT_FALSE((unsigned)k >= MAXSOUNDS-1))
5813             {
5814                 initprintf("%s:%d: error: sound index exceeds limit of %d.\n",g_scriptFileName,g_lineNumber, MAXSOUNDS-1);
5815                 g_errorCnt++;
5816                 k = MAXSOUNDS-1;
5817             }
5818             else if (EDUKE32_PREDICT_FALSE(g_sounds[k].filename != NULL))
5819             {
5820                 initprintf("%s:%d: warning: sound %d already defined (%s)\n",g_scriptFileName,g_lineNumber,k,g_sounds[k].filename);
5821                 g_warningCnt++;
5822             }
5823 
5824             g_scriptPtr--;
5825             i = 0;
5826             C_SkipComments();
5827 
5828             if (g_sounds[k].filename == NULL)
5829                 g_sounds[k].filename = (char *)Xcalloc(BMAX_PATH,sizeof(uint8_t));
5830 
5831             if (*textptr == '\"')
5832             {
5833                 textptr++;
5834                 while (*textptr && *textptr != '\"')
5835                 {
5836                     g_sounds[k].filename[i++] = *textptr++;
5837                     if (EDUKE32_PREDICT_FALSE(i >= BMAX_PATH-1))
5838                     {
5839                         initprintf("%s:%d: error: sound filename exceeds limit of %d characters.\n",g_scriptFileName,g_lineNumber,BMAX_PATH-1);
5840                         g_errorCnt++;
5841                         C_SkipComments();
5842                         break;
5843                     }
5844                 }
5845                 textptr++;
5846             }
5847             else while (*textptr != ' ' && *textptr != '\t' && *textptr != '\r' && *textptr != '\n')
5848             {
5849                 g_sounds[k].filename[i++] = *textptr++;
5850                 if (EDUKE32_PREDICT_FALSE(i >= BMAX_PATH-1))
5851                 {
5852                     initprintf("%s:%d: error: sound filename exceeds limit of %d characters.\n",g_scriptFileName,g_lineNumber,BMAX_PATH-1);
5853                     g_errorCnt++;
5854                     C_SkipComments();
5855                     break;
5856                 }
5857             }
5858             g_sounds[k].filename[i] = '\0';
5859 
5860             check_filename_case(g_sounds[k].filename);
5861 
5862             C_GetNextValue(LABEL_DEFINE);
5863             g_sounds[k].ps = g_scriptPtr[-1];
5864             C_GetNextValue(LABEL_DEFINE);
5865             g_sounds[k].pe = g_scriptPtr[-1];
5866             C_GetNextValue(LABEL_DEFINE);
5867             g_sounds[k].pr = g_scriptPtr[-1];
5868 
5869             C_GetNextValue(LABEL_DEFINE);
5870             g_sounds[k].m = g_scriptPtr[-1] & ~SF_ONEINST_INTERNAL;
5871             if (g_scriptPtr[-1] & SF_LOOP)
5872                 g_sounds[k].m |= SF_ONEINST_INTERNAL;
5873 
5874             C_GetNextValue(LABEL_DEFINE);
5875             g_sounds[k].vo = g_scriptPtr[-1];
5876             g_scriptPtr -= 5;
5877 
5878             g_sounds[k].volume = fix16_one;
5879 
5880             if (k > g_highestSoundIdx)
5881                 g_highestSoundIdx = k;
5882 
5883             if (g_dynamicSoundMapping && j >= 0 && (labeltype[j] & LABEL_DEFINE))
5884                 G_ProcessDynamicSoundMapping(label+(j<<6), k);
5885             continue;
5886 
5887         case CON_ENDEVENT:
5888 
5889             if (EDUKE32_PREDICT_FALSE(!g_scriptEventOffset))
5890             {
5891                 C_ReportError(-1);
5892                 initprintf("%s:%d: error: found `endevent' without open `onevent'.\n",g_scriptFileName,g_lineNumber);
5893                 g_errorCnt++;
5894             }
5895             if (EDUKE32_PREDICT_FALSE(g_numBraces > 0))
5896             {
5897                 C_ReportError(ERROR_NOTTOPLEVEL);
5898                 g_errorCnt++;
5899             }
5900             // if event has already been declared then put a jump in instead
5901             if (g_scriptEventChainOffset)
5902             {
5903                 g_scriptPtr--;
5904                 scriptWriteValue(CON_JUMP | LINE_NUMBER);
5905                 scriptWriteValue(GV_FLAG_CONSTANT);
5906                 scriptWriteValue(g_scriptEventChainOffset);
5907                 scriptWriteValue(CON_ENDEVENT | LINE_NUMBER);
5908 
5909                 C_FillEventBreakStackWithJump((intptr_t *)g_scriptEventBreakOffset, g_scriptEventChainOffset);
5910 
5911                 g_scriptEventChainOffset = 0;
5912             }
5913             else
5914             {
5915                 // pad space for the next potential appendevent
5916                 apScriptGameEventEnd[g_currentEvent] = &g_scriptPtr[-1] - apScript;
5917                 scriptWriteValue(CON_ENDEVENT | LINE_NUMBER);
5918                 scriptWriteValue(g_scriptEventBreakOffset);
5919                 scriptWriteValue(CON_ENDEVENT | LINE_NUMBER);
5920             }
5921 
5922             g_scriptEventBreakOffset = g_scriptEventOffset = g_scriptActorOffset = 0;
5923             g_currentEvent = -1;
5924             Bsprintf(g_szCurrentBlockName,"(none)");
5925             continue;
5926 
5927         case CON_ENDA:
5928             if (EDUKE32_PREDICT_FALSE(!g_scriptActorOffset || g_scriptEventOffset))
5929             {
5930                 C_ReportError(-1);
5931                 initprintf("%s:%d: error: found `enda' without open `actor'.\n",g_scriptFileName,g_lineNumber);
5932                 g_errorCnt++;
5933                 g_scriptEventOffset = 0;
5934             }
5935             if (EDUKE32_PREDICT_FALSE(g_numBraces > 0))
5936             {
5937                 C_ReportError(ERROR_NOTTOPLEVEL);
5938                 g_errorCnt++;
5939             }
5940             g_scriptActorOffset = 0;
5941             Bsprintf(g_szCurrentBlockName,"(none)");
5942             continue;
5943 
5944         case CON_RETURN:
5945             if (g_checkingSwitch)
5946             {
5947                 g_checkingCase = false;
5948                 return 1;
5949             }
5950             continue;
5951 
5952         case CON_BREAK:
5953             if (g_checkingSwitch)
5954             {
5955                 if (EDUKE32_PREDICT_FALSE(otw == CON_BREAK))
5956                 {
5957                     C_ReportError(-1);
5958                     initprintf("%s:%d: warning: duplicate `break'.\n",g_scriptFileName, g_lineNumber);
5959                     g_warningCnt++;
5960                     g_scriptPtr--;
5961                     continue;
5962                 }
5963 
5964                 g_checkingCase = false;
5965                 return 1;
5966             }
5967             else if (g_scriptEventOffset)
5968             {
5969                 g_scriptPtr--;
5970                 scriptWriteValue(CON_JUMP | LINE_NUMBER);
5971                 scriptWriteValue(GV_FLAG_CONSTANT);
5972                 scriptWriteValue(g_scriptEventBreakOffset);
5973                 g_scriptEventBreakOffset = &g_scriptPtr[-1] - apScript;
5974             }
5975             continue;
5976 
5977         case CON_SCRIPTSIZE:
5978             g_scriptPtr--;
5979             C_GetNextValue(LABEL_DEFINE);
5980             j = g_scriptPtr[-1];
5981             g_scriptPtr--;
5982             C_SkipComments();
5983             C_SetScriptSize(j);
5984             continue;
5985 
5986         case CON_SHADETO:
5987             g_scriptPtr--;
5988             C_GetNextValue(LABEL_DEFINE);
5989             g_scriptPtr--;
5990             continue;
5991 
5992         case CON_FALL:
5993         case CON_GETLASTPAL:
5994         case CON_GETTEXTURECEILING:
5995         case CON_GETTEXTUREFLOOR:
5996         case CON_INSERTSPRITEQ:
5997         case CON_KILLIT:
5998         case CON_MIKESND:
5999         case CON_OPERATE:
6000         case CON_PKICK:
6001         case CON_PSTOMP:
6002         case CON_RESETACTIONCOUNT:
6003         case CON_RESETCOUNT:
6004         case CON_RESETPLAYER:
6005         case CON_RESPAWNHITAG:
6006         case CON_SECTGETHITAG:
6007         case CON_SECTGETLOTAG:
6008         case CON_SPGETHITAG:
6009         case CON_SPGETLOTAG:
6010         case CON_STARTSCREEN:
6011         case CON_STOPALLMUSIC:
6012         case CON_STOPALLSOUNDS:
6013         case CON_TIP:
6014         case CON_TOSSWEAPON:
6015         case CON_WACKPLAYER:
6016             continue;
6017 
6018         case CON_NULLOP:
6019         {
6020             auto const kw = C_GetKeyword();
6021             if (EDUKE32_PREDICT_FALSE(kw != CON_ELSE && kw != CON_RIGHTBRACE))
6022             {
6023                 C_ReportError(-1);
6024                 g_warningCnt++;
6025                 initprintf("%s:%d: warning: `nullop' found without accompanying branch.\n",g_scriptFileName,g_lineNumber);
6026                 g_scriptPtr--;
6027                 g_skipBranch = true;
6028             }
6029             continue;
6030         }
6031 
6032         case CON_GAMESTARTUP:
6033             {
6034                 int32_t params[31];
6035 
6036                 g_scriptPtr--;
6037                 for (j = 0; j < 31; j++)
6038                 {
6039                     C_GetNextValue(LABEL_DEFINE);
6040                     g_scriptPtr--;
6041                     params[j] = *g_scriptPtr;
6042 
6043                     if (j != 12 && j != 21 && j != 25 && j != 29) continue;
6044 
6045                     if (C_GetKeyword() != -1)
6046                     {
6047                         if (j == 12)
6048                             g_scriptVersion = 10;
6049                         else if (j == 21)
6050                             g_scriptVersion = 11;
6051                         else if (j == 25)
6052                             g_scriptVersion = 13;
6053                         else if (j == 29)
6054                             g_scriptVersion = 14;
6055                         break;
6056                     }
6057                     else
6058                         g_scriptVersion = 16;
6059                 }
6060 
6061                 /*
6062                 v1.3d                   v1.5
6063                 DEFAULTVISIBILITY       DEFAULTVISIBILITY
6064                 GENERICIMPACTDAMAGE     GENERICIMPACTDAMAGE
6065                 MAXPLAYERHEALTH         MAXPLAYERHEALTH
6066                 STARTARMORHEALTH        STARTARMORHEALTH
6067                 RESPAWNACTORTIME        RESPAWNACTORTIME
6068                 RESPAWNITEMTIME         RESPAWNITEMTIME
6069                 RUNNINGSPEED            RUNNINGSPEED
6070                 RPGBLASTRADIUS          GRAVITATIONALCONSTANT
6071                 PIPEBOMBRADIUS          RPGBLASTRADIUS
6072                 SHRINKERBLASTRADIUS     PIPEBOMBRADIUS
6073                 TRIPBOMBBLASTRADIUS     SHRINKERBLASTRADIUS
6074                 MORTERBLASTRADIUS       TRIPBOMBBLASTRADIUS
6075                 BOUNCEMINEBLASTRADIUS   MORTERBLASTRADIUS
6076                 SEENINEBLASTRADIUS      BOUNCEMINEBLASTRADIUS
6077                 MAXPISTOLAMMO           SEENINEBLASTRADIUS
6078                 MAXSHOTGUNAMMO          MAXPISTOLAMMO
6079                 MAXCHAINGUNAMMO         MAXSHOTGUNAMMO
6080                 MAXRPGAMMO              MAXCHAINGUNAMMO
6081                 MAXHANDBOMBAMMO         MAXRPGAMMO
6082                 MAXSHRINKERAMMO         MAXHANDBOMBAMMO
6083                 MAXDEVISTATORAMMO       MAXSHRINKERAMMO
6084                 MAXTRIPBOMBAMMO         MAXDEVISTATORAMMO
6085                 MAXFREEZEAMMO           MAXTRIPBOMBAMMO
6086                 CAMERASDESTRUCTABLE     MAXFREEZEAMMO
6087                 NUMFREEZEBOUNCES        MAXGROWAMMO
6088                 FREEZERHURTOWNER        CAMERASDESTRUCTABLE
6089                 NUMFREEZEBOUNCES
6090                 FREEZERHURTOWNER
6091                 QSIZE
6092                 TRIPBOMBLASERMODE
6093                 */
6094 
6095                 G_DoGameStartup(params);
6096             }
6097             continue;
6098         }
6099     }
6100     while (loop);
6101 
6102     return 0;
6103 }
6104 
6105 /* Anything added with C_AddDefinition() cannot be overwritten in the CONs */
C_AddDefinition(const char * lLabel,int32_t lValue,int32_t lType)6106 static void C_AddDefinition(const char *lLabel,int32_t lValue,int32_t lType)
6107 {
6108     Bstrcpy(LAST_LABEL,lLabel);
6109     labeltype[g_labelCnt] = lType;
6110     hash_add(&h_labels,LAST_LABEL,g_labelCnt,0);
6111     labelcode[g_labelCnt++] = lValue;
6112 }
6113 
C_AddDefaultDefinitions(void)6114 static void C_AddDefaultDefinitions(void)
6115 {
6116     for (int i=0; i<MAXEVENTS; i++)
6117         C_AddDefinition(EventNames[i], i, LABEL_DEFINE|LABEL_EVENT);
6118 
6119 #if 0
6120     for (int i=0; i<NUMGAMEFUNCTIONS; i++)
6121     {
6122         int32_t j;
6123 
6124         if (gamefunctions[i][0] == '\0')
6125             continue;
6126 
6127         // if (!Bstrcmp(gamefunctions[i],"Show_Console")) continue;
6128 
6129         j = Bsprintf(tempbuf,"GAMEFUNC_%s", gamefunctions[i]);
6130 
6131         for (; j>=0; j--)
6132             tempbuf[j] = Btoupper(tempbuf[j]);
6133 
6134         C_AddDefinition(tempbuf, i, LABEL_DEFINE);
6135     }
6136 #endif
6137 
6138     static tokenmap_t predefined[] =
6139     {
6140         { "CLIPMASK0",         CLIPMASK0 },
6141         { "CLIPMASK1",         CLIPMASK1 },
6142 
6143         { "GAMEARRAY_BOOLEAN", GAMEARRAY_BITMAP },
6144         { "GAMEARRAY_INT16",   GAMEARRAY_INT16 },
6145         { "GAMEARRAY_INT8",    GAMEARRAY_INT8 },
6146         { "GAMEARRAY_RESTORE", GAMEARRAY_RESTORE },
6147         { "GAMEARRAY_UINT16",  GAMEARRAY_UINT16 },
6148         { "GAMEARRAY_UINT8",   GAMEARRAY_UINT8 },
6149 
6150         { "GAMEVAR_NODEFAULT", GAMEVAR_NODEFAULT },
6151         { "GAMEVAR_NOMULTI",   GAMEVAR_NOMULTI },
6152         { "GAMEVAR_NORESET",   GAMEVAR_NORESET },
6153         { "GAMEVAR_PERACTOR",  GAMEVAR_PERACTOR },
6154         { "GAMEVAR_PERPLAYER", GAMEVAR_PERPLAYER },
6155         { "GAMEVAR_SERIALIZE", GAMEVAR_SERIALIZE },
6156 
6157         { "MAX_WEAPONS",        MAX_WEAPONS },
6158         { "MAXSPRITES",         MAXSPRITES },
6159         { "MAXSPRITESONSCREEN", MAXSPRITESONSCREEN },
6160         { "MAXSTATUS",          MAXSTATUS },
6161         { "MAXTILES",           MAXTILES },
6162 
6163         { "PROJ_BOUNCES",     PROJ_BOUNCES },
6164         { "PROJ_BSOUND",      PROJ_BSOUND },
6165         { "PROJ_CLIPDIST",    PROJ_CLIPDIST },
6166         { "PROJ_CSTAT",       PROJ_CSTAT },
6167         { "PROJ_DECAL",       PROJ_DECAL },
6168         { "PROJ_DROP",        PROJ_DROP },
6169         { "PROJ_EXTRA",       PROJ_EXTRA },
6170         { "PROJ_EXTRA_RAND",  PROJ_EXTRA_RAND },
6171         { "PROJ_FLASH_COLOR", PROJ_FLASH_COLOR },
6172         { "PROJ_HITRADIUS",   PROJ_HITRADIUS },
6173         { "PROJ_ISOUND",      PROJ_ISOUND },
6174         { "PROJ_OFFSET",      PROJ_OFFSET },
6175         { "PROJ_PAL",         PROJ_PAL },
6176         { "PROJ_RANGE",       PROJ_RANGE },
6177         { "PROJ_SHADE",       PROJ_SHADE },
6178         { "PROJ_SOUND",       PROJ_SOUND },
6179         { "PROJ_SPAWNS",      PROJ_SPAWNS },
6180         { "PROJ_SXREPEAT",    PROJ_SXREPEAT },
6181         { "PROJ_SYREPEAT",    PROJ_SYREPEAT },
6182         { "PROJ_TNUM",        PROJ_TNUM },
6183         { "PROJ_TOFFSET",     PROJ_TOFFSET },
6184         { "PROJ_TRAIL",       PROJ_TRAIL },
6185         { "PROJ_TXREPEAT",    PROJ_TXREPEAT },
6186         { "PROJ_TYREPEAT",    PROJ_TYREPEAT },
6187         { "PROJ_USERDATA",    PROJ_USERDATA },
6188         { "PROJ_VEL",         PROJ_VEL },
6189         { "PROJ_VEL_MULT",    PROJ_MOVECNT },
6190         { "PROJ_WORKSLIKE",   PROJ_WORKSLIKE },
6191         { "PROJ_XREPEAT",     PROJ_XREPEAT },
6192         { "PROJ_YREPEAT",     PROJ_YREPEAT },
6193 
6194         { "SFLAG_BADGUY",          SFLAG_BADGUY },
6195         { "SFLAG_DAMAGEEVENT",     SFLAG_DAMAGEEVENT },
6196         { "SFLAG_GREENSLIMEFOOD",  SFLAG_GREENSLIMEFOOD },
6197         { "SFLAG_HURTSPAWNBLOOD",  SFLAG_HURTSPAWNBLOOD },
6198         { "SFLAG_NOCLIP",          SFLAG_NOCLIP },
6199         { "SFLAG_NODAMAGEPUSH",    SFLAG_NODAMAGEPUSH },
6200         { "SFLAG_NOEVENTS",        SFLAG_NOEVENTCODE },
6201         { "SFLAG_NOLIGHT",         SFLAG_NOLIGHT },
6202         { "SFLAG_NOPAL",           SFLAG_NOPAL },
6203         { "SFLAG_NOSHADE",         SFLAG_NOSHADE },
6204         { "SFLAG_NOTELEPORT",      SFLAG_NOTELEPORT },
6205         { "SFLAG_NOWATERDIP",      SFLAG_NOWATERDIP },
6206         { "SFLAG_NVG",             SFLAG_NVG },
6207         { "SFLAG_REALCLIPDIST",    SFLAG_REALCLIPDIST },
6208         { "SFLAG_SHADOW",          SFLAG_SHADOW },
6209         { "SFLAG_SMOOTHMOVE",      SFLAG_SMOOTHMOVE },
6210         { "SFLAG_USEACTIVATOR",    SFLAG_USEACTIVATOR },
6211         { "SFLAG_WAKEUPBADGUYS",   SFLAG_WAKEUPBADGUYS },
6212         { "SFLAG_NOWATERSECTOR",   SFLAG_NOWATERSECTOR },
6213         { "SFLAG_QUEUEDFORDELETE", SFLAG_QUEUEDFORDELETE },
6214 
6215         { "STAT_ACTIVATOR",   STAT_ACTIVATOR },
6216         { "STAT_ACTOR",       STAT_ACTOR },
6217         { "STAT_DEFAULT",     STAT_DEFAULT },
6218         { "STAT_DUMMYPLAYER", STAT_DUMMYPLAYER },
6219         { "STAT_EFFECTOR",    STAT_EFFECTOR },
6220         { "STAT_FALLER",      STAT_FALLER },
6221         { "STAT_FX",          STAT_FX },
6222         { "STAT_LIGHT",       STAT_LIGHT },
6223         { "STAT_LOCATOR",     STAT_LOCATOR },
6224         { "STAT_MISC",        STAT_MISC },
6225         { "STAT_PLAYER",      STAT_PLAYER },
6226         { "STAT_PROJECTILE",  STAT_PROJECTILE },
6227         { "STAT_STANDABLE",   STAT_STANDABLE },
6228         { "STAT_TRANSPORT",   STAT_TRANSPORT },
6229         { "STAT_ZOMBIEACTOR", STAT_ZOMBIEACTOR },
6230 
6231         { "STR_BESTTIME",        STR_BESTTIME },
6232         { "STR_DESIGNERTIME",    STR_DESIGNERTIME },
6233         { "STR_GAMETYPE",        STR_GAMETYPE },
6234         { "STR_MAPFILENAME",     STR_MAPFILENAME },
6235         { "STR_MAPNAME",         STR_MAPNAME },
6236         { "STR_PARTIME",         STR_PARTIME },
6237         { "STR_PLAYERNAME",      STR_PLAYERNAME },
6238         { "STR_REVISION",        STR_REVISION },
6239         { "STR_USERMAPFILENAME", STR_USERMAPFILENAME },
6240         { "STR_VERSION",         STR_VERSION },
6241         { "STR_VOLUMENAME",      STR_VOLUMENAME },
6242         { "STR_YOURTIME",        STR_YOURTIME },
6243     };
6244 
6245     for (auto & def : predefined)
6246         C_AddDefinition(def.token, def.val, LABEL_DEFINE);
6247 
6248     C_AddDefinition("NO", 0, LABEL_DEFINE | LABEL_ACTION | LABEL_AI | LABEL_MOVE);
6249 }
6250 
C_InitProjectiles(void)6251 void C_InitProjectiles(void)
6252 {
6253     defaultprojectile_t const Projectile =
6254     {
6255         // workslike, cstat, hitradius, range, flashcolor;
6256         // spawns, sound, isound, vel, decal, trail, tnum, drop;
6257         // offset, bounces, bsound, toffset, extra, extra_rand;
6258         // sxrepeat, syrepeat, txrepeat, tyrepeat;
6259         // shade, xrepeat, yrepeat, pal;
6260         // movecnt, clipdist, filler[2], userdata;
6261 
6262         // XXX: The default projectie seems to mimic a union of hard-coded ones.
6263 
6264         1, -1, 2048, 0, 0,
6265         (int16_t)SMALLSMOKE, -1, -1, 600, (int16_t)BULLETHOLE, -1, 0, 0,
6266         448, (int16_t)g_numFreezeBounces, (int16_t)PIPEBOMB_BOUNCE, 1, 100, -1,
6267         -1, -1, -1, -1,
6268         -96, 18, 18, 0,
6269         1, 32, { 0, 0 }, 0,
6270     };
6271 
6272     DefaultProjectile = Projectile;
6273 
6274     for (auto & tile : g_tile)
6275     {
6276         if (tile.proj)
6277             *tile.proj = DefaultProjectile;
6278 
6279         if (tile.defproj)
6280             *tile.defproj = DefaultProjectile;
6281     }
6282 }
6283 
C_ScriptVersionString(int32_t version)6284 static char const * C_ScriptVersionString(int32_t version)
6285 {
6286 #ifdef EDUKE32_STANDALONE
6287     UNREFERENCED_PARAMETER(version);
6288 #else
6289     switch (version)
6290     {
6291     case 9:
6292         return ", v0.99 compatibility mode";
6293     case 10:
6294         return ", v1.0 compatibility mode";
6295     case 11:
6296         return ", v1.1 compatibility mode";
6297     case 13:
6298         return ", v1.3D compatibility mode";
6299     }
6300 #endif
6301     return "";
6302 }
6303 
C_PrintStats(void)6304 void C_PrintStats(void)
6305 {
6306     initprintf("%d/%d labels, %d/%d variables, %d/%d arrays\n", g_labelCnt, MAXLABELS,
6307         g_gameVarCount, MAXGAMEVARS, g_gameArrayCount, MAXGAMEARRAYS);
6308 
6309     int cnt = g_numXStrings;
6310 
6311     for (auto &ptr : apStrings)
6312         if (ptr)
6313             cnt++;
6314 
6315     if (cnt) initprintf("%d strings, ", cnt);
6316     cnt = 0;
6317 
6318     for (auto & apScriptEvent : apScriptEvents)
6319         if (apScriptEvent)
6320             cnt++;
6321 
6322     if (cnt) initprintf("%d events, ", cnt);
6323     cnt = 0;
6324 
6325     for (auto & tile : g_tile)
6326         if (tile.execPtr)
6327             cnt++;
6328 
6329     if (cnt) initprintf("%d actors", cnt);
6330     initprintf("\n");
6331 }
6332 
6333 // TODO: add some kind of mapping between the table and the struct holding the tokens
scriptInitTables()6334 void scriptInitTables()
6335 {
6336     for (auto table : tables)
6337         hash_init(table);
6338 
6339     for (auto table : inttables)
6340         inthash_init(table);
6341 
6342     for (auto &keyword : vm_keywords)
6343         hash_add(&h_keywords, keyword.token, keyword.val, 0);
6344 
6345     for (auto &iter_token : iter_tokens)
6346         hash_add(&h_iter, iter_token.token, iter_token.val, 0);
6347 
6348     for (auto &varvar : varvartable)
6349         inthash_add(&h_varvar, varvar.x, varvar.y, 0);
6350 
6351     for (auto &globalvar : globalvartable)
6352         inthash_add(&h_globalvar, globalvar.x, globalvar.y, 0);
6353 
6354     for (auto &playervar : playervartable)
6355         inthash_add(&h_playervar, playervar.x, playervar.y, 0);
6356 
6357     for (auto &actorvar : actorvartable)
6358         inthash_add(&h_actorvar, actorvar.x, actorvar.y, 0);
6359 }
6360 
6361 #if MICROPROFILE_ENABLED != 0
C_GetLabelIndex(int32_t val,int type)6362 static int C_GetLabelIndex(int32_t val, int type)
6363 {
6364     for (int i=0;i<g_labelCnt;i++)
6365         if (labelcode[i] == val && (labeltype[i] & type) != 0)
6366             return i;
6367 
6368     for (int i=0;i<g_labelCnt;i++)
6369         if (labelcode[i] == val)
6370             return i;
6371 
6372     return -1;
6373 }
6374 #endif
6375 
C_Compile(const char * fileName)6376 void C_Compile(const char *fileName)
6377 {
6378     Bmemset(apScriptEvents, 0, sizeof(apScriptEvents));
6379     Bmemset(apScriptGameEventEnd, 0, sizeof(apScriptGameEventEnd));
6380 
6381     for (auto & i : g_tile)
6382         Bmemset(&i, 0, sizeof(tiledata_t));
6383 
6384     scriptInitTables();
6385     VM_InitHashTables();
6386 
6387     Gv_Init();
6388     C_InitProjectiles();
6389 
6390     buildvfs_kfd kFile = kopen4loadfrommod(fileName, g_loadFromGroupOnly);
6391 
6392     if (kFile == buildvfs_kfd_invalid) // JBF: was 0
6393     {
6394         if (g_loadFromGroupOnly == 1 || numgroupfiles == 0)
6395         {
6396 #ifndef EDUKE32_STANDALONE
6397             char const *gf = G_GrpFile();
6398             Bsprintf(tempbuf,"Required game data was not found.  A valid copy of \"%s\" or other compatible data is needed to run EDuke32.\n\n"
6399                      "You must copy \"%s\" to your game directory before continuing!", gf, gf);
6400             G_GameExit(tempbuf);
6401 #else
6402             G_GameExit(" ");
6403 #endif
6404         }
6405         else
6406         {
6407             Bsprintf(tempbuf,"CON file `%s' missing.", fileName);
6408             G_GameExit(tempbuf);
6409         }
6410 
6411         //g_loadFromGroupOnly = 1;
6412         return; //Not there
6413     }
6414 
6415     int const kFileLen = kfilelength(kFile);
6416 
6417     initprintf("Compiling: %s (%d bytes)\n", fileName, kFileLen);
6418 
6419     g_logFlushWindow = 0;
6420 
6421     uint32_t const startcompiletime = timerGetTicks();
6422 
6423     char * mptr = (char *)Xmalloc(kFileLen+1);
6424     mptr[kFileLen] = 0;
6425 
6426     textptr = (char *)mptr;
6427     kread(kFile, (char *)textptr, kFileLen);
6428     kclose(kFile);
6429 
6430     Xfree(apScript);
6431 
6432     apScript = (intptr_t *)Xcalloc(1, g_scriptSize * sizeof(intptr_t));
6433     bitptr   = (uint8_t *)Xcalloc(1, (((g_scriptSize + 7) >> 3) + 1) * sizeof(uint8_t));
6434 
6435     g_errorCnt   = 0;
6436     g_labelCnt   = 0;
6437     g_lineNumber = 1;
6438     g_scriptPtr  = apScript + 3;  // move permits constants 0 and 1; moveptr[1] would be script[2] (reachable?)
6439     g_totalLines = 0;
6440     g_warningCnt = 0;
6441 
6442     Bstrcpy(g_scriptFileName, fileName);
6443 
6444     C_AddDefaultDefinitions();
6445     C_ParseCommand(true);
6446 
6447     for (char * m : g_scriptModules)
6448     {
6449         C_Include(m);
6450         Bfree(m);
6451     }
6452     g_scriptModules.clear();
6453 
6454     g_logFlushWindow = 1;
6455 
6456     if (g_errorCnt > 63)
6457         initprintf("fatal error: too many errors: Aborted\n");
6458 
6459     //*script = (intptr_t) g_scriptPtr;
6460 
6461     DO_FREE_AND_NULL(mptr);
6462 
6463     if (g_warningCnt || g_errorCnt)
6464     {
6465         initprintf("Found %d warning(s), %d error(s).\n", g_warningCnt, g_errorCnt);
6466 
6467         if (g_errorCnt)
6468         {
6469             Bsprintf(buf, "Error compiling CON files.");
6470             G_GameExit(buf);
6471         }
6472     }
6473 
6474     for (intptr_t i : apScriptGameEventEnd)
6475     {
6476         if (!i)
6477             continue;
6478 
6479         auto const eventEnd = apScript + i;
6480         auto breakPtr = (intptr_t*)*(eventEnd + 2);
6481 
6482         while (breakPtr)
6483         {
6484             breakPtr = apScript + (intptr_t)breakPtr;
6485             scriptWriteAtOffset(CON_ENDEVENT | LINE_NUMBER, breakPtr-2);
6486             breakPtr = (intptr_t*)*breakPtr;
6487         }
6488     }
6489 
6490     g_totalLines += g_lineNumber;
6491 
6492     C_SetScriptSize(g_scriptPtr-apScript+8);
6493 
6494     initprintf("Compiled %d bytes in %ums%s\n", (int)((intptr_t)g_scriptPtr - (intptr_t)apScript),
6495                timerGetTicks() - startcompiletime, C_ScriptVersionString(g_scriptVersion));
6496 
6497     for (auto i : tables_free)
6498         hash_free(i);
6499 
6500     for (auto i : inttables)
6501         inthash_free(i);
6502 
6503     freehashnames();
6504     freesoundhashnames();
6505 
6506     if (g_scriptDebug)
6507         C_PrintStats();
6508 
6509     C_InitQuotes();
6510 
6511 #if MICROPROFILE_ENABLED != 0
6512     for (int i=0; i<MAXEVENTS; i++)
6513     {
6514         if (VM_HaveEvent(i))
6515         {
6516             g_eventTokens[i]        = MicroProfileGetToken("CON VM Events", EventNames[i], MP_AUTO, MicroProfileTokenTypeCpu);
6517             g_eventCounterTokens[i] = MicroProfileGetCounterToken(EventNames[i]);
6518         }
6519     }
6520 
6521 #if 0
6522     for (int i=0; i<CON_END; i++)
6523     {
6524         Bassert(VM_GetKeywordForID(i) != nullptr);
6525         g_instTokens[i] = MicroProfileGetToken("CON VM Instructions", VM_GetKeywordForID(i), MP_AUTO, MicroProfileTokenTypeCpu);
6526     }
6527 #endif
6528 
6529     for (int i=0; i<MAXTILES; i++)
6530     {
6531         if (G_TileHasActor(i))
6532         {
6533             int const index = C_GetLabelIndex(i, LABEL_ACTOR);
6534 
6535             if (index != -1)
6536                 Bsprintf(tempbuf,"%s (%d)", label+(index<<6), i);
6537             else Bsprintf(tempbuf,"unnamed (%d)", i);
6538 
6539             g_actorTokens[i] = MicroProfileGetToken("CON VM Actors", tempbuf, MP_AUTO, MicroProfileTokenTypeCpu);
6540         }
6541     }
6542 
6543     for (int i=0; i<MAXSTATUS; i++)
6544     {
6545         Bsprintf(tempbuf,"statnum%d", i);
6546         g_statnumTokens[i] = MicroProfileGetToken("CON VM Actors", tempbuf, MP_AUTO, MicroProfileTokenTypeCpu);
6547     }
6548 #endif
6549 }
6550 
C_ReportError(int error)6551 void C_ReportError(int error)
6552 {
6553     if (Bstrcmp(g_szCurrentBlockName,g_szLastBlockName))
6554     {
6555         if (g_scriptEventOffset || g_processingState || g_scriptActorOffset)
6556             initprintf("%s: In %s `%s':\n",g_scriptFileName,g_scriptEventOffset?"event":g_scriptActorOffset?"actor":"state",g_szCurrentBlockName);
6557         else initprintf("%s: At top level:\n",g_scriptFileName);
6558         Bstrcpy(g_szLastBlockName,g_szCurrentBlockName);
6559     }
6560     switch (error)
6561     {
6562     case ERROR_NOTTOPLEVEL:
6563         initprintf("%s:%d: error: `%s' not at top level within script.\n",g_scriptFileName,g_lineNumber,tempbuf);
6564         break;
6565     case ERROR_EVENTONLY:
6566         initprintf("%s:%d: error: keyword `%s' only available during events.\n",g_scriptFileName,g_lineNumber,tempbuf);
6567         break;
6568     case ERROR_EXCEEDSMAXTILES:
6569         initprintf("%s:%d: error: `%s' value exceeds MAXTILES.  Maximum is %d.\n",g_scriptFileName,g_lineNumber,tempbuf,MAXTILES-1);
6570         break;
6571     case ERROR_EXPECTEDKEYWORD:
6572         initprintf("%s:%d: error: expected a keyword but found `%s'.\n",g_scriptFileName,g_lineNumber,tempbuf);
6573         break;
6574     case ERROR_FOUNDWITHIN:
6575         initprintf("%s:%d: error: found `%s' within %s.\n",g_scriptFileName,g_lineNumber,tempbuf,g_scriptEventOffset?"an event":g_scriptActorOffset?"an actor":"a state");
6576         break;
6577     case ERROR_ISAKEYWORD:
6578         initprintf("%s:%d: error: symbol `%s' is a keyword.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6579         break;
6580     case ERROR_NOENDSWITCH:
6581         initprintf("%s:%d: error: did not find `endswitch' before `%s'.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6582         break;
6583     case ERROR_NOTAGAMEDEF:
6584         initprintf("%s:%d: error: symbol `%s' is not a definition.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6585         break;
6586     case ERROR_NOTAGAMEVAR:
6587         initprintf("%s:%d: error: symbol `%s' is not a variable.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6588         break;
6589     case ERROR_NOTAGAMEARRAY:
6590         initprintf("%s:%d: error: symbol `%s' is not an array.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6591         break;
6592     case ERROR_GAMEARRAYBNC:
6593         initprintf("%s:%d: error: malformed array index: expected ], found %c\n",g_scriptFileName,g_lineNumber,*textptr);
6594         break;
6595     case ERROR_GAMEARRAYBNO:
6596         initprintf("%s:%d: error: malformed array index: expected [, found %c\n",g_scriptFileName,g_lineNumber,*textptr);
6597         break;
6598     case ERROR_INVALIDARRAYWRITE:
6599         initprintf("%s:%d: error: arrays can only be written to using `setarray'.\n",g_scriptFileName,g_lineNumber);
6600         break;
6601     case ERROR_PARAMUNDEFINED:
6602         initprintf("%s:%d: error: parameter `%s' is undefined.\n",g_scriptFileName,g_lineNumber,tempbuf);
6603         break;
6604     case ERROR_NOTAMEMBER:
6605         initprintf("%s:%d: error: symbol `%s' is not a valid structure member.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6606         break;
6607     case ERROR_SYNTAXERROR:
6608         initprintf("%s:%d: error: syntax error.\n",g_scriptFileName,g_lineNumber);
6609         break;
6610     case ERROR_VARREADONLY:
6611         initprintf("%s:%d: error: variable `%s' is read-only.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6612         break;
6613     case ERROR_ARRAYREADONLY:
6614         initprintf("%s:%d: error: array `%s' is read-only.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6615         break;
6616     case ERROR_VARTYPEMISMATCH:
6617         initprintf("%s:%d: error: variable `%s' is of the wrong type.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6618         break;
6619     case ERROR_TOOMANYLABELS:
6620         initprintf("%s:%d: error: too many labels defined! Maximum is %d\n.",g_scriptFileName,g_lineNumber, MAXLABELS);
6621         break;
6622     case WARNING_BADGAMEVAR:
6623         initprintf("%s:%d: warning: variable `%s' should be either per-player OR per-actor, not both.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6624         break;
6625     case WARNING_DUPLICATECASE:
6626         initprintf("%s:%d: warning: duplicate case ignored.\n",g_scriptFileName,g_lineNumber);
6627         break;
6628     case WARNING_DUPLICATEDEFINITION:
6629         initprintf("%s:%d: warning: duplicate definition `%s' ignored.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6630         break;
6631     case WARNING_EVENTSYNC:
6632         initprintf("%s:%d: warning: found `%s' within a local event.\n",g_scriptFileName,g_lineNumber,tempbuf);
6633         break;
6634     case WARNING_LABELSONLY:
6635         initprintf("%s:%d: warning: expected a label, found a constant.\n",g_scriptFileName,g_lineNumber);
6636         break;
6637     case WARNING_NAMEMATCHESVAR:
6638         initprintf("%s:%d: warning: symbol `%s' already used for variable.\n",g_scriptFileName,g_lineNumber,LAST_LABEL);
6639         break;
6640     case WARNING_VARMASKSKEYWORD:
6641         initprintf("%s:%d: warning: variable `%s' masks keyword.\n", g_scriptFileName, g_lineNumber, LAST_LABEL);
6642         break;
6643     case WARNING_ARRAYMASKSKEYWORD:
6644         initprintf("%s:%d: warning: array `%s' masks keyword.\n", g_scriptFileName, g_lineNumber, LAST_LABEL);
6645         break;
6646     }
6647 }
6648