OpenTTD
newgrf_text.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * 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.
4  * 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.
5  * 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/>.
6  */
7 
18 #include "stdafx.h"
19 
20 #include <algorithm>
21 #include <array>
22 
23 #include "newgrf.h"
24 #include "strings_func.h"
25 #include "newgrf_storage.h"
26 #include "newgrf_text.h"
27 #include "newgrf_cargo.h"
28 #include "string_func.h"
29 #include "date_type.h"
30 #include "debug.h"
31 #include "core/alloc_type.hpp"
32 #include "core/smallmap_type.hpp"
33 #include "language.h"
34 
35 #include "table/strings.h"
36 #include "table/control_codes.h"
37 
38 #include "safeguards.h"
39 
46  GRFLB_AMERICAN = 0x01,
47  GRFLB_ENGLISH = 0x02,
48  GRFLB_GERMAN = 0x04,
49  GRFLB_FRENCH = 0x08,
50  GRFLB_SPANISH = 0x10,
51  GRFLB_GENERIC = 0x80,
52 };
53 
54 enum GRFExtendedLanguages {
55  GRFLX_AMERICAN = 0x00,
56  GRFLX_ENGLISH = 0x01,
57  GRFLX_GERMAN = 0x02,
58  GRFLX_FRENCH = 0x03,
59  GRFLX_SPANISH = 0x04,
60  GRFLX_UNSPECIFIED = 0x7F,
61 };
62 
68 struct GRFText {
69 public:
80  static GRFText *New(byte langid, const char *text, size_t len)
81  {
82  return new (len) GRFText(langid, text, len);
83  }
84 
90  static GRFText *Copy(GRFText *orig)
91  {
92  return GRFText::New(orig->langid, orig->text, orig->len);
93  }
94 
100  void *operator new(size_t size)
101  {
102  NOT_REACHED();
103  }
104 
109  void operator delete(void *p)
110  {
111  free(p);
112  }
113 private:
120  GRFText(byte langid_, const char *text_, size_t len_) : next(nullptr), len(len_), langid(langid_)
121  {
122  /* We need to use memcpy instead of strcpy due to
123  * the possibility of "choice lists" and therefore
124  * intermediate string terminators. */
125  memcpy(this->text, text_, len);
126  }
127 
134  void *operator new(size_t size, size_t extra)
135  {
136  return MallocT<byte>(size + extra);
137  }
138 
139 public:
141  size_t len;
142  byte langid;
143  char text[];
144 };
145 
146 
152 struct GRFTextEntry {
153  uint32 grfid;
154  uint16 stringid;
155  StringID def_string;
156  GRFText *textholder;
157 };
158 
159 
160 static uint _num_grf_texts = 0;
161 static GRFTextEntry _grf_text[TAB_SIZE_NEWGRF];
162 static byte _currentLangID = GRFLX_ENGLISH;
163 
170 int LanguageMap::GetMapping(int newgrf_id, bool gender) const
171 {
172  const std::vector<Mapping> &map = gender ? this->gender_map : this->case_map;
173  for (const Mapping &m : map) {
174  if (m.newgrf_id == newgrf_id) return m.openttd_id;
175  }
176  return -1;
177 }
178 
185 int LanguageMap::GetReverseMapping(int openttd_id, bool gender) const
186 {
187  const std::vector<Mapping> &map = gender ? this->gender_map : this->case_map;
188  for (const Mapping &m : map) {
189  if (m.openttd_id == openttd_id) return m.newgrf_id;
190  }
191  return -1;
192 }
193 
198  {
199  for (SmallPair<byte, char *> p : this->strings) {
200  free(p.second);
201  }
202  }
203 
210  UnmappedChoiceList(StringControlCode type, char *old_d, int offset) :
211  type(type), old_d(old_d), offset(offset)
212  {
213  }
214 
216  char *old_d;
217  int offset;
218 
221 
227  char *Flush(const LanguageMap *lm)
228  {
229  if (!this->strings.Contains(0)) {
230  /* In case of a (broken) NewGRF without a default,
231  * assume an empty string. */
232  grfmsg(1, "choice list misses default value");
233  this->strings[0] = stredup("");
234  }
235 
236  char *d = old_d;
237  if (lm == nullptr) {
238  /* In case there is no mapping, just ignore everything but the default.
239  * A probable cause for this happening is when the language file has
240  * been removed by the user and as such no mapping could be made. */
241  size_t len = strlen(this->strings[0]);
242  memcpy(d, this->strings[0], len);
243  return d + len;
244  }
245 
246  d += Utf8Encode(d, this->type);
247 
248  if (this->type == SCC_SWITCH_CASE) {
249  /*
250  * Format for case switch:
251  * <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
252  * Each LEN is printed using 2 bytes in big endian order.
253  */
254 
255  /* "<NUM CASES>" */
256  int count = 0;
257  for (uint8 i = 0; i < _current_language->num_cases; i++) {
258  /* Count the ones we have a mapped string for. */
259  if (this->strings.Contains(lm->GetReverseMapping(i, false))) count++;
260  }
261  *d++ = count;
262 
263  for (uint8 i = 0; i < _current_language->num_cases; i++) {
264  /* Resolve the string we're looking for. */
265  int idx = lm->GetReverseMapping(i, false);
266  if (!this->strings.Contains(idx)) continue;
267  char *str = this->strings[idx];
268 
269  /* "<CASEn>" */
270  *d++ = i + 1;
271 
272  /* "<LENn>" */
273  size_t len = strlen(str) + 1;
274  *d++ = GB(len, 8, 8);
275  *d++ = GB(len, 0, 8);
276 
277  /* "<STRINGn>" */
278  memcpy(d, str, len);
279  d += len;
280  }
281 
282  /* "<STRINGDEFAULT>" */
283  size_t len = strlen(this->strings[0]) + 1;
284  memcpy(d, this->strings[0], len);
285  d += len;
286  } else {
287  if (this->type == SCC_PLURAL_LIST) {
288  *d++ = lm->plural_form;
289  }
290 
291  /*
292  * Format for choice list:
293  * <OFFSET> <NUM CHOICES> <LENs> <STRINGs>
294  */
295 
296  /* "<OFFSET>" */
297  *d++ = this->offset - 0x80;
298 
299  /* "<NUM CHOICES>" */
300  int count = (this->type == SCC_GENDER_LIST ? _current_language->num_genders : LANGUAGE_MAX_PLURAL_FORMS);
301  *d++ = count;
302 
303  /* "<LENs>" */
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);
310  }
311 
312  /* "<STRINGs>" */
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];
316  /* Limit the length of the string we copy to 0xFE. The length is written above
317  * as a byte and we need room for the final '\0'. */
318  size_t len = min<size_t>(0xFE, strlen(str));
319  memcpy(d, str, len);
320  d += len;
321  *d++ = '\0';
322  }
323  }
324  return d;
325  }
326 };
327 
338 char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const char *str, int *olen, StringControlCode byte80)
339 {
340  char *tmp = MallocT<char>(strlen(str) * 10 + 1); // Allocate space to allow for expansion
341  char *d = tmp;
342  bool unicode = false;
343  WChar c;
344  size_t len = Utf8Decode(&c, str);
345 
346  /* Helper variable for a possible (string) mapping. */
347  UnmappedChoiceList *mapping = nullptr;
348 
349  if (c == NFO_UTF8_IDENTIFIER) {
350  unicode = true;
351  str += len;
352  }
353 
354  for (;;) {
355  if (unicode && Utf8EncodedCharLen(*str) != 0) {
356  c = Utf8Consume(&str);
357  /* 'Magic' range of control codes. */
358  if (GB(c, 8, 8) == 0xE0) {
359  c = GB(c, 0, 8);
360  } else if (c >= 0x20) {
361  if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
362  d += Utf8Encode(d, c);
363  continue;
364  }
365  } else {
366  c = (byte)*str++;
367  }
368  if (c == '\0') break;
369 
370  switch (c) {
371  case 0x01:
372  if (str[0] == '\0') goto string_end;
373  d += Utf8Encode(d, ' ');
374  str++;
375  break;
376  case 0x0A: break;
377  case 0x0D:
378  if (allow_newlines) {
379  *d++ = 0x0A;
380  } else {
381  grfmsg(1, "Detected newline in string that does not allow one");
382  }
383  break;
384  case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break;
385  case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break;
386  case 0x1F:
387  if (str[0] == '\0' || str[1] == '\0') goto string_end;
388  d += Utf8Encode(d, ' ');
389  str += 2;
390  break;
391  case 0x7B:
392  case 0x7C:
393  case 0x7D:
394  case 0x7E:
395  case 0x7F: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_SIGNED + c - 0x7B); break;
396  case 0x80: d += Utf8Encode(d, byte80); break;
397  case 0x81: {
398  if (str[0] == '\0' || str[1] == '\0') goto string_end;
399  StringID string;
400  string = ((uint8)*str++);
401  string |= ((uint8)*str++) << 8;
402  d += Utf8Encode(d, SCC_NEWGRF_STRINL);
403  d += Utf8Encode(d, MapGRFStringID(grfid, string));
404  break;
405  }
406  case 0x82:
407  case 0x83:
408  case 0x84: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_DATE_LONG + c - 0x82); break;
409  case 0x85: d += Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD); break;
410  case 0x86: d += Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break;
411  case 0x87: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_VOLUME_LONG); break;
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;
429  case 0x9A: {
430  int code = *str++;
431  switch (code) {
432  case 0x00: goto string_end;
433  case 0x01: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break;
434  /* 0x02: ignore next colour byte is not supported. It works on the final
435  * string and as such hooks into the string drawing routine. At that
436  * point many things already happened, such as splitting up of strings
437  * when drawn over multiple lines or right-to-left translations, which
438  * make the behaviour peculiar, e.g. only happening at specific width
439  * of windows. Or we need to add another pass over the string to just
440  * support this. As such it is not implemented in OpenTTD. */
441  case 0x03: {
442  if (str[0] == '\0' || str[1] == '\0') goto string_end;
443  uint16 tmp = ((uint8)*str++);
444  tmp |= ((uint8)*str++) << 8;
446  d += Utf8Encode(d, tmp);
447  break;
448  }
449  case 0x04:
450  if (str[0] == '\0') goto string_end;
452  d += Utf8Encode(d, *str++);
453  break;
454  case 0x06: d += Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX); break;
455  case 0x07: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX); break;
456  case 0x08: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX); break;
457  /* 0x09, 0x0A are TTDPatch internal use only string codes. */
458  case 0x0B: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_HEX); break;
459  case 0x0C: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_STATION_NAME); break;
460  case 0x0D: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG); break;
461  case 0x0E:
462  case 0x0F: {
463  if (str[0] == '\0') goto string_end;
464  const LanguageMap *lm = LanguageMap::GetLanguageMap(grfid, language_id);
465  int index = *str++;
466  int mapped = lm != nullptr ? lm->GetMapping(index, code == 0x0E) : -1;
467  if (mapped >= 0) {
468  d += Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
469  d += Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
470  }
471  break;
472  }
473 
474  case 0x10:
475  case 0x11:
476  if (str[0] == '\0') goto string_end;
477  if (mapping == nullptr) {
478  if (code == 0x10) str++; // Skip the index
479  grfmsg(1, "choice list %s marker found when not expected", code == 0x10 ? "next" : "default");
480  break;
481  } else {
482  /* Terminate the previous string. */
483  *d = '\0';
484  int index = (code == 0x10 ? *str++ : 0);
485  if (mapping->strings.Contains(index)) {
486  grfmsg(1, "duplicate choice list string, ignoring");
487  d++;
488  } else {
489  d = mapping->strings[index] = MallocT<char>(strlen(str) * 10 + 1);
490  }
491  }
492  break;
493 
494  case 0x12:
495  if (mapping == nullptr) {
496  grfmsg(1, "choice list end marker found when not expected");
497  } else {
498  /* Terminate the previous string. */
499  *d = '\0';
500 
501  /* Now we can start flushing everything and clean everything up. */
502  d = mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id));
503  delete mapping;
504  mapping = nullptr;
505  }
506  break;
507 
508  case 0x13:
509  case 0x14:
510  case 0x15:
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++;
515  } else {
516  static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
517  mapping = new UnmappedChoiceList(mp[code - 0x13], d, code == 0x14 ? 0 : *str++);
518  }
519  break;
520 
521  case 0x16:
522  case 0x17:
523  case 0x18:
524  case 0x19:
525  case 0x1A:
526  case 0x1B:
527  case 0x1C:
528  case 0x1D:
529  case 0x1E:
530  d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_DATE_LONG + code - 0x16);
531  break;
532 
533  case 0x1F: d += Utf8Encode(d, SCC_PUSH_COLOUR); break;
534  case 0x20: d += Utf8Encode(d, SCC_POP_COLOUR); break;
535 
536  default:
537  grfmsg(1, "missing handler for extended format code");
538  break;
539  }
540  break;
541  }
542 
543  case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro
544  case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis
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;
558  default:
559  /* Validate any unhandled character */
560  if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
561  d += Utf8Encode(d, c);
562  break;
563  }
564  }
565 
566 string_end:
567  if (mapping != nullptr) {
568  grfmsg(1, "choice list was incomplete, the whole list is ignored");
569  delete mapping;
570  }
571 
572  *d = '\0';
573  if (olen != nullptr) *olen = d - tmp + 1;
574  tmp = ReallocT(tmp, d - tmp + 1);
575  return tmp;
576 }
577 
583 void AddGRFTextToList(GRFText **list, GRFText *text_to_add)
584 {
585  GRFText **ptext, *text;
586 
587  /* Loop through all languages and see if we can replace a string */
588  for (ptext = list; (text = *ptext) != nullptr; ptext = &text->next) {
589  if (text->langid == text_to_add->langid) {
590  text_to_add->next = text->next;
591  *ptext = text_to_add;
592  delete text;
593  return;
594  }
595  }
596 
597  /* If a string wasn't replaced, then we must append the new string */
598  *ptext = text_to_add;
599 }
600 
610 void AddGRFTextToList(struct GRFText **list, byte langid, uint32 grfid, bool allow_newlines, const char *text_to_add)
611 {
612  int len;
613  char *translatedtext = TranslateTTDPatchCodes(grfid, langid, allow_newlines, text_to_add, &len);
614  GRFText *newtext = GRFText::New(langid, translatedtext, len);
615  free(translatedtext);
616 
617  AddGRFTextToList(list, newtext);
618 }
619 
626 void AddGRFTextToList(struct GRFText **list, const char *text_to_add)
627 {
628  AddGRFTextToList(list, GRFText::New(0x7F, text_to_add, strlen(text_to_add) + 1));
629 }
630 
637 {
638  GRFText *newtext = nullptr;
639  GRFText **ptext = &newtext;
640  for (; orig != nullptr; orig = orig->next) {
641  *ptext = GRFText::Copy(orig);
642  ptext = &(*ptext)->next;
643  }
644  return newtext;
645 }
646 
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)
651 {
652  char *translatedtext;
653  uint id;
654 
655  /* When working with the old language scheme (grf_version is less than 7) and
656  * English or American is among the set bits, simply add it as English in
657  * the new scheme, i.e. as langid = 1.
658  * If English is set, it is pretty safe to assume the translations are not
659  * actually translated.
660  */
661  if (!new_scheme) {
662  if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
663  langid_to_add = GRFLX_ENGLISH;
664  } else {
665  StringID ret = STR_EMPTY;
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);
669  return ret;
670  }
671  }
672 
673  for (id = 0; id < _num_grf_texts; id++) {
674  if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
675  break;
676  }
677  }
678 
679  /* Too many strings allocated, return empty */
680  if (id == lengthof(_grf_text)) return STR_EMPTY;
681 
682  int len;
683  translatedtext = TranslateTTDPatchCodes(grfid, langid_to_add, allow_newlines, text_to_add, &len);
684 
685  GRFText *newtext = GRFText::New(langid_to_add, translatedtext, len);
686 
687  free(translatedtext);
688 
689  /* If we didn't find our stringid and grfid in the list, allocate a new id */
690  if (id == _num_grf_texts) _num_grf_texts++;
691 
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;
696  }
697  AddGRFTextToList(&_grf_text[id].textholder, newtext);
698 
699  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));
700 
702 }
703 
707 StringID GetGRFStringID(uint32 grfid, StringID stringid)
708 {
709  for (uint id = 0; id < _num_grf_texts; id++) {
710  if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
712  }
713  }
714 
715  return STR_UNDEFINED;
716 }
717 
718 
727 {
728  const char *default_text = nullptr;
729 
730  /* Search the list of lang-strings of this stringid for current lang */
731  for (; text != nullptr; text = text->next) {
732  if (text->langid == _currentLangID) return text->text;
733 
734  /* If the current string is English or American, set it as the
735  * fallback language if the specific language isn't available. */
736  if (text->langid == GRFLX_UNSPECIFIED || (default_text == nullptr && (text->langid == GRFLX_ENGLISH || text->langid == GRFLX_AMERICAN))) {
737  default_text = text->text;
738  }
739  }
740 
741  return default_text;
742 }
743 
747 const char *GetGRFStringPtr(uint16 stringid)
748 {
749  assert(_grf_text[stringid].grfid != 0);
750 
751  const char *str = GetGRFStringFromGRFText(_grf_text[stringid].textholder);
752  if (str != nullptr) return str;
753 
754  /* Use the default string ID if the fallback string isn't available */
755  return GetStringPtr(_grf_text[stringid].def_string);
756 }
757 
766 void SetCurrentGrfLangID(byte language_id)
767 {
768  _currentLangID = language_id;
769 }
770 
771 bool CheckGrfLangID(byte lang_id, byte grf_version)
772 {
773  if (grf_version < 7) {
774  switch (_currentLangID) {
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;
779  }
780  }
781 
782  return (lang_id == _currentLangID || lang_id == GRFLX_UNSPECIFIED);
783 }
784 
789 void CleanUpGRFText(GRFText *grftext)
790 {
791  while (grftext != nullptr) {
792  GRFText *grftext2 = grftext->next;
793  delete grftext;
794  grftext = grftext2;
795  }
796 }
797 
803 {
804  uint id;
805 
806  for (id = 0; id < _num_grf_texts; id++) {
807  CleanUpGRFText(_grf_text[id].textholder);
808  _grf_text[id].grfid = 0;
809  _grf_text[id].stringid = 0;
810  _grf_text[id].textholder = nullptr;
811  }
812 
813  _num_grf_texts = 0;
814 }
815 
816 struct TextRefStack {
817  std::array<byte, 0x30> stack;
818  byte position;
819  const GRFFile *grffile;
820  bool used;
821 
822  TextRefStack() : position(0), grffile(nullptr), used(false) {}
823 
824  uint8 PopUnsignedByte() { assert(this->position < this->stack.size()); return this->stack[this->position++]; }
825  int8 PopSignedByte() { return (int8)this->PopUnsignedByte(); }
826 
827  uint16 PopUnsignedWord()
828  {
829  uint16 val = this->PopUnsignedByte();
830  return val | (this->PopUnsignedByte() << 8);
831  }
832  int16 PopSignedWord() { return (int32)this->PopUnsignedWord(); }
833 
834  uint32 PopUnsignedDWord()
835  {
836  uint32 val = this->PopUnsignedWord();
837  return val | (this->PopUnsignedWord() << 16);
838  }
839  int32 PopSignedDWord() { return (int32)this->PopUnsignedDWord(); }
840 
841  uint64 PopUnsignedQWord()
842  {
843  uint64 val = this->PopUnsignedDWord();
844  return val | (((uint64)this->PopUnsignedDWord()) << 32);
845  }
846  int64 PopSignedQWord() { return (int64)this->PopUnsignedQWord(); }
847 
850  {
851  byte tmp[2];
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];
855  }
856 
857  void PushWord(uint16 word)
858  {
859  if (this->position >= 2) {
860  this->position -= 2;
861  } else {
862  // Rotate right 2 positions
863  std::rotate(this->stack.rbegin(), this->stack.rbegin() + 2, this->stack.rend());
864  }
865  this->stack[this->position] = GB(word, 0, 8);
866  this->stack[this->position + 1] = GB(word, 8, 8);
867  }
868 
869  void ResetStack(const GRFFile *grffile)
870  {
871  assert(grffile != nullptr);
872  this->position = 0;
873  this->grffile = grffile;
874  this->used = true;
875  }
876 
877  void RewindStack() { this->position = 0; }
878 };
879 
882 
888 {
889  return _newgrf_textrefstack.used;
890 }
891 
897 {
898  return new TextRefStack(_newgrf_textrefstack);
899 }
900 
906 {
907  _newgrf_textrefstack = *backup;
908  delete backup;
909 }
910 
929 void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32 *values)
930 {
931  extern TemporaryStorageArray<int32, 0x110> _temp_store;
932 
933  _newgrf_textrefstack.ResetStack(grffile);
934 
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);
940  stack_it++;
941  }
942  }
943 }
944 
947 {
948  _newgrf_textrefstack.used = false;
949 }
950 
951 void RewindTextRefStack()
952 {
953  _newgrf_textrefstack.RewindStack();
954 }
955 
966 uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, uint argv_size, bool modify_argv)
967 {
968  switch (scc) {
969  default: break;
970 
994  if (argv_size < 1) {
995  DEBUG(misc, 0, "Too many NewGRF string parameters.");
996  return 0;
997  }
998  break;
999 
1003  if (argv_size < 2) {
1004  DEBUG(misc, 0, "Too many NewGRF string parameters.");
1005  return 0;
1006  }
1007  break;
1008  }
1009 
1010  if (_newgrf_textrefstack.used && modify_argv) {
1011  switch (scc) {
1012  default: NOT_REACHED();
1013  case SCC_NEWGRF_PRINT_BYTE_SIGNED: *argv = _newgrf_textrefstack.PopSignedByte(); break;
1014  case SCC_NEWGRF_PRINT_QWORD_CURRENCY: *argv = _newgrf_textrefstack.PopSignedQWord(); break;
1015 
1017  case SCC_NEWGRF_PRINT_DWORD_SIGNED: *argv = _newgrf_textrefstack.PopSignedDWord(); break;
1018 
1019  case SCC_NEWGRF_PRINT_BYTE_HEX: *argv = _newgrf_textrefstack.PopUnsignedByte(); break;
1020  case SCC_NEWGRF_PRINT_QWORD_HEX: *argv = _newgrf_textrefstack.PopUnsignedQWord(); break;
1021 
1025  case SCC_NEWGRF_PRINT_WORD_SIGNED: *argv = _newgrf_textrefstack.PopSignedWord(); break;
1026 
1032  case SCC_NEWGRF_PRINT_WORD_UNSIGNED: *argv = _newgrf_textrefstack.PopUnsignedWord(); break;
1033 
1036  case SCC_NEWGRF_PRINT_DWORD_HEX: *argv = _newgrf_textrefstack.PopUnsignedDWord(); break;
1037 
1039  case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: *argv = _newgrf_textrefstack.PopUnsignedWord() + DAYS_TILL_ORIGINAL_BASE_YEAR; break;
1040 
1041  case SCC_NEWGRF_DISCARD_WORD: _newgrf_textrefstack.PopUnsignedWord(); break;
1042 
1043  case SCC_NEWGRF_ROTATE_TOP_4_WORDS: _newgrf_textrefstack.RotateTop4Words(); break;
1044  case SCC_NEWGRF_PUSH_WORD: _newgrf_textrefstack.PushWord(Utf8Consume(str)); break;
1045  case SCC_NEWGRF_UNPRINT: *buff = max(*buff - Utf8Consume(str), buf_start); break;
1046 
1050  argv[0] = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1051  argv[1] = _newgrf_textrefstack.PopUnsignedWord();
1052  break;
1053 
1055  *argv = MapGRFStringID(_newgrf_textrefstack.grffile->grfid, _newgrf_textrefstack.PopUnsignedWord());
1056  break;
1057 
1059  CargoID cargo = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1060  *argv = cargo < NUM_CARGO ? 1 << cargo : 0;
1061  break;
1062  }
1063  }
1064  } else {
1065  /* Consume additional parameter characters */
1066  switch (scc) {
1067  default: break;
1068 
1069  case SCC_NEWGRF_PUSH_WORD:
1070  case SCC_NEWGRF_UNPRINT:
1071  Utf8Consume(str);
1072  break;
1073  }
1074  }
1075 
1076  switch (scc) {
1077  default: NOT_REACHED();
1082  return SCC_COMMA;
1083 
1088  return SCC_HEX;
1089 
1092  return SCC_CURRENCY_LONG;
1093 
1096 
1099  return SCC_DATE_LONG;
1100 
1103  return SCC_DATE_SHORT;
1104 
1106  return SCC_VELOCITY;
1107 
1109  return SCC_VOLUME_LONG;
1110 
1112  return SCC_VOLUME_SHORT;
1113 
1115  return SCC_WEIGHT_LONG;
1116 
1118  return SCC_WEIGHT_SHORT;
1119 
1121  return SCC_POWER;
1122 
1124  return SCC_CARGO_LONG;
1125 
1127  return SCC_CARGO_SHORT;
1128 
1130  return SCC_CARGO_TINY;
1131 
1133  return SCC_CARGO_LIST;
1134 
1136  return SCC_STATION_NAME;
1137 
1140  case SCC_NEWGRF_PUSH_WORD:
1141  case SCC_NEWGRF_UNPRINT:
1142  return 0;
1143  }
1144 }
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:60
#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:80
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:17
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:64
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:46
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:31
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:47
size_t Utf8Decode(WChar *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Definition: string.cpp:446
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:24
static const LanguageMap * GetLanguageMap(uint32 grfid, uint8 language_id)
Get the language map associated with a given NewGRF and language.
Definition: newgrf.cpp:2573
Simple mapping class targeted for small sets of data.
uint8 num_cases
the number of cases of this language
Definition: language.h:54
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:47
static int8 Utf8EncodedCharLen(char c)
Return the length of an UTF-8 encoded value based on a single char.
Definition: string_func.h:116
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:348
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:80
~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:52
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:45
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:547
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:111
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
Definition: string.cpp:136
byte langid
The language associated with this GRFText.
GRFBaseLanguages
Explains the newgrf shift bit positioning.
Definition: newgrf_text.cpp:45
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:40
static TextRefStack _newgrf_textrefstack
The stack that is used for TTDP compatible string code parsing.
Element of the linked list.
Definition: newgrf_text.cpp:68
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:85
uint32 StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
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:35
GRFText * DuplicateGRFText(GRFText *orig)
Create a copy of this GRFText list.
uint8 num_genders
the number of genders of this language
Definition: language.h:53
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:27
static const WChar NFO_UTF8_IDENTIFIER
This character, the thorn (&#39;รพ&#39;), indicates a unicode string to NFO.
Definition: newgrf_text.h:19
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:375
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:40
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:486
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:129
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:20
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:35
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:30
static GRFText * Copy(GRFText *orig)
Create a copy of this GRFText.
Definition: newgrf_text.cpp:90
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:105
Base for the NewGRF implementation.