From f1040fa699baa4c202b365c55afc23499fe51605 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 12 Apr 2020 00:11:50 +0200 Subject: [PATCH] - match with GZDoom's console code, move to 'common'. --- source/CMakeLists.txt | 24 +- source/{core => common}/console/c_bind.cpp | 0 source/{core => common}/console/c_bind.h | 0 source/{core => common}/console/c_buttons.cpp | 0 source/{core => common}/console/c_buttons.h | 0 .../console/c_commandline.cpp | 0 .../{core => common}/console/c_commandline.h | 0 source/{core => common}/console/c_console.h | 0 .../console/c_consolebuffer.cpp | 0 .../console/c_consolebuffer.h | 0 source/{core => common}/console/c_cvars.cpp | 0 source/{core => common}/console/c_cvars.h | 0 .../{core => common}/console/c_dispatch.cpp | 0 source/{core => common}/console/c_dispatch.h | 0 source/common/console/c_enginecmds.cpp | 338 ++++++++ source/common/console/c_expr.cpp | 812 ++++++++++++++++++ source/{core => common}/console/keydef.h | 0 source/common/engine/printf.h | 3 + source/core/gamecontrol.cpp | 69 +- source/core/rendering/r_videoscale.cpp | 2 +- source/core/utility/gstrings.h | 48 -- source/core/utility/stringtable.cpp | 678 --------------- source/core/utility/stringtable.h | 133 --- 23 files changed, 1195 insertions(+), 912 deletions(-) rename source/{core => common}/console/c_bind.cpp (100%) rename source/{core => common}/console/c_bind.h (100%) rename source/{core => common}/console/c_buttons.cpp (100%) rename source/{core => common}/console/c_buttons.h (100%) rename source/{core => common}/console/c_commandline.cpp (100%) rename source/{core => common}/console/c_commandline.h (100%) rename source/{core => common}/console/c_console.h (100%) rename source/{core => common}/console/c_consolebuffer.cpp (100%) rename source/{core => common}/console/c_consolebuffer.h (100%) rename source/{core => common}/console/c_cvars.cpp (100%) rename source/{core => common}/console/c_cvars.h (100%) rename source/{core => common}/console/c_dispatch.cpp (100%) rename source/{core => common}/console/c_dispatch.h (100%) create mode 100644 source/common/console/c_enginecmds.cpp create mode 100644 source/common/console/c_expr.cpp rename source/{core => common}/console/keydef.h (100%) delete mode 100644 source/core/utility/gstrings.h delete mode 100644 source/core/utility/stringtable.cpp delete mode 100644 source/core/utility/stringtable.h diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 71048cb71..f6e058bd0 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -712,17 +712,19 @@ set (PCH_SOURCES core/fonts/v_text.cpp core/fonts/fontchars.cpp - core/console/c_cvars.cpp core/console/c_console.cpp - core/console/c_consolebuffer.cpp - core/console/c_bind.cpp - core/console/c_buttons.cpp - core/console/c_commandline.cpp - core/console/c_dispatch.cpp core/console/d_event.cpp common/thirdparty/sfmt/SFMT.cpp common/textures/bitmap.cpp + common/console/c_commandline.cpp + common/console/c_buttons.cpp + common/console/c_bind.cpp + common/console/c_enginecmds.cpp + common/console/c_consolebuffer.cpp + common/console/c_cvars.cpp + common/console/c_dispatch.cpp + common/console/c_expr.cpp common/utility/engineerrors.cpp common/utility/i_module.cpp common/utility/m_alloc.cpp @@ -754,9 +756,9 @@ set (PCH_SOURCES common/filesystem/resourcefile.cpp common/engine/sc_man.cpp #common/engine/palettecontainer.cpp // not yet operational. + common/engine/stringtable.cpp core/utility/m_png.cpp - core/utility/stringtable.cpp core/utility/stats.cpp core/textures/buildtiles.cpp @@ -915,6 +917,7 @@ include_directories( common/textures common/filesystem common/utility + common/console common/engine ${CMAKE_BINARY_DIR}/libraries/gdtoa @@ -1024,9 +1027,10 @@ source_group("Platform" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platfor source_group("Platform\\Win32" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platform/win32/.+") source_group("Platform\\POSIX" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platform/posix/.+") source_group("Common" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/.+") -source_group("Common\\Utility" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/utility/.+") -source_group("Common\\Engine" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/engine/.+") -source_group("Common\\File System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/filesystem/.+") +source_group("Common\\Console" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/console/.+") +source_group("Common\\Utility" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/utility/.+") +source_group("Common\\Engine" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/engine/.+") +source_group("Common\\File System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/.+") source_group("Common\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/.+") source_group("Common\\Third Party" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/thirdparty/.+") source_group("Common\\Third Party\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/thirdparty/math/.+") diff --git a/source/core/console/c_bind.cpp b/source/common/console/c_bind.cpp similarity index 100% rename from source/core/console/c_bind.cpp rename to source/common/console/c_bind.cpp diff --git a/source/core/console/c_bind.h b/source/common/console/c_bind.h similarity index 100% rename from source/core/console/c_bind.h rename to source/common/console/c_bind.h diff --git a/source/core/console/c_buttons.cpp b/source/common/console/c_buttons.cpp similarity index 100% rename from source/core/console/c_buttons.cpp rename to source/common/console/c_buttons.cpp diff --git a/source/core/console/c_buttons.h b/source/common/console/c_buttons.h similarity index 100% rename from source/core/console/c_buttons.h rename to source/common/console/c_buttons.h diff --git a/source/core/console/c_commandline.cpp b/source/common/console/c_commandline.cpp similarity index 100% rename from source/core/console/c_commandline.cpp rename to source/common/console/c_commandline.cpp diff --git a/source/core/console/c_commandline.h b/source/common/console/c_commandline.h similarity index 100% rename from source/core/console/c_commandline.h rename to source/common/console/c_commandline.h diff --git a/source/core/console/c_console.h b/source/common/console/c_console.h similarity index 100% rename from source/core/console/c_console.h rename to source/common/console/c_console.h diff --git a/source/core/console/c_consolebuffer.cpp b/source/common/console/c_consolebuffer.cpp similarity index 100% rename from source/core/console/c_consolebuffer.cpp rename to source/common/console/c_consolebuffer.cpp diff --git a/source/core/console/c_consolebuffer.h b/source/common/console/c_consolebuffer.h similarity index 100% rename from source/core/console/c_consolebuffer.h rename to source/common/console/c_consolebuffer.h diff --git a/source/core/console/c_cvars.cpp b/source/common/console/c_cvars.cpp similarity index 100% rename from source/core/console/c_cvars.cpp rename to source/common/console/c_cvars.cpp diff --git a/source/core/console/c_cvars.h b/source/common/console/c_cvars.h similarity index 100% rename from source/core/console/c_cvars.h rename to source/common/console/c_cvars.h diff --git a/source/core/console/c_dispatch.cpp b/source/common/console/c_dispatch.cpp similarity index 100% rename from source/core/console/c_dispatch.cpp rename to source/common/console/c_dispatch.cpp diff --git a/source/core/console/c_dispatch.h b/source/common/console/c_dispatch.h similarity index 100% rename from source/core/console/c_dispatch.h rename to source/common/console/c_dispatch.h diff --git a/source/common/console/c_enginecmds.cpp b/source/common/console/c_enginecmds.cpp new file mode 100644 index 000000000..0af6f3b56 --- /dev/null +++ b/source/common/console/c_enginecmds.cpp @@ -0,0 +1,338 @@ +/* +** c_cmds.cpp +** Miscellaneous game independent console commands. +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 _WIN32 +#include +#else +#include +#endif + +#include +#include +#include +#include +#include "c_console.h" +#include "c_dispatch.h" +#include "engineerrors.h" +#include "printf.h" +#include "files.h" +#include "filesystem.h" +#include "gstrings.h" +#include "version.h" +#include "findfile.h" +#include "md5.h" + +extern FILE* Logfile; + +CCMD (quit) +{ + throw CExitEvent(0); +} + +CCMD (exit) +{ + throw CExitEvent(0); +} + +CCMD (gameversion) +{ + Printf ("%s @ %s\nCommit %s\n", GetVersionString(), GetGitTime(), GetGitHash()); +} + +CCMD (print) +{ + if (argv.argc() != 2) + { + Printf ("print : Print a string from the string table\n"); + return; + } + const char *str = GStrings[argv[1]]; + if (str == NULL) + { + Printf ("%s unknown\n", argv[1]); + } + else + { + Printf ("%s\n", str); + } +} + +UNSAFE_CCMD (exec) +{ + if (argv.argc() < 2) + return; + + for (int i = 1; i < argv.argc(); ++i) + { + if (!C_ExecFile(argv[i])) + { + Printf ("Could not exec \"%s\"\n", argv[i]); + break; + } + } +} + +void execLogfile(const char *fn, bool append) +{ + if ((Logfile = fopen(fn, append? "a" : "w"))) + { + const char *timestr = myasctime(); + Printf("Log started: %s\n", timestr); + } + else + { + Printf("Could not start log\n"); + } +} + +UNSAFE_CCMD (logfile) +{ + + if (Logfile) + { + const char *timestr = myasctime(); + Printf("Log stopped: %s\n", timestr); + fclose (Logfile); + Logfile = NULL; + } + + if (argv.argc() >= 2) + { + execLogfile(argv[1], argv.argc() >=3? !!argv[2]:false); + } +} + +CCMD (error) +{ + if (argv.argc() > 1) + { + I_Error ("%s", argv[1]); + } + else + { + Printf ("Usage: error \n"); + } +} + +UNSAFE_CCMD (error_fatal) +{ + if (argv.argc() > 1) + { + I_FatalError ("%s", argv[1]); + } + else + { + Printf ("Usage: error_fatal \n"); + } +} + +//========================================================================== +// +// CCMD crashout +// +// Debugging routine for testing the crash logger. +// Useless in a win32 debug build, because that doesn't enable the crash logger. +// +//========================================================================== + +#if !defined(_WIN32) || !defined(_DEBUG) +UNSAFE_CCMD (crashout) +{ + *(volatile int *)0 = 0; +} +#endif + + +UNSAFE_CCMD (dir) +{ + FString dir, path; + char curdir[256]; + const char *match; + findstate_t c_file; + void *file; + + if (!getcwd (curdir, countof(curdir))) + { + Printf ("Current path too long\n"); + return; + } + + if (argv.argc() > 1) + { + path = NicePath(argv[1]); + if (chdir(path)) + { + match = path; + dir = ExtractFilePath(path); + if (dir[0] != '\0') + { + match += dir.Len(); + } + else + { + dir = "./"; + } + if (match[0] == '\0') + { + match = "*"; + } + if (chdir (dir)) + { + Printf ("%s not found\n", dir.GetChars()); + return; + } + } + else + { + match = "*"; + dir = path; + } + } + else + { + match = "*"; + dir = curdir; + } + if (dir[dir.Len()-1] != '/') + { + dir += '/'; + } + + if ( (file = I_FindFirst (match, &c_file)) == ((void *)(-1))) + Printf ("Nothing matching %s%s\n", dir.GetChars(), match); + else + { + Printf ("Listing of %s%s:\n", dir.GetChars(), match); + do + { + if (I_FindAttr (&c_file) & FA_DIREC) + Printf (PRINT_BOLD, "%s \n", I_FindName (&c_file)); + else + Printf ("%s\n", I_FindName (&c_file)); + } while (I_FindNext (file, &c_file) == 0); + I_FindClose (file); + } + + chdir (curdir); +} + +//========================================================================== +// +// CCMD wdir +// +// Lists the contents of a loaded wad file. +// +//========================================================================== + +CCMD (wdir) +{ + int wadnum; + if (argv.argc() != 2) wadnum = -1; + else + { + wadnum = fileSystem.CheckIfResourceFileLoaded (argv[1]); + if (wadnum < 0) + { + Printf ("%s must be loaded to view its directory.\n", argv[1]); + return; + } + } + for (int i = 0; i < fileSystem.GetNumEntries(); ++i) + { + if (wadnum == -1 || fileSystem.GetFileContainer(i) == wadnum) + { + Printf ("%10d %s\n", fileSystem.FileLength(i), fileSystem.GetFileFullName(i)); + } + } +} + +//========================================================================== +// +// CCMD md5sum +// +// Like the command-line tool, because I wanted to make sure I had it right. +// +//========================================================================== + +CCMD (md5sum) +{ + if (argv.argc() < 2) + { + Printf("Usage: md5sum ...\n"); + } + for (int i = 1; i < argv.argc(); ++i) + { + FileReader fr; + if (!fr.OpenFile(argv[i])) + { + Printf("%s: %s\n", argv[i], strerror(errno)); + } + else + { + MD5Context md5; + uint8_t readbuf[8192]; + size_t len; + + while ((len = fr.Read(readbuf, sizeof(readbuf))) > 0) + { + md5.Update(readbuf, (unsigned int)len); + } + md5.Final(readbuf); + for(int j = 0; j < 16; ++j) + { + Printf("%02x", readbuf[j]); + } + Printf(" *%s\n", argv[i]); + } + } +} + +CCMD(printlocalized) +{ + if (argv.argc() > 1) + { + if (argv.argc() > 2) + { + FString lang = argv[2]; + lang.ToLower(); + if (lang.Len() >= 2) + { + Printf("%s\n", GStrings.GetLanguageString(argv[1], MAKE_ID(lang[0], lang[1], lang[2], 0))); + return; + } + } + Printf("%s\n", GStrings(argv[1])); + } + +} diff --git a/source/common/console/c_expr.cpp b/source/common/console/c_expr.cpp new file mode 100644 index 000000000..d203c8cd5 --- /dev/null +++ b/source/common/console/c_expr.cpp @@ -0,0 +1,812 @@ +/* +** c_expr.cpp +** Console commands dealing with mathematical expressions +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. 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. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include + +#include "c_dispatch.h" +#include "c_cvars.h" +#include "cmdlib.h" +#include "printf.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +enum EProductionType +{ + PROD_String, PROD_Double +}; + +struct FProduction +{ + EProductionType Type; +}; + +struct FStringProd : public FProduction +{ + char Value[1]; +}; + +struct FDoubleProd : public FProduction +{ + double Value; +}; + +struct FProducer +{ + char Token[4]; + FProduction *(*DoubleProducer) (FDoubleProd *prod1, FDoubleProd *prod2); + FProduction *(*StringProducer) (FStringProd *prod1, FStringProd *prod2); +}; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +bool IsFloat (const char *str); + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static FProduction *ParseExpression (FCommandLine &argv, int &parsept); +static const char *CIsNum (const char *str); +static FStringProd *NewStringProd (const char *str); +static FStringProd *NewStringProd (size_t len); +static FDoubleProd *NewDoubleProd (double val); +static FStringProd *DoubleToString (FProduction *prod); +static FDoubleProd *StringToDouble (FProduction *prod); +void MaybeStringCoerce (FProduction *&prod1, FProduction *&prod2); +void MustStringCoerce (FProduction *&prod1, FProduction *&prod2); +void DoubleCoerce (FProduction *&prod1, FProduction *&prod2); + +FProduction *ProdAddDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdAddStr (FStringProd *prod1, FStringProd *prod2); +FProduction *ProdSubDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdMulDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdDivDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdModDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdPowDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdLTDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdLTEDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdGTDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdGTEDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdEqDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdNeqDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdXorDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdAndDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdOrDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdLAndDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdLOrDbl (FDoubleProd *prod1, FDoubleProd *prod2); +FProduction *ProdLTStr (FStringProd *prod1, FStringProd *prod2); +FProduction *ProdLTEStr (FStringProd *prod1, FStringProd *prod2); +FProduction *ProdGTStr (FStringProd *prod1, FStringProd *prod2); +FProduction *ProdGTEStr (FStringProd *prod1, FStringProd *prod2); +FProduction *ProdEqStr (FStringProd *prod1, FStringProd *prod2); +FProduction *ProdNeqStr (FStringProd *prod1, FStringProd *prod2); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static FProducer Producers[] = +{ + { "+", ProdAddDbl, ProdAddStr }, + { "-", ProdSubDbl, NULL }, + { "*", ProdMulDbl, NULL }, + { "/", ProdDivDbl, NULL }, + { "%", ProdModDbl, NULL }, + { "^", ProdPowDbl, NULL }, + { "<", ProdLTDbl, ProdLTStr }, + { "<=", ProdLTEDbl, ProdLTEStr }, + { ">", ProdGTDbl, ProdGTStr }, + { ">=", ProdGTEDbl, ProdGTEStr }, + { "=", ProdEqDbl, ProdEqStr }, + { "==", ProdEqDbl, ProdEqStr }, + { "!=", ProdNeqDbl, ProdNeqStr }, + { "<>", ProdNeqDbl, ProdNeqStr }, + { "xor", ProdXorDbl, NULL }, + { "&", ProdAndDbl, NULL }, + { "|", ProdOrDbl, NULL }, + { "&&", ProdLAndDbl, NULL }, + { "||", ProdLOrDbl, NULL } +}; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// ParseExpression +// +// Builds a production from an expression. The supported syntax is LISP-like +// but without parentheses. +// +//========================================================================== + +static FProduction *ParseExpression (FCommandLine &argv, int &parsept) +{ + if (parsept >= argv.argc()) + return NULL; + + const char *token = argv[parsept++]; + FProduction *prod1 = NULL, *prod2 = NULL, *prod3 = NULL; + + if (IsFloat (token)) + { + return NewDoubleProd (atof(token)); + } + else if (stricmp (token, "true") == 0) + { + return NewDoubleProd (1.0); + } + else if (stricmp (token, "false") == 0) + { + return NewDoubleProd (0.0); + } + else + { + for (size_t i = 0; i < countof(Producers); ++i) + { + if (strcmp (Producers[i].Token, token) == 0) + { + prod1 = ParseExpression (argv, parsept); + prod2 = ParseExpression (argv, parsept); + if (prod1 == NULL || prod2 == NULL) + { + goto missing; + } + if (Producers[i].StringProducer == NULL) + { + DoubleCoerce (prod1, prod2); + } + else if (Producers[i].DoubleProducer == NULL) + { + MustStringCoerce (prod1, prod2); + } + else + { + MaybeStringCoerce (prod1, prod2); + } + if (prod1->Type == PROD_String) + { + prod3 = Producers[i].StringProducer ((FStringProd *)prod1, (FStringProd *)prod2); + } + else + { + prod3 = Producers[i].DoubleProducer ((FDoubleProd *)prod1, (FDoubleProd *)prod2); + } + goto done; + } + } + if (strcmp ("!", token) == 0) + { + prod1 = ParseExpression (argv, parsept); + if (prod1 == NULL) + { + goto missing; + } + if (prod1->Type == PROD_String) + { + prod1 = StringToDouble (prod1); + } + prod3 = NewDoubleProd (!static_cast(prod1)->Value); + goto done; + } + return NewStringProd (token); + } + +missing: + Printf ("Missing argument to %s\n", token); + +done: + if (prod2 != NULL) M_Free (prod2); + if (prod1 != NULL) M_Free (prod1); + return prod3; +} + +//========================================================================== +// +// IsFloat +// +//========================================================================== + +bool IsFloat (const char *str) +{ + const char *pt; + + if (*str == '+' || *str == '-') + str++; + + if (*str == '.') + { + pt = str; + } + else + { + pt = CIsNum (str); + if (pt == NULL) + return false; + } + if (*pt == '.') + { + pt = CIsNum (pt+1); + if (pt == NULL) + return false; + } + if (*pt == 'e' || *pt == 'E') + { + pt++; + if (*pt == '+' || *pt == '-') + pt++; + pt = CIsNum (pt); + } + return pt != NULL && *pt == 0; +} + +//========================================================================== +// +// IsNum +// +//========================================================================== + +static const char *CIsNum (const char *str) +{ + const char *start = str; + + while (*str) + { + if (*str >= '0' && *str <= '9') + str++; + else + break; + } + + return (str > start) ? str : NULL; +} + +//========================================================================== +// +// NewStringProd (from a string) +// +//========================================================================== + +static FStringProd *NewStringProd (const char *str) +{ + FStringProd *prod = (FStringProd *)M_Malloc (sizeof(FStringProd)+strlen(str)); + prod->Type = PROD_String; + strcpy (prod->Value, str); + return prod; +} + +//========================================================================== +// +// NewStringProd (from a length) +// +//========================================================================== + +static FStringProd *NewStringProd (size_t len) +{ + FStringProd *prod = (FStringProd *)M_Malloc (sizeof(FStringProd)+len); + prod->Type = PROD_String; + prod->Value[0] = 0; + return prod; +} + +//========================================================================== +// +// NewDoubleProd +// +//========================================================================== + +static FDoubleProd *NewDoubleProd (double val) +{ + FDoubleProd *prod = (FDoubleProd *)M_Malloc (sizeof(FDoubleProd)); + prod->Type = PROD_Double; + prod->Value = val; + return prod; +} + +//========================================================================== +// +// DoubleToString +// +//========================================================================== + +static FStringProd *DoubleToString (FProduction *prod) +{ + char buf[128]; + FStringProd *newprod; + + mysnprintf (buf, countof(buf), "%g", static_cast(prod)->Value); + newprod = NewStringProd (buf); + M_Free (prod); + return newprod; +} + +//========================================================================== +// +// StringToDouble +// +//========================================================================== + +static FDoubleProd *StringToDouble (FProduction *prod) +{ + FDoubleProd *newprod; + + newprod = NewDoubleProd (atof (static_cast(prod)->Value)); + M_Free (prod); + return newprod; +} + +//========================================================================== +// +// MaybeStringCoerce +// +// If one of the parameters is a string, convert the other to a string. +// +//========================================================================== + +void MaybeStringCoerce (FProduction *&prod1, FProduction *&prod2) +{ + if (prod1->Type == PROD_String) + { + if (prod2->Type == PROD_Double) + { + prod2 = DoubleToString (prod2); + } + } + else if (prod2->Type == PROD_String) + { + prod1 = DoubleToString (prod1); + } +} + +//========================================================================== +// +// MustStringCoerce +// +// Ensures that both parameters are strings +// +//========================================================================== + +void MustStringCoerce (FProduction *&prod1, FProduction *&prod2) +{ + if (prod1->Type == PROD_Double) + { + prod1 = DoubleToString (prod1); + } + if (prod2->Type == PROD_Double) + { + prod2 = DoubleToString (prod2); + } +} + +//========================================================================== +// +// DoubleCoerce +// +// Ensures that both parameters are doubles +// +//========================================================================== + +void DoubleCoerce (FProduction *&prod1, FProduction *&prod2) +{ + if (prod1->Type == PROD_String) + { + prod1 = StringToDouble (prod1); + } + if (prod2->Type == PROD_String) + { + prod2 = StringToDouble (prod2); + } +} + +//========================================================================== +// +// ProdAddDbl +// +//========================================================================== + +FProduction *ProdAddDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (prod1->Value + prod2->Value); +} + +//========================================================================== +// +// ProdAddStr +// +//========================================================================== + +FProduction *ProdAddStr (FStringProd *prod1, FStringProd *prod2) +{ + size_t len = strlen (prod1->Value) + strlen (prod2->Value) + 1; + FStringProd *prod = NewStringProd (len); + strcpy (prod->Value, prod1->Value); + strcat (prod->Value, prod2->Value); + return prod; +} + +//========================================================================== +// +// ProdSubDbl +// +//========================================================================== + +FProduction *ProdSubDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (prod1->Value - prod2->Value); +} + +//========================================================================== +// +// ProdMulDbl +// +//========================================================================== + +FProduction *ProdMulDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (prod1->Value * prod2->Value); +} + +//========================================================================== +// +// ProdDivDbl +// +//========================================================================== + +FProduction *ProdDivDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (prod1->Value / prod2->Value); +} + +//========================================================================== +// +// ProdModDbl +// +//========================================================================== + +FProduction *ProdModDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (fmod (prod1->Value, prod2->Value)); +} + +//========================================================================== +// +// ProdPowDbl +// +//========================================================================== + +FProduction *ProdPowDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (pow (prod1->Value, prod2->Value)); +} + +//========================================================================== +// +// ProdLTDbl +// +//========================================================================== + +FProduction *ProdLTDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (prod1->Value < prod2->Value); +} + +//========================================================================== +// +// ProdLTEDbl +// +//========================================================================== + +FProduction *ProdLTEDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (prod1->Value <= prod2->Value); +} + +//========================================================================== +// +// ProdGTDbl +// +//========================================================================== + +FProduction *ProdGTDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (prod1->Value > prod2->Value); +} + +//========================================================================== +// +// ProdGTEDbl +// +//========================================================================== + +FProduction *ProdGTEDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (prod1->Value >= prod2->Value); +} + +//========================================================================== +// +// ProdEqDbl +// +//========================================================================== + +FProduction *ProdEqDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (prod1->Value == prod2->Value); +} + +//========================================================================== +// +// ProdNeqDbl +// +//========================================================================== + +FProduction *ProdNeqDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd (prod1->Value != prod2->Value); +} + +//========================================================================== +// +// ProdLTStr +// +//========================================================================== + +FProduction *ProdLTStr (FStringProd *prod1, FStringProd *prod2) +{ + return NewDoubleProd (stricmp (prod1->Value, prod2->Value) < 0); +} + +//========================================================================== +// +// ProdLTEStr +// +//========================================================================== + +FProduction *ProdLTEStr (FStringProd *prod1, FStringProd *prod2) +{ + return NewDoubleProd (stricmp (prod1->Value, prod2->Value) <= 0); +} + +//========================================================================== +// +// ProdGTStr +// +//========================================================================== + +FProduction *ProdGTStr (FStringProd *prod1, FStringProd *prod2) +{ + return NewDoubleProd (stricmp (prod1->Value, prod2->Value) > 0); +} + +//========================================================================== +// +// ProdGTEStr +// +//========================================================================== + +FProduction *ProdGTEStr (FStringProd *prod1, FStringProd *prod2) +{ + return NewDoubleProd (stricmp (prod1->Value, prod2->Value) >= 0); +} + +//========================================================================== +// +// ProdEqStr +// +//========================================================================== + +FProduction *ProdEqStr (FStringProd *prod1, FStringProd *prod2) +{ + return NewDoubleProd (stricmp (prod1->Value, prod2->Value) == 0); +} + +//========================================================================== +// +// ProdNeqStr +// +//========================================================================== + +FProduction *ProdNeqStr (FStringProd *prod1, FStringProd *prod2) +{ + return NewDoubleProd (stricmp (prod1->Value, prod2->Value) != 0); +} + +//========================================================================== +// +// ProdXorDbl +// +//========================================================================== + +FProduction *ProdXorDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd ((double)((int64_t)prod1->Value ^ (int64_t)prod2->Value)); +} + +//========================================================================== +// +// ProdAndDbl +// +//========================================================================== + +FProduction *ProdAndDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd ((double)((int64_t)prod1->Value & (int64_t)prod2->Value)); +} + +//========================================================================== +// +// ProdOrDbl +// +//========================================================================== + +FProduction *ProdOrDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd ((double)((int64_t)prod1->Value | (int64_t)prod2->Value)); +} + +//========================================================================== +// +// ProdLAndDbl +// +//========================================================================== + +FProduction *ProdLAndDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd ((double)((int64_t)prod1->Value && (int64_t)prod2->Value)); +} + +//========================================================================== +// +// ProdLOrDbl +// +//========================================================================== + +FProduction *ProdLOrDbl (FDoubleProd *prod1, FDoubleProd *prod2) +{ + return NewDoubleProd ((double)((int64_t)prod1->Value || (int64_t)prod2->Value)); +} + + +//========================================================================== +// +// +// +//========================================================================== + +//========================================================================== +// +// CCMD test +// +// If is non-zero, execute . +// If is zero, execute [false cmd] if specified. +// +//========================================================================== + +CCMD (test) +{ + int parsept = 1; + FProduction *prod = ParseExpression (argv, parsept); + + if (prod == NULL || parsept >= argv.argc()) + { + Printf ("Usage: test [false cmd]\n"); + } + else + { + if (prod->Type == PROD_String) + { + prod = StringToDouble (prod); + } + + if (static_cast(prod)->Value != 0.0) + { + AddCommandString (argv[parsept]); + } + else if (++parsept < argv.argc()) + { + AddCommandString (argv[parsept]); + } + } + if (prod != NULL) + { + M_Free (prod); + } +} + +//========================================================================== +// +// CCMD eval +// +// Evaluates an expression and either prints it to the console or stores +// it in an existing cvar. +// +//========================================================================== + +CCMD (eval) +{ + if (argv.argc() >= 2) + { + int parsept = 1; + FProduction *prod = ParseExpression (argv, parsept); + + if (prod != NULL) + { + if (parsept < argv.argc()) + { + FBaseCVar *var = FindCVar (argv[parsept], NULL); + if (var == NULL) + { + Printf ("Unknown variable %s\n", argv[parsept]); + } + else + { + UCVarValue val; + + if (prod->Type == PROD_Double) + { + val.Float = (float)static_cast(prod)->Value; + var->SetGenericRep (val, CVAR_Float); + } + else + { + val.String = static_cast(prod)->Value; + var->SetGenericRep (val, CVAR_String); + } + } + } + else + { + if (prod->Type == PROD_Double) + { + Printf ("%g\n", static_cast(prod)->Value); + } + else + { + Printf ("%s\n", static_cast(prod)->Value); + } + } + M_Free (prod); + return; + } + } + + Printf ("Usage: eval [variable]\n"); +} diff --git a/source/core/console/keydef.h b/source/common/console/keydef.h similarity index 100% rename from source/core/console/keydef.h rename to source/common/console/keydef.h diff --git a/source/common/engine/printf.h b/source/common/engine/printf.h index ef107ae64..70439f7a1 100644 --- a/source/common/engine/printf.h +++ b/source/common/engine/printf.h @@ -11,6 +11,9 @@ extern "C" int mysnprintf(char* buffer, size_t count, const char* format, ...) ATTRIBUTE((format(printf, 3, 4))); extern "C" int myvsnprintf(char* buffer, size_t count, const char* format, va_list argptr) ATTRIBUTE((format(printf, 3, 0))); +#define TEXTCOLOR_ESCAPE '\034' +#define TEXTCOLOR_ESCAPESTR "\034" + #define TEXTCOLOR_BRICK "\034A" #define TEXTCOLOR_TAN "\034B" #define TEXTCOLOR_GRAY "\034C" diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index a3af49e12..76d84f95e 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -59,6 +59,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "engineerrors.h" #include "mmulti.h" #include "gamestate.h" +#include "gstrings.h" + +CUSTOM_CVAR(String, language, "auto", CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG) +{ + GStrings.UpdateLanguage(self); +} // The last remains of sdlayer.cpp double g_beforeSwapTime; @@ -106,6 +112,25 @@ CVAR(Bool, disableautoload, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALC extern int hud_size_max; +CUSTOM_CVAR(Int, cl_gender, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0 || self > 3) self = 0; +} + +int StrTable_GetGender() +{ + return cl_gender; +} + +bool validFilter(const char* str); + +static StringtableCallbacks stblcb = +{ + validFilter, + StrTable_GetGender +}; + + //========================================================================== // @@ -655,7 +680,7 @@ int RunGame() G_ReadConfig(currentGame); V_InitFontColors(); - GStrings.LoadStrings(); + GStrings.LoadStrings(language); I_Init(); V_InitScreenSize(); @@ -909,49 +934,9 @@ CCMD (togglemsg) } } -CCMD(quit) -{ - throw CExitEvent(0); -} - -CCMD(exit) -{ - throw CExitEvent(0); -} - -extern FILE* Logfile; -void execLogfile(const char* fn, bool append) -{ - if ((Logfile = fopen(fn, append ? "a" : "w"))) - { - const char* timestr = myasctime(); - Printf("Log started: %s\n", timestr); - } - else - { - Printf("Could not start log\n"); - } -} - -CCMD(logfile) -{ - - if (Logfile) - { - const char* timestr = myasctime(); - Printf("Log stopped: %s\n", timestr); - fclose(Logfile); - Logfile = NULL; - } - - if (argv.argc() >= 2) - { - execLogfile(argv[1], argv.argc() >= 3 ? !!argv[2] : false); - } -} - // Just a placeholder for now. bool CheckCheatmode(bool printmsg) { return false; } + diff --git a/source/core/rendering/r_videoscale.cpp b/source/core/rendering/r_videoscale.cpp index 812d4d2c7..d4daa51c5 100644 --- a/source/core/rendering/r_videoscale.cpp +++ b/source/core/rendering/r_videoscale.cpp @@ -38,7 +38,7 @@ #include "r_videoscale.h" #include "cmdlib.h" -#include "console/c_console.h" +#include "c_console.h" #include "menu/menu.h" #define NUMSCALEMODES countof(vScaleTable) diff --git a/source/core/utility/gstrings.h b/source/core/utility/gstrings.h deleted file mode 100644 index b9b8fff62..000000000 --- a/source/core/utility/gstrings.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -** gstrings.h -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. 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. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 __GSTRINGS_H__ -#define __GSTRINGS_H__ - -#ifdef _MSC_VER -#pragma once -#endif - -#include "stringtable.h" - -extern FStringTable GStrings; - -extern const char *endmsg[]; - - -#endif //__GSTRINGS_H__ diff --git a/source/core/utility/stringtable.cpp b/source/core/utility/stringtable.cpp deleted file mode 100644 index 692f9ad2e..000000000 --- a/source/core/utility/stringtable.cpp +++ /dev/null @@ -1,678 +0,0 @@ -/* -** stringtable.cpp -** Implements the FStringTable class -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. 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. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 "stringtable.h" -#include "cmdlib.h" -#include "filesystem.h" -#include "sc_man.h" -#include "c_dispatch.h" -#include "v_text.h" -#include "c_cvars.h" -#include "printf.h" - -bool validFilter(const char *str); -EXTERN_CVAR(String, language) -CUSTOM_CVAR(Int, cl_gender, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - if (self < 0 || self > 3) self = 0; -} - -//========================================================================== -// -// -// -//========================================================================== - -void FStringTable::LoadStrings () -{ - int lastlump, lump; - - lastlump = 0; - while ((lump = fileSystem.FindLumpFullName("engine/lmacros", &lastlump, true)) != -1) - { - readMacros(lump); - } - - lastlump = 0; - while ((lump = fileSystem.FindLumpFullName("engine/language", &lastlump, true)) != -1) - { - auto lumpdata = fileSystem.GetFileData(lump); - - if (!ParseLanguageCSV(lump, lumpdata)) - LoadLanguage (lump, lumpdata); - } - UpdateLanguage(); - allMacros.Clear(); -} - - -//========================================================================== -// -// This was tailored to parse CSV as exported by Google Docs. -// -//========================================================================== - - -TArray> FStringTable::parseCSV(const TArray &buffer) -{ - const size_t bufLength = buffer.Size(); - TArray> data; - TArray row; - TArray cell; - bool quoted = false; - - /* - auto myisspace = [](int ch) { return ch == '\t' || ch == '\r' || ch == '\n' || ch == ' '; }; - while (*vcopy && myisspace((unsigned char)*vcopy)) vcopy++; // skip over leaading whitespace; - auto vend = vcopy + strlen(vcopy); - while (vend > vcopy && myisspace((unsigned char)vend[-1])) *--vend = 0; // skip over trailing whitespace - */ - - for (size_t i = 0; i < bufLength; ++i) - { - if (buffer[i] == '"') - { - // Double quotes inside a quoted string count as an escaped quotation mark. - if (quoted && i < bufLength - 1 && buffer[i + 1] == '"') - { - cell.Push('"'); - i++; - } - else if (cell.Size() == 0 || quoted) - { - quoted = !quoted; - } - } - else if (buffer[i] == ',') - { - if (!quoted) - { - cell.Push(0); - ProcessEscapes(cell.Data()); - row.Push(cell.Data()); - cell.Clear(); - } - else - { - cell.Push(buffer[i]); - } - } - else if (buffer[i] == '\r') - { - // Ignore all CR's. - } - else if (buffer[i] == '\n' && !quoted) - { - cell.Push(0); - ProcessEscapes(cell.Data()); - row.Push(cell.Data()); - data.Push(std::move(row)); - cell.Clear(); - } - else - { - cell.Push(buffer[i]); - } - } - - // Handle last line without linebreak - if (cell.Size() > 0 || row.Size() > 0) - { - cell.Push(0); - ProcessEscapes(cell.Data()); - row.Push(cell.Data()); - data.Push(std::move(row)); - } - return data; -} - -//========================================================================== -// -// -// -//========================================================================== - -bool FStringTable::readMacros(int lumpnum) -{ - auto lumpdata = fileSystem.GetFileData(lumpnum); - auto data = parseCSV(lumpdata); - - for (unsigned i = 1; i < data.Size(); i++) - { - auto macroname = data[i][0]; - auto language = data[i][1]; - if (macroname.IsEmpty() || language.IsEmpty()) continue; - FStringf combined_name("%s/%s", language.GetChars(), macroname.GetChars()); - FName name = combined_name.GetChars(); - - StringMacro macro; - - for (int k = 0; k < 4; k++) - { - macro.Replacements[k] = data[i][k+2]; - } - allMacros.Insert(name, macro); - } - return true; -} - -//========================================================================== -// -// -// -//========================================================================== - -bool FStringTable::ParseLanguageCSV(int lumpnum, const TArray &buffer) -{ - if (memcmp(buffer.Data(), "default,", 8)) return false; - auto data = parseCSV(buffer); - - int labelcol = -1; - int filtercol = -1; - TArray> langrows; - bool hasDefaultEntry = false; - - if (data.Size() > 0) - { - for (unsigned column = 0; column < data[0].Size(); column++) - { - auto &entry = data[0][column]; - if (entry.CompareNoCase("filter") == 0) - { - filtercol = column; - } - else if (entry.CompareNoCase("identifier") == 0) - { - labelcol = column; - } - else - { - auto languages = entry.Split(" ", FString::TOK_SKIPEMPTY); - for (auto &lang : languages) - { - if (lang.CompareNoCase("default") == 0) - { - langrows.Push(std::make_pair(column, default_table)); - hasDefaultEntry = true; - } - else if (lang.Len() < 4) - { - lang.ToLower(); - langrows.Push(std::make_pair(column, MAKE_ID(lang[0], lang[1], lang[2], 0))); - } - } - } - } - - for (unsigned i = 1; i < data.Size(); i++) - { - auto &row = data[i]; -#if 1 - if (filtercol > -1) - { - auto filterstr = row[filtercol]; - if (filterstr.IsNotEmpty()) - { - auto filter = filterstr.Split(" ", FString::TOK_SKIPEMPTY); - bool ok = false; - for (auto& entry : filter) - { - if (validFilter(entry)) - { - ok = true; - break; - } - } - if (!ok) continue; - } - } -#endif - - FName strName = row[labelcol].GetChars(); - if (hasDefaultEntry) - { - DeleteForLabel(lumpnum, strName); - } - for (auto &langentry : langrows) - { - auto str = row[langentry.first]; - if (str.Len() > 0) - { - InsertString(lumpnum, langentry.second, strName, str); - } - else - { - DeleteString(langentry.second, strName); - } - } - } - } - return true; -} - -//========================================================================== -// -// -// -//========================================================================== - -void FStringTable::LoadLanguage (int lumpnum, const TArray &buffer) -{ - bool errordone = false; - TArray activeMaps; - FScanner sc; - bool hasDefaultEntry = false; - - sc.OpenMem("LANGUAGE", (const char*)buffer.Data(), buffer.Size()); - sc.SetCMode (true); - while (sc.GetString ()) - { - if (sc.Compare ("[")) - { // Process language identifiers - activeMaps.Clear(); - sc.MustGetString (); - do - { - size_t len = sc.StringLen; - if (len != 2 && len != 3) - { - if (len == 1 && sc.String[0] == '~') - { - // deprecated and ignored - sc.ScriptMessage("Deprecated option '~' found in language list"); - sc.MustGetString (); - continue; - } - if (len == 1 && sc.String[0] == '*') - { - activeMaps.Clear(); - activeMaps.Push(global_table); - } - else if (len == 7 && stricmp (sc.String, "default") == 0) - { - activeMaps.Clear(); - activeMaps.Push(default_table); - hasDefaultEntry = true; - } - else - { - sc.ScriptError ("The language code must be 2 or 3 characters long.\n'%s' is %lu characters long.", - sc.String, len); - } - } - else - { - if (activeMaps.Size() != 1 || (activeMaps[0] != default_table && activeMaps[0] != global_table)) - activeMaps.Push(MAKE_ID(tolower(sc.String[0]), tolower(sc.String[1]), tolower(sc.String[2]), 0)); - } - sc.MustGetString (); - } while (!sc.Compare ("]")); - } - else - { // Process string definitions. - if (activeMaps.Size() == 0) - { - // LANGUAGE lump is bad. We need to check if this is an old binary - // lump and if so just skip it to allow old WADs to run which contain - // such a lump. - if (!sc.isText()) - { - if (!errordone) Printf("Skipping binary 'LANGUAGE' lump.\n"); - errordone = true; - return; - } - sc.ScriptError ("Found a string without a language specified."); - } - - bool skip = false; -#if 0 // I don't think this is needed. - if (sc.Compare("$")) - { - sc.MustGetStringName("ifgame"); - sc.MustGetStringName("("); - sc.MustGetString(); - if (sc.Compare("strifeteaser")) - { - skip |= (gameinfo.gametype != GAME_Strife) || !(gameinfo.flags & GI_SHAREWARE); - } - else - { - skip |= !sc.Compare(GameTypeName()); - } - sc.MustGetStringName(")"); - sc.MustGetString(); - - } -#endif - - FName strName (sc.String); - sc.MustGetStringName ("="); - sc.MustGetString (); - FString strText (sc.String, ProcessEscapes (sc.String)); - sc.MustGetString (); - while (!sc.Compare (";")) - { - ProcessEscapes (sc.String); - strText += sc.String; - sc.MustGetString (); - } - if (!skip) - { - if (hasDefaultEntry) - { - DeleteForLabel(lumpnum, strName); - } - // Insert the string into all relevant tables. - for (auto map : activeMaps) - { - InsertString(lumpnum, map, strName, strText); - } - } - } - } -} - -//========================================================================== -// -// -// -//========================================================================== - -void FStringTable::DeleteString(int langid, FName label) -{ - allStrings[langid].Remove(label); -} - -//========================================================================== -// -// This deletes all older entries for a given label. This gets called -// when a string in the default table gets updated. -// -//========================================================================== - -void FStringTable::DeleteForLabel(int lumpnum, FName label) -{ - decltype(allStrings)::Iterator it(allStrings); - decltype(allStrings)::Pair *pair; - auto filenum = fileSystem.GetFileContainer(lumpnum); - - while (it.NextPair(pair)) - { - auto entry = pair->Value.CheckKey(label); - if (entry && entry->filenum < filenum) - { - pair->Value.Remove(label); - } - } - -} - -//========================================================================== -// -// -// -//========================================================================== - -void FStringTable::InsertString(int lumpnum, int langid, FName label, const FString &string) -{ - const char *strlangid = (const char *)&langid; - TableElement te = { fileSystem.GetFileContainer(lumpnum), { string, string, string, string } }; - long index; - while ((index = te.strings[0].IndexOf("@[")) >= 0) - { - auto endindex = te.strings[0].IndexOf(']', index); - if (endindex == -1) - { - Printf("Bad macro in %s : %s\n", strlangid, label.GetChars()); - break; - } - FString macroname(te.strings[0].GetChars() + index + 2, endindex - index - 2); - FStringf lookupstr("%s/%s", strlangid, macroname.GetChars()); - FStringf replacee("@[%s]", macroname.GetChars()); - FName lookupname(lookupstr.GetChars(), true); - auto replace = allMacros.CheckKey(lookupname); - for (int i = 0; i < 4; i++) - { - const char *replacement = replace? replace->Replacements[i].GetChars() : ""; - te.strings[i].Substitute(replacee, replacement); - } - } - allStrings[langid].Insert(label, te); -} - -//========================================================================== -// -// -// -//========================================================================== - -void FStringTable::UpdateLanguage() -{ - size_t langlen = strlen(language); - - int LanguageID = (langlen < 2 || langlen > 3) ? - MAKE_ID('e', 'n', 'u', '\0') : - MAKE_ID(language[0], language[1], language[2], '\0'); - - currentLanguageSet.Clear(); - - auto checkone = [&](uint32_t lang_id) - { - auto list = allStrings.CheckKey(lang_id); - if (list && currentLanguageSet.FindEx([&](const auto &element) { return element.first == lang_id; }) == currentLanguageSet.Size()) - currentLanguageSet.Push(std::make_pair(lang_id, list)); - }; - - checkone(global_table); - checkone(LanguageID); - checkone(LanguageID & MAKE_ID(0xff, 0xff, 0, 0)); - checkone(default_table); -} - -//========================================================================== -// -// Replace \ escape sequences in a string with the escaped characters. -// -//========================================================================== - -size_t FStringTable::ProcessEscapes (char *iptr) -{ - char *sptr = iptr, *optr = iptr, c; - - while ((c = *iptr++) != '\0') - { - if (c == '\\') - { - c = *iptr++; - if (c == 'n') - c = '\n'; - else if (c == 'c') - c = TEXTCOLOR_ESCAPE; - else if (c == 'r') - c = '\r'; - else if (c == 't') - c = '\t'; - else if (c == '\n') - continue; - } - *optr++ = c; - } - *optr = '\0'; - return optr - sptr; -} - -//========================================================================== -// -// Checks if the given key exists in any one of the default string tables that are valid for all languages. -// To replace IWAD content this condition must be true. -// -//========================================================================== - -bool FStringTable::exists(const char *name) -{ - if (name == nullptr || *name == 0) - { - return false; - } - FName nm(name, true); - if (nm != NAME_None) - { - uint32_t defaultStrings[] = { default_table, global_table }; - - for (auto mapid : defaultStrings) - { - auto map = allStrings.CheckKey(mapid); - if (map) - { - auto item = map->CheckKey(nm); - if (item) return true; - } - } - } - return false; -} - -//========================================================================== -// -// Finds a string by name and returns its value -// -//========================================================================== - -const char *FStringTable::GetString(const char *name, uint32_t *langtable, int gender) const -{ - if (name == nullptr || *name == 0) - { - return nullptr; - } - if (gender == -1) gender = cl_gender; - if (gender < 0 || gender > 3) gender = 0; - FName nm(name, true); - if (nm != NAME_None) - { - for (auto map : currentLanguageSet) - { - auto item = map.second->CheckKey(nm); - if (item) - { - if (langtable) *langtable = map.first; - return item->strings[gender].GetChars(); - } - } - } - return nullptr; -} - -//========================================================================== -// -// Finds a string by name in a given language -// -//========================================================================== - -const char *FStringTable::GetLanguageString(const char *name, uint32_t langtable, int gender) const -{ - if (name == nullptr || *name == 0) - { - return nullptr; - } - if (gender == -1) gender = cl_gender; - if (gender < 0 || gender > 3) gender = 0; - FName nm(name, true); - if (nm != NAME_None) - { - auto map = allStrings.CheckKey(langtable); - if (map == nullptr) return nullptr; - auto item = map->CheckKey(nm); - if (item) - { - return item->strings[gender].GetChars(); - } - } - return nullptr; -} - -bool FStringTable::MatchDefaultString(const char *name, const char *content) const -{ - // This only compares the first line to avoid problems with bad linefeeds. For the few cases where this feature is needed it is sufficient. - auto c = GetLanguageString(name, FStringTable::default_table); - if (!c) return false; - - // Check a secondary key, in case the text comparison cannot be done due to needed orthographic fixes (see Harmony's exit text) - FStringf checkkey("%s_CHECK", name); - auto cc = GetLanguageString(checkkey, FStringTable::default_table); - if (cc) c = cc; - - return (c && !strnicmp(c, content, strcspn(content, "\n\r\t"))); -} - -//========================================================================== -// -// Finds a string by name and returns its value. If the string does -// not exist, returns the passed name instead. -// -//========================================================================== - -const char *FStringTable::operator() (const char *name) const -{ - const char *str = operator[] (name); - return str ? str : name; -} - - -//========================================================================== -// -// Find a string with the same exact text. Returns its name. -// This does not need to check genders, it is only used by -// Dehacked on the English table for finding stock strings. -// -//========================================================================== - -const char *StringMap::MatchString (const char *string) const -{ - StringMap::ConstIterator it(*this); - StringMap::ConstPair *pair; - - while (it.NextPair(pair)) - { - if (pair->Value.strings[0].CompareNoCase(string) == 0) - { - return pair->Key.GetChars(); - } - } - return nullptr; -} - -FStringTable GStrings; -CVAR(String, language, "en", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) diff --git a/source/core/utility/stringtable.h b/source/core/utility/stringtable.h deleted file mode 100644 index 5849d2b2c..000000000 --- a/source/core/utility/stringtable.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -** stringtable.h -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. 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. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. -**--------------------------------------------------------------------------- -** -** -** FStringTable -** -** This class manages a list of localizable strings stored in a wad file. -*/ - -#ifndef __STRINGTABLE_H__ -#define __STRINGTABLE_H__ - -#ifdef _MSC_VER -#pragma once -#endif - - -#include -#include "basics.h" -#include "zstring.h" -#include "tarray.h" -#include "name.h" - -struct TableElement -{ - int filenum; - FString strings[4]; -}; - -// This public interface is for Dehacked -class StringMap : public TMap -{ -public: - const char *MatchString(const char *string) const; -}; - - -struct StringMacro -{ - FString Replacements[4]; -}; - - -class FStringTable -{ -public: - enum : uint32_t - { - default_table = MAKE_ID('*', '*', 0, 0), - global_table = MAKE_ID('*', 0, 0, 0), - }; - - using LangMap = TMap; - using StringMacroMap = TMap; - - void LoadStrings (); - void UpdateLanguage(); - - const char *GetLanguageString(const char *name, uint32_t langtable, int gender = -1) const; - bool MatchDefaultString(const char *name, const char *content) const; - const char *GetString(const char *name, uint32_t *langtable, int gender = -1) const; - const char *operator() (const char *name) const; // Never returns NULL - const char *operator[] (const char *name) const - { - return GetString(name, nullptr); - } - bool exists(const char *name); - -private: - - StringMacroMap allMacros; - LangMap allStrings; - TArray> currentLanguageSet; - - void LoadLanguage (int lumpnum, const TArray &buffer); - TArray> parseCSV(const TArray &buffer); - bool ParseLanguageCSV(int lumpnum, const TArray &buffer); - - bool LoadLanguageFromSpreadsheet(int lumpnum, const TArray &buffer); - bool readMacros(int lumpnum); - void InsertString(int lumpnum, int langid, FName label, const FString &string); - void DeleteString(int langid, FName label); - void DeleteForLabel(int lumpnum, FName label); - - static size_t ProcessEscapes (char *str); -public: - static FString MakeMacro(const char *str) - { - if (*str == '$') return str; - return FString("$") + str; - } - - static FString MakeMacro(const char *str, size_t len) - { - if (*str == '$') return FString(str, len); - return "$" + FString(str, len); - } - - const char* localize(const char* str) - { - return *str == '$' ? operator()(str + 1) : str; - } -}; - -#endif //__STRINGTABLE_H__