37 #include "table/strings.h" 48 GRFLB_AMERICAN = 0x01,
56 enum GRFExtendedLanguages {
57 GRFLX_AMERICAN = 0x00,
62 GRFLX_UNSPECIFIED = 0x7F,
102 void *
operator new(
size_t size)
111 void operator delete(
void *p)
127 memcpy(this->
text, text_,
len);
136 void *
operator new(
size_t size,
size_t extra)
138 return MallocT<byte>(size + extra);
162 static uint _num_grf_texts = 0;
174 const std::vector<Mapping> &map = gender ? this->gender_map : this->case_map;
176 if (m.newgrf_id == newgrf_id)
return m.openttd_id;
189 const std::vector<Mapping> &map = gender ? this->gender_map : this->case_map;
191 if (m.openttd_id == openttd_id)
return m.newgrf_id;
213 type(type), old_d(old_d), offset(offset)
234 grfmsg(1,
"choice list misses default value");
235 this->strings[0] =
stredup(
"");
243 size_t len = strlen(this->strings[0]);
244 memcpy(d, this->strings[0], len);
250 if (this->type == SCC_SWITCH_CASE) {
268 if (!this->strings.
Contains(idx))
continue;
269 char *str = this->strings[idx];
275 size_t len = strlen(str) + 1;
276 *d++ =
GB(len, 8, 8);
277 *d++ =
GB(len, 0, 8);
285 size_t len = strlen(this->strings[0]) + 1;
286 memcpy(d, this->strings[0], len);
289 if (this->type == SCC_PLURAL_LIST) {
299 *d++ = this->offset - 0x80;
306 for (
int i = 0; i < count; i++) {
307 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
308 const char *str = this->strings[this->strings.
Contains(idx) ? idx : 0];
309 size_t len = strlen(str) + 1;
310 if (len > 0xFF)
grfmsg(1,
"choice list string is too long");
311 *d++ =
GB(len, 0, 8);
315 for (
int i = 0; i < count; i++) {
316 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
317 const char *str = this->strings[this->strings.
Contains(idx) ? idx : 0];
320 size_t len = min<size_t>(0xFE, strlen(str));
342 char *tmp = MallocT<char>(strlen(str) * 10 + 1);
344 bool unicode =
false;
358 c = Utf8Consume(&str);
360 if (
GB(c, 8, 8) == 0xE0) {
362 }
else if (c >= 0x20) {
370 if (c ==
'\0')
break;
374 if (str[0] ==
'\0')
goto string_end;
380 if (allow_newlines) {
383 grfmsg(1,
"Detected newline in string that does not allow one");
389 if (str[0] ==
'\0' || str[1] ==
'\0')
goto string_end;
400 if (str[0] ==
'\0' || str[1] ==
'\0')
goto string_end;
402 string = ((uint8)*str++);
403 string |= ((uint8)*str++) << 8;
414 case 0x88: d +=
Utf8Encode(d, SCC_BLUE);
break;
415 case 0x89: d +=
Utf8Encode(d, SCC_SILVER);
break;
416 case 0x8A: d +=
Utf8Encode(d, SCC_GOLD);
break;
417 case 0x8B: d +=
Utf8Encode(d, SCC_RED);
break;
418 case 0x8C: d +=
Utf8Encode(d, SCC_PURPLE);
break;
419 case 0x8D: d +=
Utf8Encode(d, SCC_LTBROWN);
break;
420 case 0x8E: d +=
Utf8Encode(d, SCC_ORANGE);
break;
421 case 0x8F: d +=
Utf8Encode(d, SCC_GREEN);
break;
422 case 0x90: d +=
Utf8Encode(d, SCC_YELLOW);
break;
423 case 0x91: d +=
Utf8Encode(d, SCC_DKGREEN);
break;
424 case 0x92: d +=
Utf8Encode(d, SCC_CREAM);
break;
425 case 0x93: d +=
Utf8Encode(d, SCC_BROWN);
break;
426 case 0x94: d +=
Utf8Encode(d, SCC_WHITE);
break;
427 case 0x95: d +=
Utf8Encode(d, SCC_LTBLUE);
break;
428 case 0x96: d +=
Utf8Encode(d, SCC_GRAY);
break;
429 case 0x97: d +=
Utf8Encode(d, SCC_DKBLUE);
break;
430 case 0x98: d +=
Utf8Encode(d, SCC_BLACK);
break;
434 case 0x00:
goto string_end;
444 if (str[0] ==
'\0' || str[1] ==
'\0')
goto string_end;
445 uint16 tmp = ((uint8)*str++);
446 tmp |= ((uint8)*str++) << 8;
452 if (str[0] ==
'\0')
goto string_end;
465 if (str[0] ==
'\0')
goto string_end;
468 int mapped = lm !=
nullptr ? lm->
GetMapping(index, code == 0x0E) : -1;
470 d +=
Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
471 d +=
Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
478 if (str[0] ==
'\0')
goto string_end;
479 if (mapping ==
nullptr) {
480 if (code == 0x10) str++;
481 grfmsg(1,
"choice list %s marker found when not expected", code == 0x10 ?
"next" :
"default");
486 int index = (code == 0x10 ? *str++ : 0);
488 grfmsg(1,
"duplicate choice list string, ignoring");
491 d = mapping->
strings[index] = MallocT<char>(strlen(str) * 10 + 1);
497 if (mapping ==
nullptr) {
498 grfmsg(1,
"choice list end marker found when not expected");
513 if (str[0] ==
'\0')
goto string_end;
514 if (mapping !=
nullptr) {
515 grfmsg(1,
"choice lists can't be stacked, it's going to get messy now...");
516 if (code != 0x14) str++;
518 static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
535 case 0x1F: d +=
Utf8Encode(d, SCC_PUSH_COLOUR);
break;
536 case 0x20: d +=
Utf8Encode(d, SCC_POP_COLOUR);
break;
539 grfmsg(1,
"missing handler for extended format code");
547 case 0xA0: d +=
Utf8Encode(d, SCC_UP_ARROW);
break;
548 case 0xAA: d +=
Utf8Encode(d, SCC_DOWN_ARROW);
break;
549 case 0xAC: d +=
Utf8Encode(d, SCC_CHECKMARK);
break;
550 case 0xAD: d +=
Utf8Encode(d, SCC_CROSS);
break;
551 case 0xAF: d +=
Utf8Encode(d, SCC_RIGHT_ARROW);
break;
552 case 0xB4: d +=
Utf8Encode(d, SCC_TRAIN);
break;
553 case 0xB5: d +=
Utf8Encode(d, SCC_LORRY);
break;
554 case 0xB6: d +=
Utf8Encode(d, SCC_BUS);
break;
555 case 0xB7: d +=
Utf8Encode(d, SCC_PLANE);
break;
556 case 0xB8: d +=
Utf8Encode(d, SCC_SHIP);
break;
557 case 0xB9: d +=
Utf8Encode(d, SCC_SUPERSCRIPT_M1);
break;
558 case 0xBC: d +=
Utf8Encode(d, SCC_SMALL_UP_ARROW);
break;
559 case 0xBD: d +=
Utf8Encode(d, SCC_SMALL_DOWN_ARROW);
break;
569 if (mapping !=
nullptr) {
570 grfmsg(1,
"choice list was incomplete, the whole list is ignored");
575 if (olen !=
nullptr) *olen = d - tmp + 1;
590 for (ptext = list; (text = *ptext) !=
nullptr; ptext = &text->
next) {
593 *ptext = text_to_add;
600 *ptext = text_to_add;
617 free(translatedtext);
642 for (; orig !=
nullptr; orig = orig->
next) {
644 ptext = &(*ptext)->
next;
652 StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add,
bool new_scheme,
bool allow_newlines,
const char *text_to_add,
StringID def_string)
654 char *translatedtext;
664 if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
665 langid_to_add = GRFLX_ENGLISH;
668 if (langid_to_add & GRFLB_GERMAN) ret =
AddGRFString(grfid, stringid, GRFLX_GERMAN,
true, allow_newlines, text_to_add, def_string);
669 if (langid_to_add & GRFLB_FRENCH) ret =
AddGRFString(grfid, stringid, GRFLX_FRENCH,
true, allow_newlines, text_to_add, def_string);
670 if (langid_to_add & GRFLB_SPANISH) ret =
AddGRFString(grfid, stringid, GRFLX_SPANISH,
true, allow_newlines, text_to_add, def_string);
675 for (
id = 0;
id < _num_grf_texts;
id++) {
676 if (_grf_text[
id].grfid == grfid && _grf_text[
id].stringid == stringid) {
682 if (
id ==
lengthof(_grf_text))
return STR_EMPTY;
689 free(translatedtext);
692 if (
id == _num_grf_texts) _num_grf_texts++;
694 if (_grf_text[
id].textholder ==
nullptr) {
695 _grf_text[id].grfid = grfid;
696 _grf_text[id].stringid = stringid;
697 _grf_text[id].def_string = def_string;
711 for (uint
id = 0;
id < _num_grf_texts;
id++) {
712 if (_grf_text[
id].grfid == grfid && _grf_text[
id].stringid == stringid) {
717 return STR_UNDEFINED;
730 const char *default_text =
nullptr;
733 for (; text !=
nullptr; text = text->
next) {
738 if (text->
langid == GRFLX_UNSPECIFIED || (default_text ==
nullptr && (text->
langid == GRFLX_ENGLISH || text->
langid == GRFLX_AMERICAN))) {
739 default_text = text->
text;
751 assert(_grf_text[stringid].grfid != 0);
754 if (str !=
nullptr)
return str;
757 return GetStringPtr(_grf_text[stringid].def_string);
773 bool CheckGrfLangID(byte lang_id, byte grf_version)
775 if (grf_version < 7) {
777 case GRFLX_GERMAN:
return (lang_id & GRFLB_GERMAN) != 0;
778 case GRFLX_FRENCH:
return (lang_id & GRFLB_FRENCH) != 0;
779 case GRFLX_SPANISH:
return (lang_id & GRFLB_SPANISH) != 0;
780 default:
return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
784 return (lang_id ==
_currentLangID || lang_id == GRFLX_UNSPECIFIED);
793 while (grftext !=
nullptr) {
808 for (
id = 0;
id < _num_grf_texts;
id++) {
810 _grf_text[id].grfid = 0;
811 _grf_text[id].stringid = 0;
812 _grf_text[id].textholder =
nullptr;
819 std::array<byte, 0x30> stack;
824 TextRefStack() : position(0), grffile(
nullptr), used(
false) {}
826 uint8 PopUnsignedByte() { assert(this->position < this->stack.size());
return this->stack[this->position++]; }
827 int8 PopSignedByte() {
return (int8)this->PopUnsignedByte(); }
829 uint16 PopUnsignedWord()
831 uint16 val = this->PopUnsignedByte();
832 return val | (this->PopUnsignedByte() << 8);
834 int16 PopSignedWord() {
return (int32)this->PopUnsignedWord(); }
836 uint32 PopUnsignedDWord()
838 uint32 val = this->PopUnsignedWord();
839 return val | (this->PopUnsignedWord() << 16);
841 int32 PopSignedDWord() {
return (int32)this->PopUnsignedDWord(); }
843 uint64 PopUnsignedQWord()
845 uint64 val = this->PopUnsignedDWord();
846 return val | (((uint64)this->PopUnsignedDWord()) << 32);
848 int64 PopSignedQWord() {
return (int64)this->PopUnsignedQWord(); }
854 for (
int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6];
855 for (
int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
856 for (
int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i];
859 void PushWord(uint16 word)
861 if (this->position >= 2) {
865 std::rotate(this->stack.rbegin(), this->stack.rbegin() + 2, this->stack.rend());
867 this->stack[this->position] =
GB(word, 0, 8);
868 this->stack[this->position + 1] =
GB(word, 8, 8);
871 void ResetStack(
const GRFFile *grffile)
873 assert(grffile !=
nullptr);
875 this->grffile = grffile;
879 void RewindStack() { this->position = 0; }
891 return _newgrf_textrefstack.used;
909 _newgrf_textrefstack = *backup;
935 _newgrf_textrefstack.ResetStack(grffile);
937 auto stack_it = _newgrf_textrefstack.stack.begin();
938 for (uint i = 0; i < numEntries; i++) {
939 uint32 value = values !=
nullptr ? values[i] : _temp_store.
GetValue(0x100 + i);
940 for (uint j = 0; j < 32; j += 8) {
941 *stack_it =
GB(value, j, 8);
950 _newgrf_textrefstack.used =
false;
953 void RewindTextRefStack()
955 _newgrf_textrefstack.RewindStack();
997 DEBUG(misc, 0,
"Too many NewGRF string parameters.");
1005 if (argv_size < 2) {
1006 DEBUG(misc, 0,
"Too many NewGRF string parameters.");
1012 if (_newgrf_textrefstack.used && modify_argv) {
1014 default: NOT_REACHED();
1052 argv[0] =
GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1053 argv[1] = _newgrf_textrefstack.PopUnsignedWord();
1057 *argv =
MapGRFStringID(_newgrf_textrefstack.grffile->grfid, _newgrf_textrefstack.PopUnsignedWord());
1062 *argv = cargo <
NUM_CARGO ? 1 << cargo : 0;
1079 default: NOT_REACHED();
1094 return SCC_CURRENCY_LONG;
1101 return SCC_DATE_LONG;
1105 return SCC_DATE_SHORT;
1108 return SCC_VELOCITY;
1111 return SCC_VOLUME_LONG;
1114 return SCC_VOLUME_SHORT;
1117 return SCC_WEIGHT_LONG;
1120 return SCC_WEIGHT_SHORT;
1126 return SCC_CARGO_LONG;
1129 return SCC_CARGO_SHORT;
1132 return SCC_CARGO_TINY;
1135 return SCC_CARGO_LIST;
1138 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.