2016-03-01 15:47:10 +00:00
|
|
|
/*
|
|
|
|
** 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 <stddef.h>
|
|
|
|
|
|
|
|
#include "stringtable.h"
|
|
|
|
#include "cmdlib.h"
|
|
|
|
#include "m_swap.h"
|
|
|
|
#include "w_wad.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "sc_man.h"
|
|
|
|
#include "c_dispatch.h"
|
|
|
|
#include "v_text.h"
|
|
|
|
#include "gi.h"
|
|
|
|
|
|
|
|
|
2019-02-06 12:59:41 +00:00
|
|
|
void FStringTable::LoadStrings ()
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
int lastlump, lump;
|
|
|
|
|
|
|
|
lastlump = 0;
|
|
|
|
|
|
|
|
while ((lump = Wads.FindLump ("LANGUAGE", &lastlump)) != -1)
|
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
LoadLanguage (lump);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
2019-02-20 18:30:39 +00:00
|
|
|
SetLanguageIDs();
|
2019-02-06 12:59:41 +00:00
|
|
|
UpdateLanguage();
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
2019-02-06 12:59:41 +00:00
|
|
|
void FStringTable::LoadLanguage (int lumpnum)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
bool errordone = false;
|
|
|
|
TArray<uint32_t> activeMaps;
|
2016-03-01 15:47:10 +00:00
|
|
|
FScanner sc(lumpnum);
|
|
|
|
sc.SetCMode (true);
|
|
|
|
while (sc.GetString ())
|
|
|
|
{
|
|
|
|
if (sc.Compare ("["))
|
|
|
|
{ // Process language identifiers
|
2019-02-06 12:59:41 +00:00
|
|
|
activeMaps.Clear();
|
2016-03-01 15:47:10 +00:00
|
|
|
sc.MustGetString ();
|
|
|
|
do
|
|
|
|
{
|
|
|
|
size_t len = sc.StringLen;
|
|
|
|
if (len != 2 && len != 3)
|
|
|
|
{
|
|
|
|
if (len == 1 && sc.String[0] == '~')
|
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
// deprecated and ignored
|
|
|
|
sc.ScriptMessage("Deprecated option '~' found in language list");
|
2016-03-01 15:47:10 +00:00
|
|
|
sc.MustGetString ();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (len == 1 && sc.String[0] == '*')
|
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
activeMaps.Push(MAKE_ID('*', 0, 0, 0));
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
else if (len == 7 && stricmp (sc.String, "default") == 0)
|
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
activeMaps.Push(MAKE_ID('*', '*', 0, 0));
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sc.ScriptError ("The language code must be 2 or 3 characters long.\n'%s' is %lu characters long.",
|
|
|
|
sc.String, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
activeMaps.Push(MAKE_ID(tolower(sc.String[0]), tolower(sc.String[1]), tolower(sc.String[2]), 0));
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
sc.MustGetString ();
|
|
|
|
} while (!sc.Compare ("]"));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Process string definitions.
|
2019-02-06 12:59:41 +00:00
|
|
|
if (activeMaps.Size() == 0)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
// 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.");
|
|
|
|
}
|
|
|
|
|
2019-02-06 12:59:41 +00:00
|
|
|
bool skip = false;
|
2016-03-01 15:47:10 +00:00
|
|
|
if (sc.Compare("$"))
|
|
|
|
{
|
|
|
|
sc.MustGetStringName("ifgame");
|
|
|
|
sc.MustGetStringName("(");
|
|
|
|
sc.MustGetString();
|
2019-02-09 11:52:50 +00:00
|
|
|
if (sc.Compare("strifeteaser"))
|
|
|
|
{
|
|
|
|
skip |= (gameinfo.gametype != GAME_Strife) || !(gameinfo.flags & GI_SHAREWARE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
skip |= !sc.Compare(GameTypeName());
|
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
sc.MustGetStringName(")");
|
|
|
|
sc.MustGetString();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-02-06 12:59:41 +00:00
|
|
|
FName strName (sc.String);
|
2016-03-01 15:47:10 +00:00
|
|
|
sc.MustGetStringName ("=");
|
|
|
|
sc.MustGetString ();
|
|
|
|
FString strText (sc.String, ProcessEscapes (sc.String));
|
|
|
|
sc.MustGetString ();
|
|
|
|
while (!sc.Compare (";"))
|
|
|
|
{
|
|
|
|
ProcessEscapes (sc.String);
|
|
|
|
strText += sc.String;
|
|
|
|
sc.MustGetString ();
|
|
|
|
}
|
2019-02-09 11:52:50 +00:00
|
|
|
if (!skip)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2019-02-09 11:52:50 +00:00
|
|
|
// Insert the string into all relevant tables.
|
|
|
|
for (auto map : activeMaps)
|
|
|
|
{
|
|
|
|
allStrings[map].Insert(strName, strText);
|
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-06 12:59:41 +00:00
|
|
|
void FStringTable::UpdateLanguage()
|
|
|
|
{
|
|
|
|
currentLanguageSet.Clear();
|
|
|
|
|
|
|
|
auto checkone = [&](uint32_t lang_id)
|
|
|
|
{
|
|
|
|
auto list = allStrings.CheckKey(lang_id);
|
|
|
|
if (list && currentLanguageSet.Find(list) == currentLanguageSet.Size())
|
|
|
|
currentLanguageSet.Push(list);
|
|
|
|
};
|
|
|
|
|
|
|
|
checkone(MAKE_ID('*', '*', '*', 0));
|
|
|
|
checkone(MAKE_ID('*', 0, 0, 0));
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
|
{
|
|
|
|
checkone(LanguageIDs[i]);
|
|
|
|
checkone(LanguageIDs[i] & MAKE_ID(0xff, 0xff, 0, 0));
|
|
|
|
}
|
|
|
|
checkone(MAKE_ID('*', '*', 0, 0));
|
|
|
|
}
|
|
|
|
|
2016-03-01 15:47:10 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2019-02-10 23:46:13 +00:00
|
|
|
bool FStringTable::exists(const char *name)
|
|
|
|
{
|
|
|
|
// 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.
|
|
|
|
if (name == nullptr || *name == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
FName nm(name, true);
|
|
|
|
if (nm != NAME_None)
|
|
|
|
{
|
|
|
|
uint32_t defaultStrings[] = { MAKE_ID('*', '*', '*', 0), MAKE_ID('*', 0, 0, 0), MAKE_ID('*', '*', 0, 0) };
|
|
|
|
|
|
|
|
for (auto mapid : defaultStrings)
|
|
|
|
{
|
|
|
|
auto map = allStrings.CheckKey(mapid);
|
|
|
|
if (map)
|
|
|
|
{
|
|
|
|
auto item = map->CheckKey(nm);
|
|
|
|
if (item) return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-03-01 15:47:10 +00:00
|
|
|
// Finds a string by name and returns its value
|
|
|
|
const char *FStringTable::operator[] (const char *name) const
|
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
if (name == nullptr || *name == 0)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
return nullptr;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
2019-02-06 12:59:41 +00:00
|
|
|
FName nm(name, true);
|
|
|
|
if (nm != NAME_None)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
for (auto map : currentLanguageSet)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
auto item = map->CheckKey(nm);
|
|
|
|
if (item) return item->GetChars();
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-06 12:59:41 +00:00
|
|
|
return nullptr;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2019-02-06 12:59:41 +00:00
|
|
|
const char *StringMap::MatchString (const char *string) const
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
StringMap::ConstIterator it(*this);
|
|
|
|
StringMap::ConstPair *pair;
|
|
|
|
|
|
|
|
while (it.NextPair(pair))
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
if (pair->Value.Compare(string) == 0)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2019-02-06 12:59:41 +00:00
|
|
|
return pair->Key.GetChars();
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-06 12:59:41 +00:00
|
|
|
return nullptr;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|