22#include "strings_internal.h"
33#include "table/strings.h"
44 GRFLB_AMERICAN = 0x01,
52enum GRFExtendedLanguages : uint8_t {
53 GRFLX_AMERICAN = 0x00,
58 GRFLX_UNSPECIFIED = 0x7F,
88 if (m.newgrf_id == newgrf_id)
return m.openttd_id;
103 if (m.openttd_id == openttd_id)
return m.newgrf_id;
133 if (this->strings.find(0) == this->strings.end()) {
136 GrfMsg(1,
"choice list misses default value");
137 this->strings[0] = std::string();
146 builder += this->strings[0];
152 if (this->type == SCC_SWITCH_CASE) {
163 if (this->strings.find(lm->
GetReverseMapping(i,
false)) != this->strings.end()) count++;
167 auto add_case = [&](std::string_view str) {
169 uint16_t len = ClampTo<uint16_t>(str.size());
173 builder += str.substr(0, len);
179 if (this->strings.find(idx) == this->strings.end())
continue;
180 auto &str = this->strings[idx];
189 add_case(this->strings[0]);
191 if (this->type == SCC_PLURAL_LIST) {
201 builder.
PutUint8(this->offset - 0x80);
208 for (
int i = 0; i < count; i++) {
209 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
210 const auto &str = this->strings[this->strings.find(idx) != this->strings.end() ? idx : 0];
211 size_t len = str.size();
212 if (len > UINT8_MAX) GrfMsg(1,
"choice list string is too long");
213 builder.
PutUint8(ClampTo<uint8_t>(len));
217 for (
int i = 0; i < count; i++) {
218 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
219 const auto &str = this->strings[this->strings.find(idx) != this->strings.end() ? idx : 0];
220 uint8_t len = ClampTo<uint8_t>(str.size());
221 builder += str.substr(0, len);
239 if (str.empty())
return {};
241 std::string_view::const_iterator src = str.cbegin();
244 bool unicode =
false;
254 std::optional<UnmappedChoiceList> mapping_pg, mapping_c;
255 std::optional<std::reference_wrapper<std::string>> dest_c;
259 while (src != str.cend()) {
263 c = Utf8Consume(src);
265 if (
GB(c, 8, 8) == 0xE0) {
267 }
else if (c >= 0x20) {
273 c =
static_cast<uint8_t
>(*src++);
276 if (c ==
'\0')
break;
280 if (*src ==
'\0')
goto string_end;
286 if (allow_newlines) {
289 GrfMsg(1,
"Detected newline in string that does not allow one");
295 if (src[0] ==
'\0' || src[1] ==
'\0')
goto string_end;
304 case 0x80: builder.
PutUtf8(byte80);
break;
307 if (src[0] ==
'\0' || src[1] ==
'\0')
goto string_end;
309 string =
static_cast<uint8_t
>(*src++);
310 string |=
static_cast<uint8_t
>(*src++) << 8;
321 case 0x88: builder.
PutUtf8(SCC_BLUE);
break;
322 case 0x89: builder.
PutUtf8(SCC_SILVER);
break;
323 case 0x8A: builder.
PutUtf8(SCC_GOLD);
break;
324 case 0x8B: builder.
PutUtf8(SCC_RED);
break;
325 case 0x8C: builder.
PutUtf8(SCC_PURPLE);
break;
326 case 0x8D: builder.
PutUtf8(SCC_LTBROWN);
break;
327 case 0x8E: builder.
PutUtf8(SCC_ORANGE);
break;
328 case 0x8F: builder.
PutUtf8(SCC_GREEN);
break;
329 case 0x90: builder.
PutUtf8(SCC_YELLOW);
break;
330 case 0x91: builder.
PutUtf8(SCC_DKGREEN);
break;
331 case 0x92: builder.
PutUtf8(SCC_CREAM);
break;
332 case 0x93: builder.
PutUtf8(SCC_BROWN);
break;
333 case 0x94: builder.
PutUtf8(SCC_WHITE);
break;
334 case 0x95: builder.
PutUtf8(SCC_LTBLUE);
break;
335 case 0x96: builder.
PutUtf8(SCC_GRAY);
break;
336 case 0x97: builder.
PutUtf8(SCC_DKBLUE);
break;
337 case 0x98: builder.
PutUtf8(SCC_BLACK);
break;
342 case 0x00:
goto string_end;
353 if (src[0] ==
'\0' || src[1] ==
'\0')
goto string_end;
354 uint16_t tmp =
static_cast<uint8_t
>(*src++);
355 tmp |=
static_cast<uint8_t
>(*src++) << 8;
370 if (str[0] ==
'\0')
goto string_end;
373 int mapped = lm !=
nullptr ? lm->
GetMapping(index, code == 0x0E) : -1;
375 builder.
PutUtf8(code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
376 builder.
PutUtf8(code == 0x0E ? mapped : mapped + 1);
383 if (str[0] ==
'\0')
goto string_end;
384 if (!mapping_pg.has_value() && !mapping_c.has_value()) {
385 if (code == 0x10) src++;
386 GrfMsg(1,
"choice list {} marker found when not expected", code == 0x10 ?
"next" :
"default");
389 auto &mapping = mapping_pg ? mapping_pg : mapping_c;
390 int index = (code == 0x10 ? *src++ : 0);
391 if (mapping->strings.find(index) != mapping->strings.end()) {
392 GrfMsg(1,
"duplicate choice list string, ignoring");
395 if (!mapping_pg) dest_c = mapping->strings[index];
401 if (!mapping_pg.has_value() && !mapping_c.has_value()) {
402 GrfMsg(1,
"choice list end marker found when not expected");
404 auto &mapping = mapping_pg ? mapping_pg : mapping_c;
405 auto &new_dest = mapping_pg && dest_c ? dest_c->get() : dest;
408 if (!mapping_pg) dest_c.reset();
418 auto &mapping = code == 0x14 ? mapping_c : mapping_pg;
419 if (src[0] ==
'\0')
goto string_end;
421 if (mapping.has_value() || mapping_pg.has_value()) {
422 GrfMsg(1,
"choice lists can't be stacked, it's going to get messy now...");
423 if (code != 0x14) src++;
425 static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
426 mapping.emplace(mp[code - 0x13], code == 0x14 ? 0 : *src++);
443 case 0x1F: builder.
PutUtf8(SCC_PUSH_COLOUR);
break;
444 case 0x20: builder.
PutUtf8(SCC_POP_COLOUR);
break;
449 GrfMsg(1,
"missing handler for extended format code");
455 case 0x9E: builder.
PutUtf8(0x20AC);
break;
456 case 0x9F: builder.
PutUtf8(0x0178);
break;
457 case 0xA0: builder.
PutUtf8(SCC_UP_ARROW);
break;
458 case 0xAA: builder.
PutUtf8(SCC_DOWN_ARROW);
break;
459 case 0xAC: builder.
PutUtf8(SCC_CHECKMARK);
break;
460 case 0xAD: builder.
PutUtf8(SCC_CROSS);
break;
461 case 0xAF: builder.
PutUtf8(SCC_RIGHT_ARROW);
break;
462 case 0xB4: builder.
PutUtf8(SCC_TRAIN);
break;
463 case 0xB5: builder.
PutUtf8(SCC_LORRY);
break;
464 case 0xB6: builder.
PutUtf8(SCC_BUS);
break;
465 case 0xB7: builder.
PutUtf8(SCC_PLANE);
break;
466 case 0xB8: builder.
PutUtf8(SCC_SHIP);
break;
467 case 0xB9: builder.
PutUtf8(SCC_SUPERSCRIPT_M1);
break;
468 case 0xBC: builder.
PutUtf8(SCC_SMALL_UP_ARROW);
break;
469 case 0xBD: builder.
PutUtf8(SCC_SMALL_DOWN_ARROW);
break;
479 if (mapping_pg.has_value() || mapping_c.has_value()) {
480 GrfMsg(1,
"choice list was incomplete, the whole list is ignored");
495 for (
auto &text : list) {
496 if (text.langid == langid) {
497 text.text = text_to_add;
503 list.emplace_back(langid, std::string{text_to_add});
531 if (list ==
nullptr) list = std::make_shared<GRFTextList>();
543 if (list ==
nullptr) list = std::make_shared<GRFTextList>();
559 if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
560 langid_to_add = GRFLX_ENGLISH;
563 if (langid_to_add & GRFLB_GERMAN) ret =
AddGRFString(grfid, stringid, GRFLX_GERMAN,
true, allow_newlines, text_to_add, def_string);
564 if (langid_to_add & GRFLB_FRENCH) ret =
AddGRFString(grfid, stringid, GRFLX_FRENCH,
true, allow_newlines, text_to_add, def_string);
565 if (langid_to_add & GRFLB_SPANISH) ret =
AddGRFString(grfid, stringid, GRFLX_SPANISH,
true, allow_newlines, text_to_add, def_string);
570 auto it = std::ranges::find_if(_grf_text, [&grfid, &stringid](
const GRFTextEntry &grf_text) {
return grf_text.grfid == grfid && grf_text.stringid == stringid; });
571 if (it == std::end(_grf_text)) {
576 it = _grf_text.emplace(std::end(_grf_text));
578 it->stringid = stringid;
579 it->def_string = def_string;
586 GrfMsg(3,
"Added 0x{:X} grfid {:08X} string 0x{:X} lang 0x{:X} string '{}' ({:X})",
id, grfid, stringid, langid_to_add, newtext,
MakeStringID(
TEXT_TAB_NEWGRF_START,
id));
596 auto it = std::ranges::find_if(_grf_text, [&grfid, &stringid](
const GRFTextEntry &grf_text) {
return grf_text.grfid == grfid && grf_text.stringid == stringid; });
597 if (it != std::end(_grf_text)) {
602 return STR_UNDEFINED;
615 std::optional<std::string_view> default_text;
618 for (
const auto &text : text_list) {
623 if (text.langid == GRFLX_UNSPECIFIED || (!default_text.has_value() && (text.langid == GRFLX_ENGLISH || text.langid == GRFLX_AMERICAN))) {
624 default_text = text.text;
648 assert(stringid.base() < _grf_text.size());
649 assert(_grf_text[stringid].grfid != 0);
652 if (str.has_value())
return *str;
655 return GetStringPtr(_grf_text[stringid].def_string);
671bool CheckGrfLangID(uint8_t lang_id, uint8_t grf_version)
673 if (grf_version < 7) {
675 case GRFLX_GERMAN:
return (lang_id & GRFLB_GERMAN) != 0;
676 case GRFLX_FRENCH:
return (lang_id & GRFLB_FRENCH) != 0;
677 case GRFLX_SPANISH:
return (lang_id & GRFLB_SPANISH) != 0;
678 default:
return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
695 std::array<uint8_t, 0x30> stack{};
696 uint8_t position = 0;
697 const GRFFile *grffile =
nullptr;
703 assert(num_entries <
sizeof(uint32_t) * std::size(stack));
705 auto stack_it = this->stack.begin();
706 for (uint i = 0; i < num_entries; i++) {
707 uint32_t value = _temp_store.GetValue(0x100 + i);
708 for (uint j = 0; j < 32; j += 8) {
709 *stack_it++ =
GB(value, j, 8);
714 uint8_t PopUnsignedByte() { assert(this->position < this->stack.size());
return this->stack[this->position++]; }
715 int8_t PopSignedByte() {
return (int8_t)this->PopUnsignedByte(); }
717 uint16_t PopUnsignedWord()
719 uint16_t val = this->PopUnsignedByte();
720 return val | (this->PopUnsignedByte() << 8);
722 int16_t PopSignedWord() {
return (int32_t)this->PopUnsignedWord(); }
724 uint32_t PopUnsignedDWord()
726 uint32_t val = this->PopUnsignedWord();
727 return val | (this->PopUnsignedWord() << 16);
729 int32_t PopSignedDWord() {
return (int32_t)this->PopUnsignedDWord(); }
731 uint64_t PopUnsignedQWord()
733 uint64_t val = this->PopUnsignedDWord();
734 return val | (((uint64_t)this->PopUnsignedDWord()) << 32);
736 int64_t PopSignedQWord() {
return (int64_t)this->PopUnsignedQWord(); }
742 for (
int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6];
743 for (
int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
744 for (
int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i];
747 void PushWord(uint16_t word)
749 if (this->position >= 2) {
753 std::rotate(this->stack.rbegin(), this->stack.rbegin() + 2, this->stack.rend());
755 this->stack[this->position] =
GB(word, 0, 8);
756 this->stack[this->position + 1] =
GB(word, 8, 8);
776 case SCC_PLURAL_LIST:
779 case SCC_GENDER_LIST: {
782 uint num =
static_cast<uint8_t
>(*str++);
784 for (uint i = 0; i != num; i++) {
785 total_len +=
static_cast<uint8_t
>(*str++);
791 case SCC_SWITCH_CASE: {
793 uint num =
static_cast<uint8_t
>(*str++);
794 for (uint i = 0; i != num; i++) {
795 str += 3 +
static_cast<uint8_t
>(str[1]) + (
static_cast<uint8_t
>(str[2]) << 8);
801 case SCC_GENDER_INDEX:
849 params.emplace_back(stack.PopUnsignedWord());
853 StringID stringid = Utf8Consume(str);
861 params.emplace_back(stringid);
869 params.emplace_back(cargo <
NUM_CARGO ? 1ULL << cargo : 0);
901 return SCC_CURRENCY_LONG;
905 return SCC_DATE_LONG;
909 return SCC_DATE_SHORT;
915 return SCC_VOLUME_LONG;
918 return SCC_VOLUME_SHORT;
921 return SCC_WEIGHT_LONG;
924 return SCC_WEIGHT_SHORT;
933 return SCC_CARGO_LONG;
936 return SCC_CARGO_SHORT;
939 return SCC_CARGO_TINY;
942 return SCC_CARGO_LIST;
945 return SCC_STATION_NAME;
966 for (
const char *p = str.data(), *end = str.data() + str.size(); p < end; ) {
984 auto str = GetStringPtr(stringid);
986 std::vector<StringParameter> params;
Helper types related to the allocation of memory.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
void PutUtf8(char32_t c)
Append UTF.8 char.
void PutUint16LE(uint16_t value)
Append binary uint16 using little endian.
void PutChar(char c)
Append 8-bit char.
void PutUint8(uint8_t value)
Append binary uint8.
A sort-of mixin that adds 'at(pos)' and 'operator[](pos)' implementations for 'ConvertibleThroughBase...
Compose data into a growing std::string.
static constexpr TimerGame< struct Calendar >::Date DAYS_TILL_ORIGINAL_BASE_YEAR
The date of the first day of the original base year.
Control codes that are embedded in the translation strings.
StringControlCode
List of string control codes used for string formatting, displaying, and by strgen to generate the la...
@ SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT
9A 19: Read 2 bytes from the stack as short signed volume
@ SCC_NEWGRF_PRINT_DWORD_CURRENCY
8F: Read 4 bytes from the stack as currency
@ SCC_NEWGRF_PRINT_WORD_HEX
9A 07: Read 2 bytes from the stack and print it as hex
@ SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT
9A 1A: Read 2 bytes from the stack as short unsigned weight
@ SCC_NEWGRF_ROTATE_TOP_4_WORDS
86: Rotate the top 4 words of the stack (W4 W1 W2 W3)
@ SCC_TINYFONT
Switch to small font.
@ SCC_NEWGRF_PRINT_DWORD_DATE_LONG
9A 16: Read 4 bytes from the stack as base 0 date
@ SCC_NEWGRF_PRINT_DWORD_HEX
9A 08: Read 4 bytes from the stack and print it as hex
@ SCC_NEWGRF_PRINT_WORD_DATE_SHORT
83: Read 2 bytes from the stack as base 1920 date
@ SCC_BIGFONT
Switch to large font.
@ SCC_NEWGRF_PRINT_BYTE_HEX
9A 06: Read 1 byte from the stack and print it as hex
@ SCC_NEWGRF_PRINT_WORD_UNSIGNED
7E: Read 2 bytes from the stack as unsigned value
@ SCC_NEWGRF_PRINT_BYTE_SIGNED
7D: Read 1 byte from the stack as signed value
@ SCC_NEWGRF_PRINT_WORD_CARGO_NAME
9A 1E: Read 2 bytes from the stack as cargo name
@ SCC_NEWGRF_PRINT_DWORD_SIGNED
7B: Read 4 bytes from the stack
@ SCC_NEWGRF_STRINL
Inline another string at the current position, StringID is encoded in the string.
@ SCC_NEWGRF_PRINT_WORD_CARGO_SHORT
9A 1C: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount
@ SCC_NEWGRF_PRINT_WORD_STATION_NAME
9A 0C: Read 2 bytes from the stack as station name
@ SCC_NEWGRF_PRINT_WORD_DATE_LONG
82: Read 2 bytes from the stack as base 1920 date
@ SCC_NEWGRF_PRINT_WORD_SIGNED
7C: Read 2 bytes from the stack as signed value
@ SCC_NEWGRF_PRINT_WORD_POWER
9A 18: Read 2 bytes from the stack as unsigned power
@ SCC_NEWGRF_PUSH_WORD
9A 03: Pushes 2 bytes onto the stack
@ SCC_NEWGRF_PRINT_WORD_STRING_ID
81: Read 2 bytes from the stack as String ID
@ SCC_NEWGRF_PRINT_WORD_VOLUME_LONG
87: Read 2 bytes from the stack as long signed volume
@ SCC_NEWGRF_DISCARD_WORD
85: Discard the next two bytes
@ SCC_NEWGRF_PRINT_DWORD_FORCE
9A 21: Read 4 bytes from the stack as unsigned force
@ SCC_NEWGRF_PRINT_WORD_CARGO_TINY
9A 1D: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount
@ SCC_NEWGRF_PRINT_QWORD_HEX
9A 0B: Read 8 bytes from the stack and print it as hex
@ SCC_NEWGRF_PRINT_WORD_CARGO_LONG
9A 1B: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount
@ SCC_NEWGRF_PRINT_WORD_SPEED
84: Read 2 bytes from the stack as signed speed
@ SCC_NEWGRF_PRINT_DWORD_DATE_SHORT
9A 17: Read 4 bytes from the stack as base 0 date
@ SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG
9A 0D: Read 2 bytes from the stack as long unsigned weight
@ SCC_NEWGRF_PRINT_QWORD_CURRENCY
9A 01: Read 8 bytes from the stack as currency
Functions related to debugging.
Information about languages and their files.
const LanguageMetadata * _current_language
The currently loaded language.
Base for the NewGRF implementation.
StringID MapGRFStringID(uint32_t grfid, GRFStringID str)
Used when setting an object's property to map to the GRF's strings while taking in consideration the ...
CargoType GetCargoTranslation(uint8_t cargo, const GRFFile *grffile, bool usebit)
Translate a GRF-local cargo slot/bitnum into a CargoType.
Cargo support for NewGRFs.
Functionality related to the temporary and persistent storage arrays for NewGRFs.
void CleanUpStrings()
House cleaning.
static void AddGRFTextToList(GRFTextList &list, uint8_t langid, std::string_view text_to_add)
Add a new text to a GRFText list.
std::string_view GetGRFStringPtr(StringIndexInTab stringid)
Get a C-string from a stringid set by a newgrf.
GRFBaseLanguages
Explains the newgrf shift bit positioning.
static void HandleNewGRFStringControlCodes(std::string_view str, TextRefStack &stack, std::vector< StringParameter > ¶ms)
Handle control codes in a NewGRF string, processing the stack and filling parameters.
std::string TranslateTTDPatchCodes(uint32_t grfid, uint8_t language_id, bool allow_newlines, std::string_view str, StringControlCode byte80)
Translate TTDPatch string codes into something OpenTTD can handle (better).
void SetCurrentGrfLangID(uint8_t language_id)
Equivalence Setter function between game and newgrf langID.
StringID GetGRFStringID(uint32_t grfid, GRFStringID stringid)
Returns the index for this stringid associated with its grfID.
static void ProcessNewGRFStringControlCode(char32_t scc, const char *&str, TextRefStack &stack, std::vector< StringParameter > ¶ms)
Process NewGRF string control code instructions.
std::optional< std::string_view > GetGRFStringFromGRFText(const GRFTextList &text_list)
Get a C-string from a GRFText-list.
std::vector< StringParameter > GetGRFSringTextStackParameters(const GRFFile *grffile, StringID stringid, uint8_t num_entries)
Process the text ref stack for a GRF String and return its parameters.
StringID AddGRFString(uint32_t grfid, GRFStringID stringid, uint8_t langid_to_add, bool new_scheme, bool allow_newlines, std::string_view text_to_add, StringID def_string)
Add the new read string into our structure.
static uint8_t _current_lang_id
by default, english is used.
char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str)
Emit OpenTTD's internal string code for the different NewGRF string codes.
std::string GetGRFStringWithTextStack(const struct GRFFile *grffile, GRFStringID grfstringid, uint8_t num_entries)
Format a GRF string using the text ref stack for parameters.
Header of Action 04 "universal holder" structure and functions.
std::shared_ptr< GRFTextList > GRFTextWrapper
Reference counted wrapper around a GRFText pointer.
std::vector< GRFText > GRFTextList
A GRF text with a list of translations.
static const char32_t NFO_UTF8_IDENTIFIER
This character (thorn) indicates a unicode string to NFO.
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
bool IsValidChar(char32_t key, CharSetFilter afilter)
Only allow certain keys.
size_t Utf8Decode(char32_t *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Compose strings from textual and binary data.
Functions related to low-level strings.
int8_t Utf8EncodedCharLen(char c)
Return the length of an UTF-8 encoded value based on a single char.
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
StringID MakeStringID(StringTab tab, StringIndexInTab index)
Create a StringID.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
static const uint TAB_SIZE_NEWGRF
Number of strings for NewGRFs.
@ TEXT_TAB_NEWGRF_START
Start of NewGRF supplied strings.
Dynamic data of a loaded NewGRF.
Holder of the above structure.
Mapping between NewGRF and OpenTTD IDs.
Mapping of language data between a NewGRF and OpenTTD.
std::vector< Mapping > gender_map
Mapping of NewGRF and OpenTTD IDs for genders.
int GetReverseMapping(int openttd_id, bool gender) const
Get the mapping from OpenTTD's internal ID to the NewGRF supplied ID.
int plural_form
The plural form used for this language.
static const LanguageMap * GetLanguageMap(uint32_t grfid, uint8_t language_id)
Get the language map associated with a given NewGRF and language.
std::vector< Mapping > case_map
Mapping of NewGRF and OpenTTD IDs for cases.
int GetMapping(int newgrf_id, bool gender) const
Get the mapping from the NewGRF supplied ID to OpenTTD's internal ID.
Class for temporary storage of data.
void RotateTop4Words()
Rotate the top four words down: W1, W2, W3, W4 -> W4, W1, W2, W3.
Helper structure for mapping choice lists.
StringControlCode type
The type of choice list.
int offset
The offset for the plural/gender form.
std::map< int, std::string > strings
Mapping of NewGRF supplied ID to the different strings in the choice list.
UnmappedChoiceList(StringControlCode type, int offset)
Initialise the mapping.
void Flush(const LanguageMap *lm, std::string &dest)
Flush this choice list into the destination string.
Definition of the game-calendar-timer.