33 #include "table/strings.h" 44 GRFLB_AMERICAN = 0x01,
52 enum GRFExtendedLanguages {
53 GRFLX_AMERICAN = 0x00,
58 GRFLX_UNSPECIFIED = 0x7F,
98 void *
operator new(
size_t size)
107 void operator delete(
void *p)
123 memcpy(this->
text, text_,
len);
132 void *
operator new(
size_t size,
size_t extra)
134 return MallocT<byte>(size + extra);
158 static uint _num_grf_texts = 0;
172 if (m->newgrf_id == newgrf_id)
return m->openttd_id;
187 if (m->openttd_id == openttd_id)
return m->newgrf_id;
209 type(type), old_d(old_d), offset(offset)
230 grfmsg(1,
"choice list misses default value");
231 this->strings[0] =
stredup(
"");
239 size_t len = strlen(this->strings[0]);
240 memcpy(d, this->strings[0], len);
246 if (this->type == SCC_SWITCH_CASE) {
264 if (!this->strings.
Contains(idx))
continue;
265 char *str = this->strings[idx];
271 size_t len = strlen(str) + 1;
272 *d++ =
GB(len, 8, 8);
273 *d++ =
GB(len, 0, 8);
281 size_t len = strlen(this->strings[0]) + 1;
282 memcpy(d, this->strings[0], len);
285 if (this->type == SCC_PLURAL_LIST) {
295 *d++ = this->offset - 0x80;
302 for (
int i = 0; i < count; i++) {
303 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
304 const char *str = this->strings[this->strings.
Contains(idx) ? idx : 0];
305 size_t len = strlen(str) + 1;
306 if (len > 0xFF)
grfmsg(1,
"choice list string is too long");
307 *d++ =
GB(len, 0, 8);
311 for (
int i = 0; i < count; i++) {
312 int idx = (this->type == SCC_GENDER_LIST ? lm->
GetReverseMapping(i,
true) : i + 1);
313 const char *str = this->strings[this->strings.
Contains(idx) ? idx : 0];
316 size_t len = min<size_t>(0xFE, strlen(str));
338 char *tmp = MallocT<char>(strlen(str) * 10 + 1);
340 bool unicode =
false;
354 c = Utf8Consume(&str);
356 if (
GB(c, 8, 8) == 0xE0) {
358 }
else if (c >= 0x20) {
366 if (c ==
'\0')
break;
370 if (str[0] ==
'\0')
goto string_end;
376 if (allow_newlines) {
379 grfmsg(1,
"Detected newline in string that does not allow one");
385 if (str[0] ==
'\0' || str[1] ==
'\0')
goto string_end;
396 if (str[0] ==
'\0' || str[1] ==
'\0')
goto string_end;
398 string = ((uint8)*str++);
399 string |= ((uint8)*str++) << 8;
410 case 0x88: d +=
Utf8Encode(d, SCC_BLUE);
break;
411 case 0x89: d +=
Utf8Encode(d, SCC_SILVER);
break;
412 case 0x8A: d +=
Utf8Encode(d, SCC_GOLD);
break;
413 case 0x8B: d +=
Utf8Encode(d, SCC_RED);
break;
414 case 0x8C: d +=
Utf8Encode(d, SCC_PURPLE);
break;
415 case 0x8D: d +=
Utf8Encode(d, SCC_LTBROWN);
break;
416 case 0x8E: d +=
Utf8Encode(d, SCC_ORANGE);
break;
417 case 0x8F: d +=
Utf8Encode(d, SCC_GREEN);
break;
418 case 0x90: d +=
Utf8Encode(d, SCC_YELLOW);
break;
419 case 0x91: d +=
Utf8Encode(d, SCC_DKGREEN);
break;
420 case 0x92: d +=
Utf8Encode(d, SCC_CREAM);
break;
421 case 0x93: d +=
Utf8Encode(d, SCC_BROWN);
break;
422 case 0x94: d +=
Utf8Encode(d, SCC_WHITE);
break;
423 case 0x95: d +=
Utf8Encode(d, SCC_LTBLUE);
break;
424 case 0x96: d +=
Utf8Encode(d, SCC_GRAY);
break;
425 case 0x97: d +=
Utf8Encode(d, SCC_DKBLUE);
break;
426 case 0x98: d +=
Utf8Encode(d, SCC_BLACK);
break;
430 case 0x00:
goto string_end;
440 if (str[0] ==
'\0' || str[1] ==
'\0')
goto string_end;
441 uint16 tmp = ((uint8)*str++);
442 tmp |= ((uint8)*str++) << 8;
448 if (str[0] ==
'\0')
goto string_end;
461 if (str[0] ==
'\0')
goto string_end;
464 int mapped = lm != NULL ? lm->
GetMapping(index, code == 0x0E) : -1;
466 d +=
Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
467 d +=
Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
474 if (str[0] ==
'\0')
goto string_end;
475 if (mapping == NULL) {
476 if (code == 0x10) str++;
477 grfmsg(1,
"choice list %s marker found when not expected", code == 0x10 ?
"next" :
"default");
482 int index = (code == 0x10 ? *str++ : 0);
484 grfmsg(1,
"duplicate choice list string, ignoring");
487 d = mapping->
strings[index] = MallocT<char>(strlen(str) * 10 + 1);
493 if (mapping == NULL) {
494 grfmsg(1,
"choice list end marker found when not expected");
509 if (str[0] ==
'\0')
goto string_end;
510 if (mapping != NULL) {
511 grfmsg(1,
"choice lists can't be stacked, it's going to get messy now...");
512 if (code != 0x14) str++;
514 static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
532 grfmsg(1,
"missing handler for extended format code");
540 case 0xA0: d +=
Utf8Encode(d, SCC_UP_ARROW);
break;
541 case 0xAA: d +=
Utf8Encode(d, SCC_DOWN_ARROW);
break;
542 case 0xAC: d +=
Utf8Encode(d, SCC_CHECKMARK);
break;
543 case 0xAD: d +=
Utf8Encode(d, SCC_CROSS);
break;
544 case 0xAF: d +=
Utf8Encode(d, SCC_RIGHT_ARROW);
break;
545 case 0xB4: d +=
Utf8Encode(d, SCC_TRAIN);
break;
546 case 0xB5: d +=
Utf8Encode(d, SCC_LORRY);
break;
547 case 0xB6: d +=
Utf8Encode(d, SCC_BUS);
break;
548 case 0xB7: d +=
Utf8Encode(d, SCC_PLANE);
break;
549 case 0xB8: d +=
Utf8Encode(d, SCC_SHIP);
break;
550 case 0xB9: d +=
Utf8Encode(d, SCC_SUPERSCRIPT_M1);
break;
551 case 0xBC: d +=
Utf8Encode(d, SCC_SMALL_UP_ARROW);
break;
552 case 0xBD: d +=
Utf8Encode(d, SCC_SMALL_DOWN_ARROW);
break;
562 if (mapping != NULL) {
563 grfmsg(1,
"choice list was incomplete, the whole list is ignored");
568 if (olen != NULL) *olen = d - tmp + 1;
583 for (ptext = list; (text = *ptext) != NULL; ptext = &text->
next) {
586 *ptext = text_to_add;
593 *ptext = text_to_add;
610 free(translatedtext);
635 for (; orig != NULL; orig = orig->
next) {
637 ptext = &(*ptext)->
next;
645 StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add,
bool new_scheme,
bool allow_newlines,
const char *text_to_add,
StringID def_string)
647 char *translatedtext;
657 if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
658 langid_to_add = GRFLX_ENGLISH;
661 if (langid_to_add & GRFLB_GERMAN) ret =
AddGRFString(grfid, stringid, GRFLX_GERMAN,
true, allow_newlines, text_to_add, def_string);
662 if (langid_to_add & GRFLB_FRENCH) ret =
AddGRFString(grfid, stringid, GRFLX_FRENCH,
true, allow_newlines, text_to_add, def_string);
663 if (langid_to_add & GRFLB_SPANISH) ret =
AddGRFString(grfid, stringid, GRFLX_SPANISH,
true, allow_newlines, text_to_add, def_string);
668 for (
id = 0;
id < _num_grf_texts;
id++) {
669 if (_grf_text[
id].grfid == grfid && _grf_text[
id].stringid == stringid) {
675 if (
id ==
lengthof(_grf_text))
return STR_EMPTY;
682 free(translatedtext);
685 if (
id == _num_grf_texts) _num_grf_texts++;
687 if (_grf_text[
id].textholder == NULL) {
688 _grf_text[id].grfid = grfid;
689 _grf_text[id].stringid = stringid;
690 _grf_text[id].def_string = def_string;
704 for (uint
id = 0;
id < _num_grf_texts;
id++) {
705 if (_grf_text[
id].grfid == grfid && _grf_text[
id].stringid == stringid) {
710 return STR_UNDEFINED;
723 const char *default_text = NULL;
726 for (; text != NULL; text = text->
next) {
731 if (text->
langid == GRFLX_UNSPECIFIED || (default_text == NULL && (text->
langid == GRFLX_ENGLISH || text->
langid == GRFLX_AMERICAN))) {
732 default_text = text->
text;
744 assert(_grf_text[stringid].grfid != 0);
747 if (str != NULL)
return str;
750 return GetStringPtr(_grf_text[stringid].def_string);
766 bool CheckGrfLangID(byte lang_id, byte grf_version)
768 if (grf_version < 7) {
770 case GRFLX_GERMAN:
return (lang_id & GRFLB_GERMAN) != 0;
771 case GRFLX_FRENCH:
return (lang_id & GRFLB_FRENCH) != 0;
772 case GRFLX_SPANISH:
return (lang_id & GRFLB_SPANISH) != 0;
773 default:
return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
777 return (lang_id ==
_currentLangID || lang_id == GRFLX_UNSPECIFIED);
786 while (grftext != NULL) {
801 for (
id = 0;
id < _num_grf_texts;
id++) {
803 _grf_text[id].grfid = 0;
804 _grf_text[id].stringid = 0;
805 _grf_text[id].textholder = NULL;
817 TextRefStack() : position(0), grffile(NULL), used(
false) {}
820 position(stack.position),
821 grffile(stack.grffile),
824 memcpy(this->stack, stack.stack,
sizeof(this->stack));
827 uint8 PopUnsignedByte() { assert(this->position <
lengthof(this->stack));
return this->stack[this->position++]; }
828 int8 PopSignedByte() {
return (int8)this->PopUnsignedByte(); }
830 uint16 PopUnsignedWord()
832 uint16 val = this->PopUnsignedByte();
833 return val | (this->PopUnsignedByte() << 8);
835 int16 PopSignedWord() {
return (int32)this->PopUnsignedWord(); }
837 uint32 PopUnsignedDWord()
839 uint32 val = this->PopUnsignedWord();
840 return val | (this->PopUnsignedWord() << 16);
842 int32 PopSignedDWord() {
return (int32)this->PopUnsignedDWord(); }
844 uint64 PopUnsignedQWord()
846 uint64 val = this->PopUnsignedDWord();
847 return val | (((uint64)this->PopUnsignedDWord()) << 32);
849 int64 PopSignedQWord() {
return (int64)this->PopUnsignedQWord(); }
855 for (
int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6];
856 for (
int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
857 for (
int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i];
860 void PushWord(uint16 word)
862 if (this->position >= 2) {
865 for (
int i =
lengthof(stack) - 1; i >= this->position + 2; i--) {
866 this->stack[i] = this->stack[i - 2];
869 this->stack[this->position] =
GB(word, 0, 8);
870 this->stack[this->position + 1] =
GB(word, 8, 8);
873 void ResetStack(
const GRFFile *grffile)
875 assert(grffile != NULL);
877 this->grffile = grffile;
881 void RewindStack() { this->position = 0; }
893 return _newgrf_textrefstack.used;
911 _newgrf_textrefstack = *backup;
937 _newgrf_textrefstack.ResetStack(grffile);
939 byte *p = _newgrf_textrefstack.stack;
940 for (uint i = 0; i < numEntries; i++) {
941 uint32 value = values != NULL ? values[i] : _temp_store.
GetValue(0x100 + i);
942 for (uint j = 0; j < 32; j += 8) {
943 *p =
GB(value, j, 8);
952 _newgrf_textrefstack.used =
false;
955 void RewindTextRefStack()
957 _newgrf_textrefstack.RewindStack();
999 DEBUG(misc, 0,
"Too many NewGRF string parameters.");
1007 if (argv_size < 2) {
1008 DEBUG(misc, 0,
"Too many NewGRF string parameters.");
1014 if (_newgrf_textrefstack.used && modify_argv) {
1016 default: NOT_REACHED();
1054 argv[0] =
GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1055 argv[1] = _newgrf_textrefstack.PopUnsignedWord();
1059 *argv =
MapGRFStringID(_newgrf_textrefstack.grffile->grfid, _newgrf_textrefstack.PopUnsignedWord());
1064 *argv = cargo <
NUM_CARGO ? 1 << cargo : 0;
1081 default: NOT_REACHED();
1096 return SCC_CURRENCY_LONG;
1103 return SCC_DATE_LONG;
1107 return SCC_DATE_SHORT;
1110 return SCC_VELOCITY;
1113 return SCC_VOLUME_LONG;
1116 return SCC_VOLUME_SHORT;
1119 return SCC_WEIGHT_LONG;
1122 return SCC_WEIGHT_SHORT;
1128 return SCC_CARGO_LONG;
1131 return SCC_CARGO_SHORT;
1134 return SCC_CARGO_TINY;
1137 return SCC_CARGO_LIST;
1140 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.
bool Contains(const T &key) const
Tests whether a key is assigned in this map.
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).
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)
const T * Begin() const
Get the pointer to the first item (const)
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
Simple vector template class.
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.
const T * End() const
Get the pointer behind the last valid item (const)
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.
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.
Maximal number of cargo types in a game.
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.