21#include "strings_internal.h"
32#include "table/strings.h"
43 GRFLB_AMERICAN = 0x01,
51enum GRFExtendedLanguages {
52 GRFLX_AMERICAN = 0x00,
57 GRFLX_UNSPECIFIED = 0x7F,
74static std::vector<GRFTextEntry> _grf_text;
87 if (m.newgrf_id == newgrf_id)
return m.openttd_id;
102 if (m.openttd_id == openttd_id)
return m.newgrf_id;
132 if (this->strings.find(0) == this->strings.end()) {
135 GrfMsg(1,
"choice list misses default value");
136 this->strings[0] = std::stringstream();
139 std::ostreambuf_iterator<char> d(dest);
145 dest << this->strings[0].rdbuf();
151 if (this->type == SCC_SWITCH_CASE) {
162 if (this->strings.find(lm->
GetReverseMapping(i,
false)) != this->strings.end()) count++;
169 if (this->strings.find(idx) == this->strings.end())
continue;
170 auto str = this->strings[idx].str();
176 size_t len = std::min<size_t>(0xFFFE, str.size());
177 *d++ =
GB(len + 1, 8, 8);
178 *d++ =
GB(len + 1, 0, 8);
181 dest.write(str.c_str(), len);
186 dest << this->strings[0].rdbuf() <<
'\0';
188 if (this->type == SCC_PLURAL_LIST) {
198 *d++ = this->offset - 0x80;
205 for (
int i = 0; i < count; i++) {
206 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
207 const auto &str = this->strings[this->strings.find(idx) != this->strings.end() ? idx : 0].str();
208 size_t len = str.size() + 1;
209 if (len > 0xFF) GrfMsg(1,
"choice list string is too long");
210 *d++ =
GB(len, 0, 8);
214 for (
int i = 0; i < count; i++) {
215 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
216 const auto &str = this->strings[this->strings.find(idx) != this->strings.end() ? idx : 0].str();
219 size_t len = std::min<size_t>(0xFE, str.size());
220 dest.write(str.c_str(), len);
239 if (str.empty())
return {};
241 std::string_view::const_iterator src = str.cbegin();
244 bool unicode =
false;
256 std::ostringstream dest;
257 std::ostreambuf_iterator<char> d(dest);
258 while (src != str.cend()) {
262 c = Utf8Consume(src);
264 if (
GB(c, 8, 8) == 0xE0) {
266 }
else if (c >= 0x20) {
272 c =
static_cast<uint8_t
>(*src++);
275 if (c ==
'\0')
break;
279 if (*src ==
'\0')
goto string_end;
285 if (allow_newlines) {
288 GrfMsg(1,
"Detected newline in string that does not allow one");
294 if (src[0] ==
'\0' || src[1] ==
'\0')
goto string_end;
306 if (src[0] ==
'\0' || src[1] ==
'\0')
goto string_end;
308 string =
static_cast<uint8_t
>(*src++);
309 string |=
static_cast<uint8_t
>(*src++) << 8;
341 case 0x00:
goto string_end;
352 if (src[0] ==
'\0' || src[1] ==
'\0')
goto string_end;
353 uint16_t tmp =
static_cast<uint8_t
>(*src++);
354 tmp |=
static_cast<uint8_t
>(*src++) << 8;
369 if (str[0] ==
'\0')
goto string_end;
372 int mapped = lm !=
nullptr ? lm->
GetMapping(index, code == 0x0E) : -1;
374 Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
375 Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
382 if (str[0] ==
'\0')
goto string_end;
383 if (mapping ==
nullptr) {
384 if (code == 0x10) src++;
385 GrfMsg(1,
"choice list {} marker found when not expected", code == 0x10 ?
"next" :
"default");
388 int index = (code == 0x10 ? *src++ : 0);
390 GrfMsg(1,
"duplicate choice list string, ignoring");
392 d = std::ostreambuf_iterator<char>(mapping->
strings[index]);
398 if (mapping ==
nullptr) {
399 GrfMsg(1,
"choice list end marker found when not expected");
406 d = std::ostreambuf_iterator<char>(dest);
413 if (src[0] ==
'\0')
goto string_end;
414 if (mapping !=
nullptr) {
415 GrfMsg(1,
"choice lists can't be stacked, it's going to get messy now...");
416 if (code != 0x14) src++;
418 static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
435 case 0x1F:
Utf8Encode(d, SCC_PUSH_COLOUR);
break;
436 case 0x20:
Utf8Encode(d, SCC_POP_COLOUR);
break;
441 GrfMsg(1,
"missing handler for extended format code");
449 case 0xA0:
Utf8Encode(d, SCC_UP_ARROW);
break;
450 case 0xAA:
Utf8Encode(d, SCC_DOWN_ARROW);
break;
451 case 0xAC:
Utf8Encode(d, SCC_CHECKMARK);
break;
453 case 0xAF:
Utf8Encode(d, SCC_RIGHT_ARROW);
break;
459 case 0xB9:
Utf8Encode(d, SCC_SUPERSCRIPT_M1);
break;
460 case 0xBC:
Utf8Encode(d, SCC_SMALL_UP_ARROW);
break;
461 case 0xBD:
Utf8Encode(d, SCC_SMALL_DOWN_ARROW);
break;
471 if (mapping !=
nullptr) {
472 GrfMsg(1,
"choice list was incomplete, the whole list is ignored");
488 for (
auto &text : list) {
489 if (text.langid == langid) {
490 text.text = text_to_add;
496 list.push_back(
GRFText{ langid, std::string(text_to_add) });
524 if (list ==
nullptr) list = std::make_shared<GRFTextList>();
536 if (list ==
nullptr) list = std::make_shared<GRFTextList>();
543StringID AddGRFString(uint32_t grfid, uint16_t stringid, uint8_t langid_to_add,
bool new_scheme,
bool allow_newlines, std::string_view text_to_add,
StringID def_string)
552 if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
553 langid_to_add = GRFLX_ENGLISH;
556 if (langid_to_add & GRFLB_GERMAN) ret =
AddGRFString(grfid, stringid, GRFLX_GERMAN,
true, allow_newlines, text_to_add, def_string);
557 if (langid_to_add & GRFLB_FRENCH) ret =
AddGRFString(grfid, stringid, GRFLX_FRENCH,
true, allow_newlines, text_to_add, def_string);
558 if (langid_to_add & GRFLB_SPANISH) ret =
AddGRFString(grfid, stringid, GRFLX_SPANISH,
true, allow_newlines, text_to_add, def_string);
563 auto it = std::ranges::find_if(_grf_text, [&grfid, &stringid](
const GRFTextEntry &grf_text) {
return grf_text.grfid == grfid && grf_text.stringid == stringid; });
564 if (it == std::end(_grf_text)) {
569 it = _grf_text.emplace(std::end(_grf_text));
571 it->stringid = stringid;
572 it->def_string = def_string;
574 uint
id =
static_cast<uint
>(it - std::begin(_grf_text));
579 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));
589 auto it = std::ranges::find_if(_grf_text, [&grfid, &stringid](
const GRFTextEntry &grf_text) {
return grf_text.grfid == grfid && grf_text.stringid == stringid; });
590 if (it != std::end(_grf_text)) {
591 uint
id =
static_cast<uint
>(it - std::begin(_grf_text));
595 return STR_UNDEFINED;
608 const char *default_text =
nullptr;
611 for (
const auto &text : text_list) {
616 if (text.langid == GRFLX_UNSPECIFIED || (default_text ==
nullptr && (text.langid == GRFLX_ENGLISH || text.langid == GRFLX_AMERICAN))) {
617 default_text = text.text.c_str();
641 assert(stringid < _grf_text.size());
642 assert(_grf_text[stringid].grfid != 0);
645 if (str !=
nullptr)
return str;
648 return GetStringPtr(_grf_text[stringid].def_string);
664bool CheckGrfLangID(uint8_t lang_id, uint8_t grf_version)
666 if (grf_version < 7) {
668 case GRFLX_GERMAN:
return (lang_id & GRFLB_GERMAN) != 0;
669 case GRFLX_FRENCH:
return (lang_id & GRFLB_FRENCH) != 0;
670 case GRFLX_SPANISH:
return (lang_id & GRFLB_SPANISH) != 0;
671 default:
return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
675 return (lang_id ==
_currentLangID || lang_id == GRFLX_UNSPECIFIED);
688 std::array<uint8_t, 0x30> stack;
693 TextRefStack() : position(0), grffile(
nullptr), used(
false) {}
695 uint8_t PopUnsignedByte() { assert(this->position < this->stack.size());
return this->stack[this->position++]; }
696 int8_t PopSignedByte() {
return (int8_t)this->PopUnsignedByte(); }
698 uint16_t PopUnsignedWord()
700 uint16_t val = this->PopUnsignedByte();
701 return val | (this->PopUnsignedByte() << 8);
703 int16_t PopSignedWord() {
return (int32_t)this->PopUnsignedWord(); }
705 uint32_t PopUnsignedDWord()
707 uint32_t val = this->PopUnsignedWord();
708 return val | (this->PopUnsignedWord() << 16);
710 int32_t PopSignedDWord() {
return (int32_t)this->PopUnsignedDWord(); }
712 uint64_t PopUnsignedQWord()
714 uint64_t val = this->PopUnsignedDWord();
715 return val | (((uint64_t)this->PopUnsignedDWord()) << 32);
717 int64_t PopSignedQWord() {
return (int64_t)this->PopUnsignedQWord(); }
723 for (
int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6];
724 for (
int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
725 for (
int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i];
728 void PushWord(uint16_t word)
730 if (this->position >= 2) {
734 std::rotate(this->stack.rbegin(), this->stack.rbegin() + 2, this->stack.rend());
736 this->stack[this->position] =
GB(word, 0, 8);
737 this->stack[this->position + 1] =
GB(word, 8, 8);
740 void ResetStack(
const GRFFile *grffile)
742 assert(grffile !=
nullptr);
744 this->grffile = grffile;
805 for (uint i = 0; i < numEntries; i++) {
806 uint32_t value = values !=
nullptr ? values[i] : _temp_store.GetValue(0x100 + i);
807 for (uint j = 0; j < 32; j += 8) {
808 *stack_it =
GB(value, j, 8);
859 Debug(misc, 0,
"Too many NewGRF string parameters.");
869 Debug(misc, 0,
"Too many NewGRF string parameters.");
879 default: NOT_REACHED();
928 parameters.SetParam(0, cargo <
NUM_CARGO ? 1ULL << cargo : 0);
945 default: NOT_REACHED();
960 return SCC_CURRENCY_LONG;
967 return SCC_DATE_LONG;
971 return SCC_DATE_SHORT;
977 return SCC_VOLUME_LONG;
980 return SCC_VOLUME_SHORT;
983 return SCC_WEIGHT_LONG;
986 return SCC_WEIGHT_SHORT;
995 return SCC_CARGO_LONG;
998 return SCC_CARGO_SHORT;
1001 return SCC_CARGO_TINY;
1004 return SCC_CARGO_LIST;
1007 return SCC_STATION_NAME;
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 CargoID
Cargo slots to indicate a cargo type within a game.
static const CargoID NUM_CARGO
Maximum number of cargo types in a game.
size_t GetDataLeft() const
Return the amount of elements which can still be read.
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.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Information about languages and their files.
const LanguageMetadata * _current_language
The currently loaded language.
StringID MapGRFStringID(uint32_t grfid, StringID str)
Used when setting an object's property to map to the GRF's strings while taking in consideration the ...
Base for the NewGRF implementation.
CargoID GetCargoTranslation(uint8_t cargo, const GRFFile *grffile, bool usebit)
Translate a GRF-local cargo slot/bitnum into a CargoID.
Cargo support for NewGRFs.
Functionality related to the temporary and persistent storage arrays for NewGRFs.
struct TextRefStack * CreateTextRefStackBackup()
Create a backup of the current NewGRF text stack.
void CleanUpStrings()
House cleaning.
static TextRefStack _newgrf_textrefstack
The stack that is used for TTDP compatible string code parsing.
static void AddGRFTextToList(GRFTextList &list, uint8_t langid, std::string_view text_to_add)
Add a new text to a GRFText list.
char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str, StringParameters ¶meters, bool modify_parameters)
FormatString for NewGRF specific "magic" string control codes.
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.
void StartTextRefStackUsage(const GRFFile *grffile, uint8_t numEntries, const uint32_t *values)
Start using the TTDP compatible string code parsing.
const char * GetGRFStringPtr(uint32_t stringid)
Get a C-string from a stringid set by a newgrf.
const char * GetGRFStringFromGRFText(const GRFTextList &text_list)
Get a C-string from a GRFText-list.
static uint8_t _currentLangID
by default, english is used.
void RestoreTextRefStackBackup(struct TextRefStack *backup)
Restore a copy of the text stack to the used stack.
bool UsingNewGRFTextStack()
Check whether the NewGRF text stack is in use.
StringID AddGRFString(uint32_t grfid, uint16_t 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.
StringID GetGRFStringID(uint32_t grfid, StringID stringid)
Returns the index for this stringid associated with its grfID.
GRFBaseLanguages
Explains the newgrf shift bit positioning.
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
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.
size_t Utf8Encode(T buf, char32_t c)
Encode a unicode character and place it in the buffer.
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.
StringID MakeStringID(StringTab tab, uint index)
Create a StringID.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
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.
A GRF text with associated language ID.
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.
std::map< uint8_t, std::stringstream > strings
Mapping of NewGRF supplied ID to the different strings in the choice list.
StringControlCode type
The type of choice list.
int offset
The offset for the plural/gender form.
UnmappedChoiceList(StringControlCode type, int offset)
Initialise the mapping.
void Flush(const LanguageMap *lm, std::ostringstream &dest)
Flush this choice list into the destination string.
Definition of the game-calendar-timer.