OpenTTD Source
12.0-beta2
|
Go to the documentation of this file.
36 #include "table/strings.h"
47 GRFLB_AMERICAN = 0x01,
55 enum GRFExtendedLanguages {
56 GRFLX_AMERICAN = 0x00,
61 GRFLX_UNSPECIFIED = 0x7F,
78 static uint _num_grf_texts = 0;
92 if (m.newgrf_id == newgrf_id)
return m.openttd_id;
107 if (m.openttd_id == openttd_id)
return m.newgrf_id;
137 if (this->strings.find(0) == this->strings.end()) {
140 grfmsg(1,
"choice list misses default value");
141 this->strings[0] = std::stringstream();
144 std::ostreambuf_iterator<char> d(dest);
150 dest << this->strings[0].rdbuf();
156 if (this->type == SCC_SWITCH_CASE) {
167 if (this->strings.find(lm->
GetReverseMapping(i,
false)) != this->strings.end()) count++;
174 if (this->strings.find(idx) == this->strings.end())
continue;
175 auto str = this->strings[idx].str();
181 size_t len = std::min<size_t>(0xFFFE, str.size());
182 *d++ =
GB(len + 1, 8, 8);
183 *d++ =
GB(len + 1, 0, 8);
186 dest.write(str.c_str(), len);
191 dest << this->strings[0].rdbuf() <<
'\0';
193 if (this->type == SCC_PLURAL_LIST) {
203 *d++ = this->offset - 0x80;
210 for (
int i = 0; i < count; i++) {
211 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
212 const auto &str = this->strings[this->strings.find(idx) != this->strings.end() ? idx : 0].str();
213 size_t len = str.size() + 1;
214 if (len > 0xFF)
grfmsg(1,
"choice list string is too long");
215 *d++ =
GB(len, 0, 8);
219 for (
int i = 0; i < count; i++) {
220 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
221 const auto &str = this->strings[this->strings.find(idx) != this->strings.end() ? idx : 0].str();
224 size_t len = std::min<size_t>(0xFE, str.size());
225 dest.write(str.c_str(), len);
244 if (str.empty())
return str;
246 std::string::const_iterator src = str.cbegin();
249 bool unicode =
false;
261 std::ostringstream dest;
262 std::ostreambuf_iterator<char> d(dest);
263 while (src != str.cend()) {
267 c = Utf8Consume(src);
269 if (
GB(c, 8, 8) == 0xE0) {
271 }
else if (c >= 0x20) {
280 if (c ==
'\0')
break;
284 if (*src ==
'\0')
goto string_end;
290 if (allow_newlines) {
293 grfmsg(1,
"Detected newline in string that does not allow one");
299 if (src[0] ==
'\0' || src[1] ==
'\0')
goto string_end;
311 if (src[0] ==
'\0' || src[1] ==
'\0')
goto string_end;
313 string = ((uint8)* src++);
314 string |= ((uint8)* src++) << 8;
346 case 0x00:
goto string_end;
357 if (src[0] ==
'\0' || src[1] ==
'\0')
goto string_end;
358 uint16 tmp = ((uint8)* src++);
359 tmp |= ((uint8)* src++) << 8;
365 if (src[0] ==
'\0')
goto string_end;
379 if (str[0] ==
'\0')
goto string_end;
382 int mapped = lm !=
nullptr ? lm->
GetMapping(index, code == 0x0E) : -1;
384 Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
385 Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
392 if (str[0] ==
'\0')
goto string_end;
393 if (mapping ==
nullptr) {
394 if (code == 0x10) src++;
395 grfmsg(1,
"choice list %s marker found when not expected", code == 0x10 ?
"next" :
"default");
398 int index = (code == 0x10 ? *src++ : 0);
400 grfmsg(1,
"duplicate choice list string, ignoring");
402 d = std::ostreambuf_iterator<char>(mapping->
strings[index]);
408 if (mapping ==
nullptr) {
409 grfmsg(1,
"choice list end marker found when not expected");
416 d = std::ostreambuf_iterator<char>(dest);
423 if (src[0] ==
'\0')
goto string_end;
424 if (mapping !=
nullptr) {
425 grfmsg(1,
"choice lists can't be stacked, it's going to get messy now...");
426 if (code != 0x14) src++;
428 static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
445 case 0x1F:
Utf8Encode(d, SCC_PUSH_COLOUR);
break;
446 case 0x20:
Utf8Encode(d, SCC_POP_COLOUR);
break;
449 grfmsg(1,
"missing handler for extended format code");
457 case 0xA0:
Utf8Encode(d, SCC_UP_ARROW);
break;
458 case 0xAA:
Utf8Encode(d, SCC_DOWN_ARROW);
break;
459 case 0xAC:
Utf8Encode(d, SCC_CHECKMARK);
break;
461 case 0xAF:
Utf8Encode(d, SCC_RIGHT_ARROW);
break;
467 case 0xB9:
Utf8Encode(d, SCC_SUPERSCRIPT_M1);
break;
468 case 0xBC:
Utf8Encode(d, SCC_SMALL_UP_ARROW);
break;
469 case 0xBD:
Utf8Encode(d, SCC_SMALL_DOWN_ARROW);
break;
479 if (mapping !=
nullptr) {
480 grfmsg(1,
"choice list was incomplete, the whole list is ignored");
496 for (
auto &text : list) {
497 if (text.langid == langid) {
498 text.text = text_to_add;
504 list.push_back(
GRFText{ langid, text_to_add });
551 StringID AddGRFString(uint32 grfid, uint16 stringid,
byte langid_to_add,
bool new_scheme,
bool allow_newlines,
const char *text_to_add,
StringID def_string)
560 if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
561 langid_to_add = GRFLX_ENGLISH;
564 if (langid_to_add & GRFLB_GERMAN) ret =
AddGRFString(grfid, stringid, GRFLX_GERMAN,
true, allow_newlines, text_to_add, def_string);
565 if (langid_to_add & GRFLB_FRENCH) ret =
AddGRFString(grfid, stringid, GRFLX_FRENCH,
true, allow_newlines, text_to_add, def_string);
566 if (langid_to_add & GRFLB_SPANISH) ret =
AddGRFString(grfid, stringid, GRFLX_SPANISH,
true, allow_newlines, text_to_add, def_string);
572 for (
id = 0;
id < _num_grf_texts;
id++) {
573 if (_grf_text[
id].grfid == grfid && _grf_text[
id].stringid == stringid) {
579 if (
id ==
lengthof(_grf_text))
return STR_EMPTY;
584 if (
id == _num_grf_texts) _num_grf_texts++;
586 if (_grf_text[
id].textholder.empty()) {
587 _grf_text[id].grfid = grfid;
588 _grf_text[id].stringid = stringid;
589 _grf_text[id].def_string = def_string;
603 for (uint
id = 0;
id < _num_grf_texts;
id++) {
604 if (_grf_text[
id].grfid == grfid && _grf_text[
id].stringid == stringid) {
609 return STR_UNDEFINED;
622 const char *default_text =
nullptr;
625 for (
const auto &text : text_list) {
630 if (text.langid == GRFLX_UNSPECIFIED || (default_text ==
nullptr && (text.langid == GRFLX_ENGLISH || text.langid == GRFLX_AMERICAN))) {
631 default_text = text.text.c_str();
655 assert(_grf_text[stringid].grfid != 0);
658 if (str !=
nullptr)
return str;
661 return GetStringPtr(_grf_text[stringid].def_string);
677 bool CheckGrfLangID(
byte lang_id,
byte grf_version)
679 if (grf_version < 7) {
681 case GRFLX_GERMAN:
return (lang_id & GRFLB_GERMAN) != 0;
682 case GRFLX_FRENCH:
return (lang_id & GRFLB_FRENCH) != 0;
683 case GRFLX_SPANISH:
return (lang_id & GRFLB_SPANISH) != 0;
684 default:
return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
688 return (lang_id ==
_currentLangID || lang_id == GRFLX_UNSPECIFIED);
699 for (
id = 0;
id < _num_grf_texts;
id++) {
700 _grf_text[id].grfid = 0;
701 _grf_text[id].stringid = 0;
702 _grf_text[id].textholder.clear();
709 std::array<byte, 0x30> stack;
714 TextRefStack() : position(0), grffile(
nullptr), used(
false) {}
716 uint8 PopUnsignedByte() { assert(this->position < this->stack.size());
return this->stack[this->position++]; }
717 int8 PopSignedByte() {
return (int8)this->PopUnsignedByte(); }
719 uint16 PopUnsignedWord()
721 uint16 val = this->PopUnsignedByte();
722 return val | (this->PopUnsignedByte() << 8);
724 int16 PopSignedWord() {
return (int32)this->PopUnsignedWord(); }
726 uint32 PopUnsignedDWord()
728 uint32 val = this->PopUnsignedWord();
729 return val | (this->PopUnsignedWord() << 16);
731 int32 PopSignedDWord() {
return (int32)this->PopUnsignedDWord(); }
733 uint64 PopUnsignedQWord()
735 uint64 val = this->PopUnsignedDWord();
736 return val | (((uint64)this->PopUnsignedDWord()) << 32);
738 int64 PopSignedQWord() {
return (int64)this->PopUnsignedQWord(); }
744 for (
int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6];
745 for (
int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
746 for (
int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i];
749 void PushWord(uint16 word)
751 if (this->position >= 2) {
755 std::rotate(this->stack.rbegin(), this->stack.rbegin() + 2, this->stack.rend());
757 this->stack[this->position] =
GB(word, 0, 8);
758 this->stack[this->position + 1] =
GB(word, 8, 8);
761 void ResetStack(
const GRFFile *grffile)
763 assert(grffile !=
nullptr);
765 this->grffile = grffile;
769 void RewindStack() { this->position = 0; }
828 for (uint i = 0; i < numEntries; i++) {
829 uint32 value = values !=
nullptr ? values[i] : _temp_store.GetValue(0x100 + i);
830 for (uint j = 0; j < 32; j += 8) {
831 *stack_it =
GB(value, j, 8);
843 void RewindTextRefStack()
887 Debug(misc, 0,
"Too many NewGRF string parameters.");
896 Debug(misc, 0,
"Too many NewGRF string parameters.");
904 default: NOT_REACHED();
937 case SCC_NEWGRF_UNPRINT: *buff = std::max(*buff - Utf8Consume(str), buf_start);
break;
952 *argv = cargo <
NUM_CARGO ? 1ULL << cargo : 0;
969 default: NOT_REACHED();
984 return SCC_CURRENCY_LONG;
991 return SCC_DATE_LONG;
995 return SCC_DATE_SHORT;
1001 return SCC_VOLUME_LONG;
1004 return SCC_VOLUME_SHORT;
1007 return SCC_WEIGHT_LONG;
1010 return SCC_WEIGHT_SHORT;
1016 return SCC_CARGO_LONG;
1019 return SCC_CARGO_SHORT;
1022 return SCC_CARGO_TINY;
1025 return SCC_CARGO_LIST;
1028 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.
char32_t WChar
Type for wide characters, i.e.
std::vector< GRFText > GRFTextList
A GRF text with a list of translations.
@ SCC_NEWGRF_STRINL
Inline another string at the current position, StringID is encoded in the string.
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
@ 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.
static StringID MakeStringID(StringTab tab, uint index)
Create a StringID.
void CDECL grfmsg(int severity, const char *str,...)
Debug() function dedicated to newGRF debugging messages Function is essentially the same as Debug(grf...
int plural_form
The plural form used for this language.
@ SCC_BIGFONT
Switch to large font.
@ SCC_NEWGRF_PRINT_WORD_HEX
9A 07: Read 2 bytes from the stack and print it as hex
Holder of the above structure.
size_t Utf8Encode(T buf, WChar c)
Encode a unicode character and place it in the buffer.
void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32 *values)
Start using the TTDP compatible string code parsing.
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
static const uint TAB_SIZE_NEWGRF
Number of strings for NewGRFs.
std::string TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const std::string &str, StringControlCode byte80)
Translate TTDPatch string codes into something OpenTTD can handle (better).
@ 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.
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
StringID AddGRFString(uint32 grfid, uint16 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.
@ SCC_NEWGRF_PUSH_WORD
9A 03: Pushes 2 bytes onto the stack
static const WChar NFO_UTF8_IDENTIFIER
This character, the thorn ('รพ'), indicates a unicode string to NFO.
@ 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.
static const LanguageMap * GetLanguageMap(uint32 grfid, uint8 language_id)
Get the language map associated with a given NewGRF and language.
uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, uint argv_size, bool modify_argv)
FormatString for NewGRF specific "magic" string control codes.
@ SCC_TINYFONT
Switch to small font.
void RotateTop4Words()
Rotate the top four words down: W1, W2, W3, W4 -> W4, W1, W2, W3.
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.
const char * GetGRFStringPtr(uint16 stringid)
Get a C-string from a stringid set by a newgrf.
bool IsValidChar(WChar key, CharSetFilter afilter)
Only allow certain keys.
StringID GetGRFStringID(uint32 grfid, StringID stringid)
Returns the index for this stringid associated with its grfID.
Helper structure for mapping choice lists.
@ 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.
size_t Utf8Decode(WChar *c, const char *s)
Decode and consume the next UTF-8 encoded character.
@ SCC_NEWGRF_PRINT_WORD_DATE_LONG
82: Read 2 bytes from the stack as base 1920 date
uint32 StringID
Numeric value that represents a string, independent of the selected language.
@ SCC_NEWGRF_PRINT_QWORD_CURRENCY
9A 01: Read 8 bytes from the stack as currency
int offset
The offset for the plural/gender form.
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.
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
@ SCC_NEWGRF_PRINT_DWORD_DATE_SHORT
9A 17: Read 4 bytes from the stack as base 0 date
@ NUM_CARGO
Maximal number of cargo types in a game.
CargoID GetCargoTranslation(uint8 cargo, const GRFFile *grffile, bool usebit)
Translate a GRF-local cargo slot/bitnum into a CargoID.
@ 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.
@ 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
#define Debug(name, level, format_string,...)
Ouptut a line of debugging information.
#define lengthof(x)
Return the length of an fixed size array.
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
byte CargoID
Cargo slots to indicate a cargo type within a game.
Class for temporary storage of data.
@ SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG
9A 0D: Read 2 bytes from the stack as long unsigned weight
std::shared_ptr< GRFTextList > GRFTextWrapper
Reference counted wrapper around a GRFText pointer.
@ SCC_NEWGRF_PRINT_DWORD_HEX
9A 08: Read 4 bytes from the stack and print it as hex
StringID MapGRFStringID(uint32 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_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
@ SCC_NEWGRF_UNPRINT
9A 04: "Unprints" the given number of bytes from the string
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.
#define DAYS_TILL_ORIGINAL_BASE_YEAR
The offset in days from the '_date == 0' till 'ConvertYMDToDate(ORIGINAL_BASE_YEAR,...
@ SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT
9A 1A: Read 2 bytes from the stack as short unsigned weight
static int8 Utf8EncodedCharLen(char c)
Return the length of an UTF-8 encoded value based on a single char.