diff --git a/README.md b/README.md index 25e8a10aa..95517f638 100644 --- a/README.md +++ b/README.md @@ -178,10 +178,9 @@ Copyright: © 2023 Andrew Apted. License: [MIT](https://opensource.org/licenses/MIT) -Files: `src/u_scanner.*` +Files: `src/m_scanner.*` Copyright: - © 2010 Braden "Blzut3" Obrzut; - © 2019 Fernando Carmona Varo. + © 2015 Braden "Blzut3" Obrzut. License: [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) Files: `src/v_flextran.*` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f9e66d800..7fdc73e46 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -64,6 +64,7 @@ set(WOOF_SOURCES m_input.c m_input.h m_io.c m_io.h m_json.c m_json.h + m_scanner.c m_scanner.h mn_font.c mn_font.h mn_menu.c mn_menu.h mn_setup.c mn_internal.h @@ -137,7 +138,6 @@ set(WOOF_SOURCES statdump.c statdump.h tables.c tables.h u_mapinfo.c u_mapinfo.h - u_scanner.c u_scanner.h v_flextran.c v_flextran.h v_fmt.c v_fmt.h v_trans.c v_trans.h diff --git a/src/d_main.c b/src/d_main.c index 48f5e8df3..b3d0dd06a 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -824,9 +824,9 @@ static boolean CheckMapLump(const char *lumpname, const char *filename) static boolean FileContainsMaps(const char *filename) { - for (int i = 0; i < U_mapinfo.mapcount; ++i) + for (int i = 0; i < array_size(umapinfo); ++i) { - if (CheckMapLump(U_mapinfo.maps[i].mapname, filename)) + if (CheckMapLump(umapinfo[i].mapname, filename)) { return true; } diff --git a/src/g_game.c b/src/g_game.c index 372bc5373..726e31fce 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3851,21 +3851,21 @@ void G_SetFastParms(int fast_pending) mapentry_t *G_LookupMapinfo(int episode, int map) { - int i; char lumpname[9]; - strcpy(lumpname, MapName(episode, map)); - for (i = 0; i < U_mapinfo.mapcount; i++) + mapentry_t *entry; + + array_foreach(entry, umapinfo) { - if (!strcasecmp(lumpname, U_mapinfo.maps[i].mapname)) - return &U_mapinfo.maps[i]; + if (!strcasecmp(lumpname, entry->mapname)) + return entry; } - for (i = 0; i < default_mapinfo.mapcount; i++) + array_foreach(entry, umapdef) { - if (!strcasecmp(lumpname, default_mapinfo.maps[i].mapname)) - return &default_mapinfo.maps[i]; + if (!strcasecmp(lumpname, entry->mapname)) + return entry; } return NULL; diff --git a/src/m_array.h b/src/m_array.h index 2955c663b..641f70213 100644 --- a/src/m_array.h +++ b/src/m_array.h @@ -72,20 +72,20 @@ inline static void array_clear(const void *v) } } -#define array_grow(v, n) ((v) = M_ArrayGrow((v), sizeof(*(v)), n)) +#define array_grow(v, n) ((v) = M_ArrayGrow(v, sizeof(*(v)), n)) #define array_push(v, e) \ do \ { \ if (!(v)) \ { \ - (v) = M_ArrayGrow((v), sizeof(*(v)), M_ARRAY_INIT_CAPACITY); \ + (v) = M_ArrayGrow(v, sizeof(*(v)), M_ARRAY_INIT_CAPACITY); \ } \ - else if (array_ptr((v))->size == array_ptr((v))->capacity) \ + else if (array_ptr(v)->size == array_ptr(v)->capacity) \ { \ - (v) = M_ArrayGrow((v), sizeof(*(v)), array_ptr((v))->capacity); \ + (v) = M_ArrayGrow(v, sizeof(*(v)), array_ptr(v)->capacity); \ } \ - (v)[array_ptr((v))->size++] = (e); \ + (v)[array_ptr(v)->size++] = (e); \ } while (0) #define array_free(v) \ @@ -93,13 +93,15 @@ inline static void array_clear(const void *v) { \ if (v) \ { \ - M_ARRAY_FREE(array_ptr((v))); \ + M_ARRAY_FREE(array_ptr(v)); \ (v) = NULL; \ } \ } while (0) +#define array_end(v) ((v) + array_size(v)) + #define array_foreach(ptr, v) \ - for (ptr = (v); ptr != &(v)[array_size((v))]; ++ptr) + for (ptr = (v); ptr < array_end(v); ++ptr) inline static void *M_ArrayGrow(void *v, size_t esize, int n) { diff --git a/src/m_cheat.c b/src/m_cheat.c index 1f6b85d51..50c345b5b 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -32,6 +32,7 @@ #include "g_game.h" #include "info.h" #include "m_cheat.h" +#include "m_array.h" #include "m_fixed.h" #include "m_input.h" #include "m_misc.h" @@ -876,7 +877,7 @@ static void cheat_spechits() plyr->cards[i] = origcards[i]; } - if (gamemapinfo && gamemapinfo->numbossactions > 0) + if (gamemapinfo && array_size(gamemapinfo->bossactions)) { thinker_t *th; @@ -886,13 +887,14 @@ static void cheat_spechits() { mobj_t *mo = (mobj_t *) th; - for (i = 0; i < gamemapinfo->numbossactions; i++) + bossaction_t *bossaction; + array_foreach(bossaction, gamemapinfo->bossactions) { - if (gamemapinfo->bossactions[i].type == mo->type) + if (bossaction->type == mo->type) { dummy = *lines; - dummy.special = (short)gamemapinfo->bossactions[i].special; - dummy.tag = (short)gamemapinfo->bossactions[i].tag; + dummy.special = (short)bossaction->special; + dummy.tag = (short)bossaction->tag; // use special semantics for line activation to block problem types. if (!P_UseSpecialLine(mo, &dummy, 0, true)) P_CrossSpecialLine(&dummy, 0, mo, true); diff --git a/src/m_misc.c b/src/m_misc.c index 59cc68d8e..14c5f6cf2 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -235,19 +235,22 @@ const char *M_BaseName(const char *path) char M_ToUpper(const char c) { - if (c >= 'a' && c <= 'z') - return c + 'A' - 'a'; - else - return c; + if (c >= 'a' && c <= 'z') + { + return c + 'A' - 'a'; + } + else + { + return c; + } } -void M_StringToUpper(char *text) +void M_StringToUpper(char *str) { - char *p; - - for (p = text; *p != '\0'; ++p) + while (*str) { - *p = M_ToUpper(*p); + *str = M_ToUpper(*str); + ++str; } } @@ -255,19 +258,22 @@ void M_StringToUpper(char *text) char M_ToLower(const char c) { - if (c >= 'A' && c <= 'Z') - return c - 'A' + 'a'; - else - return c; + if (c >= 'A' && c <= 'Z') + { + return c - 'A' + 'a'; + } + else + { + return c; + } } -void M_StringToLower(char *text) +void M_StringToLower(char *str) { - char *p; - - for (p = text; *p != '\0'; ++p) + while (*str) { - *p = M_ToLower(*p); + *str = M_ToLower(*str); + ++str; } } diff --git a/src/m_scanner.c b/src/m_scanner.c new file mode 100644 index 000000000..eca0fce69 --- /dev/null +++ b/src/m_scanner.c @@ -0,0 +1,706 @@ +// +// Copyright (c) 2015, Braden "Blzut3" Obrzut +// Copyright (c) 2024, Roman Fomin +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the +// distribution. +// * The names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#include "m_scanner.h" + +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_misc.h" + +static const char* const token_names[] = +{ + [TK_Identifier] = "Identifier", + [TK_StringConst] = "String Constant", + [TK_IntConst] = "Integer Constant", + [TK_BoolConst] = "boolean Constant", + [TK_FloatConst] = "Float Constant", + [TK_AnnotateStart] = "Annotation Start", + [TK_AnnotateEnd] = "Annotation End", + [TK_LumpName] = "Lump Name" +}; + +typedef struct +{ + char *string; + int number; + double decimal; + char token; + + int tokenline; + int tokenlinepos; + int scanpos; +} parserstate_t; + +struct scanner_s +{ + parserstate_t state; + parserstate_t nextstate, prevstate; + + char *data; + size_t length; + + int line; + int linestart; + int logicalpos; + int scanpos; + + boolean neednext; // If CheckToken() returns false this will be false. + + const char *scriptname; +}; + +static void IncrementLine(scanner_t *s) +{ + s->line++; + s->linestart = s->scanpos; +} + +static void CheckForWhitespace(scanner_t *s) +{ + int comment = 0; // 1 = till next new line, 2 = till end block + + while (s->scanpos < s->length) + { + char cur = s->data[s->scanpos]; + char next = (s->scanpos + 1 < s->length) ? s->data[s->scanpos + 1] : 0; + + if (comment == 2) + { + if (cur != '*' || next != '/') + { + if (cur == '\n' || cur == '\r') + { + s->scanpos++; + + // Do a quick check for Windows style new line + if (cur == '\r' && next == '\n') + { + s->scanpos++; + } + IncrementLine(s); + } + else + { + s->scanpos++; + } + } + else + { + comment = 0; + s->scanpos += 2; + } + continue; + } + + if (cur == ' ' || cur == '\t' || cur == 0) + { + s->scanpos++; + } + else if (cur == '\n' || cur == '\r') + { + s->scanpos++; + if (comment == 1) + { + comment = 0; + } + + // Do a quick check for Windows style new line + if (cur == '\r' && next == '\n') + { + s->scanpos++; + } + IncrementLine(s); + } + else if (cur == '/' && comment == 0) + { + switch (next) + { + case '/': + comment = 1; + break; + case '*': + comment = 2; + break; + default: + return; + } + s->scanpos += 2; + } + else + { + if (comment == 0) + { + return; + } + else + { + s->scanpos++; + } + } + } +} + +static void CopyState(parserstate_t *to, const parserstate_t *from) +{ + if (to->string) + { + free(to->string); + to->string = NULL; + } + if (from->string) + { + to->string = M_StringDuplicate(from->string); + } + to->number = from->number; + to->decimal = from->decimal; + to->token = from->token; + to->tokenline = from->tokenline; + to->tokenlinepos = from->tokenlinepos; + to->scanpos = from->scanpos; +} + +static void ExpandState(scanner_t *s) +{ + s->scanpos = s->nextstate.scanpos; + s->logicalpos = s->scanpos; + CheckForWhitespace(s); + + CopyState(&s->prevstate, &s->state); + CopyState(&s->state, &s->nextstate); +} + +static void Unescape(char *str) +{ + char *p = str, c; + + while ((c = *p++)) + { + if (c != '\\') + { + *str++ = c; + } + else + { + switch (*p) + { + case 'n': + *str++ = '\n'; + break; + case 'r': + *str++ = '\r'; + break; + case 't': + *str++ = '\t'; + break; + case '"': + *str++ = '\"'; + break; + default: + *str++ = *p; + break; + } + p++; + } + } + *str = 0; +} + +boolean SC_GetNextToken(scanner_t *s, boolean expandstate) +{ + if (!s->neednext) + { + s->neednext = true; + if (expandstate) + { + ExpandState(s); + } + return true; + } + + s->nextstate.tokenline = s->line; + s->nextstate.tokenlinepos = s->scanpos - s->linestart; + s->nextstate.token = TK_NoToken; + if (s->scanpos >= s->length) + { + if (expandstate) + { + ExpandState(s); + } + return false; + } + + int start = s->scanpos; + int end = s->scanpos; + int integer_base = 10; + boolean float_has_decimal = false; + boolean float_has_exponent = false; + boolean string_finished = + false; // Strings are the only things that can have 0 length tokens. + + char cur = s->data[s->scanpos++]; + // Determine by first character + if (cur == '_' || (cur >= 'A' && cur <= 'Z') || (cur >= 'a' && cur <= 'z')) + { + s->nextstate.token = TK_Identifier; + } + else if (cur >= '0' && cur <= '9') + { + if (cur == '0') + { + integer_base = 8; + } + s->nextstate.token = TK_IntConst; + } + else if (cur == '.' && s->scanpos < s->length && s->data[s->scanpos] != '.') + { + float_has_decimal = true; + s->nextstate.token = TK_FloatConst; + } + else if (cur == '"') + { + end = ++start; // Move the start up one character so we don't have to + // trim it later. + s->nextstate.token = TK_StringConst; + } + else + { + end = s->scanpos; + s->nextstate.token = cur; + + // Now check for operator tokens + if (s->scanpos < s->length) + { + char next = s->data[s->scanpos]; + + if (cur == ':' && next == ':') + { + s->nextstate.token = TK_ScopeResolution; + } + else if (cur == '/' && next == '*') + { + s->nextstate.token = TK_AnnotateStart; + } + else if (cur == '*' && next == '/') + { + s->nextstate.token = TK_AnnotateEnd; + } + + if (s->nextstate.token != cur) + { + s->scanpos++; + end = s->scanpos; + } + } + } + + if (start == end) + { + while (s->scanpos < s->length) + { + cur = s->data[s->scanpos]; + switch (s->nextstate.token) + { + default: + break; + + case TK_Identifier: + if (cur != '_' && (cur < 'A' || cur > 'Z') + && (cur < 'a' || cur > 'z') && (cur < '0' || cur > '9')) + { + end = s->scanpos; + } + break; + + case TK_IntConst: + if (cur == '.' || (s->scanpos - 1 != start && cur == 'e')) + { + s->nextstate.token = TK_FloatConst; + } + else if ((cur == 'x' || cur == 'X') + && s->scanpos - 1 == start) + { + integer_base = 16; + break; + } + else + { + switch (integer_base) + { + default: + if (cur < '0' || cur > '9') + { + end = s->scanpos; + } + break; + case 8: + if (cur < '0' || cur > '7') + { + end = s->scanpos; + } + break; + case 16: + if ((cur < '0' || cur > '9') + && (cur < 'A' || cur > 'F') + && (cur < 'a' || cur > 'f')) + { + end = s->scanpos; + } + break; + } + break; + } + + case TK_FloatConst: + if (cur < '0' || cur > '9') + { + if (!float_has_decimal && cur == '.') + { + float_has_decimal = true; + break; + } + else if (!float_has_exponent && cur == 'e') + { + float_has_decimal = true; + float_has_exponent = true; + if (s->scanpos + 1 < s->length) + { + char next = s->data[s->scanpos + 1]; + if ((next < '0' || next > '9') && next != '+' + && next != '-') + { + end = s->scanpos; + } + else + { + s->scanpos++; + } + } + break; + } + end = s->scanpos; + } + break; + + case TK_StringConst: + if (cur == '"') + { + string_finished = true; + end = s->scanpos; + s->scanpos++; + } + else if (cur == '\\') + { + s->scanpos++; // Will add two since the loop + // automatically adds one + } + break; + } + if (start == end && !string_finished) + { + s->scanpos++; + } + else + { + break; + } + } + // Handle small tokens at the end of a file. + if (s->scanpos == s->length && !string_finished) + { + end = s->scanpos; + } + } + + s->nextstate.scanpos = s->scanpos; + if (end - start > 0 || string_finished) + { + if (s->nextstate.string) + { + free(s->nextstate.string); + } + int length = end - start; + s->nextstate.string = malloc(length + 1); + memcpy(s->nextstate.string, s->data + start, length); + s->nextstate.string[length] = '\0'; + + if (s->nextstate.token == TK_FloatConst) + { + if (float_has_decimal && strlen(s->nextstate.string) == 1) + { + // Don't treat a lone '.' as a decimal. + s->nextstate.token = '.'; + } + else + { + s->nextstate.decimal = atof(s->nextstate.string); + s->nextstate.number = (int)s->nextstate.decimal; + } + } + else if (s->nextstate.token == TK_IntConst) + { + s->nextstate.number = + strtol(s->nextstate.string, NULL, integer_base); + s->nextstate.decimal = (double)s->nextstate.number; + } + else if (s->nextstate.token == TK_Identifier) + { + // Check for a boolean constant. + if (!strcasecmp(s->nextstate.string, "true")) + { + s->nextstate.token = TK_BoolConst; + s->nextstate.number = true; + } + else if (!strcasecmp(s->nextstate.string, "false")) + { + s->nextstate.token = TK_BoolConst; + s->nextstate.number = false; + } + } + else if (s->nextstate.token == TK_StringConst) + { + Unescape(s->nextstate.string); + } + if (expandstate) + { + ExpandState(s); + } + return true; + } + s->nextstate.token = TK_NoToken; + if (expandstate) + { + ExpandState(s); + } + return false; +} + +void SC_GetNextTokenLumpName(scanner_t *s) +{ + if (!s->neednext) + { + s->neednext = true; + ExpandState(s); + return; + } + + s->nextstate.tokenline = s->line; + s->nextstate.tokenlinepos = s->scanpos - s->linestart; + s->nextstate.token = TK_NoToken; + if (s->scanpos >= s->length) + { + ExpandState(s); + return; + } + + int start = s->scanpos++; + while (s->scanpos < s->length) + { + char cur = s->data[s->scanpos]; + if (cur == ' ' || cur == '\t' || cur == '\n' || cur == '\r' || cur == 0) + { + break; + } + s->scanpos++; + } + s->nextstate.scanpos = s->scanpos; + + int length = s->scanpos - start; + if (length > 0) + { + s->nextstate.token = TK_LumpName; + if (s->nextstate.string) + { + free(s->nextstate.string); + } + s->nextstate.string = malloc(length + 1); + memcpy(s->nextstate.string, s->data + start, length); + s->nextstate.string[length] = '\0'; + } + + ExpandState(s); +} + +// Skips all Tokens in current line and parses the first token on the next +// line. +void SC_GetNextLineToken(scanner_t *s) +{ + int line = s->line; + while (SC_GetNextToken(s, true) && s->line == line) + ; +} + +boolean SC_CheckToken(scanner_t *s, char token) +{ + if (s->neednext) + { + if (!SC_GetNextToken(s, false)) + { + return false; + } + } + + // An int can also be a float. + if (s->nextstate.token == token + || (s->nextstate.token == TK_IntConst && token == TK_FloatConst)) + { + s->neednext = true; + ExpandState(s); + return true; + } + s->neednext = false; + return false; +} + +void SC_Error(scanner_t *s, const char *msg, ...) +{ + char buffer[1024]; + va_list args; + va_start(args, msg); + M_vsnprintf(buffer, sizeof(buffer), msg, args); + va_end(args); + + I_Error("%s(%d:%d): %s", s->scriptname, s->state.tokenline + 1, + s->state.tokenlinepos + 1, buffer); +} + +void SC_MustGetToken(scanner_t *s, char token) +{ + if (SC_CheckToken(s, token)) + { + return; + } + + ExpandState(s); + if (s->state.token == TK_NoToken) + { + SC_Error(s, "Unexpected end of script."); + } + else if (token < TK_NumSpecialTokens + && s->state.token < TK_NumSpecialTokens) + { + SC_Error(s, "Expected '%s' but got '%s' instead.", + token_names[(int)token], token_names[(int)s->state.token]); + } + else if (token < TK_NumSpecialTokens + && s->state.token >= TK_NumSpecialTokens) + { + SC_Error(s, "Expected '%s' but got '%c' instead.", + token_names[(int)token], s->state.token); + } + else if (token >= TK_NumSpecialTokens + && s->state.token < TK_NumSpecialTokens) + { + SC_Error(s, "Expected '%c' but got '%s' instead.", token, + token_names[(int)s->state.token]); + } + else + { + SC_Error(s, "Expected '%c' but got '%c' instead.", token, + s->state.token); + } +} + +void SC_Rewind(scanner_t *s) // Only can rewind one step. +{ + s->neednext = false; + + CopyState(&s->nextstate, &s->state); + CopyState(&s->state, &s->prevstate); + + s->line = s->prevstate.tokenline; + s->logicalpos = s->prevstate.tokenlinepos; + s->scanpos = s->prevstate.scanpos; +} + +boolean SC_TokensLeft(scanner_t *s) +{ + return s->scanpos < s->length; +} + +const char *SC_GetString(scanner_t *s) +{ + return s->state.string; +} + +int SC_GetNumber(scanner_t *s) +{ + return s->state.number; +} + +boolean SC_GetBoolean(scanner_t *s) +{ + return (boolean)s->state.number; +} + +double SC_GetDecimal(scanner_t *s) +{ + return s->state.decimal; +} + +scanner_t *SC_Open(const char *scriptname, const char *data, int length) +{ + scanner_t *s = calloc(1, sizeof(*s)); + + s->line = 1; + s->neednext = true; + + s->length = length; + s->data = malloc(length); + memcpy(s->data, data, length); + + CheckForWhitespace(s); + s->state.scanpos = s->scanpos; + s->scriptname = M_StringDuplicate(scriptname); + return s; +} + +void SC_Close(scanner_t *s) +{ + if (s->state.string) + { + free(s->state.string); + } + if (s->prevstate.string) + { + free(s->prevstate.string); + } + if (s->nextstate.string) + { + free(s->nextstate.string); + } + if (s->scriptname) + { + free((char *)s->scriptname); + } + free(s->data); + free(s); +} diff --git a/src/m_scanner.h b/src/m_scanner.h new file mode 100644 index 000000000..a3b65a4fe --- /dev/null +++ b/src/m_scanner.h @@ -0,0 +1,71 @@ +// +// Copyright (c) 2015, Braden "Blzut3" Obrzut +// Copyright (c) 2024, Roman Fomin +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the +// distribution. +// * The names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#include "doomtype.h" + +typedef struct scanner_s scanner_t; + +enum +{ + TK_Identifier, // Ex: SomeIdentifier + TK_StringConst, // Ex: "Some String" + TK_IntConst, // Ex: 27 + TK_BoolConst, // Ex: true + TK_FloatConst, // Ex: 1.5 + + TK_LumpName, + + TK_AnnotateStart, // Block comment start + TK_AnnotateEnd, // Block comment end + + TK_ScopeResolution, // :: + + TK_NumSpecialTokens, + + TK_NoToken = -1 +}; + +scanner_t *SC_Open(const char *scriptname, const char *data, int length); +void SC_Close(scanner_t *s); + +const char *SC_GetString(scanner_t *s); +int SC_GetNumber(scanner_t *s); +boolean SC_GetBoolean(scanner_t *s); +double SC_GetDecimal(scanner_t *s); + +boolean SC_TokensLeft(scanner_t *s); +boolean SC_CheckToken(scanner_t *s, char token); +boolean SC_GetNextToken(scanner_t *s, boolean expandstate); +void SC_GetNextLineToken(scanner_t *s); +void SC_MustGetToken(scanner_t *s, char token); +void SC_Rewind(scanner_t *s); // Only can rewind one step. + +void SC_GetNextTokenLumpName(scanner_t *s); + +void SC_Error(scanner_t *s, const char *msg, ...) PRINTF_ATTR(2, 3); diff --git a/src/mn_menu.c b/src/mn_menu.c index b41deddc6..bf52372d3 100644 --- a/src/mn_menu.c +++ b/src/mn_menu.c @@ -523,8 +523,7 @@ void M_ClearEpisodes(void) NewDef.prevMenu = &MainDef; } -void M_AddEpisode(const char *map, const char *gfx, const char *txt, - const char *alpha) +void M_AddEpisode(const char *map, const char *gfx, const char *txt, char key) { int epi, mapnum; @@ -554,7 +553,7 @@ void M_AddEpisode(const char *map, const char *gfx, const char *txt, strncpy(EpisodeMenu[EpiDef.numitems].name, gfx, 8); EpisodeMenu[EpiDef.numitems].name[9] = 0; EpisodeMenu[EpiDef.numitems].alttext = txt ? strdup(txt) : NULL; - EpisodeMenu[EpiDef.numitems].alphaKey = alpha ? *alpha : 0; + EpisodeMenu[EpiDef.numitems].alphaKey = key; EpiDef.numitems++; if (EpiDef.numitems <= 4) diff --git a/src/p_enemy.c b/src/p_enemy.c index 24753f28d..4cce3840c 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -33,6 +33,7 @@ #include "i_printf.h" #include "i_system.h" #include "info.h" +#include "m_array.h" #include "m_bbox.h" #include "m_fixed.h" #include "m_random.h" @@ -2284,13 +2285,11 @@ void A_BossDeath(mobj_t *mo) line_t junk; int i; - // numbossactions == 0 means to use the defaults. - // numbossactions == -1 means to do nothing. - // positive values mean to check the list of boss actions and run all that apply. - if (gamemapinfo && gamemapinfo->numbossactions != 0) - { - if (gamemapinfo->numbossactions < 0) return; + if (gamemapinfo && gamemapinfo->nobossactions) + return; + if (gamemapinfo && array_size(gamemapinfo->bossactions)) + { // make sure there is a player alive for victory for (i=0; i 0) @@ -2299,36 +2298,38 @@ void A_BossDeath(mobj_t *mo) if (i==MAXPLAYERS) return; // no one left alive, so do not end game - for (i = 0; i < gamemapinfo->numbossactions; i++) - { - if (gamemapinfo->bossactions[i].type == mo->type) - break; - } - if (i >= gamemapinfo->numbossactions) + bossaction_t *bossaction; + array_foreach(bossaction, gamemapinfo->bossactions) + { + if (bossaction->type == mo->type) + break; + } + if (bossaction == array_end(gamemapinfo->bossactions)) return; // no matches found // scan the remaining thinkers to see // if all bosses are dead for (th = thinkercap.next ; th != &thinkercap ; th=th->next) if (th->function.p1 == (actionf_p1)P_MobjThinker) - { - mobj_t *mo2 = (mobj_t *) th; - if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) - return; // other boss not dead - } - for (i = 0; i < gamemapinfo->numbossactions; i++) { - if (gamemapinfo->bossactions[i].type == mo->type) - { - junk = *lines; - junk.special = (short)gamemapinfo->bossactions[i].special; - junk.tag = (short)gamemapinfo->bossactions[i].tag; - // use special semantics for line activation to block problem types. - if (!P_UseSpecialLine(mo, &junk, 0, true)) - P_CrossSpecialLine(&junk, 0, mo, true); - } + mobj_t *mo2 = (mobj_t *) th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + return; // other boss not dead } + array_foreach(bossaction, gamemapinfo->bossactions) + { + if (bossaction->type == mo->type) + { + junk = *lines; + junk.special = (short)bossaction->special; + junk.tag = (short)bossaction->tag; + // use special semantics for line activation to block problem types. + if (!P_UseSpecialLine(mo, &junk, 0, true)) + P_CrossSpecialLine(&junk, 0, mo, true); + } + } + return; } diff --git a/src/r_bmaps.c b/src/r_bmaps.c index dd91768a5..314aa3175 100644 --- a/src/r_bmaps.c +++ b/src/r_bmaps.c @@ -29,7 +29,7 @@ #include "m_array.h" #include "m_misc.h" #include "r_data.h" -#include "u_scanner.h" +#include "m_scanner.h" #include "w_wad.h" #include "z_zone.h" @@ -49,40 +49,35 @@ typedef struct byte colormask[COLORMASK_SIZE]; } brightmap_t; -static void ReadColormask(u_scanner_t *s, byte *colormask) +static void ReadColormask(scanner_t *s, byte *colormask) { memset(colormask, 0, COLORMASK_SIZE); do { unsigned int color1 = 0, color2 = 0; - if (U_MustGetInteger(s)) + SC_MustGetToken(s, TK_IntConst); + color1 = SC_GetNumber(s); + if (color1 >= 0 && color1 < COLORMASK_SIZE) { - color1 = s->number; - if (color1 >= 0 && color1 < COLORMASK_SIZE) - { - colormask[color1] = 1; - } + colormask[color1] = 1; } - if (!U_CheckToken(s, '-')) + if (!SC_CheckToken(s, '-')) { continue; } - if (U_MustGetInteger(s)) + SC_MustGetToken(s, TK_IntConst); + color2 = SC_GetNumber(s); + if (color2 >= 0 && color2 < COLORMASK_SIZE) { - color2 = s->number; - if (color2 >= 0 && color2 < COLORMASK_SIZE) + for (int i = color1 + 1; i <= color2; ++i) { - int i; - for (i = color1 + 1; i <= color2; ++i) - { - colormask[i] = 1; - } + colormask[i] = 1; } } - } while (U_CheckToken(s, ',')); + } while (SC_CheckToken(s, ',')); } static brightmap_t *brightmaps_array = NULL; @@ -119,43 +114,50 @@ enum DOOM2ONLY }; -static boolean ParseProperty(u_scanner_t *s, elem_t *elem) +static boolean ParseProperty(scanner_t *s, elem_t *elem) { char *name; int idx; int game = DOOM1AND2; - U_GetString(s); - name = M_StringDuplicate(s->string); - U_MustGetToken(s, TK_Identifier); - idx = GetBrightmap(s->string); + SC_GetNextTokenLumpName(s); + name = M_StringDuplicate(SC_GetString(s)); + SC_MustGetToken(s, TK_Identifier); + idx = GetBrightmap(SC_GetString(s)); if (idx < 0) { - U_Error(s, "brightmap '%s' not found", s->string); + SC_Error(s, "brightmap '%s' not found", SC_GetString(s)); free(name); return false; } - if (U_CheckToken(s, TK_Identifier)) + if (SC_CheckToken(s, TK_Identifier)) { - if (!strcasecmp("DOOM", s->string) || !strcasecmp("DOOM1", s->string)) + if (!strcasecmp("DOOM", SC_GetString(s)) + || !strcasecmp("DOOM1", SC_GetString(s))) { game = DOOM1ONLY; - if (U_CheckToken(s, '|')) + if (SC_CheckToken(s, '|')) { - if (U_MustGetIdentifier(s, "DOOM2")) + SC_MustGetToken(s, TK_Identifier); + if (!strcasecmp("DOOM2", SC_GetString(s))) { game = DOOM1AND2; } + else + { + SC_Error(s, "Expected 'DOOM2' but got '%s' instead.", + SC_GetString(s)); + } } } - else if (!strcasecmp("DOOM2", s->string)) + else if (!strcasecmp("DOOM2", SC_GetString(s))) { game = DOOM2ONLY; } else { - U_Unget(s); + SC_Rewind(s); } } if ((gamemission == doom && game == DOOM2ONLY) @@ -251,10 +253,6 @@ const byte *R_BrightmapForState(const int state) void R_ParseBrightmaps(int lumpnum) { - u_scanner_t *s; - const char *data = W_CacheLumpNum(lumpnum, PU_CACHE); - int length = W_LumpLength(lumpnum); - force_brightmaps = W_IsWADLump(lumpnum); if (!array_size(brightmaps_array)) @@ -265,24 +263,25 @@ void R_ParseBrightmaps(int lumpnum) array_push(brightmaps_array, brightmap); } - s = U_ScanOpen(data, length, "BRGHTMPS"); + scanner_t *s = SC_Open("BRGHTMPS", W_CacheLumpNum(lumpnum, PU_CACHE), + W_LumpLength(lumpnum)); - while (U_HasTokensLeft(s)) + while (SC_TokensLeft(s)) { - if (!U_CheckToken(s, TK_Identifier)) + if (!SC_CheckToken(s, TK_Identifier)) { - U_GetNextToken(s, true); + SC_GetNextToken(s, true); continue; } - if (!strcasecmp("BRIGHTMAP", s->string)) + if (!strcasecmp("BRIGHTMAP", SC_GetString(s))) { brightmap_t brightmap; - U_MustGetToken(s, TK_Identifier); - brightmap.name = M_StringDuplicate(s->string); + SC_MustGetToken(s, TK_Identifier); + brightmap.name = M_StringDuplicate(SC_GetString(s)); ReadColormask(s, brightmap.colormask); array_push(brightmaps_array, brightmap); } - else if (!strcasecmp("TEXTURE", s->string)) + else if (!strcasecmp("TEXTURE", SC_GetString(s))) { elem_t elem; if (ParseProperty(s, &elem)) @@ -290,7 +289,7 @@ void R_ParseBrightmaps(int lumpnum) array_push(textures_bm, elem); } } - else if (!strcasecmp("SPRITE", s->string)) + else if (!strcasecmp("SPRITE", SC_GetString(s))) { elem_t elem; if (ParseProperty(s, &elem)) @@ -307,7 +306,7 @@ void R_ParseBrightmaps(int lumpnum) } } } - else if (!strcasecmp("FLAT", s->string)) + else if (!strcasecmp("FLAT", SC_GetString(s))) { elem_t elem; if (ParseProperty(s, &elem)) @@ -315,29 +314,29 @@ void R_ParseBrightmaps(int lumpnum) array_push(flats_bm, elem); } } - else if (!strcasecmp("STATE", s->string)) + else if (!strcasecmp("STATE", SC_GetString(s))) { elem_t elem; elem.name = NULL; - U_MustGetInteger(s); - elem.num = s->number; + SC_MustGetToken(s, TK_IntConst); + elem.num = SC_GetNumber(s); if (elem.num < 0 || elem.num >= num_states) { - U_Error(s, "state '%d' not found", elem.num); + SC_Error(s, "state '%d' not found", elem.num); } - U_MustGetToken(s, TK_Identifier); - elem.idx = GetBrightmap(s->string); + SC_MustGetToken(s, TK_Identifier); + elem.idx = GetBrightmap(SC_GetString(s)); if (elem.idx >= 0) { array_push(states_bm, elem); } else { - U_Error(s, "brightmap '%s' not found", s->string); + SC_Error(s, "brightmap '%s' not found", SC_GetString(s)); } } } - U_ScanClose(s); + SC_Close(s); brightmaps_found = (array_size(brightmaps_array) > 1); } diff --git a/src/s_musinfo.c b/src/s_musinfo.c index d3cea70f1..a76592ab4 100644 --- a/src/s_musinfo.c +++ b/src/s_musinfo.c @@ -27,7 +27,7 @@ #include "i_printf.h" #include "p_mobj.h" #include "s_sound.h" -#include "u_scanner.h" +#include "m_scanner.h" #include "w_wad.h" #include "z_zone.h" @@ -40,51 +40,47 @@ musinfo_t musinfo = {0}; void S_ParseMusInfo(const char *mapid) { - u_scanner_t *s; - int num, lumpnum; - - lumpnum = W_CheckNumForName("MUSINFO"); - + int lumpnum = W_CheckNumForName("MUSINFO"); if (lumpnum < 0) { return; } - s = U_ScanOpen(W_CacheLumpNum(lumpnum, PU_CACHE), W_LumpLength(lumpnum), - "MUSINFO"); + scanner_t *s = SC_Open("MUSINFO", W_CacheLumpNum(lumpnum, PU_CACHE), + W_LumpLength(lumpnum)); - while (U_HasTokensLeft(s)) + while (SC_TokensLeft(s)) { - if (U_CheckToken(s, TK_Identifier)) + if (SC_CheckToken(s, TK_Identifier)) { - if (!strcasecmp(s->string, mapid)) + if (!strcasecmp(SC_GetString(s), mapid)) { break; } } else { - U_GetNextLineToken(s); + SC_GetNextLineToken(s); } } - while (U_HasTokensLeft(s)) + while (SC_TokensLeft(s)) { - if (U_CheckToken(s, TK_Identifier)) + if (SC_CheckToken(s, TK_Identifier)) { - if (G_ValidateMapName(s->string, NULL, NULL)) + if (G_ValidateMapName(SC_GetString(s), NULL, NULL)) { break; } } - else if (U_CheckInteger(s)) + else if (SC_CheckToken(s, TK_IntConst)) { - num = s->number; + int num = SC_GetNumber(s); // Check number in range if (num > 0 && num < MAX_MUS_ENTRIES) { - U_GetString(s); - lumpnum = W_CheckNumForName(s->string); + SC_GetNextTokenLumpName(s); + lumpnum = W_CheckNumForName(SC_GetString(s)); if (lumpnum > 0) { musinfo.items[num] = lumpnum; @@ -92,7 +88,7 @@ void S_ParseMusInfo(const char *mapid) else { I_Printf(VB_WARNING, "S_ParseMusInfo: Unknown MUS lump %s", - s->string); + SC_GetString(s)); } } else @@ -104,11 +100,11 @@ void S_ParseMusInfo(const char *mapid) } else { - U_GetNextLineToken(s); + SC_GetNextLineToken(s); } } - U_ScanClose(s); + SC_Close(s); } void T_MusInfo(void) diff --git a/src/u_mapinfo.c b/src/u_mapinfo.c index 78c892450..150b5ae4d 100644 --- a/src/u_mapinfo.c +++ b/src/u_mapinfo.c @@ -24,27 +24,23 @@ #include "doomdef.h" #include "doomstat.h" #include "doomtype.h" -#include "i_system.h" #include "m_array.h" #include "m_misc.h" +#include "m_scanner.h" #include "u_mapinfo.h" -#include "u_scanner.h" #include "w_wad.h" #include "z_zone.h" -void M_AddEpisode(const char *map, const char *gfx, const char *txt, - const char *alpha); +void M_AddEpisode(const char *map, const char *gfx, const char *txt, char key); void M_ClearEpisodes(void); int G_ValidateMapName(const char *mapname, int *pEpi, int *pMap); -umapinfo_t U_mapinfo; - -umapinfo_t default_mapinfo; +mapentry_t *umapinfo = NULL, *umapdef = NULL; static level_t *secretlevels; -static const char *const ActorNames[] = +static const char *const actor_names[] = { "DoomPlayer", "ZombieMan", "ShotgunGuy", "Archvile", "ArchvileFire", "Revenant", "RevenantTracer", "RevenantTracerSmoke", "Fatso", "FatShot", @@ -179,7 +175,7 @@ static const char *const ActorNames[] = "Deh_Actor_247", // Extra thing 97 "Deh_Actor_248", // Extra thing 98 "Deh_Actor_249", // Extra thing 99 - NULL}; +}; static void FreeMap(mapentry_t *mape) { @@ -207,7 +203,7 @@ static void FreeMap(mapentry_t *mape) { free(mape->author); } - mape->mapname = NULL; + memset(mape, 0, sizeof(*mape)); } static void ReplaceString(char **pptr, const char *newstring) @@ -216,7 +212,7 @@ static void ReplaceString(char **pptr, const char *newstring) { free(*pptr); } - *pptr = strdup(newstring); + *pptr = M_StringDuplicate(newstring); } static void UpdateMapEntry(mapentry_t *mape, mapentry_t *newe) @@ -301,24 +297,13 @@ static void UpdateMapEntry(mapentry_t *mape, mapentry_t *newe) { mape->nointermission = newe->nointermission; } - if (newe->numbossactions) + if (array_size(newe->bossactions)) { - mape->numbossactions = newe->numbossactions; - if (mape->numbossactions == -1) - { - if (mape->bossactions) - { - free(mape->bossactions); - } - mape->bossactions = NULL; - } - else + if (mape->bossactions) { - mape->bossactions = realloc(mape->bossactions, - sizeof(bossaction_t) * mape->numbossactions); - memcpy(mape->bossactions, newe->bossactions, - sizeof(bossaction_t) * mape->numbossactions); + array_free(mape->bossactions); } + mape->bossactions = newe->bossactions; } } @@ -326,414 +311,327 @@ static void UpdateMapEntry(mapentry_t *mape, mapentry_t *newe) // Parses a set of string and concatenates them // Returns a pointer to the string (must be freed) // ----------------------------------------------- -static char *ParseMultiString(u_scanner_t *s, int error) +static char *ParseMultiString(scanner_t *s) { char *build = NULL; - if (U_CheckToken(s, TK_Identifier)) + if (SC_CheckToken(s, TK_Identifier)) { - if (!strcasecmp(s->string, "clear")) + if (!strcasecmp(SC_GetString(s), "clear")) { // this was explicitly deleted to override the default. return strdup("-"); } else { - U_Error(s, "Either 'clear' or string constant expected"); + SC_Error(s, "Either 'clear' or string constant expected"); } } do { - U_MustGetToken(s, TK_StringConst); + SC_MustGetToken(s, TK_StringConst); if (build == NULL) { - build = strdup(s->string); + build = M_StringDuplicate(SC_GetString(s)); } else { - // plus room for one \n and one \0 - size_t newlen = strlen(build) + strlen(s->string) + 2; - build = I_Realloc(build, newlen); - // Replace the existing text's \0 terminator with a \n - strcat(build, "\n"); - // Concatenate the new line onto the existing text - strcat(build, s->string); + char *tmp = build; + build = M_StringJoin(tmp, "\n", SC_GetString(s)); + free(tmp); } - } while (U_CheckToken(s, ',')); + } while (SC_CheckToken(s, ',')); + return build; } -// ----------------------------------------------- // Parses a lump name. The buffer must be at least 9 characters. -// If parsed name is longer than 8 chars, sets NULL pointer. -// -// returns 1 on successfully parsing an element -// 0 on parse error in last read token -// ----------------------------------------------- -static int ParseLumpName(u_scanner_t *s, char *buffer) -{ - if (!U_MustGetToken(s, TK_StringConst)) - { - return 0; - } - if (strlen(s->string) > 8) +static void ParseLumpName(scanner_t *s, char *buffer) +{ + SC_MustGetToken(s, TK_StringConst); + if (strlen(SC_GetString(s)) > 8) { - U_Error(s, "String too long. Maximum size is 8 characters."); - return 1; // not a parse error + SC_Error(s, "String too long. Maximum size is 8 characters."); } - strncpy(buffer, s->string, 8); + strncpy(buffer, SC_GetString(s), 8); buffer[8] = 0; M_StringToUpper(buffer); - return 1; } -// ----------------------------------------------- // Parses a standard property that is already known // These do not get stored in the property list // but in dedicated struct member variables. -// -// returns 1 on successfully parsing an element -// 0 on parse error in last read token -// ----------------------------------------------- -static int ParseStandardProperty(u_scanner_t *s, mapentry_t *mape) -{ - char *pname; - int status = 1; // 1 for success, 0 for parse error - if (!U_MustGetToken(s, TK_Identifier)) - { - return 0; - } - pname = strdup(s->string); - U_MustGetToken(s, '='); +static void ParseStandardProperty(scanner_t *s, mapentry_t *mape) +{ + SC_MustGetToken(s, TK_Identifier); + char *prop = M_StringDuplicate(SC_GetString(s)); - if (!strcasecmp(pname, "levelname")) + SC_MustGetToken(s, '='); + if (!strcasecmp(prop, "levelname")) { - if (U_MustGetToken(s, TK_StringConst)) - { - ReplaceString(&mape->levelname, s->string); - } + SC_MustGetToken(s, TK_StringConst); + ReplaceString(&mape->levelname, SC_GetString(s)); } - else if (!strcasecmp(pname, "label")) + else if (!strcasecmp(prop, "label")) { - if (U_CheckToken(s, TK_Identifier)) + if (SC_CheckToken(s, TK_Identifier)) { - if (!strcasecmp(s->string, "clear")) + if (!strcasecmp(SC_GetString(s), "clear")) { ReplaceString(&mape->label, "-"); } else { - U_Error(s, "Either 'clear' or string constant expected"); + SC_Error(s, "Either 'clear' or string constant expected"); } } - else if (U_MustGetToken(s, TK_StringConst)) + else { - ReplaceString(&mape->label, s->string); + SC_MustGetToken(s, TK_StringConst); + ReplaceString(&mape->label, SC_GetString(s)); } } - else if (!strcasecmp(pname, "author")) + else if (!strcasecmp(prop, "author")) { - if (U_MustGetToken(s, TK_StringConst)) - { - ReplaceString(&mape->author, s->string); - } + SC_MustGetToken(s, TK_StringConst); + ReplaceString(&mape->author, SC_GetString(s)); } - else if (!strcasecmp(pname, "episode")) + else if (!strcasecmp(prop, "episode")) { - if (U_CheckToken(s, TK_Identifier)) + if (SC_CheckToken(s, TK_Identifier)) { - if (!strcasecmp(s->string, "clear")) + if (!strcasecmp(SC_GetString(s), "clear")) { M_ClearEpisodes(); } else { - U_Error(s, "Either 'clear' or string constant expected"); + SC_Error(s, "Either 'clear' or string constant expected"); } } else { char lumpname[9] = {0}; char *alttext = NULL; - char *key = NULL; + char key = 0; ParseLumpName(s, lumpname); - if (U_CheckToken(s, ',')) + if (SC_CheckToken(s, ',')) { - if (U_MustGetToken(s, TK_StringConst)) - { - alttext = strdup(s->string); - } - if (U_CheckToken(s, ',')) + SC_MustGetToken(s, TK_StringConst); + alttext = M_StringDuplicate(SC_GetString(s)); + if (SC_CheckToken(s, ',')) { - if (U_MustGetToken(s, TK_StringConst)) - { - key = strdup(s->string); - key[0] = M_ToLower(key[0]); - } + SC_MustGetToken(s, TK_StringConst); + const char *tmp = SC_GetString(s); + key = M_ToLower(tmp[0]); } } M_AddEpisode(mape->mapname, lumpname, alttext, key); - mape->flags |= MapInfo_Episode; if (alttext) { free(alttext); } - if (key) - { - free(key); - } } } - else if (!strcasecmp(pname, "next")) + else if (!strcasecmp(prop, "next")) { - status = ParseLumpName(s, mape->nextmap); + ParseLumpName(s, mape->nextmap); if (!G_ValidateMapName(mape->nextmap, NULL, NULL)) { - U_Error(s, "Invalid map name %s.", mape->nextmap); - status = 0; + SC_Error(s, "Invalid map name %s.", mape->nextmap); } } - else if (!strcasecmp(pname, "nextsecret")) + else if (!strcasecmp(prop, "nextsecret")) { - status = ParseLumpName(s, mape->nextsecret); + ParseLumpName(s, mape->nextsecret); level_t level = {0}; if (!G_ValidateMapName(mape->nextsecret, &level.episode, &level.map)) { - U_Error(s, "Invalid map name %s", mape->nextsecret); - status = 0; + SC_Error(s, "Invalid map name %s", mape->nextsecret); } array_push(secretlevels, level); } - else if (!strcasecmp(pname, "levelpic")) + else if (!strcasecmp(prop, "levelpic")) { - status = ParseLumpName(s, mape->levelpic); + ParseLumpName(s, mape->levelpic); } - else if (!strcasecmp(pname, "skytexture")) + else if (!strcasecmp(prop, "skytexture")) { - status = ParseLumpName(s, mape->skytexture); + ParseLumpName(s, mape->skytexture); } - else if (!strcasecmp(pname, "music")) + else if (!strcasecmp(prop, "music")) { - status = ParseLumpName(s, mape->music); + ParseLumpName(s, mape->music); } - else if (!strcasecmp(pname, "endpic")) + else if (!strcasecmp(prop, "endpic")) { - status = ParseLumpName(s, mape->endpic); + ParseLumpName(s, mape->endpic); } - else if (!strcasecmp(pname, "endcast")) + else if (!strcasecmp(prop, "endcast")) { - U_MustGetToken(s, TK_BoolConst); - if (s->sc_boolean) + SC_MustGetToken(s, TK_BoolConst); + if (SC_GetBoolean(s)) { strcpy(mape->endpic, "$CAST"); - mape->flags |= MapInfo_Endgame; } else { strcpy(mape->endpic, "-"); } } - else if (!strcasecmp(pname, "endbunny")) + else if (!strcasecmp(prop, "endbunny")) { - U_MustGetToken(s, TK_BoolConst); - if (s->sc_boolean) + SC_MustGetToken(s, TK_BoolConst); + if (SC_GetBoolean(s)) { strcpy(mape->endpic, "$BUNNY"); - mape->flags |= MapInfo_Endgame; } else { strcpy(mape->endpic, "-"); } } - else if (!strcasecmp(pname, "endgame")) + else if (!strcasecmp(prop, "endgame")) { - U_MustGetToken(s, TK_BoolConst); - if (s->sc_boolean) + SC_MustGetToken(s, TK_BoolConst); + if (SC_GetBoolean(s)) { strcpy(mape->endpic, "!"); - mape->flags |= MapInfo_Endgame; } else { strcpy(mape->endpic, "-"); } } - else if (!strcasecmp(pname, "exitpic")) + else if (!strcasecmp(prop, "exitpic")) { - status = ParseLumpName(s, mape->exitpic); + ParseLumpName(s, mape->exitpic); } - else if (!strcasecmp(pname, "enterpic")) + else if (!strcasecmp(prop, "enterpic")) { - status = ParseLumpName(s, mape->enterpic); + ParseLumpName(s, mape->enterpic); } - else if (!strcasecmp(pname, "exitanim")) + else if (!strcasecmp(prop, "exitanim")) { - status = ParseLumpName(s, mape->exitanim); + ParseLumpName(s, mape->exitanim); } - else if (!strcasecmp(pname, "enteranim")) + else if (!strcasecmp(prop, "enteranim")) { - status = ParseLumpName(s, mape->enteranim); + ParseLumpName(s, mape->enteranim); } - else if (!strcasecmp(pname, "nointermission")) + else if (!strcasecmp(prop, "nointermission")) { - if (U_MustGetToken(s, TK_BoolConst)) - { - mape->nointermission = s->sc_boolean; - } + SC_MustGetToken(s, TK_BoolConst); + mape->nointermission = SC_GetBoolean(s); } - else if (!strcasecmp(pname, "partime")) + else if (!strcasecmp(prop, "partime")) { - if (U_MustGetInteger(s)) - { - mape->partime = s->number; - } + SC_MustGetToken(s, TK_IntConst); + mape->partime = SC_GetNumber(s); } - else if (!strcasecmp(pname, "intertext")) + else if (!strcasecmp(prop, "intertext")) { - char *lname = ParseMultiString(s, 1); - if (!lname) - { - return 0; - } - if (mape->intertext != NULL) + char *text = ParseMultiString(s); + if (mape->intertext) { free(mape->intertext); } - mape->intertext = lname; + mape->intertext = text; } - else if (!strcasecmp(pname, "intertextsecret")) + else if (!strcasecmp(prop, "intertextsecret")) { - char *lname = ParseMultiString(s, 1); - if (!lname) - { - return 0; - } - if (mape->intertextsecret != NULL) + char *text = ParseMultiString(s); + if (mape->intertextsecret) { free(mape->intertextsecret); } - mape->intertextsecret = lname; + mape->intertextsecret = text; } - else if (!strcasecmp(pname, "interbackdrop")) + else if (!strcasecmp(prop, "interbackdrop")) { - status = ParseLumpName(s, mape->interbackdrop); + ParseLumpName(s, mape->interbackdrop); } - else if (!strcasecmp(pname, "intermusic")) + else if (!strcasecmp(prop, "intermusic")) { - status = ParseLumpName(s, mape->intermusic); + ParseLumpName(s, mape->intermusic); } - else if (!strcasecmp(pname, "bossaction")) + else if (!strcasecmp(prop, "bossaction")) { - if (U_MustGetToken(s, TK_Identifier)) + SC_MustGetToken(s, TK_Identifier); + if (!strcasecmp(SC_GetString(s), "clear")) + { + array_free(mape->bossactions); + mape->nobossactions = true; // mark level free of boss actions + } + else { - if (!strcasecmp(s->string, "clear")) + int i, special, tag; + for (i = 0; arrlen(actor_names); i++) { - // mark level free of boss actions - if (mape->bossactions) + if (!strcasecmp(SC_GetString(s), actor_names[i])) { - free(mape->bossactions); + break; } - mape->bossactions = NULL; - mape->numbossactions = -1; } - else + if (i == arrlen(actor_names)) { - int i, special, tag; - for (i = 0; ActorNames[i]; i++) - { - if (!strcasecmp(s->string, ActorNames[i])) - { - break; - } - } - if (ActorNames[i] == NULL) - { - U_Error(s, "bossaction: unknown thing type '%s'", - s->string); - return 0; - } - U_MustGetToken(s, ','); - U_MustGetInteger(s); - special = s->number; - U_MustGetToken(s, ','); - U_MustGetInteger(s); - tag = s->number; - // allow no 0-tag specials here, unless a level exit. - if (tag != 0 || special == 11 || special == 51 || special == 52 - || special == 124) - { - if (mape->numbossactions == -1) - { - mape->numbossactions = 1; - } - else - { - mape->numbossactions++; - } - mape->bossactions = realloc(mape->bossactions, - sizeof(bossaction_t) * mape->numbossactions); - bossaction_t *bossaction = &mape->bossactions[mape->numbossactions - 1]; - bossaction->type = i; - bossaction->special = special; - bossaction->tag = tag; - } + SC_Error(s, "bossaction: unknown thing type '%s'", + SC_GetString(s)); + } + SC_MustGetToken(s, ','); + SC_MustGetToken(s, TK_IntConst); + special = SC_GetNumber(s); + SC_MustGetToken(s, ','); + SC_MustGetToken(s, TK_IntConst); + tag = SC_GetNumber(s); + // allow no 0-tag specials here, unless a level exit. + if (tag != 0 || special == 11 || special == 51 || special == 52 + || special == 124) + { + bossaction_t bossaction = {i, special, tag}; + array_push(mape->bossactions, bossaction); } } } // If no known property name was given, skip all comma-separated values // after the = sign - else if (s->token == '=') + else { do { - U_GetNextToken(s, true); - } while (U_CheckToken(s, ',')); + SC_GetNextToken(s, true); + } while (SC_CheckToken(s, ',')); } - free(pname); - return status; + free(prop); } -// ----------------------------------------------- -// -// Parses a complete map entry -// -// ----------------------------------------------- - -static int ParseMapEntry(u_scanner_t *s, mapentry_t *val) +static void ParseMapEntry(scanner_t *s, mapentry_t *entry) { - val->mapname = NULL; - - if (!U_MustGetIdentifier(s, "map")) + SC_MustGetToken(s, TK_Identifier); + if (strcasecmp(SC_GetString(s), "map")) { - return 0; + SC_Error(s, "Expected 'map' but got '%s' instead", SC_GetString(s)); } - U_MustGetToken(s, TK_Identifier); - if (!G_ValidateMapName(s->string, NULL, NULL)) + SC_MustGetToken(s, TK_Identifier); + if (!G_ValidateMapName(SC_GetString(s), NULL, NULL)) { - U_Error(s, "Invalid map name %s", s->string); - return 0; + SC_Error(s, "Invalid map name %s", SC_GetString(s)); } + ReplaceString(&entry->mapname, SC_GetString(s)); - ReplaceString(&val->mapname, s->string); - U_MustGetToken(s, '{'); - while (!U_CheckToken(s, '}')) + SC_MustGetToken(s, '{'); + while (!SC_CheckToken(s, '}')) { - if (!ParseStandardProperty(s, val)) - { - // If there was an error parsing, skip to next token - U_GetNextToken(s, true); - } + ParseStandardProperty(s, entry); } - return 1; } // ----------------------------------------------- @@ -742,63 +640,40 @@ static int ParseMapEntry(u_scanner_t *s, mapentry_t *val) // // ----------------------------------------------- -static boolean UpdateDefaultMapEntry(mapentry_t *val, int num) +static void SetDefaults(mapentry_t *entry, const char *mapname) { - int i; - - for (i = 0; i < default_mapinfo.mapcount; ++i) + mapentry_t *default_entry; + array_foreach(default_entry, umapdef) { - if (!strcmp(val->mapname, default_mapinfo.maps[i].mapname)) + if (!strcmp(mapname, default_entry->mapname)) { - memset(&U_mapinfo.maps[num], 0, sizeof(mapentry_t)); - UpdateMapEntry(&U_mapinfo.maps[num], &default_mapinfo.maps[i]); - UpdateMapEntry(&U_mapinfo.maps[num], val); - FreeMap(val); - return true; + UpdateMapEntry(entry, default_entry); + break; } } - - return false; } void U_ParseMapDefInfo(int lumpnum) { - const char *buffer = W_CacheLumpNum(lumpnum, PU_CACHE); - size_t length = W_LumpLength(lumpnum); - u_scanner_t *s = U_ScanOpen(buffer, length, "UMAPDEF"); - - while (U_HasTokensLeft(s)) + scanner_t *s = SC_Open("UMAPDEF", W_CacheLumpNum(lumpnum, PU_CACHE), + W_LumpLength(lumpnum)); + while (SC_TokensLeft(s)) { mapentry_t parsed = {0}; - if (!ParseMapEntry(s, &parsed)) - { - U_Error(s, "Skipping entry: %s", s->string); - continue; - } - - default_mapinfo.mapcount++; - default_mapinfo.maps = realloc(default_mapinfo.maps, - sizeof(mapentry_t) * default_mapinfo.mapcount); - default_mapinfo.maps[default_mapinfo.mapcount - 1] = parsed; + ParseMapEntry(s, &parsed); + array_push(umapdef, parsed); } - U_ScanClose(s); + SC_Close(s); } void U_ParseMapInfo(int lumpnum) { - unsigned int i; - const char *buffer = W_CacheLumpNum(lumpnum, PU_CACHE); - size_t length = W_LumpLength(lumpnum); - u_scanner_t *s = U_ScanOpen(buffer, length, "UMAPINFO"); - - while (U_HasTokensLeft(s)) + scanner_t *s = SC_Open("UMAPINFO", W_CacheLumpNum(lumpnum, PU_CACHE), + W_LumpLength(lumpnum)); + while (SC_TokensLeft(s)) { mapentry_t parsed = {0}; - if (!ParseMapEntry(s, &parsed)) - { - U_Error(s, "Skipping entry: %s", s->string); - continue; - } + ParseMapEntry(s, &parsed); // Set default level progression here to simplify the checks elsewhere. // Doing this lets us skip all normal code for this if nothing has been @@ -832,41 +707,34 @@ void U_ParseMapInfo(int lumpnum) else { int ep, map; - G_ValidateMapName(parsed.mapname, &ep, &map); - strcpy(parsed.nextmap, MapName(ep, map + 1)); } } - // Does this property already exist? If yes, replace it. - for (i = 0; i < U_mapinfo.mapcount; i++) + // Does this entry already exist? If yes, replace it. + mapentry_t *entry; + array_foreach(entry, umapinfo) { - if (!strcmp(parsed.mapname, U_mapinfo.maps[i].mapname)) + if (!strcmp(parsed.mapname, entry->mapname)) { - FreeMap(&U_mapinfo.maps[i]); - - if (!UpdateDefaultMapEntry(&parsed, i)) - { - U_mapinfo.maps[i] = parsed; - } + FreeMap(entry); + SetDefaults(entry, parsed.mapname); + UpdateMapEntry(entry, &parsed); break; } } // Not found so create a new one. - if (i == U_mapinfo.mapcount) + if (entry == array_end(umapinfo)) { - U_mapinfo.mapcount++; - U_mapinfo.maps = realloc(U_mapinfo.maps, - sizeof(mapentry_t) * U_mapinfo.mapcount); - - if (!UpdateDefaultMapEntry(&parsed, i)) - { - U_mapinfo.maps[U_mapinfo.mapcount - 1] = parsed; - } + mapentry_t new = {0}; + SetDefaults(&new, parsed.mapname); + UpdateMapEntry(&new, &parsed); + array_push(umapinfo, new); } } - U_ScanClose(s); + + SC_Close(s); } boolean U_CheckField(char *str) diff --git a/src/u_mapinfo.h b/src/u_mapinfo.h index ea6d6a019..69eac11c5 100644 --- a/src/u_mapinfo.h +++ b/src/u_mapinfo.h @@ -18,8 +18,8 @@ // //----------------------------------------------------------------------------- -#ifndef __UMAPINFO_H -#define __UMAPINFO_H +#ifndef UMAPINFO_H +#define UMAPINFO_H #include "doomtype.h" @@ -30,12 +30,6 @@ typedef struct int tag; } bossaction_t; -typedef enum -{ - MapInfo_Episode = 0x0001, - MapInfo_Endgame = 0x0002, -} mapinfo_flags_t; - typedef struct mapentry_s { char *mapname; @@ -57,20 +51,12 @@ typedef struct mapentry_s char interbackdrop[9]; char intermusic[9]; int partime; - mapinfo_flags_t flags; boolean nointermission; - int numbossactions; bossaction_t *bossactions; + boolean nobossactions; } mapentry_t; -typedef struct -{ - unsigned int mapcount; - mapentry_t *maps; -} umapinfo_t; - -extern umapinfo_t U_mapinfo; -extern umapinfo_t default_mapinfo; +extern mapentry_t *umapinfo, *umapdef; extern boolean EpiCustom; diff --git a/src/u_scanner.c b/src/u_scanner.c deleted file mode 100644 index 5d308484d..000000000 --- a/src/u_scanner.c +++ /dev/null @@ -1,909 +0,0 @@ -// Copyright (c) 2019, Fernando Carmona Varo -// Copyright (c) 2010, Braden "Blzut3" Obrzut -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include -#include -#include - -#include "doomtype.h" -#include "i_system.h" -#include "m_misc.h" -#include "u_scanner.h" - -static const char *U_TokenNames[TK_NumSpecialTokens] = -{ - "Identifier", // case insensitive identifier, beginning with a letter and - // may contain [a-z0-9_] - "String Constant", - "Integer Constant", - "Float Constant", - "Boolean Constant", - "Logical And", - "Logical Or", - "Equals", - "Not Equals", - "Greater Than or Equals", - "Less Than or Equals", - "Left Shift", - "Right Shift" -}; - -static void U_CheckForWhitespace(u_scanner_t *scanner); -static void U_ExpandState(u_scanner_t *scanner); -static void U_Unescape(char *str); -static void U_SetString(char **ptr, const char *start, int length); - -u_scanner_t *U_ScanOpen(const char *data, int length, const char *name) -{ - u_scanner_t *s = calloc(1, sizeof(*s)); - s->line = s->tokenLine = 1; - s->needNext = true; - s->string = NULL; - s->nextState.string = NULL; - s->name = name; - - if (length == -1) - { - length = strlen(data); - } - s->length = length; - s->data = malloc(length); - memcpy(s->data, data, length); - - U_CheckForWhitespace(s); - - return s; -} - -void U_ScanClose(u_scanner_t *s) -{ - if (s->nextState.string != NULL) - { - free(s->nextState.string); - } - if (s->data != NULL) - { - free(s->data); - } - free(s); -} - -static void U_IncrementLine(u_scanner_t *s) -{ - s->line++; - s->lineStart = s->scanPos; -} - -static void U_CheckForWhitespace(u_scanner_t *s) -{ - int comment = 0; // 1 = till next new line, 2 = till end block - while (s->scanPos < s->length) - { - char cur = s->data[s->scanPos]; - char next = (s->scanPos + 1 < s->length) ? s->data[s->scanPos + 1] : 0; - if (comment == 2) - { - if (cur != '*' || next != '/') - { - if (cur == '\n' || cur == '\r') - { - s->scanPos++; - - // Do a quick check for Windows style new line - if (cur == '\r' && next == '\n') - { - s->scanPos++; - } - U_IncrementLine(s); - } - else - { - s->scanPos++; - } - } - else - { - comment = 0; - s->scanPos += 2; - } - continue; - } - - if (cur == ' ' || cur == '\t' || cur == 0) - { - s->scanPos++; - } - else if (cur == '\n' || cur == '\r') - { - s->scanPos++; - if (comment == 1) - { - comment = 0; - } - - // Do a quick check for Windows style new line - if (cur == '\r' && next == '\n') - { - s->scanPos++; - } - U_IncrementLine(s); - } - else if (cur == '/' && comment == 0) - { - switch (next) - { - case '/': - comment = 1; - break; - case '*': - comment = 2; - break; - default: - return; - } - s->scanPos += 2; - } - else - { - if (comment == 0) - { - return; - } - else - { - s->scanPos++; - } - } - } -} - -boolean U_CheckToken(u_scanner_t *s, char token) -{ - if (s->needNext) - { - if (!U_GetNextToken(s, false)) - { - return false; - } - } - - // An int can also be a float. - if ((s->nextState).token == token - || ((s->nextState).token == TK_IntConst && s->token == TK_FloatConst)) - { - s->needNext = true; - U_ExpandState(s); - return true; - } - s->needNext = false; - return false; -} - -static void U_ExpandState(u_scanner_t *s) -{ - s->logicalPosition = s->scanPos; - U_CheckForWhitespace(s); - - U_SetString(&(s->string), s->nextState.string, -1); - s->number = s->nextState.number; - s->decimal = s->nextState.decimal; - s->sc_boolean = s->nextState.sc_boolean; - s->token = s->nextState.token; - s->tokenLine = s->nextState.tokenLine; - s->tokenLinePosition = s->nextState.tokenLinePosition; -} - - // NOLINTBEGIN(clang-analyzer-unix.Malloc) -static void U_SaveState(u_scanner_t *s, u_scanner_t savedstate) -{ - // This saves the entire parser state except for the data pointer. - if (savedstate.string != NULL) - { - free(savedstate.string); - } - if (savedstate.nextState.string != NULL) - { - free(savedstate.nextState.string); - } - - memcpy(&savedstate, s, sizeof(*s)); - savedstate.string = strdup(s->string); - savedstate.nextState.string = strdup(s->nextState.string); - savedstate.data = NULL; -} -// NOLINTEND(clang-analyzer-unix.Malloc) - -static void U_RestoreState(u_scanner_t *s, u_scanner_t savedstate) -{ - if (savedstate.data == NULL) - { - char *saveddata = s->data; - U_SaveState(&savedstate, *s); - s->data = saveddata; - } -} - -boolean U_GetString(u_scanner_t *s) -{ - unsigned int start; - char cur; - u_parserstate_t *nextState = &s->nextState; - - if (!s->needNext) - { - s->needNext = true; - U_ExpandState(s); - return true; - } - - nextState->tokenLine = s->line; - nextState->tokenLinePosition = s->scanPos - s->lineStart; - nextState->token = TK_NoToken; - if (s->scanPos >= s->length) - { - U_ExpandState(s); - return false; - } - - start = s->scanPos; - s->scanPos++; - - while (s->scanPos < s->length) - { - cur = s->data[s->scanPos]; - - if (cur == ' ' || cur == '\t' || cur == '\n' || cur == '\r' || cur == 0) - { - break; - } - else - { - s->scanPos++; - } - } - - U_SetString(&(nextState->string), s->data + start, s->scanPos - start); - U_ExpandState(s); - return true; -} - -boolean U_GetNextToken(u_scanner_t *s, boolean expandState) -{ - unsigned int start; - unsigned int end; - char cur; - int integerBase = 10; - boolean floatHasDecimal = false; - boolean floatHasExponent = false; - // Strings are the only things that can have 0 length tokens. - boolean stringFinished = false; - u_parserstate_t *nextState = &s->nextState; - - if (!s->needNext) - { - s->needNext = true; - if (expandState) - { - U_ExpandState(s); - } - return true; - } - - nextState->tokenLine = s->line; - nextState->tokenLinePosition = s->scanPos - s->lineStart; - nextState->token = TK_NoToken; - if (s->scanPos >= s->length) - { - if (expandState) - { - U_ExpandState(s); - } - return false; - } - - start = s->scanPos; - end = s->scanPos; - cur = s->data[s->scanPos++]; - - // Determine by first character - if (cur == '_' || (cur >= 'A' && cur <= 'Z') || (cur >= 'a' && cur <= 'z')) - { - nextState->token = TK_Identifier; - } - else if (cur >= '0' && cur <= '9') - { - if (cur == '0') - { - integerBase = 8; - } - nextState->token = TK_IntConst; - } - else if (cur == '.') - { - floatHasDecimal = true; - nextState->token = TK_FloatConst; - } - else if (cur == '"') - { - end = ++start; // Move the start up one character so we don't have to - // trim it later. - nextState->token = TK_StringConst; - } - else - { - end = s->scanPos; - nextState->token = cur; - - // Now check for operator tokens - if (s->scanPos < s->length) - { - char next = s->data[s->scanPos]; - if (cur == '&' && next == '&') - { - nextState->token = TK_AndAnd; - } - else if (cur == '|' && next == '|') - { - nextState->token = TK_OrOr; - } - else if (cur == '<' && next == '<') - { - nextState->token = TK_ShiftLeft; - } - else if (cur == '>' && next == '>') - { - nextState->token = TK_ShiftRight; - } - // else if(cur == '#' && next == '#') - // nextState.token = TK_MacroConcat; - else if (next == '=') - { - switch (cur) - { - case '=': - nextState->token = TK_EqEq; - break; - case '!': - nextState->token = TK_NotEq; - break; - case '>': - nextState->token = TK_GtrEq; - break; - case '<': - nextState->token = TK_LessEq; - break; - default: - break; - } - } - if (nextState->token != cur) - { - s->scanPos++; - end = s->scanPos; - } - } - } - - if (start == end) - { - while (s->scanPos < s->length) - { - cur = s->data[s->scanPos]; - switch (nextState->token) - { - default: - break; - case TK_Identifier: - if (cur != '_' && (cur < 'A' || cur > 'Z') - && (cur < 'a' || cur > 'z') && (cur < '0' || cur > '9')) - { - end = s->scanPos; - } - break; - case TK_IntConst: - if (cur == '.' || (s->scanPos - 1 != start && cur == 'e')) - { - nextState->token = TK_FloatConst; - } - else if ((cur == 'x' || cur == 'X') - && s->scanPos - 1 == start) - { - integerBase = 16; - break; - } - else - { - switch (integerBase) - { - default: - if (cur < '0' || cur > '9') - { - end = s->scanPos; - } - break; - case 8: - if (cur < '0' || cur > '7') - { - end = s->scanPos; - } - break; - case 16: - if ((cur < '0' || cur > '9') - && (cur < 'A' || cur > 'F') - && (cur < 'a' || cur > 'f')) - { - end = s->scanPos; - } - break; - } - break; - } - case TK_FloatConst: - if (cur < '0' || cur > '9') - { - if (!floatHasDecimal && cur == '.') - { - floatHasDecimal = true; - break; - } - else if (!floatHasExponent && cur == 'e') - { - floatHasDecimal = true; - floatHasExponent = true; - if (s->scanPos + 1 < s->length) - { - char next = s->data[s->scanPos + 1]; - if ((next < '0' || next > '9') && next != '+' - && next != '-') - { - end = s->scanPos; - } - else - { - s->scanPos++; - } - } - break; - } - end = s->scanPos; - } - break; - case TK_StringConst: - if (cur == '"') - { - stringFinished = true; - end = s->scanPos; - s->scanPos++; - } - else if (cur == '\\') - { - s->scanPos++; // Will add two since the loop - // automatically adds one - } - break; - } - if (start == end && !stringFinished) - { - s->scanPos++; - } - else - { - break; - } - } - // If we reached end of input while reading, set it as the end of token - if (s->scanPos == s->length && start == end) - { - end = s->length; - } - } - - if (end - start > 0 || stringFinished) - { - U_SetString(&(nextState->string), s->data + start, end - start); - if (nextState->token == TK_FloatConst) - { - nextState->decimal = atof(nextState->string); - nextState->number = (int)(nextState->decimal); - nextState->sc_boolean = (nextState->number != 0); - } - else if (nextState->token == TK_IntConst) - { - nextState->number = strtol(nextState->string, NULL, integerBase); - nextState->decimal = nextState->number; - nextState->sc_boolean = (nextState->number != 0); - } - else if (nextState->token == TK_Identifier) - { - // Identifiers should be case insensitive. - char *p = nextState->string; - while (*p) - { - *p = M_ToLower(*p); - p++; - } - // Check for a boolean constant. - if (strcmp(nextState->string, "true") == 0) - { - nextState->token = TK_BoolConst; - nextState->sc_boolean = true; - } - else if (strcmp(nextState->string, "false") == 0) - { - nextState->token = TK_BoolConst; - nextState->sc_boolean = false; - } - } - else if (nextState->token == TK_StringConst) - { - U_Unescape(nextState->string); - } - if (expandState) - { - U_ExpandState(s); - } - return true; - } - nextState->token = TK_NoToken; - if (expandState) - { - U_ExpandState(s); - } - return false; -} - -// Skips all Tokens in current line and parses the first token on the next -// line. -boolean U_GetNextLineToken(u_scanner_t *s) -{ - unsigned int line = s->line; - boolean retval = false; - - do - { - retval = U_GetNextToken(s, true); - } while (retval && s->line == line); - - return retval; -} - -void U_ErrorToken(u_scanner_t *s, int token) -{ - if (token < TK_NumSpecialTokens && s->token >= TK_Identifier - && s->token < TK_NumSpecialTokens) - { - U_Error(s, "Expected %s but got %s '%s' instead.", U_TokenNames[token], - U_TokenNames[(int)s->token], s->string); - } - else if (token < TK_NumSpecialTokens && s->token >= TK_NumSpecialTokens) - { - U_Error(s, "Expected %s but got '%c' instead.", U_TokenNames[token], - s->token); - } - else if (token < TK_NumSpecialTokens && s->token == TK_NoToken) - { - U_Error(s, "Expected %s", U_TokenNames[token]); - } - else if (token >= TK_NumSpecialTokens && s->token >= TK_Identifier - && s->token < TK_NumSpecialTokens) - { - U_Error(s, "Expected '%c' but got %s '%s' instead.", token, - U_TokenNames[(int)s->token], s->string); - } - else - { - U_Error(s, "Expected '%c' but got '%c' instead.", token, s->token); - } -} - -void U_ErrorString(u_scanner_t *s, const char *mustget) -{ - if (s->token < TK_NumSpecialTokens) - { - U_Error(s, "Expected '%s' but got %s '%s' instead.", mustget, - U_TokenNames[(int)s->token], s->string); - } - else - { - U_Error(s, "Expected '%s' but got '%c' instead.", mustget, s->token); - } -} - -void PRINTF_ATTR(2, 0) U_Error(u_scanner_t *s, const char *msg, ...) -{ - char buffer[1024]; - va_list ap; - va_start(ap, msg); - M_vsnprintf(buffer, 1024, msg, ap); - va_end(ap); - I_Error("%s:%d:%d:%s", s->name, s->tokenLine, s->tokenLinePosition, buffer); -} - -boolean U_MustGetToken(u_scanner_t *s, char token) -{ - if (!U_CheckToken(s, token)) - { - U_ExpandState(s); - U_ErrorToken(s, token); - return false; - } - return true; -} - -boolean U_MustGetIdentifier(u_scanner_t *s, const char *ident) -{ - if (!U_CheckToken(s, TK_Identifier) || strcasecmp(s->string, ident)) - { - U_ErrorString(s, ident); - return false; - } - return true; -} - -void U_Unget(u_scanner_t *s) -{ - s->needNext = false; -} - -// Convenience helpers that parse an entire number including a leading minus or -// plus sign -static boolean U_ScanInteger(u_scanner_t *s) -{ - boolean neg = false; - if (!U_GetNextToken(s, true)) - { - return false; - } - if (s->token == '-') - { - if (!U_GetNextToken(s, true)) - { - return false; - } - neg = true; - } - else if (s->token == '+') - { - if (!U_GetNextToken(s, true)) - { - return false; - } - } - if (s->token != TK_IntConst) - { - return false; - } - if (neg) - { - s->number = -(s->number); - s->decimal = -(s->decimal); - } - return true; -} - -static boolean U_ScanFloat(u_scanner_t *s) -{ - boolean neg = false; - if (!U_GetNextToken(s, true)) - { - return false; - } - if (s->token == '-') - { - if (!U_GetNextToken(s, true)) - { - return false; - } - neg = true; - } - else if (s->token == '+') - { - if (!U_GetNextToken(s, true)) - { - return false; - } - } - if (s->token != TK_IntConst && s->token != TK_FloatConst) - { - return false; - } - if (neg) - { - s->number = -(s->number); - s->decimal = -(s->decimal); - } - return true; -} - -boolean U_CheckInteger(u_scanner_t *s) -{ - boolean res; - u_scanner_t savedstate = {0}; - U_SaveState(s, savedstate); - res = U_ScanInteger(s); - if (!res) - { - U_RestoreState(s, savedstate); - } - return res; -} - -boolean U_CheckFloat(u_scanner_t *s) -{ - boolean res; - u_scanner_t savedstate = {0}; - U_SaveState(s, savedstate); - res = U_ScanFloat(s); - if (!res) - { - U_RestoreState(s, savedstate); - } - return res; -} - -boolean U_MustGetInteger(u_scanner_t *s) -{ - if (!U_ScanInteger(s)) - { - U_ErrorToken(s, TK_IntConst); - return false; - } - return true; -} - -boolean U_MustGetFloat(u_scanner_t *s) -{ - if (!U_ScanFloat(s)) - { - U_ErrorToken(s, TK_FloatConst); - return false; - } - return true; -} - -boolean U_HasTokensLeft(u_scanner_t *s) -{ - return (s->scanPos < s->length); -} - -// This is taken from ZDoom's strbin function which can do a lot more than just -// unescaping backslashes and quotation marks. -void U_Unescape(char *str) -{ - char *p = str, c; - int i; - - while ((c = *p++)) - { - if (c != '\\') - { - *str++ = c; - } - else - { - switch (*p) - { - case 'a': - *str++ = '\a'; - break; - case 'b': - *str++ = '\b'; - break; - case 'f': - *str++ = '\f'; - break; - case 'n': - *str++ = '\n'; - break; - case 't': - *str++ = '\t'; - break; - case 'r': - *str++ = '\r'; - break; - case 'v': - *str++ = '\v'; - break; - case '?': - *str++ = '\?'; - break; - case '\n': - break; - case 'x': - case 'X': - c = 0; - for (i = 0; i < 2; i++) - { - p++; - if (*p >= '0' && *p <= '9') - { - c = (c << 4) + *p - '0'; - } - else if (*p >= 'a' && *p <= 'f') - { - c = (c << 4) + 10 + *p - 'a'; - } - else if (*p >= 'A' && *p <= 'F') - { - c = (c << 4) + 10 + *p - 'A'; - } - else - { - p--; - break; - } - } - *str++ = c; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - c = *p - '0'; - for (i = 0; i < 2; i++) - { - p++; - if (*p >= '0' && *p <= '7') - { - c = (c << 3) + *p - '0'; - } - else - { - p--; - break; - } - } - *str++ = c; - break; - default: - *str++ = *p; - break; - } - p++; - } - } - *str = 0; -} - -static void U_SetString(char **ptr, const char *start, int length) -{ - if (length == -1) - { - length = strlen(start); - } - if (*ptr != NULL) - { - free(*ptr); - } - *ptr = (char *)malloc(length + 1); - memcpy(*ptr, start, length); - (*ptr)[length] = '\0'; -} diff --git a/src/u_scanner.h b/src/u_scanner.h deleted file mode 100644 index bdbef6247..000000000 --- a/src/u_scanner.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2010, Braden "Blzut3" Obrzut -// Copyright (c) 2019, Fernando Carmona Varo -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef __U_SCANNER_H__ -#define __U_SCANNER_H__ - -#include "doomtype.h" - -enum -{ - TK_Identifier, // Ex: SomeIdentifier - TK_StringConst, // Ex: "Some String" - TK_IntConst, // Ex: 27 - TK_FloatConst, // Ex: 1.5 - TK_BoolConst, // Ex: true - TK_AndAnd, // && - TK_OrOr, // || - TK_EqEq, // == - TK_NotEq, // != - TK_GtrEq, // >= - TK_LessEq, // <= - TK_ShiftLeft, // << - TK_ShiftRight, // >> - - TK_NumSpecialTokens, - - TK_NoToken = -1 -}; - -typedef struct -{ - char *string; - int number; - double decimal; - boolean sc_boolean; - char token; - unsigned int tokenLine; - unsigned int tokenLinePosition; -} u_parserstate_t; - -typedef struct -{ - const char *name; - - u_parserstate_t nextState; - - char *data; - unsigned int length; - - unsigned int line; - unsigned int lineStart; - unsigned int logicalPosition; - unsigned int tokenLine; - unsigned int tokenLinePosition; - unsigned int scanPos; - - boolean needNext; // If checkToken returns false this will be false. - - char *string; - int number; - double decimal; - boolean sc_boolean; - char token; - -} u_scanner_t; - -u_scanner_t *U_ScanOpen(const char *data, int length, const char *name); -void U_ScanClose(u_scanner_t *s); -boolean U_GetNextToken(u_scanner_t *s, boolean expandState); -boolean U_GetNextLineToken(u_scanner_t *s); -boolean U_HasTokensLeft(u_scanner_t *s); -boolean U_MustGetToken(u_scanner_t *s, char token); -boolean U_MustGetIdentifier(u_scanner_t *s, const char *ident); -boolean U_MustGetInteger(u_scanner_t *s); -boolean U_MustGetFloat(u_scanner_t *s); -boolean U_CheckToken(u_scanner_t *s, char token); -boolean U_CheckInteger(u_scanner_t *s); -boolean U_CheckFloat(u_scanner_t *s); -void U_Unget(u_scanner_t *s); -boolean U_GetString(u_scanner_t *s); - -void PRINTF_ATTR(2, 0) U_Error(u_scanner_t *s, const char *msg, ...); -void U_ErrorToken(u_scanner_t *s, int token); -void U_ErrorString(u_scanner_t *s, const char *mustget); - -#endif /* __U_SCANNER_H__ */