- implemented string table macros.

This commit is contained in:
Christoph Oelckers 2019-03-02 12:06:24 +01:00
commit ce1ecd0d4b
6 changed files with 224 additions and 63 deletions

View file

@ -42,9 +42,16 @@
#include "c_dispatch.h"
#include "v_text.h"
#include "gi.h"
#include "d_player.h"
#include "xlsxread/xlsxio_read.h"
//==========================================================================
//
//
//
//==========================================================================
void FStringTable::LoadStrings ()
{
int lastlump, lump;
@ -58,8 +65,14 @@ void FStringTable::LoadStrings ()
}
SetLanguageIDs();
UpdateLanguage();
allMacros.Clear();
}
//==========================================================================
//
//
//
//==========================================================================
bool FStringTable::LoadLanguageFromSpreadsheet(int lumpnum)
{
@ -69,14 +82,60 @@ bool FStringTable::LoadLanguageFromSpreadsheet(int lumpnum)
{
return false;
}
if (!readSheetIntoTable(xlsxio, "strings")) return false;
// readMacros(xlsxio, "macros");
readMacros(xlsxio, "macros");
readSheetIntoTable(xlsxio, "strings");
xlsxioread_close(xlsxio);
return true;
}
//==========================================================================
//
//
//
//==========================================================================
bool FStringTable::readMacros(xlsxioreader reader, const char *sheetname)
{
xlsxioreadersheet sheet = xlsxioread_sheet_open(reader, sheetname, XLSXIOREAD_SKIP_NONE);
if (sheet == nullptr) return false;
while (xlsxioread_sheet_next_row(sheet))
{
auto macroname = xlsxioread_sheet_next_cell(sheet);
auto language = xlsxioread_sheet_next_cell(sheet);
if (!macroname || !language) continue;
FStringf combined_name("%s/%s", language, macroname);
free(language);
free(macroname);
FName name = combined_name;
StringMacro macro;
char *value;
for (int i = 0; i < 4; i++)
{
value = xlsxioread_sheet_next_cell(sheet);
macro.Replacements[i] = value;
free(value);
}
// This is needed because the reader code would choke on incompletely read rows.
while ((value = xlsxioread_sheet_next_cell(sheet)) != nullptr)
{
free(value);
}
allMacros.Insert(name, macro);
}
xlsxioread_sheet_close(sheet);
return true;
}
//==========================================================================
//
//
//
//==========================================================================
bool FStringTable::readSheetIntoTable(xlsxioreader reader, const char *sheetname)
{
@ -94,7 +153,7 @@ bool FStringTable::readSheetIntoTable(xlsxioreader reader, const char *sheetname
while ((value = xlsxioread_sheet_next_cell(sheet)) != nullptr)
{
auto vcopy = value;
if (table.Size() <= row) table.Reserve(1);
if (table.Size() <= (unsigned)row) table.Reserve(1);
while (*vcopy && iswspace((unsigned char)*vcopy)) vcopy++; // skip over leaading whitespace;
auto vend = vcopy + strlen(vcopy);
while (vend > vcopy && iswspace((unsigned char)vend[-1])) *--vend = 0; // skip over trailing whitespace
@ -105,67 +164,74 @@ bool FStringTable::readSheetIntoTable(xlsxioreader reader, const char *sheetname
}
row++;
}
xlsxioread_sheet_close(sheet);
int labelcol = -1;
int filtercol = -1;
TArray<std::pair<int, unsigned>> langrows;
for (unsigned column = 0; column < table[0].Size(); column++)
if (table.Size() > 0)
{
auto &entry = table[0][column];
if (entry.CompareNoCase("filter") == 0)
for (unsigned column = 0; column < table[0].Size(); column++)
{
filtercol = column;
}
else if (entry.CompareNoCase("identifier") == 0)
{
labelcol = column;;
}
else
{
auto languages = entry.Split(" ", FString::TOK_SKIPEMPTY);
for (auto &lang : languages)
auto &entry = table[0][column];
if (entry.CompareNoCase("filter") == 0)
{
if (lang.CompareNoCase("default") == 0)
filtercol = column;
}
else if (entry.CompareNoCase("identifier") == 0)
{
labelcol = column;;
}
else
{
auto languages = entry.Split(" ", FString::TOK_SKIPEMPTY);
for (auto &lang : languages)
{
langrows.Push(std::make_pair(column, default_table));
if (lang.CompareNoCase("default") == 0)
{
langrows.Push(std::make_pair(column, default_table));
}
else if (lang.Len() < 4)
{
lang.ToLower();
langrows.Push(std::make_pair(column, MAKE_ID(lang[0], lang[1], lang[2], 0)));
}
}
else if (lang.Len() < 4)
}
}
for (unsigned i = 1; i < table.Size(); i++)
{
auto &row = table[i];
if (filtercol > -1)
{
auto filterstr = row[filtercol];
auto filter = filterstr.Split(" ", FString::TOK_SKIPEMPTY);
if (filter.Size() > 0 && filter.FindEx([](const auto &str) { return str.CompareNoCase(GameNames[gameinfo.gametype]) == 0; }) == filter.Size())
continue;
}
FName strName = row[labelcol];
for (auto &langentry : langrows)
{
auto str = row[langentry.first];
if (str.Len() > 0)
{
lang.ToLower();
langrows.Push(std::make_pair(column, MAKE_ID(lang[0], lang[1], lang[2], 0)));
InsertString(langentry.second, strName, str);
}
}
}
}
for (unsigned i = 1; i < table.Size(); i++)
{
auto &row = table[i];
if (filtercol > -1)
{
auto filterstr = row[filtercol];
auto filter = filterstr.Split(" ", FString::TOK_SKIPEMPTY);
if (filter.Size() > 0 && filter.FindEx([](const auto &str) { return str.CompareNoCase(GameNames[gameinfo.gametype]) == 0; }) == filter.Size())
continue;
}
FName strName = row[labelcol];
for (auto &langentry : langrows)
{
auto str = row[langentry.first];
if (str.Len() > 0)
{
allStrings[langentry.second].Insert(strName, str);
str.Substitute("\n", "|");
Printf(PRINT_LOG, "Setting %s for %s to %.40s\n\n", strName.GetChars(), &langentry.second, str.GetChars());
}
}
}
xlsxioread_sheet_close(sheet);
return true;
}
//==========================================================================
//
//
//
//==========================================================================
void FStringTable::LoadLanguage (int lumpnum)
{
bool errordone = false;
@ -262,13 +328,52 @@ void FStringTable::LoadLanguage (int lumpnum)
// Insert the string into all relevant tables.
for (auto map : activeMaps)
{
allStrings[map].Insert(strName, strText);
InsertString(map, strName, strText);
}
}
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void FStringTable::InsertString(int langid, FName label, const FString &string)
{
const char *strlangid = (const char *)&langid;
TableElement te = { 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(string.GetChars() + index + 2, endindex - index - 2);
FStringf lookupstr("%s/%s", strlangid, macroname.GetChars());
FStringf replacee("@[%s]", macroname.GetChars());
FName lookupname(lookupstr, true);
auto replace = allMacros.CheckKey(lookupname);
for (int i = 0; i < 4; i++)
{
const char *replacement = replace && replace->Replacements[i] ? replace->Replacements[i] : "";
te.strings[i].Substitute(replacee, replacement);
}
}
allStrings[langid].Insert(label, te);
}
//==========================================================================
//
//
//
//==========================================================================
void FStringTable::UpdateLanguage()
{
currentLanguageSet.Clear();
@ -290,7 +395,12 @@ void FStringTable::UpdateLanguage()
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;
@ -317,10 +427,15 @@ size_t FStringTable::ProcessEscapes (char *iptr)
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)
{
// 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;
@ -343,13 +458,20 @@ bool FStringTable::exists(const char *name)
return false;
}
//==========================================================================
//
// Finds a string by name and returns its value
const char *FStringTable::GetString(const char *name, uint32_t *langtable) const
//
//==========================================================================
const char *FStringTable::GetString(const char *name, uint32_t *langtable, int gender) const
{
if (name == nullptr || *name == 0)
{
return nullptr;
}
if (gender == -1) gender = players[consoleplayer].userinfo.GetGender();
if (gender < 0 || gender > 3) gender = 0;
FName nm(name, true);
if (nm != NAME_None)
{
@ -359,20 +481,27 @@ const char *FStringTable::GetString(const char *name, uint32_t *langtable) const
if (item)
{
if (langtable) *langtable = map.first;
return item->GetChars();
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) const
//
//==========================================================================
const char *FStringTable::GetLanguageString(const char *name, uint32_t langtable, int gender) const
{
if (name == nullptr || *name == 0)
{
return nullptr;
}
if (gender == -1) gender = players[consoleplayer].userinfo.GetGender();
if (gender < 0 || gender > 3) gender = 0;
FName nm(name, true);
if (nm != NAME_None)
{
@ -381,14 +510,19 @@ const char *FStringTable::GetLanguageString(const char *name, uint32_t langtable
auto item = map->CheckKey(nm);
if (item)
{
return item->GetChars();
return item->strings[gender].GetChars();
}
}
return nullptr;
}
//==========================================================================
//
// 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);
@ -396,7 +530,14 @@ const char *FStringTable::operator() (const char *name) const
}
//==========================================================================
//
// 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);
@ -404,7 +545,7 @@ const char *StringMap::MatchString (const char *string) const
while (it.NextPair(pair))
{
if (pair->Value.CompareNoCase(string) == 0)
if (pair->Value.strings[0].CompareNoCase(string) == 0)
{
return pair->Key.GetChars();
}