OpenTTD Source
1.11.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");
403 d = std::ostreambuf_iterator<char>(mapping->
strings[index]);
409 if (mapping ==
nullptr) {
410 grfmsg(1,
"choice list end marker found when not expected");
417 d = std::ostreambuf_iterator<char>(dest);
424 if (src[0] ==
'\0')
goto string_end;
425 if (mapping !=
nullptr) {
426 grfmsg(1,
"choice lists can't be stacked, it's going to get messy now...");
427 if (code != 0x14) src++;
429 static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
446 case 0x1F:
Utf8Encode(d, SCC_PUSH_COLOUR);
break;
447 case 0x20:
Utf8Encode(d, SCC_POP_COLOUR);
break;
450 grfmsg(1,
"missing handler for extended format code");
458 case 0xA0:
Utf8Encode(d, SCC_UP_ARROW);
break;
459 case 0xAA:
Utf8Encode(d, SCC_DOWN_ARROW);
break;
460 case 0xAC:
Utf8Encode(d, SCC_CHECKMARK);
break;
462 case 0xAF:
Utf8Encode(d, SCC_RIGHT_ARROW);
break;
468 case 0xB9:
Utf8Encode(d, SCC_SUPERSCRIPT_M1);
break;
469 case 0xBC:
Utf8Encode(d, SCC_SMALL_UP_ARROW);
break;
470 case 0xBD:
Utf8Encode(d, SCC_SMALL_DOWN_ARROW);
break;
480 if (mapping !=
nullptr) {
481 grfmsg(1,
"choice list was incomplete, the whole list is ignored");
497 for (
auto &text : list) {
498 if (text.langid == langid) {
499 text.text = text_to_add;
505 list.push_back(
GRFText{ langid, text_to_add });
552 StringID AddGRFString(uint32 grfid, uint16 stringid,
byte langid_to_add,
bool new_scheme,
bool allow_newlines,
const char *text_to_add,
StringID def_string)
561 if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
562 langid_to_add = GRFLX_ENGLISH;
565 if (langid_to_add & GRFLB_GERMAN) ret =
AddGRFString(grfid, stringid, GRFLX_GERMAN,
true, allow_newlines, text_to_add, def_string);
566 if (langid_to_add & GRFLB_FRENCH) ret =
AddGRFString(grfid, stringid, GRFLX_FRENCH,
true, allow_newlines, text_to_add, def_string);
567 if (langid_to_add & GRFLB_SPANISH) ret =
AddGRFString(grfid, stringid, GRFLX_SPANISH,
true, allow_newlines, text_to_add, def_string);
573 for (
id = 0;
id < _num_grf_texts;
id++) {
574 if (_grf_text[
id].grfid == grfid && _grf_text[
id].stringid == stringid) {
580 if (
id ==
lengthof(_grf_text))
return STR_EMPTY;
585 if (
id == _num_grf_texts) _num_grf_texts++;
587 if (_grf_text[
id].textholder.empty()) {
588 _grf_text[id].grfid = grfid;
589 _grf_text[id].stringid = stringid;
590 _grf_text[id].def_string = def_string;
604 for (uint
id = 0;
id < _num_grf_texts;
id++) {
605 if (_grf_text[
id].grfid == grfid && _grf_text[
id].stringid == stringid) {
610 return STR_UNDEFINED;
623 const char *default_text =
nullptr;
626 for (
const auto &text : text_list) {
631 if (text.langid == GRFLX_UNSPECIFIED || (default_text ==
nullptr && (text.langid == GRFLX_ENGLISH || text.langid == GRFLX_AMERICAN))) {
632 default_text = text.text.c_str();
656 assert(_grf_text[stringid].grfid != 0);
659 if (str !=
nullptr)
return str;
662 return GetStringPtr(_grf_text[stringid].def_string);
678 bool CheckGrfLangID(
byte lang_id,
byte grf_version)
680 if (grf_version < 7) {
682 case GRFLX_GERMAN:
return (lang_id & GRFLB_GERMAN) != 0;
683 case GRFLX_FRENCH:
return (lang_id & GRFLB_FRENCH) != 0;
684 case GRFLX_SPANISH:
return (lang_id & GRFLB_SPANISH) != 0;
685 default:
return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
689 return (lang_id ==
_currentLangID || lang_id == GRFLX_UNSPECIFIED);
700 for (
id = 0;
id < _num_grf_texts;
id++) {
701 _grf_text[id].grfid = 0;
702 _grf_text[id].stringid = 0;
703 _grf_text[id].textholder.clear();
710 std::array<byte, 0x30> stack;
715 TextRefStack() : position(0), grffile(
nullptr), used(
false) {}
717 uint8 PopUnsignedByte() { assert(this->position < this->stack.size());
return this->stack[this->position++]; }
718 int8 PopSignedByte() {
return (int8)this->PopUnsignedByte(); }
720 uint16 PopUnsignedWord()
722 uint16 val = this->PopUnsignedByte();
723 return val | (this->PopUnsignedByte() << 8);
725 int16 PopSignedWord() {
return (int32)this->PopUnsignedWord(); }
727 uint32 PopUnsignedDWord()
729 uint32 val = this->PopUnsignedWord();
730 return val | (this->PopUnsignedWord() << 16);
732 int32 PopSignedDWord() {
return (int32)this->PopUnsignedDWord(); }
734 uint64 PopUnsignedQWord()
736 uint64 val = this->PopUnsignedDWord();
737 return val | (((uint64)this->PopUnsignedDWord()) << 32);
739 int64 PopSignedQWord() {
return (int64)this->PopUnsignedQWord(); }
745 for (
int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6];
746 for (
int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
747 for (
int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i];
750 void PushWord(uint16 word)
752 if (this->position >= 2) {
756 std::rotate(this->stack.rbegin(), this->stack.rbegin() + 2, this->stack.rend());
758 this->stack[this->position] =
GB(word, 0, 8);
759 this->stack[this->position + 1] =
GB(word, 8, 8);
762 void ResetStack(
const GRFFile *grffile)
764 assert(grffile !=
nullptr);
766 this->grffile = grffile;
770 void RewindStack() { this->position = 0; }
829 for (uint i = 0; i < numEntries; i++) {
830 uint32 value = values !=
nullptr ? values[i] : _temp_store.GetValue(0x100 + i);
831 for (uint j = 0; j < 32; j += 8) {
832 *stack_it =
GB(value, j, 8);
844 void RewindTextRefStack()
888 DEBUG(misc, 0,
"Too many NewGRF string parameters.");
897 DEBUG(misc, 0,
"Too many NewGRF string parameters.");
905 default: NOT_REACHED();
938 case SCC_NEWGRF_UNPRINT: *buff = std::max(*buff - Utf8Consume(str), buf_start);
break;
953 *argv = cargo <
NUM_CARGO ? 1 << cargo : 0;
970 default: NOT_REACHED();
985 return SCC_CURRENCY_LONG;
992 return SCC_DATE_LONG;
996 return SCC_DATE_SHORT;
1002 return SCC_VOLUME_LONG;
1005 return SCC_VOLUME_SHORT;
1008 return SCC_WEIGHT_LONG;
1011 return SCC_WEIGHT_SHORT;
1017 return SCC_CARGO_LONG;
1020 return SCC_CARGO_SHORT;
1023 return SCC_CARGO_TINY;
1026 return SCC_CARGO_LIST;
1029 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.
#define DEBUG(name, level,...)
Output a line of debugging information.
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 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.