mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-27 04:00:42 +00:00
- match with GZDoom's console code, move to 'common'.
This commit is contained in:
parent
57cb22f135
commit
f1040fa699
23 changed files with 1195 additions and 912 deletions
|
@ -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/.+")
|
||||
|
|
338
source/common/console/c_enginecmds.cpp
Normal file
338
source/common/console/c_enginecmds.cpp
Normal file
|
@ -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 <unistd.h>
|
||||
#else
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#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 <name>: 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 <error text>\n");
|
||||
}
|
||||
}
|
||||
|
||||
UNSAFE_CCMD (error_fatal)
|
||||
{
|
||||
if (argv.argc() > 1)
|
||||
{
|
||||
I_FatalError ("%s", argv[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf ("Usage: error_fatal <error text>\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 <dir>\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 <file> ...\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]));
|
||||
}
|
||||
|
||||
}
|
812
source/common/console/c_expr.cpp
Normal file
812
source/common/console/c_expr.cpp
Normal file
|
@ -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 <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#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<FDoubleProd *>(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<FDoubleProd *>(prod)->Value);
|
||||
newprod = NewStringProd (buf);
|
||||
M_Free (prod);
|
||||
return newprod;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// StringToDouble
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static FDoubleProd *StringToDouble (FProduction *prod)
|
||||
{
|
||||
FDoubleProd *newprod;
|
||||
|
||||
newprod = NewDoubleProd (atof (static_cast<FStringProd *>(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 <expr> is non-zero, execute <true cmd>.
|
||||
// If <expr> 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 <expr> <true cmd> [false cmd]\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prod->Type == PROD_String)
|
||||
{
|
||||
prod = StringToDouble (prod);
|
||||
}
|
||||
|
||||
if (static_cast<FDoubleProd *>(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<FDoubleProd *>(prod)->Value;
|
||||
var->SetGenericRep (val, CVAR_Float);
|
||||
}
|
||||
else
|
||||
{
|
||||
val.String = static_cast<FStringProd *>(prod)->Value;
|
||||
var->SetGenericRep (val, CVAR_String);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prod->Type == PROD_Double)
|
||||
{
|
||||
Printf ("%g\n", static_cast<FDoubleProd *>(prod)->Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf ("%s\n", static_cast<FStringProd *>(prod)->Value);
|
||||
}
|
||||
}
|
||||
M_Free (prod);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Printf ("Usage: eval <expression> [variable]\n");
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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__
|
|
@ -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 <string.h>
|
||||
|
||||
#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<TArray<FString>> FStringTable::parseCSV(const TArray<uint8_t> &buffer)
|
||||
{
|
||||
const size_t bufLength = buffer.Size();
|
||||
TArray<TArray<FString>> data;
|
||||
TArray<FString> row;
|
||||
TArray<char> 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<uint8_t> &buffer)
|
||||
{
|
||||
if (memcmp(buffer.Data(), "default,", 8)) return false;
|
||||
auto data = parseCSV(buffer);
|
||||
|
||||
int labelcol = -1;
|
||||
int filtercol = -1;
|
||||
TArray<std::pair<int, unsigned>> 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<uint8_t> &buffer)
|
||||
{
|
||||
bool errordone = false;
|
||||
TArray<uint32_t> 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)
|
|
@ -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 <stdlib.h>
|
||||
#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<FName, TableElement>
|
||||
{
|
||||
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<uint32_t, StringMap>;
|
||||
using StringMacroMap = TMap<FName, StringMacro>;
|
||||
|
||||
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<std::pair<uint32_t, StringMap*>> currentLanguageSet;
|
||||
|
||||
void LoadLanguage (int lumpnum, const TArray<uint8_t> &buffer);
|
||||
TArray<TArray<FString>> parseCSV(const TArray<uint8_t> &buffer);
|
||||
bool ParseLanguageCSV(int lumpnum, const TArray<uint8_t> &buffer);
|
||||
|
||||
bool LoadLanguageFromSpreadsheet(int lumpnum, const TArray<uint8_t> &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__
|
Loading…
Reference in a new issue