From af49320637d89a7fd0a8a5c799b964cd2d6cda47 Mon Sep 17 00:00:00 2001 From: frosch Date: Sat, 22 Mar 2025 21:41:27 +0100 Subject: [PATCH] Fix: [NewGRF] Plurals and genders did not work in strings with cases. (#13853) --- src/newgrf_text.cpp | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/newgrf_text.cpp b/src/newgrf_text.cpp index d4a948d62e..2ee0165534 100644 --- a/src/newgrf_text.cpp +++ b/src/newgrf_text.cpp @@ -128,7 +128,7 @@ struct UnmappedChoiceList { * @param lm The current language mapping. * @param dest Target to write to. */ - void Flush(const LanguageMap *lm, std::ostringstream &dest) + void Flush(const LanguageMap *lm, std::stringstream &dest) { if (this->strings.find(0) == this->strings.end()) { /* In case of a (broken) NewGRF without a default, @@ -251,10 +251,11 @@ std::string TranslateTTDPatchCodes(uint32_t grfid, uint8_t language_id, bool all src += len; } - /* Helper variable for a possible (string) mapping. */ - std::optional mapping; + /* Helper variable for a possible (string) mapping of plural/gender and cases. */ + std::optional mapping_pg, mapping_c; + std::optional> dest_c; - std::ostringstream dest; + std::stringstream dest; std::ostreambuf_iterator d(dest); while (src != str.cend()) { char32_t c; @@ -381,37 +382,44 @@ std::string TranslateTTDPatchCodes(uint32_t grfid, uint8_t language_id, bool all case 0x10: case 0x11: if (str[0] == '\0') goto string_end; - if (!mapping.has_value()) { + if (!mapping_pg.has_value() && !mapping_c.has_value()) { if (code == 0x10) src++; // Skip the index GrfMsg(1, "choice list {} marker found when not expected", code == 0x10 ? "next" : "default"); break; } else { + auto &mapping = mapping_pg ? mapping_pg : mapping_c; int index = (code == 0x10 ? *src++ : 0); if (mapping->strings.find(index) != mapping->strings.end()) { GrfMsg(1, "duplicate choice list string, ignoring"); } else { d = std::ostreambuf_iterator(mapping->strings[index]); + if (!mapping_pg) dest_c = mapping->strings[index]; } } break; case 0x12: - if (!mapping.has_value()) { + if (!mapping_pg.has_value() && !mapping_c.has_value()) { GrfMsg(1, "choice list end marker found when not expected"); } else { + auto &mapping = mapping_pg ? mapping_pg : mapping_c; + auto &new_dest = mapping_pg && dest_c ? dest_c->get() : dest; /* Now we can start flushing everything and clean everything up. */ - mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id), dest); + mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id), new_dest); + if (!mapping_pg) dest_c.reset(); mapping.reset(); - d = std::ostreambuf_iterator(dest); + d = std::ostreambuf_iterator(new_dest); } break; case 0x13: case 0x14: - case 0x15: + case 0x15: { + auto &mapping = code == 0x14 ? mapping_c : mapping_pg; if (src[0] == '\0') goto string_end; - if (mapping.has_value()) { + /* Case mapping can have nested plural/gender mapping. Otherwise nesting is invalid. */ + if (mapping.has_value() || mapping_pg.has_value()) { GrfMsg(1, "choice lists can't be stacked, it's going to get messy now..."); if (code != 0x14) src++; } else { @@ -419,6 +427,7 @@ std::string TranslateTTDPatchCodes(uint32_t grfid, uint8_t language_id, bool all mapping.emplace(mp[code - 0x13], code == 0x14 ? 0 : *src++); } break; + } case 0x16: case 0x17: @@ -468,7 +477,7 @@ std::string TranslateTTDPatchCodes(uint32_t grfid, uint8_t language_id, bool all } string_end: - if (mapping.has_value()) { + if (mapping_pg.has_value() || mapping_c.has_value()) { GrfMsg(1, "choice list was incomplete, the whole list is ignored"); }