|
OpenTTD Source
14.0-beta3
|
Go to the documentation of this file.
21 #include "strings_internal.h"
32 #include "table/strings.h"
43 GRFLB_AMERICAN = 0x01,
51 enum GRFExtendedLanguages {
52 GRFLX_AMERICAN = 0x00,
57 GRFLX_UNSPECIFIED = 0x7F,
74 static 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 str;
241 std::string::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) {
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 = ((uint8_t)* src++);
309 string |= ((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 = ((uint8_t)* src++);
354 tmp |= ((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, text_to_add });
543 StringID AddGRFString(uint32_t grfid, uint16_t stringid,
byte langid_to_add,
bool new_scheme,
bool allow_newlines,
const char *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::find_if(std::begin(_grf_text), std::end(_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::find_if(std::begin(_grf_text), std::end(_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);
664 bool CheckGrfLangID(
byte lang_id,
byte 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<byte, 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;
@ SCC_NEWGRF_ROTATE_TOP_4_WORDS
86: Rotate the top 4 words of the stack (W4 W1 W2 W3)
A GRF text with associated language ID.
std::vector< Mapping > case_map
Mapping of NewGRF and OpenTTD IDs for cases.
std::vector< GRFText > GRFTextList
A GRF text with a list of translations.
static constexpr TimerGame< struct Calendar >::Date DAYS_TILL_ORIGINAL_BASE_YEAR
The date of the first day of the original base year.
@ SCC_NEWGRF_STRINL
Inline another string at the current position, StringID is encoded in the string.
@ SCC_NEWGRF_PRINT_WORD_SPEED
84: Read 2 bytes from the stack as signed speed
@ SCC_NEWGRF_PRINT_WORD_CARGO_SHORT
9A 1C: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount
@ TEXT_TAB_NEWGRF_START
Start of NewGRF supplied strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
int plural_form
The plural form used for this language.
@ SCC_BIGFONT
Switch to large font.
constexpr static debug_inline uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
@ SCC_NEWGRF_PRINT_WORD_HEX
9A 07: Read 2 bytes from the stack and print it as hex
void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32_t *values)
Start using the TTDP compatible string code parsing.
@ SCC_NEWGRF_PRINT_DWORD_FORCE
9A 21: Read 4 bytes from the stack as unsigned force
Holder of the above structure.
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
static const uint TAB_SIZE_NEWGRF
Number of strings for NewGRFs.
@ SCC_NEWGRF_PRINT_DWORD_DATE_LONG
9A 16: Read 4 bytes from the stack as base 0 date
GRFBaseLanguages
Explains the newgrf shift bit positioning.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
void RestoreTextRefStackBackup(struct TextRefStack *backup)
Restore a copy of the text stack to the used stack.
@ SCC_NEWGRF_DISCARD_WORD
85: Discard the next two bytes
uint RemapNewGRFStringControlCode(uint scc, const char **str, StringParameters ¶meters, bool modify_parameters)
FormatString for NewGRF specific "magic" string control codes.
@ 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
void Flush(const LanguageMap *lm, std::ostringstream &dest)
Flush this choice list into the destination string.
@ SCC_TINYFONT
Switch to small font.
void RotateTop4Words()
Rotate the top four words down: W1, W2, W3, W4 -> W4, W1, W2, W3.
size_t GetDataLeft() const
Return the amount of elements which can still be read.
const char * GetGRFStringFromGRFText(const GRFTextList &text_list)
Get a C-string from a GRFText-list.
@ SCC_NEWGRF_PRINT_WORD_CARGO_NAME
9A 1E: Read 2 bytes from the stack as cargo name
void CleanUpStrings()
House cleaning.
static byte _currentLangID
by default, english is used.
@ SCC_NEWGRF_PRINT_WORD_CARGO_TINY
9A 1D: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount
std::vector< Mapping > gender_map
Mapping of NewGRF and OpenTTD IDs for genders.
@ SCC_NEWGRF_PRINT_DWORD_SIGNED
7B: Read 4 bytes from the stack
const LanguageMetadata * _current_language
The currently loaded language.
StringID AddGRFString(uint32_t grfid, uint16_t stringid, byte langid_to_add, bool new_scheme, bool allow_newlines, const char *text_to_add, StringID def_string)
Add the new read string into our structure.
byte CargoID
Cargo slots to indicate a cargo type within a game.
Helper structure for mapping choice lists.
StringID GetGRFStringID(uint32_t grfid, StringID stringid)
Returns the index for this stringid associated with its grfID.
@ SCC_NEWGRF_PRINT_WORD_STATION_NAME
9A 0C: Read 2 bytes from the stack as station name
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
UnmappedChoiceList(StringControlCode type, int offset)
Initialise the mapping.
Mapping between NewGRF and OpenTTD IDs.
@ SCC_NEWGRF_PRINT_WORD_DATE_LONG
82: Read 2 bytes from the stack as base 1920 date
int8_t Utf8EncodedCharLen(char c)
Return the length of an UTF-8 encoded value based on a single char.
@ SCC_NEWGRF_PRINT_QWORD_CURRENCY
9A 01: Read 8 bytes from the stack as currency
int offset
The offset for the plural/gender form.
bool IsValidChar(char32_t key, CharSetFilter afilter)
Only allow certain keys.
Mapping of language data between a NewGRF and OpenTTD.
std::map< byte, std::stringstream > strings
Mapping of NewGRF supplied ID to the different strings in the choice list.
int GetMapping(int newgrf_id, bool gender) const
Get the mapping from the NewGRF supplied ID to OpenTTD's internal ID.
std::string TranslateTTDPatchCodes(uint32_t grfid, uint8_t language_id, bool allow_newlines, const std::string &str, StringControlCode byte80)
Translate TTDPatch string codes into something OpenTTD can handle (better).
StringControlCode
List of string control codes used for string formatting, displaying, and by strgen to generate the la...
@ SCC_NEWGRF_PRINT_WORD_SIGNED
7C: Read 2 bytes from the stack as signed value
@ SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT
9A 19: Read 2 bytes from the stack as short signed volume
@ SCC_NEWGRF_PRINT_QWORD_HEX
9A 0B: Read 8 bytes from the stack and print it as hex
static const LanguageMap * GetLanguageMap(uint32_t grfid, uint8_t language_id)
Get the language map associated with a given NewGRF and language.
@ SCC_NEWGRF_PRINT_DWORD_DATE_SHORT
9A 17: Read 4 bytes from the stack as base 0 date
@ SCC_NEWGRF_PRINT_DWORD_CURRENCY
8F: Read 4 bytes from the stack as currency
@ SCC_NEWGRF_PRINT_BYTE_HEX
9A 06: Read 1 byte from the stack and print it as hex
StringControlCode type
The type of choice list.
static const char32_t NFO_UTF8_IDENTIFIER
This character, the thorn ('รพ'), indicates a unicode string to NFO.
StringID MakeStringID(StringTab tab, uint index)
Create a StringID.
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 ...
@ SCC_NEWGRF_PRINT_WORD_CARGO_LONG
9A 1B: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount
struct TextRefStack * CreateTextRefStackBackup()
Create a backup of the current NewGRF text stack.
@ SCC_NEWGRF_PRINT_WORD_DATE_SHORT
83: Read 2 bytes from the stack as base 1920 date
void SetCurrentGrfLangID(byte language_id)
Equivalence Setter function between game and newgrf langID.
@ SCC_NEWGRF_PRINT_WORD_VOLUME_LONG
87: Read 2 bytes from the stack as long signed volume
int GetReverseMapping(int openttd_id, bool gender) const
Get the mapping from OpenTTD's internal ID to the NewGRF supplied ID.
@ SCC_NEWGRF_PRINT_WORD_UNSIGNED
7E: Read 2 bytes from the stack as unsigned value
Class for temporary storage of data.
@ SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG
9A 0D: Read 2 bytes from the stack as long unsigned weight
const char * GetGRFStringPtr(uint32_t stringid)
Get a C-string from a stringid set by a newgrf.
size_t Utf8Encode(T buf, char32_t c)
Encode a unicode character and place it in the buffer.
std::shared_ptr< GRFTextList > GRFTextWrapper
Reference counted wrapper around a GRFText pointer.
size_t Utf8Decode(char32_t *c, const char *s)
Decode and consume the next UTF-8 encoded character.
static const CargoID NUM_CARGO
Maximum number of cargo types in a game.
@ SCC_NEWGRF_PRINT_DWORD_HEX
9A 08: Read 4 bytes from the stack and print it as hex
CargoID GetCargoTranslation(uint8_t cargo, const GRFFile *grffile, bool usebit)
Translate a GRF-local cargo slot/bitnum into a CargoID.
@ SCC_NEWGRF_PRINT_WORD_POWER
9A 18: Read 2 bytes from the stack as unsigned power
@ SCC_NEWGRF_PRINT_BYTE_SIGNED
7D: Read 1 byte from the stack as signed value
static TextRefStack _newgrf_textrefstack
The stack that is used for TTDP compatible string code parsing.
static void AddGRFTextToList(GRFTextList &list, byte langid, const std::string &text_to_add)
Add a new text to a GRFText list.
Dynamic data of a loaded NewGRF.
bool UsingNewGRFTextStack()
Check whether the NewGRF text stack is in use.
@ SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT
9A 1A: Read 2 bytes from the stack as short unsigned weight