35 #include "table/strings.h" 46 GRFLB_AMERICAN = 0x01,
54 enum GRFExtendedLanguages {
55 GRFLX_AMERICAN = 0x00,
60 GRFLX_UNSPECIFIED = 0x7F,
100 void *
operator new(
size_t size)
109 void operator delete(
void *p)
125 memcpy(this->
text, text_,
len);
134 void *
operator new(
size_t size,
size_t extra)
136 return MallocT<byte>(size + extra);
160 static uint _num_grf_texts = 0;
172 const std::vector<Mapping> &map = gender ? this->gender_map : this->case_map;
174 if (m.newgrf_id == newgrf_id)
return m.openttd_id;
187 const std::vector<Mapping> &map = gender ? this->gender_map : this->case_map;
189 if (m.openttd_id == openttd_id)
return m.newgrf_id;
211 type(type), old_d(old_d), offset(offset)
232 grfmsg(1,
"choice list misses default value");
233 this->strings[0] =
stredup(
"");
241 size_t len = strlen(this->strings[0]);
242 memcpy(d, this->strings[0], len);
248 if (this->type == SCC_SWITCH_CASE) {
266 if (!this->strings.
Contains(idx))
continue;
267 char *str = this->strings[idx];
273 size_t len = strlen(str) + 1;
274 *d++ =
GB(len, 8, 8);
275 *d++ =
GB(len, 0, 8);
283 size_t len = strlen(this->strings[0]) + 1;
284 memcpy(d, this->strings[0], len);
287 if (this->type == SCC_PLURAL_LIST) {
297 *d++ = this->offset - 0x80;
304 for (
int i = 0; i < count; i++) {
305 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
306 const char *str = this->strings[this->strings.
Contains(idx) ? idx : 0];
307 size_t len = strlen(str) + 1;
308 if (len > 0xFF)
grfmsg(1,
"choice list string is too long");
309 *d++ =
GB(len, 0, 8);
313 for (
int i = 0; i < count; i++) {
314 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
315 const char *str = this->strings[this->strings.
Contains(idx) ? idx : 0];
318 size_t len = min<size_t>(0xFE, strlen(str));
340 char *tmp = MallocT<char>(strlen(str) * 10 + 1);
342 bool unicode =
false;
356 c = Utf8Consume(&str);
358 if (
GB(c, 8, 8) == 0xE0) {
360 }
else if (c >= 0x20) {
368 if (c ==
'\0')
break;
372 if (str[0] ==
'\0')
goto string_end;
378 if (allow_newlines) {
381 grfmsg(1,
"Detected newline in string that does not allow one");
387 if (str[0] ==
'\0' || str[1] ==
'\0')
goto string_end;
398 if (str[0] ==
'\0' || str[1] ==
'\0')
goto string_end;
400 string = ((uint8)*str++);
401 string |= ((uint8)*str++) << 8;
412 case 0x88: d +=
Utf8Encode(d, SCC_BLUE);
break;
413 case 0x89: d +=
Utf8Encode(d, SCC_SILVER);
break;
414 case 0x8A: d +=
Utf8Encode(d, SCC_GOLD);
break;
415 case 0x8B: d +=
Utf8Encode(d, SCC_RED);
break;
416 case 0x8C: d +=
Utf8Encode(d, SCC_PURPLE);
break;
417 case 0x8D: d +=
Utf8Encode(d, SCC_LTBROWN);
break;
418 case 0x8E: d +=
Utf8Encode(d, SCC_ORANGE);
break;
419 case 0x8F: d +=
Utf8Encode(d, SCC_GREEN);
break;
420 case 0x90: d +=
Utf8Encode(d, SCC_YELLOW);
break;
421 case 0x91: d +=
Utf8Encode(d, SCC_DKGREEN);
break;
422 case 0x92: d +=
Utf8Encode(d, SCC_CREAM);
break;
423 case 0x93: d +=
Utf8Encode(d, SCC_BROWN);
break;
424 case 0x94: d +=
Utf8Encode(d, SCC_WHITE);
break;
425 case 0x95: d +=
Utf8Encode(d, SCC_LTBLUE);
break;
426 case 0x96: d +=
Utf8Encode(d, SCC_GRAY);
break;
427 case 0x97: d +=
Utf8Encode(d, SCC_DKBLUE);
break;
428 case 0x98: d +=
Utf8Encode(d, SCC_BLACK);
break;
432 case 0x00:
goto string_end;
442 if (str[0] ==
'\0' || str[1] ==
'\0')
goto string_end;
443 uint16 tmp = ((uint8)*str++);
444 tmp |= ((uint8)*str++) << 8;
450 if (str[0] ==
'\0')
goto string_end;
463 if (str[0] ==
'\0')
goto string_end;
466 int mapped = lm !=
nullptr ? lm->
GetMapping(index, code == 0x0E) : -1;
468 d +=
Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
469 d +=
Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
476 if (str[0] ==
'\0')
goto string_end;
477 if (mapping ==
nullptr) {
478 if (code == 0x10) str++;
479 grfmsg(1,
"choice list %s marker found when not expected", code == 0x10 ?
"next" :
"default");
484 int index = (code == 0x10 ? *str++ : 0);
486 grfmsg(1,
"duplicate choice list string, ignoring");
489 d = mapping->
strings[index] = MallocT<char>(strlen(str) * 10 + 1);
495 if (mapping ==
nullptr) {
496 grfmsg(1,
"choice list end marker found when not expected");
511 if (str[0] ==
'\0')
goto string_end;
512 if (mapping !=
nullptr) {
513 grfmsg(1,
"choice lists can't be stacked, it's going to get messy now...");
514 if (code != 0x14) str++;
516 static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
533 case 0x1F: d +=
Utf8Encode(d, SCC_PUSH_COLOUR);
break;
534 case 0x20: d +=
Utf8Encode(d, SCC_POP_COLOUR);
break;
537 grfmsg(1,
"missing handler for extended format code");
545 case 0xA0: d +=
Utf8Encode(d, SCC_UP_ARROW);
break;
546 case 0xAA: d +=
Utf8Encode(d, SCC_DOWN_ARROW);
break;
547 case 0xAC: d +=
Utf8Encode(d, SCC_CHECKMARK);
break;
548 case 0xAD: d +=
Utf8Encode(d, SCC_CROSS);
break;
549 case 0xAF: d +=
Utf8Encode(d, SCC_RIGHT_ARROW);
break;
550 case 0xB4: d +=
Utf8Encode(d, SCC_TRAIN);
break;
551 case 0xB5: d +=
Utf8Encode(d, SCC_LORRY);
break;
552 case 0xB6: d +=
Utf8Encode(d, SCC_BUS);
break;
553 case 0xB7: d +=
Utf8Encode(d, SCC_PLANE);
break;
554 case 0xB8: d +=
Utf8Encode(d, SCC_SHIP);
break;
555 case 0xB9: d +=
Utf8Encode(d, SCC_SUPERSCRIPT_M1);
break;
556 case 0xBC: d +=
Utf8Encode(d, SCC_SMALL_UP_ARROW);
break;
557 case 0xBD: d +=
Utf8Encode(d, SCC_SMALL_DOWN_ARROW);
break;
567 if (mapping !=
nullptr) {
568 grfmsg(1,
"choice list was incomplete, the whole list is ignored");
573 if (olen !=
nullptr) *olen = d - tmp + 1;
588 for (ptext = list; (text = *ptext) !=
nullptr; ptext = &text->
next) {
591 *ptext = text_to_add;
598 *ptext = text_to_add;
615 free(translatedtext);
640 for (; orig !=
nullptr; orig = orig->
next) {
642 ptext = &(*ptext)->
next;
650 StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add,
bool new_scheme,
bool allow_newlines,
const char *text_to_add,
StringID def_string)
652 char *translatedtext;
662 if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
663 langid_to_add = GRFLX_ENGLISH;
666 if (langid_to_add & GRFLB_GERMAN) ret =
AddGRFString(grfid, stringid, GRFLX_GERMAN,
true, allow_newlines, text_to_add, def_string);
667 if (langid_to_add & GRFLB_FRENCH) ret =
AddGRFString(grfid, stringid, GRFLX_FRENCH,
true, allow_newlines, text_to_add, def_string);
668 if (langid_to_add & GRFLB_SPANISH) ret =
AddGRFString(grfid, stringid, GRFLX_SPANISH,
true, allow_newlines, text_to_add, def_string);
673 for (
id = 0;
id < _num_grf_texts;
id++) {
674 if (_grf_text[
id].grfid == grfid && _grf_text[
id].stringid == stringid) {
680 if (
id ==
lengthof(_grf_text))
return STR_EMPTY;
687 free(translatedtext);
690 if (
id == _num_grf_texts) _num_grf_texts++;
692 if (_grf_text[
id].textholder ==
nullptr) {
693 _grf_text[id].grfid = grfid;
694 _grf_text[id].stringid = stringid;
695 _grf_text[id].def_string = def_string;
709 for (uint
id = 0;
id < _num_grf_texts;
id++) {
710 if (_grf_text[
id].grfid == grfid && _grf_text[
id].stringid == stringid) {
715 return STR_UNDEFINED;
728 const char *default_text =
nullptr;
731 for (; text !=
nullptr; text = text->
next) {
736 if (text->
langid == GRFLX_UNSPECIFIED || (default_text ==
nullptr && (text->
langid == GRFLX_ENGLISH || text->
langid == GRFLX_AMERICAN))) {
737 default_text = text->
text;
749 assert(_grf_text[stringid].grfid != 0);
752 if (str !=
nullptr)
return str;
755 return GetStringPtr(_grf_text[stringid].def_string);
771 bool CheckGrfLangID(byte lang_id, byte grf_version)
773 if (grf_version < 7) {
775 case GRFLX_GERMAN:
return (lang_id & GRFLB_GERMAN) != 0;
776 case GRFLX_FRENCH:
return (lang_id & GRFLB_FRENCH) != 0;
777 case GRFLX_SPANISH:
return (lang_id & GRFLB_SPANISH) != 0;
778 default:
return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
782 return (lang_id ==
_currentLangID || lang_id == GRFLX_UNSPECIFIED);
791 while (grftext !=
nullptr) {
806 for (
id = 0;
id < _num_grf_texts;
id++) {
808 _grf_text[id].grfid = 0;
809 _grf_text[id].stringid = 0;
810 _grf_text[id].textholder =
nullptr;
817 std::array<byte, 0x30> stack;
822 TextRefStack() : position(0), grffile(
nullptr), used(
false) {}
824 uint8 PopUnsignedByte() { assert(this->position < this->stack.size());
return this->stack[this->position++]; }
825 int8 PopSignedByte() {
return (int8)this->PopUnsignedByte(); }
827 uint16 PopUnsignedWord()
829 uint16 val = this->PopUnsignedByte();
830 return val | (this->PopUnsignedByte() << 8);
832 int16 PopSignedWord() {
return (int32)this->PopUnsignedWord(); }
834 uint32 PopUnsignedDWord()
836 uint32 val = this->PopUnsignedWord();
837 return val | (this->PopUnsignedWord() << 16);
839 int32 PopSignedDWord() {
return (int32)this->PopUnsignedDWord(); }
841 uint64 PopUnsignedQWord()
843 uint64 val = this->PopUnsignedDWord();
844 return val | (((uint64)this->PopUnsignedDWord()) << 32);
846 int64 PopSignedQWord() {
return (int64)this->PopUnsignedQWord(); }
852 for (
int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6];
853 for (
int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
854 for (
int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i];
857 void PushWord(uint16 word)
859 if (this->position >= 2) {
863 std::rotate(this->stack.rbegin(), this->stack.rbegin() + 2, this->stack.rend());
865 this->stack[this->position] =
GB(word, 0, 8);
866 this->stack[this->position + 1] =
GB(word, 8, 8);
869 void ResetStack(
const GRFFile *grffile)
871 assert(grffile !=
nullptr);
873 this->grffile = grffile;
877 void RewindStack() { this->position = 0; }
889 return _newgrf_textrefstack.used;
907 _newgrf_textrefstack = *backup;
933 _newgrf_textrefstack.ResetStack(grffile);
935 auto stack_it = _newgrf_textrefstack.stack.begin();
936 for (uint i = 0; i < numEntries; i++) {
937 uint32 value = values !=
nullptr ? values[i] : _temp_store.
GetValue(0x100 + i);
938 for (uint j = 0; j < 32; j += 8) {
939 *stack_it =
GB(value, j, 8);
948 _newgrf_textrefstack.used =
false;
951 void RewindTextRefStack()
953 _newgrf_textrefstack.RewindStack();
995 DEBUG(misc, 0,
"Too many NewGRF string parameters.");
1003 if (argv_size < 2) {
1004 DEBUG(misc, 0,
"Too many NewGRF string parameters.");
1010 if (_newgrf_textrefstack.used && modify_argv) {
1012 default: NOT_REACHED();
1050 argv[0] =
GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1051 argv[1] = _newgrf_textrefstack.PopUnsignedWord();
1055 *argv =
MapGRFStringID(_newgrf_textrefstack.grffile->grfid, _newgrf_textrefstack.PopUnsignedWord());
1060 *argv = cargo <
NUM_CARGO ? 1 << cargo : 0;
1077 default: NOT_REACHED();
1092 return SCC_CURRENCY_LONG;
1099 return SCC_DATE_LONG;
1103 return SCC_DATE_SHORT;
1106 return SCC_VELOCITY;
1109 return SCC_VOLUME_LONG;
1112 return SCC_VOLUME_SHORT;
1115 return SCC_WEIGHT_LONG;
1118 return SCC_WEIGHT_SHORT;
1124 return SCC_CARGO_LONG;
1127 return SCC_CARGO_SHORT;
1130 return SCC_CARGO_TINY;
1133 return SCC_CARGO_LIST;
1136 return SCC_STATION_NAME;
Functions related to OTTD's strings.
9A 17: Read 4 bytes from the stack as base 0 date
9A 03: Pushes 2 bytes onto the stack
Inline another string at the current position, StringID is encoded in the string. ...
int offset
The offset for the plural/gender form.
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.
Control codes that are embedded in the translation strings.
bool UsingNewGRFTextStack()
Check whether the NewGRF text stack is in use.
int plural_form
The plural form used for this language.
#define DAYS_TILL_ORIGINAL_BASE_YEAR
The offset in days from the '_date == 0' till 'ConvertYMDToDate(ORIGINAL_BASE_YEAR, 0, 1)'.
Functionality related to the temporary and persistent storage arrays for NewGRFs. ...
StringControlCode
List of string control codes used for string formatting, displaying, and by strgen to generate the la...
Functions related to debugging.
void SetCurrentGrfLangID(byte language_id)
Equivalence Setter function between game and newgrf langID.
CargoID GetCargoTranslation(uint8 cargo, const GRFFile *grffile, bool usebit)
Translate a GRF-local cargo slot/bitnum into a CargoID.
Class for temporary storage of data.
char * TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const char *str, int *olen, StringControlCode byte80)
Translate TTDPatch string codes into something OpenTTD can handle (better).
Maximal number of cargo types in a game.
static byte _currentLangID
by default, english is used.
GRFText * next
The next GRFText in this chain.
const LanguageMetadata * _current_language
The currently loaded language.
Implementation of simple mapping class.
83: Read 2 bytes from the stack as base 1920 date
86: Rotate the top 4 words of the stack (W4 W1 W2 W3)
82: Read 2 bytes from the stack as base 1920 date
9A 1C: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount ...
static StringID MakeStringID(StringTab tab, uint index)
Create a StringID.
size_t Utf8Decode(WChar *c, const char *s)
Decode and consume the next UTF-8 encoded character.
9A 04: "Unprints" the given number of bytes from the string
StringID GetGRFStringID(uint32 grfid, StringID stringid)
Returns the index for this stringid associated with its grfID.
Helper types related to the allocation of memory.
static T max(const T a, const T b)
Returns the maximum of two values.
static const LanguageMap * GetLanguageMap(uint32 grfid, uint8 language_id)
Get the language map associated with a given NewGRF and language.
Simple mapping class targeted for small sets of data.
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.
Mapping between NewGRF and OpenTTD IDs.
static int8 Utf8EncodedCharLen(char c)
Return the length of an UTF-8 encoded value based on a single char.
Header of Action 04 "universal holder" structure and functions.
size_t len
The length of the stored string, used for copying.
9A 0C: Read 2 bytes from the stack as station name
Functions related to low-level strings.
bool IsValidChar(WChar key, CharSetFilter afilter)
Only allow certain keys.
9A 07: Read 2 bytes from the stack and print it as hex
static GRFText * New(byte langid, const char *text, size_t len)
Allocate, and assign a new GRFText with the given text.
~UnmappedChoiceList()
Clean everything up.
9A 1B: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount ...
void AddGRFTextToList(GRFText **list, GRFText *text_to_add)
Add a GRFText to a GRFText list.
9A 18: Read 2 bytes from the stack as unsigned power
StringControlCode type
The type of choice list.
9A 1A: Read 2 bytes from the stack as short unsigned weight
81: Read 2 bytes from the stack as String ID
static const uint TAB_SIZE_NEWGRF
Number of strings for NewGRFs.
87: Read 2 bytes from the stack as long signed volume
void CleanUpGRFText(GRFText *grftext)
Delete all items of a linked GRFText list.
bool Contains(const T &key) const
Tests whether a key is assigned in this map.
Mapping of language data between a NewGRF and OpenTTD.
Definition of base types and functions in a cross-platform compatible way.
A number of safeguards to prevent using unsafe methods.
char * old_d
The old/original location of the "d" local variable.
void RotateTop4Words()
Rotate the top four words down: W1, W2, W3, W4 -> W4, W1, W2, W3.
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 ...
9A 08: Read 4 bytes from the stack and print it as hex
9A 0B: Read 8 bytes from the stack and print it as hex
void CleanUpStrings()
House cleaning.
Information about languages and their files.
9A 1E: Read 2 bytes from the stack as cargo name
static T * ReallocT(T *t_ptr, size_t num_elements)
Simplified reallocation function that allocates the specified number of elements of the given type...
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
byte langid
The language associated with this GRFText.
GRFBaseLanguages
Explains the newgrf shift bit positioning.
#define lengthof(x)
Return the length of an fixed size array.
static TextRefStack _newgrf_textrefstack
The stack that is used for TTDP compatible string code parsing.
Element of the linked list.
void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32 *values)
Start using the TTDP compatible string code parsing.
Base class that provides memory initialization on dynamically created objects.
uint32 StringID
Numeric value that represents a string, independent of the selected language.
int GetReverseMapping(int openttd_id, bool gender) const
Get the mapping from OpenTTD's internal ID to the NewGRF supplied ID.
char * Flush(const LanguageMap *lm)
Flush this choice list into the old d variable.
char text[]
The actual (translated) text.
Helper structure for mapping choice lists.
#define DEBUG(name, level,...)
Output a line of debugging information.
GRFText * DuplicateGRFText(GRFText *orig)
Create a copy of this GRFText list.
7C: Read 2 bytes from the stack as signed value
const char * GetGRFStringFromGRFText(const GRFText *text)
Get a C-string from a GRFText-list.
Holder of the above structure.
85: Discard the next two bytes
Both numeric and alphabetic and spaces and stuff.
static const WChar NFO_UTF8_IDENTIFIER
This character, the thorn ('รพ'), indicates a unicode string to NFO.
9A 16: Read 4 bytes from the stack as base 0 date
7D: Read 1 byte from the stack as signed value
Cargo support for NewGRFs.
7B: Read 4 bytes from the stack
int GetMapping(int newgrf_id, bool gender) const
Get the mapping from the NewGRF supplied ID to OpenTTD's internal ID.
void CDECL grfmsg(int severity, const char *str,...)
DEBUG() function dedicated to newGRF debugging messages Function is essentially the same as DEBUG(grf...
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
GRFText(byte langid_, const char *text_, size_t len_)
Actually construct the GRFText.
void RestoreTextRefStackBackup(struct TextRefStack *backup)
Restore a copy of the text stack to the used stack.
UnmappedChoiceList(StringControlCode type, char *old_d, int offset)
Initialise the mapping.
const char * GetGRFStringPtr(uint16 stringid)
Get a C-string from a stringid set by a newgrf.
Start of NewGRF supplied strings.
8F: Read 4 bytes from the stack as currency
9A 01: Read 8 bytes from the stack as currency
struct TextRefStack * CreateTextRefStackBackup()
Create a backup of the current NewGRF text stack.
84: Read 2 bytes from the stack as signed speed
9A 0D: Read 2 bytes from the stack as long unsigned weight
Types related to the dates in OpenTTD.
size_t Utf8Encode(char *buf, WChar c)
Encode a unicode character and place it in the buffer.
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
9A 06: Read 1 byte from the stack and print it as hex
TYPE GetValue(uint pos) const
Gets the value from a given position.
byte CargoID
Cargo slots to indicate a cargo type within a game.
7E: Read 2 bytes from the stack as unsigned value
9A 19: Read 2 bytes from the stack as short signed volume
uint32 WChar
Type for wide characters, i.e.
SmallMap< byte, char * > strings
Mapping of NewGRF supplied ID to the different strings in the choice list.
static GRFText * Copy(GRFText *orig)
Create a copy of this GRFText.
9A 1D: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount ...
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
Dynamic data of a loaded NewGRF.
Base for the NewGRF implementation.