- implemented string table macros.

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

View file

@ -2581,7 +2581,8 @@ void D_DoomMain (void)
};
for (p = 0; p < 5; ++p)
{
const char *str = GStrings[startupString[p]];
// At this point we cannot use the player's gender info yet so force 'male' here.
const char *str = GStrings.GetString(startupString[p], nullptr, 0);
if (str != NULL && str[0] != '\0')
{
Printf("%s\n", str);

View file

@ -2164,13 +2164,14 @@ static int PatchMusic (int dummy)
while ((result = GetLine()) == 1)
{
const char *newname = skipwhite (Line2);
FString newname = skipwhite (Line2);
FString keystring;
keystring << "MUSIC_" << Line1;
DehStrings.Insert(keystring, newname);
DPrintf (DMSG_SPAMMY, "Music %s set to:\n%s\n", keystring.GetChars(), newname);
TableElement te = { newname, newname, newname, newname };
DehStrings.Insert(keystring, te);
DPrintf (DMSG_SPAMMY, "Music %s set to:\n%s\n", keystring.GetChars(), newname.GetChars());
}
return result;
@ -2283,7 +2284,9 @@ static int PatchText (int oldSize)
str = EnglishStrings.MatchString(oldStr);
if (str != NULL)
{
DehStrings.Insert(str, newStr);
FString newname = newStr;
TableElement te = { newname, newname, newname, newname };
DehStrings.Insert(str, te);
EnglishStrings.Remove(str); // remove entry so that it won't get found again by the next iteration or by another replacement later
good = true;
}
@ -2337,7 +2340,8 @@ static int PatchStrings (int dummy)
// Account for a discrepancy between Boom's and ZDoom's name for the red skull key pickup message
const char *ll = Line1;
if (!stricmp(ll, "GOTREDSKULL")) ll = "GOTREDSKUL";
DehStrings.Insert(ll, holdstring);
TableElement te = { holdstring, holdstring, holdstring, holdstring };
DehStrings.Insert(ll, te);
DPrintf (DMSG_SPAMMY, "%s set to:\n%s\n", Line1, holdstring.GetChars());
}

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,12 +164,13 @@ 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;
if (table.Size() > 0)
{
for (unsigned column = 0; column < table[0].Size(); column++)
{
auto &entry = table[0][column];
@ -157,15 +217,21 @@ bool FStringTable::readSheetIntoTable(xlsxioreader reader, const char *sheetname
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());
InsertString(langentry.second, strName, str);
}
}
}
}
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();
}

View file

@ -47,14 +47,25 @@
#include "doomdef.h"
#include "doomtype.h"
struct TableElement
{
FString strings[4];
};
// This public interface is for Dehacked
class StringMap : public TMap<FName, FString>
class StringMap : public TMap<FName, TableElement>
{
public:
const char *MatchString(const char *string) const;
};
struct StringMacro
{
FString Replacements[4];
};
class FStringTable
{
public:
@ -66,6 +77,7 @@ public:
};
using LangMap = TMap<uint32_t, StringMap>;
using StringMacroMap = TMap<FName, StringMacro>;
void LoadStrings ();
void UpdateLanguage();
@ -76,8 +88,8 @@ public:
UpdateLanguage();
}
const char *GetLanguageString(const char *name, uint32_t langtable) const;
const char *GetString(const char *name, uint32_t *langtable) const;
const char *GetLanguageString(const char *name, uint32_t langtable, int gender = -1) 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
{
@ -87,12 +99,15 @@ public:
private:
StringMacroMap allMacros;
LangMap allStrings;
TArray<std::pair<uint32_t, StringMap*>> currentLanguageSet;
void LoadLanguage (int lumpnum);
bool LoadLanguageFromSpreadsheet(int lumpnum);
bool readMacros(struct xlsxio_read_struct *reader, const char *sheet);
bool readSheetIntoTable(struct xlsxio_read_struct *reader, const char *sheet);
void InsertString(int langid, FName label, const FString &string);
static size_t ProcessEscapes (char *str);
};

View file

@ -261,12 +261,12 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf
if (message != NULL && message[0] == '$')
{
message = GStrings[message+1];
message = GStrings.GetString(message+1, nullptr, self->player->userinfo.GetGender());
}
if (message == NULL)
{
message = GStrings("OB_DEFAULT");
message = GStrings.GetString("OB_DEFAULT", nullptr, self->player->userinfo.GetGender());
}
// [CK] Don't display empty strings

View file

@ -63,7 +63,7 @@ void zip_close(zip_t *zipfile)
zip_file_t *zip_fopen(zip_t *zipfile, const char *filename)
{
if (!zipfile) return NULL;
if (!zipfile || !filename) return NULL;
auto lump = zipfile->FindLump(filename);
if (!lump) return NULL;
return new FileReader(std::move(lump->NewReader()));