OpenTTD
newgrf_text.cpp
Go to the documentation of this file.
1 /* $Id$ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8  */
9 
20 #include "stdafx.h"
21 
22 #include <algorithm>
23 #include <array>
24 
25 #include "newgrf.h"
26 #include "strings_func.h"
27 #include "newgrf_storage.h"
28 #include "newgrf_text.h"
29 #include "newgrf_cargo.h"
30 #include "string_func.h"
31 #include "date_type.h"
32 #include "debug.h"
33 #include "core/alloc_type.hpp"
34 #include "core/smallmap_type.hpp"
35 #include "language.h"
36 
37 #include "table/strings.h"
38 #include "table/control_codes.h"
39 
40 #include "safeguards.h"
41 
48  GRFLB_AMERICAN = 0x01,
49  GRFLB_ENGLISH = 0x02,
50  GRFLB_GERMAN = 0x04,
51  GRFLB_FRENCH = 0x08,
52  GRFLB_SPANISH = 0x10,
53  GRFLB_GENERIC = 0x80,
54 };
55 
56 enum GRFExtendedLanguages {
57  GRFLX_AMERICAN = 0x00,
58  GRFLX_ENGLISH = 0x01,
59  GRFLX_GERMAN = 0x02,
60  GRFLX_FRENCH = 0x03,
61  GRFLX_SPANISH = 0x04,
62  GRFLX_UNSPECIFIED = 0x7F,
63 };
64 
70 struct GRFText {
71 public:
82  static GRFText *New(byte langid, const char *text, size_t len)
83  {
84  return new (len) GRFText(langid, text, len);
85  }
86 
92  static GRFText *Copy(GRFText *orig)
93  {
94  return GRFText::New(orig->langid, orig->text, orig->len);
95  }
96 
102  void *operator new(size_t size)
103  {
104  NOT_REACHED();
105  }
106 
111  void operator delete(void *p)
112  {
113  free(p);
114  }
115 private:
122  GRFText(byte langid_, const char *text_, size_t len_) : next(nullptr), len(len_), langid(langid_)
123  {
124  /* We need to use memcpy instead of strcpy due to
125  * the possibility of "choice lists" and therefore
126  * intermediate string terminators. */
127  memcpy(this->text, text_, len);
128  }
129 
136  void *operator new(size_t size, size_t extra)
137  {
138  return MallocT<byte>(size + extra);
139  }
140 
141 public:
143  size_t len;
144  byte langid;
145  char text[];
146 };
147 
148 
154 struct GRFTextEntry {
155  uint32 grfid;
156  uint16 stringid;
157  StringID def_string;
158  GRFText *textholder;
159 };
160 
161 
162 static uint _num_grf_texts = 0;
163 static GRFTextEntry _grf_text[TAB_SIZE_NEWGRF];
164 static byte _currentLangID = GRFLX_ENGLISH;
165 
172 int LanguageMap::GetMapping(int newgrf_id, bool gender) const
173 {
174  const std::vector<Mapping> &map = gender ? this->gender_map : this->case_map;
175  for (const Mapping &m : map) {
176  if (m.newgrf_id == newgrf_id) return m.openttd_id;
177  }
178  return -1;
179 }
180 
187 int LanguageMap::GetReverseMapping(int openttd_id, bool gender) const
188 {
189  const std::vector<Mapping> &map = gender ? this->gender_map : this->case_map;
190  for (const Mapping &m : map) {
191  if (m.openttd_id == openttd_id) return m.newgrf_id;
192  }
193  return -1;
194 }
195 
200  {
201  for (SmallPair<byte, char *> p : this->strings) {
202  free(p.second);
203  }
204  }
205 
212  UnmappedChoiceList(StringControlCode type, char *old_d, int offset) :
213  type(type), old_d(old_d), offset(offset)
214  {
215  }
216 
218  char *old_d;
219  int offset;
220 
223 
229  char *Flush(const LanguageMap *lm)
230  {
231  if (!this->strings.Contains(0)) {
232  /* In case of a (broken) NewGRF without a default,
233  * assume an empty string. */
234  grfmsg(1, "choice list misses default value");
235  this->strings[0] = stredup("");
236  }
237 
238  char *d = old_d;
239  if (lm == nullptr) {
240  /* In case there is no mapping, just ignore everything but the default.
241  * A probable cause for this happening is when the language file has
242  * been removed by the user and as such no mapping could be made. */
243  size_t len = strlen(this->strings[0]);
244  memcpy(d, this->strings[0], len);
245  return d + len;
246  }
247 
248  d += Utf8Encode(d, this->type);
249 
250  if (this->type == SCC_SWITCH_CASE) {
251  /*
252  * Format for case switch:
253  * <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
254  * Each LEN is printed using 2 bytes in big endian order.
255  */
256 
257  /* "<NUM CASES>" */
258  int count = 0;
259  for (uint8 i = 0; i < _current_language->num_cases; i++) {
260  /* Count the ones we have a mapped string for. */
261  if (this->strings.Contains(lm->GetReverseMapping(i, false))) count++;
262  }
263  *d++ = count;
264 
265  for (uint8 i = 0; i < _current_language->num_cases; i++) {
266  /* Resolve the string we're looking for. */
267  int idx = lm->GetReverseMapping(i, false);
268  if (!this->strings.Contains(idx)) continue;
269  char *str = this->strings[idx];
270 
271  /* "<CASEn>" */
272  *d++ = i + 1;
273 
274  /* "<LENn>" */
275  size_t len = strlen(str) + 1;
276  *d++ = GB(len, 8, 8);
277  *d++ = GB(len, 0, 8);
278 
279  /* "<STRINGn>" */
280  memcpy(d, str, len);
281  d += len;
282  }
283 
284  /* "<STRINGDEFAULT>" */
285  size_t len = strlen(this->strings[0]) + 1;
286  memcpy(d, this->strings[0], len);
287  d += len;
288  } else {
289  if (this->type == SCC_PLURAL_LIST) {
290  *d++ = lm->plural_form;
291  }
292 
293  /*
294  * Format for choice list:
295  * <OFFSET> <NUM CHOICES> <LENs> <STRINGs>
296  */
297 
298  /* "<OFFSET>" */
299  *d++ = this->offset - 0x80;
300 
301  /* "<NUM CHOICES>" */
302  int count = (this->type == SCC_GENDER_LIST ? _current_language->num_genders : LANGUAGE_MAX_PLURAL_FORMS);
303  *d++ = count;
304 
305  /* "<LENs>" */
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);
312  }
313 
314  /* "<STRINGs>" */
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];
318  /* Limit the length of the string we copy to 0xFE. The length is written above
319  * as a byte and we need room for the final '\0'. */
320  size_t len = min<size_t>(0xFE, strlen(str));
321  memcpy(d, str, len);
322  d += len;
323  *d++ = '\0';
324  }
325  }
326  return d;
327  }
328 };
329 
340 char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const char *str, int *olen, StringControlCode byte80)
341 {
342  char *tmp = MallocT<char>(strlen(str) * 10 + 1); // Allocate space to allow for expansion
343  char *d = tmp;
344  bool unicode = false;
345  WChar c;
346  size_t len = Utf8Decode(&c, str);
347 
348  /* Helper variable for a possible (string) mapping. */
349  UnmappedChoiceList *mapping = nullptr;
350 
351  if (c == NFO_UTF8_IDENTIFIER) {
352  unicode = true;
353  str += len;
354  }
355 
356  for (;;) {
357  if (unicode && Utf8EncodedCharLen(*str) != 0) {
358  c = Utf8Consume(&str);
359  /* 'Magic' range of control codes. */
360  if (GB(c, 8, 8) == 0xE0) {
361  c = GB(c, 0, 8);
362  } else if (c >= 0x20) {
363  if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
364  d += Utf8Encode(d, c);
365  continue;
366  }
367  } else {
368  c = (byte)*str++;
369  }
370  if (c == '\0') break;
371 
372  switch (c) {
373  case 0x01:
374  if (str[0] == '\0') goto string_end;
375  d += Utf8Encode(d, ' ');
376  str++;
377  break;
378  case 0x0A: break;
379  case 0x0D:
380  if (allow_newlines) {
381  *d++ = 0x0A;
382  } else {
383  grfmsg(1, "Detected newline in string that does not allow one");
384  }
385  break;
386  case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break;
387  case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break;
388  case 0x1F:
389  if (str[0] == '\0' || str[1] == '\0') goto string_end;
390  d += Utf8Encode(d, ' ');
391  str += 2;
392  break;
393  case 0x7B:
394  case 0x7C:
395  case 0x7D:
396  case 0x7E:
397  case 0x7F: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_SIGNED + c - 0x7B); break;
398  case 0x80: d += Utf8Encode(d, byte80); break;
399  case 0x81: {
400  if (str[0] == '\0' || str[1] == '\0') goto string_end;
401  StringID string;
402  string = ((uint8)*str++);
403  string |= ((uint8)*str++) << 8;
404  d += Utf8Encode(d, SCC_NEWGRF_STRINL);
405  d += Utf8Encode(d, MapGRFStringID(grfid, string));
406  break;
407  }
408  case 0x82:
409  case 0x83:
410  case 0x84: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_DATE_LONG + c - 0x82); break;
411  case 0x85: d += Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD); break;
412  case 0x86: d += Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break;
413  case 0x87: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_VOLUME_LONG); break;
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;
431  case 0x9A: {
432  int code = *str++;
433  switch (code) {
434  case 0x00: goto string_end;
435  case 0x01: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break;
436  /* 0x02: ignore next colour byte is not supported. It works on the final
437  * string and as such hooks into the string drawing routine. At that
438  * point many things already happened, such as splitting up of strings
439  * when drawn over multiple lines or right-to-left translations, which
440  * make the behaviour peculiar, e.g. only happening at specific width
441  * of windows. Or we need to add another pass over the string to just
442  * support this. As such it is not implemented in OpenTTD. */
443  case 0x03: {
444  if (str[0] == '\0' || str[1] == '\0') goto string_end;
445  uint16 tmp = ((uint8)*str++);
446  tmp |= ((uint8)*str++) << 8;
448  d += Utf8Encode(d, tmp);
449  break;
450  }
451  case 0x04:
452  if (str[0] == '\0') goto string_end;
454  d += Utf8Encode(d, *str++);
455  break;
456  case 0x06: d += Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX); break;
457  case 0x07: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX); break;
458  case 0x08: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX); break;
459  /* 0x09, 0x0A are TTDPatch internal use only string codes. */
460  case 0x0B: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_HEX); break;
461  case 0x0C: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_STATION_NAME); break;
462  case 0x0D: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG); break;
463  case 0x0E:
464  case 0x0F: {
465  if (str[0] == '\0') goto string_end;
466  const LanguageMap *lm = LanguageMap::GetLanguageMap(grfid, language_id);
467  int index = *str++;
468  int mapped = lm != nullptr ? lm->GetMapping(index, code == 0x0E) : -1;
469  if (mapped >= 0) {
470  d += Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
471  d += Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
472  }
473  break;
474  }
475 
476  case 0x10:
477  case 0x11:
478  if (str[0] == '\0') goto string_end;
479  if (mapping == nullptr) {
480  if (code == 0x10) str++; // Skip the index
481  grfmsg(1, "choice list %s marker found when not expected", code == 0x10 ? "next" : "default");
482  break;
483  } else {
484  /* Terminate the previous string. */
485  *d = '\0';
486  int index = (code == 0x10 ? *str++ : 0);
487  if (mapping->strings.Contains(index)) {
488  grfmsg(1, "duplicate choice list string, ignoring");
489  d++;
490  } else {
491  d = mapping->strings[index] = MallocT<char>(strlen(str) * 10 + 1);
492  }
493  }
494  break;
495 
496  case 0x12:
497  if (mapping == nullptr) {
498  grfmsg(1, "choice list end marker found when not expected");
499  } else {
500  /* Terminate the previous string. */
501  *d = '\0';
502 
503  /* Now we can start flushing everything and clean everything up. */
504  d = mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id));
505  delete mapping;
506  mapping = nullptr;
507  }
508  break;
509 
510  case 0x13:
511  case 0x14:
512  case 0x15:
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++;
517  } else {
518  static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
519  mapping = new UnmappedChoiceList(mp[code - 0x13], d, code == 0x14 ? 0 : *str++);
520  }
521  break;
522 
523  case 0x16:
524  case 0x17:
525  case 0x18:
526  case 0x19:
527  case 0x1A:
528  case 0x1B:
529  case 0x1C:
530  case 0x1D:
531  case 0x1E:
532  d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_DATE_LONG + code - 0x16);
533  break;
534 
535  case 0x1F: d += Utf8Encode(d, SCC_PUSH_COLOUR); break;
536  case 0x20: d += Utf8Encode(d, SCC_POP_COLOUR); break;
537 
538  default:
539  grfmsg(1, "missing handler for extended format code");
540  break;
541  }
542  break;
543  }
544 
545  case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro
546  case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis
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;
560  default:
561  /* Validate any unhandled character */
562  if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
563  d += Utf8Encode(d, c);
564  break;
565  }
566  }
567 
568 string_end:
569  if (mapping != nullptr) {
570  grfmsg(1, "choice list was incomplete, the whole list is ignored");
571  delete mapping;
572  }
573 
574  *d = '\0';
575  if (olen != nullptr) *olen = d - tmp + 1;
576  tmp = ReallocT(tmp, d - tmp + 1);
577  return tmp;
578 }
579 
585 void AddGRFTextToList(GRFText **list, GRFText *text_to_add)
586 {
587  GRFText **ptext, *text;
588 
589  /* Loop through all languages and see if we can replace a string */
590  for (ptext = list; (text = *ptext) != nullptr; ptext = &text->next) {
591  if (text->langid == text_to_add->langid) {
592  text_to_add->next = text->next;
593  *ptext = text_to_add;
594  delete text;
595  return;
596  }
597  }
598 
599  /* If a string wasn't replaced, then we must append the new string */
600  *ptext = text_to_add;
601 }
602 
612 void AddGRFTextToList(struct GRFText **list, byte langid, uint32 grfid, bool allow_newlines, const char *text_to_add)
613 {
614  int len;
615  char *translatedtext = TranslateTTDPatchCodes(grfid, langid, allow_newlines, text_to_add, &len);
616  GRFText *newtext = GRFText::New(langid, translatedtext, len);
617  free(translatedtext);
618 
619  AddGRFTextToList(list, newtext);
620 }
621 
628 void AddGRFTextToList(struct GRFText **list, const char *text_to_add)
629 {
630  AddGRFTextToList(list, GRFText::New(0x7F, text_to_add, strlen(text_to_add) + 1));
631 }
632 
639 {
640  GRFText *newtext = nullptr;
641  GRFText **ptext = &newtext;
642  for (; orig != nullptr; orig = orig->next) {
643  *ptext = GRFText::Copy(orig);
644  ptext = &(*ptext)->next;
645  }
646  return newtext;
647 }
648 
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)
653 {
654  char *translatedtext;
655  uint id;
656 
657  /* When working with the old language scheme (grf_version is less than 7) and
658  * English or American is among the set bits, simply add it as English in
659  * the new scheme, i.e. as langid = 1.
660  * If English is set, it is pretty safe to assume the translations are not
661  * actually translated.
662  */
663  if (!new_scheme) {
664  if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
665  langid_to_add = GRFLX_ENGLISH;
666  } else {
667  StringID ret = STR_EMPTY;
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);
671  return ret;
672  }
673  }
674 
675  for (id = 0; id < _num_grf_texts; id++) {
676  if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
677  break;
678  }
679  }
680 
681  /* Too many strings allocated, return empty */
682  if (id == lengthof(_grf_text)) return STR_EMPTY;
683 
684  int len;
685  translatedtext = TranslateTTDPatchCodes(grfid, langid_to_add, allow_newlines, text_to_add, &len);
686 
687  GRFText *newtext = GRFText::New(langid_to_add, translatedtext, len);
688 
689  free(translatedtext);
690 
691  /* If we didn't find our stringid and grfid in the list, allocate a new id */
692  if (id == _num_grf_texts) _num_grf_texts++;
693 
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;
698  }
699  AddGRFTextToList(&_grf_text[id].textholder, newtext);
700 
701  grfmsg(3, "Added 0x%X: grfid %08X string 0x%X lang 0x%X string '%s' (%X)", id, grfid, stringid, newtext->langid, newtext->text, MakeStringID(TEXT_TAB_NEWGRF_START, id));
702 
704 }
705 
709 StringID GetGRFStringID(uint32 grfid, StringID stringid)
710 {
711  for (uint id = 0; id < _num_grf_texts; id++) {
712  if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
714  }
715  }
716 
717  return STR_UNDEFINED;
718 }
719 
720 
729 {
730  const char *default_text = nullptr;
731 
732  /* Search the list of lang-strings of this stringid for current lang */
733  for (; text != nullptr; text = text->next) {
734  if (text->langid == _currentLangID) return text->text;
735 
736  /* If the current string is English or American, set it as the
737  * fallback language if the specific language isn't available. */
738  if (text->langid == GRFLX_UNSPECIFIED || (default_text == nullptr && (text->langid == GRFLX_ENGLISH || text->langid == GRFLX_AMERICAN))) {
739  default_text = text->text;
740  }
741  }
742 
743  return default_text;
744 }
745 
749 const char *GetGRFStringPtr(uint16 stringid)
750 {
751  assert(_grf_text[stringid].grfid != 0);
752 
753  const char *str = GetGRFStringFromGRFText(_grf_text[stringid].textholder);
754  if (str != nullptr) return str;
755 
756  /* Use the default string ID if the fallback string isn't available */
757  return GetStringPtr(_grf_text[stringid].def_string);
758 }
759 
768 void SetCurrentGrfLangID(byte language_id)
769 {
770  _currentLangID = language_id;
771 }
772 
773 bool CheckGrfLangID(byte lang_id, byte grf_version)
774 {
775  if (grf_version < 7) {
776  switch (_currentLangID) {
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;
781  }
782  }
783 
784  return (lang_id == _currentLangID || lang_id == GRFLX_UNSPECIFIED);
785 }
786 
791 void CleanUpGRFText(GRFText *grftext)
792 {
793  while (grftext != nullptr) {
794  GRFText *grftext2 = grftext->next;
795  delete grftext;
796  grftext = grftext2;
797  }
798 }
799 
805 {
806  uint id;
807 
808  for (id = 0; id < _num_grf_texts; id++) {
809  CleanUpGRFText(_grf_text[id].textholder);
810  _grf_text[id].grfid = 0;
811  _grf_text[id].stringid = 0;
812  _grf_text[id].textholder = nullptr;
813  }
814 
815  _num_grf_texts = 0;
816 }
817 
818 struct TextRefStack {
819  std::array<byte, 0x30> stack;
820  byte position;
821  const GRFFile *grffile;
822  bool used;
823 
824  TextRefStack() : position(0), grffile(nullptr), used(false) {}
825 
826  uint8 PopUnsignedByte() { assert(this->position < this->stack.size()); return this->stack[this->position++]; }
827  int8 PopSignedByte() { return (int8)this->PopUnsignedByte(); }
828 
829  uint16 PopUnsignedWord()
830  {
831  uint16 val = this->PopUnsignedByte();
832  return val | (this->PopUnsignedByte() << 8);
833  }
834  int16 PopSignedWord() { return (int32)this->PopUnsignedWord(); }
835 
836  uint32 PopUnsignedDWord()
837  {
838  uint32 val = this->PopUnsignedWord();
839  return val | (this->PopUnsignedWord() << 16);
840  }
841  int32 PopSignedDWord() { return (int32)this->PopUnsignedDWord(); }
842 
843  uint64 PopUnsignedQWord()
844  {
845  uint64 val = this->PopUnsignedDWord();
846  return val | (((uint64)this->PopUnsignedDWord()) << 32);
847  }
848  int64 PopSignedQWord() { return (int64)this->PopUnsignedQWord(); }
849 
852  {
853  byte tmp[2];
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];
857  }
858 
859  void PushWord(uint16 word)
860  {
861  if (this->position >= 2) {
862  this->position -= 2;
863  } else {
864  // Rotate right 2 positions
865  std::rotate(this->stack.rbegin(), this->stack.rbegin() + 2, this->stack.rend());
866  }
867  this->stack[this->position] = GB(word, 0, 8);
868  this->stack[this->position + 1] = GB(word, 8, 8);
869  }
870 
871  void ResetStack(const GRFFile *grffile)
872  {
873  assert(grffile != nullptr);
874  this->position = 0;
875  this->grffile = grffile;
876  this->used = true;
877  }
878 
879  void RewindStack() { this->position = 0; }
880 };
881 
884 
890 {
891  return _newgrf_textrefstack.used;
892 }
893 
899 {
900  return new TextRefStack(_newgrf_textrefstack);
901 }
902 
908 {
909  _newgrf_textrefstack = *backup;
910  delete backup;
911 }
912 
931 void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32 *values)
932 {
933  extern TemporaryStorageArray<int32, 0x110> _temp_store;
934 
935  _newgrf_textrefstack.ResetStack(grffile);
936 
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);
942  stack_it++;
943  }
944  }
945 }
946 
949 {
950  _newgrf_textrefstack.used = false;
951 }
952 
953 void RewindTextRefStack()
954 {
955  _newgrf_textrefstack.RewindStack();
956 }
957 
968 uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, uint argv_size, bool modify_argv)
969 {
970  switch (scc) {
971  default: break;
972 
996  if (argv_size < 1) {
997  DEBUG(misc, 0, "Too many NewGRF string parameters.");
998  return 0;
999  }
1000  break;
1001 
1005  if (argv_size < 2) {
1006  DEBUG(misc, 0, "Too many NewGRF string parameters.");
1007  return 0;
1008  }
1009  break;
1010  }
1011 
1012  if (_newgrf_textrefstack.used && modify_argv) {
1013  switch (scc) {
1014  default: NOT_REACHED();
1015  case SCC_NEWGRF_PRINT_BYTE_SIGNED: *argv = _newgrf_textrefstack.PopSignedByte(); break;
1016  case SCC_NEWGRF_PRINT_QWORD_CURRENCY: *argv = _newgrf_textrefstack.PopSignedQWord(); break;
1017 
1019  case SCC_NEWGRF_PRINT_DWORD_SIGNED: *argv = _newgrf_textrefstack.PopSignedDWord(); break;
1020 
1021  case SCC_NEWGRF_PRINT_BYTE_HEX: *argv = _newgrf_textrefstack.PopUnsignedByte(); break;
1022  case SCC_NEWGRF_PRINT_QWORD_HEX: *argv = _newgrf_textrefstack.PopUnsignedQWord(); break;
1023 
1027  case SCC_NEWGRF_PRINT_WORD_SIGNED: *argv = _newgrf_textrefstack.PopSignedWord(); break;
1028 
1034  case SCC_NEWGRF_PRINT_WORD_UNSIGNED: *argv = _newgrf_textrefstack.PopUnsignedWord(); break;
1035 
1038  case SCC_NEWGRF_PRINT_DWORD_HEX: *argv = _newgrf_textrefstack.PopUnsignedDWord(); break;
1039 
1041  case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: *argv = _newgrf_textrefstack.PopUnsignedWord() + DAYS_TILL_ORIGINAL_BASE_YEAR; break;
1042 
1043  case SCC_NEWGRF_DISCARD_WORD: _newgrf_textrefstack.PopUnsignedWord(); break;
1044 
1045  case SCC_NEWGRF_ROTATE_TOP_4_WORDS: _newgrf_textrefstack.RotateTop4Words(); break;
1046  case SCC_NEWGRF_PUSH_WORD: _newgrf_textrefstack.PushWord(Utf8Consume(str)); break;
1047  case SCC_NEWGRF_UNPRINT: *buff = max(*buff - Utf8Consume(str), buf_start); break;
1048 
1052  argv[0] = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1053  argv[1] = _newgrf_textrefstack.PopUnsignedWord();
1054  break;
1055 
1057  *argv = MapGRFStringID(_newgrf_textrefstack.grffile->grfid, _newgrf_textrefstack.PopUnsignedWord());
1058  break;
1059 
1061  CargoID cargo = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1062  *argv = cargo < NUM_CARGO ? 1 << cargo : 0;
1063  break;
1064  }
1065  }
1066  } else {
1067  /* Consume additional parameter characters */
1068  switch (scc) {
1069  default: break;
1070 
1071  case SCC_NEWGRF_PUSH_WORD:
1072  case SCC_NEWGRF_UNPRINT:
1073  Utf8Consume(str);
1074  break;
1075  }
1076  }
1077 
1078  switch (scc) {
1079  default: NOT_REACHED();
1084  return SCC_COMMA;
1085 
1090  return SCC_HEX;
1091 
1094  return SCC_CURRENCY_LONG;
1095 
1098 
1101  return SCC_DATE_LONG;
1102 
1105  return SCC_DATE_SHORT;
1106 
1108  return SCC_VELOCITY;
1109 
1111  return SCC_VOLUME_LONG;
1112 
1114  return SCC_VOLUME_SHORT;
1115 
1117  return SCC_WEIGHT_LONG;
1118 
1120  return SCC_WEIGHT_SHORT;
1121 
1123  return SCC_POWER;
1124 
1126  return SCC_CARGO_LONG;
1127 
1129  return SCC_CARGO_SHORT;
1130 
1132  return SCC_CARGO_TINY;
1133 
1135  return SCC_CARGO_LIST;
1136 
1138  return SCC_STATION_NAME;
1139 
1142  case SCC_NEWGRF_PUSH_WORD:
1143  case SCC_NEWGRF_UNPRINT:
1144  return 0;
1145  }
1146 }
Functions related to OTTD&#39;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.
Definition: newgrf_text.h:62
#define DAYS_TILL_ORIGINAL_BASE_YEAR
The offset in days from the &#39;_date == 0&#39; till &#39;ConvertYMDToDate(ORIGINAL_BASE_YEAR, 0, 1)&#39;.
Definition: date_type.h:82
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...
Definition: control_codes.h:19
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.
Definition: cargo_type.h:66
static byte _currentLangID
by default, english is used.
GRFText * next
The next GRFText in this chain.
const LanguageMetadata * _current_language
The currently loaded language.
Definition: strings.cpp:48
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)
Switch to large font.
Definition: control_codes.h:33
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.
Definition: strings_func.h:49
size_t Utf8Decode(WChar *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Definition: string.cpp:448
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.
Definition: math_func.hpp:26
static const LanguageMap * GetLanguageMap(uint32 grfid, uint8 language_id)
Get the language map associated with a given NewGRF and language.
Definition: newgrf.cpp:2575
Simple mapping class targeted for small sets of data.
uint8 num_cases
the number of cases of this language
Definition: language.h:56
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.
Definition: newgrf_text.h:49
static int8 Utf8EncodedCharLen(char c)
Return the length of an UTF-8 encoded value based on a single char.
Definition: string_func.h:118
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.
Definition: string.cpp:350
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.
Definition: newgrf_text.cpp:82
~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.
Simple pair of data.
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.
Definition: strings_type.h:54
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: newgrf_text.h:47
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&#39;s property to map to the GRF&#39;s strings while taking in consideration the ...
Definition: newgrf.cpp:549
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...
Definition: alloc_func.hpp:113
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
Definition: string.cpp:138
byte langid
The language associated with this GRFText.
GRFBaseLanguages
Explains the newgrf shift bit positioning.
Definition: newgrf_text.cpp:47
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
static TextRefStack _newgrf_textrefstack
The stack that is used for TTDP compatible string code parsing.
Element of the linked list.
Definition: newgrf_text.cpp:70
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.
Definition: alloc_type.hpp:87
uint32 StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:18
int GetReverseMapping(int openttd_id, bool gender) const
Get the mapping from OpenTTD&#39;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.
Definition: debug.h:37
GRFText * DuplicateGRFText(GRFText *orig)
Create a copy of this GRFText list.
uint8 num_genders
the number of genders of this language
Definition: language.h:55
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.
Definition: string_type.h:29
static const WChar NFO_UTF8_IDENTIFIER
This character, the thorn (&#39;รพ&#39;), indicates a unicode string to NFO.
Definition: newgrf_text.h:21
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&#39;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...
Definition: newgrf.cpp:377
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.
Definition: strings_type.h:42
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.
Definition: string.cpp:488
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:131
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.
Definition: cargo_type.h:22
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.
Definition: string_type.h:37
SmallMap< byte, char * > strings
Mapping of NewGRF supplied ID to the different strings in the choice list.
Switch to small font.
Definition: control_codes.h:32
static GRFText * Copy(GRFText *orig)
Create a copy of this GRFText.
Definition: newgrf_text.cpp:92
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.
Definition: newgrf.h:107
Base for the NewGRF implementation.