OpenTTD
newgrf_text.cpp
Go to the documentation of this file.
1 /* $Id: newgrf_text.cpp 27851 2017-04-09 21:58:07Z peter1138 $ */
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 #include "newgrf.h"
22 #include "strings_func.h"
23 #include "newgrf_storage.h"
24 #include "newgrf_text.h"
25 #include "newgrf_cargo.h"
26 #include "string_func.h"
27 #include "date_type.h"
28 #include "debug.h"
29 #include "core/alloc_type.hpp"
30 #include "core/smallmap_type.hpp"
31 #include "language.h"
32 
33 #include "table/strings.h"
34 #include "table/control_codes.h"
35 
36 #include "safeguards.h"
37 
44  GRFLB_AMERICAN = 0x01,
45  GRFLB_ENGLISH = 0x02,
46  GRFLB_GERMAN = 0x04,
47  GRFLB_FRENCH = 0x08,
48  GRFLB_SPANISH = 0x10,
49  GRFLB_GENERIC = 0x80,
50 };
51 
52 enum GRFExtendedLanguages {
53  GRFLX_AMERICAN = 0x00,
54  GRFLX_ENGLISH = 0x01,
55  GRFLX_GERMAN = 0x02,
56  GRFLX_FRENCH = 0x03,
57  GRFLX_SPANISH = 0x04,
58  GRFLX_UNSPECIFIED = 0x7F,
59 };
60 
66 struct GRFText {
67 public:
78  static GRFText *New(byte langid, const char *text, size_t len)
79  {
80  return new (len) GRFText(langid, text, len);
81  }
82 
88  static GRFText *Copy(GRFText *orig)
89  {
90  return GRFText::New(orig->langid, orig->text, orig->len);
91  }
92 
98  void *operator new(size_t size)
99  {
100  NOT_REACHED();
101  }
102 
107  void operator delete(void *p)
108  {
109  free(p);
110  }
111 private:
118  GRFText(byte langid_, const char *text_, size_t len_) : next(NULL), len(len_), langid(langid_)
119  {
120  /* We need to use memcpy instead of strcpy due to
121  * the possibility of "choice lists" and therefore
122  * intermediate string terminators. */
123  memcpy(this->text, text_, len);
124  }
125 
132  void *operator new(size_t size, size_t extra)
133  {
134  return MallocT<byte>(size + extra);
135  }
136 
137 public:
139  size_t len;
140  byte langid;
141  char text[];
142 };
143 
144 
150 struct GRFTextEntry {
151  uint32 grfid;
152  uint16 stringid;
153  StringID def_string;
154  GRFText *textholder;
155 };
156 
157 
158 static uint _num_grf_texts = 0;
159 static GRFTextEntry _grf_text[TAB_SIZE_NEWGRF];
160 static byte _currentLangID = GRFLX_ENGLISH;
161 
168 int LanguageMap::GetMapping(int newgrf_id, bool gender) const
169 {
170  const SmallVector<Mapping, 1> &map = gender ? this->gender_map : this->case_map;
171  for (const Mapping *m = map.Begin(); m != map.End(); m++) {
172  if (m->newgrf_id == newgrf_id) return m->openttd_id;
173  }
174  return -1;
175 }
176 
183 int LanguageMap::GetReverseMapping(int openttd_id, bool gender) const
184 {
185  const SmallVector<Mapping, 1> &map = gender ? this->gender_map : this->case_map;
186  for (const Mapping *m = map.Begin(); m != map.End(); m++) {
187  if (m->openttd_id == openttd_id) return m->newgrf_id;
188  }
189  return -1;
190 }
191 
196  {
197  for (SmallPair<byte, char *> *p = this->strings.Begin(); p < this->strings.End(); p++) {
198  free(p->second);
199  }
200  }
201 
208  UnmappedChoiceList(StringControlCode type, char *old_d, int offset) :
209  type(type), old_d(old_d), offset(offset)
210  {
211  }
212 
214  char *old_d;
215  int offset;
216 
219 
225  char *Flush(const LanguageMap *lm)
226  {
227  if (!this->strings.Contains(0)) {
228  /* In case of a (broken) NewGRF without a default,
229  * assume an empty string. */
230  grfmsg(1, "choice list misses default value");
231  this->strings[0] = stredup("");
232  }
233 
234  char *d = old_d;
235  if (lm == NULL) {
236  /* In case there is no mapping, just ignore everything but the default.
237  * A probable cause for this happening is when the language file has
238  * been removed by the user and as such no mapping could be made. */
239  size_t len = strlen(this->strings[0]);
240  memcpy(d, this->strings[0], len);
241  return d + len;
242  }
243 
244  d += Utf8Encode(d, this->type);
245 
246  if (this->type == SCC_SWITCH_CASE) {
247  /*
248  * Format for case switch:
249  * <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
250  * Each LEN is printed using 2 bytes in big endian order.
251  */
252 
253  /* "<NUM CASES>" */
254  int count = 0;
255  for (uint8 i = 0; i < _current_language->num_cases; i++) {
256  /* Count the ones we have a mapped string for. */
257  if (this->strings.Contains(lm->GetReverseMapping(i, false))) count++;
258  }
259  *d++ = count;
260 
261  for (uint8 i = 0; i < _current_language->num_cases; i++) {
262  /* Resolve the string we're looking for. */
263  int idx = lm->GetReverseMapping(i, false);
264  if (!this->strings.Contains(idx)) continue;
265  char *str = this->strings[idx];
266 
267  /* "<CASEn>" */
268  *d++ = i + 1;
269 
270  /* "<LENn>" */
271  size_t len = strlen(str) + 1;
272  *d++ = GB(len, 8, 8);
273  *d++ = GB(len, 0, 8);
274 
275  /* "<STRINGn>" */
276  memcpy(d, str, len);
277  d += len;
278  }
279 
280  /* "<STRINGDEFAULT>" */
281  size_t len = strlen(this->strings[0]) + 1;
282  memcpy(d, this->strings[0], len);
283  d += len;
284  } else {
285  if (this->type == SCC_PLURAL_LIST) {
286  *d++ = lm->plural_form;
287  }
288 
289  /*
290  * Format for choice list:
291  * <OFFSET> <NUM CHOICES> <LENs> <STRINGs>
292  */
293 
294  /* "<OFFSET>" */
295  *d++ = this->offset - 0x80;
296 
297  /* "<NUM CHOICES>" */
298  int count = (this->type == SCC_GENDER_LIST ? _current_language->num_genders : LANGUAGE_MAX_PLURAL_FORMS);
299  *d++ = count;
300 
301  /* "<LENs>" */
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);
308  }
309 
310  /* "<STRINGs>" */
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];
314  /* Limit the length of the string we copy to 0xFE. The length is written above
315  * as a byte and we need room for the final '\0'. */
316  size_t len = min<size_t>(0xFE, strlen(str));
317  memcpy(d, str, len);
318  d += len;
319  *d++ = '\0';
320  }
321  }
322  return d;
323  }
324 };
325 
336 char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const char *str, int *olen, StringControlCode byte80)
337 {
338  char *tmp = MallocT<char>(strlen(str) * 10 + 1); // Allocate space to allow for expansion
339  char *d = tmp;
340  bool unicode = false;
341  WChar c;
342  size_t len = Utf8Decode(&c, str);
343 
344  /* Helper variable for a possible (string) mapping. */
345  UnmappedChoiceList *mapping = NULL;
346 
347  if (c == NFO_UTF8_IDENTIFIER) {
348  unicode = true;
349  str += len;
350  }
351 
352  for (;;) {
353  if (unicode && Utf8EncodedCharLen(*str) != 0) {
354  c = Utf8Consume(&str);
355  /* 'Magic' range of control codes. */
356  if (GB(c, 8, 8) == 0xE0) {
357  c = GB(c, 0, 8);
358  } else if (c >= 0x20) {
359  if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
360  d += Utf8Encode(d, c);
361  continue;
362  }
363  } else {
364  c = (byte)*str++;
365  }
366  if (c == '\0') break;
367 
368  switch (c) {
369  case 0x01:
370  if (str[0] == '\0') goto string_end;
371  d += Utf8Encode(d, ' ');
372  str++;
373  break;
374  case 0x0A: break;
375  case 0x0D:
376  if (allow_newlines) {
377  *d++ = 0x0A;
378  } else {
379  grfmsg(1, "Detected newline in string that does not allow one");
380  }
381  break;
382  case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break;
383  case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break;
384  case 0x1F:
385  if (str[0] == '\0' || str[1] == '\0') goto string_end;
386  d += Utf8Encode(d, ' ');
387  str += 2;
388  break;
389  case 0x7B:
390  case 0x7C:
391  case 0x7D:
392  case 0x7E:
393  case 0x7F: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_SIGNED + c - 0x7B); break;
394  case 0x80: d += Utf8Encode(d, byte80); break;
395  case 0x81: {
396  if (str[0] == '\0' || str[1] == '\0') goto string_end;
397  StringID string;
398  string = ((uint8)*str++);
399  string |= ((uint8)*str++) << 8;
400  d += Utf8Encode(d, SCC_NEWGRF_STRINL);
401  d += Utf8Encode(d, MapGRFStringID(grfid, string));
402  break;
403  }
404  case 0x82:
405  case 0x83:
406  case 0x84: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_DATE_LONG + c - 0x82); break;
407  case 0x85: d += Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD); break;
408  case 0x86: d += Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break;
409  case 0x87: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_VOLUME_LONG); break;
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;
427  case 0x9A: {
428  int code = *str++;
429  switch (code) {
430  case 0x00: goto string_end;
431  case 0x01: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break;
432  /* 0x02: ignore next colour byte is not supported. It works on the final
433  * string and as such hooks into the string drawing routine. At that
434  * point many things already happened, such as splitting up of strings
435  * when drawn over multiple lines or right-to-left translations, which
436  * make the behaviour peculiar, e.g. only happening at specific width
437  * of windows. Or we need to add another pass over the string to just
438  * support this. As such it is not implemented in OpenTTD. */
439  case 0x03: {
440  if (str[0] == '\0' || str[1] == '\0') goto string_end;
441  uint16 tmp = ((uint8)*str++);
442  tmp |= ((uint8)*str++) << 8;
444  d += Utf8Encode(d, tmp);
445  break;
446  }
447  case 0x04:
448  if (str[0] == '\0') goto string_end;
450  d += Utf8Encode(d, *str++);
451  break;
452  case 0x06: d += Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX); break;
453  case 0x07: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX); break;
454  case 0x08: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX); break;
455  /* 0x09, 0x0A are TTDPatch internal use only string codes. */
456  case 0x0B: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_HEX); break;
457  case 0x0C: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_STATION_NAME); break;
458  case 0x0D: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG); break;
459  case 0x0E:
460  case 0x0F: {
461  if (str[0] == '\0') goto string_end;
462  const LanguageMap *lm = LanguageMap::GetLanguageMap(grfid, language_id);
463  int index = *str++;
464  int mapped = lm != NULL ? lm->GetMapping(index, code == 0x0E) : -1;
465  if (mapped >= 0) {
466  d += Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
467  d += Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
468  }
469  break;
470  }
471 
472  case 0x10:
473  case 0x11:
474  if (str[0] == '\0') goto string_end;
475  if (mapping == NULL) {
476  if (code == 0x10) str++; // Skip the index
477  grfmsg(1, "choice list %s marker found when not expected", code == 0x10 ? "next" : "default");
478  break;
479  } else {
480  /* Terminate the previous string. */
481  *d = '\0';
482  int index = (code == 0x10 ? *str++ : 0);
483  if (mapping->strings.Contains(index)) {
484  grfmsg(1, "duplicate choice list string, ignoring");
485  d++;
486  } else {
487  d = mapping->strings[index] = MallocT<char>(strlen(str) * 10 + 1);
488  }
489  }
490  break;
491 
492  case 0x12:
493  if (mapping == NULL) {
494  grfmsg(1, "choice list end marker found when not expected");
495  } else {
496  /* Terminate the previous string. */
497  *d = '\0';
498 
499  /* Now we can start flushing everything and clean everything up. */
500  d = mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id));
501  delete mapping;
502  mapping = NULL;
503  }
504  break;
505 
506  case 0x13:
507  case 0x14:
508  case 0x15:
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++;
513  } else {
514  static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
515  mapping = new UnmappedChoiceList(mp[code - 0x13], d, code == 0x14 ? 0 : *str++);
516  }
517  break;
518 
519  case 0x16:
520  case 0x17:
521  case 0x18:
522  case 0x19:
523  case 0x1A:
524  case 0x1B:
525  case 0x1C:
526  case 0x1D:
527  case 0x1E:
528  d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_DATE_LONG + code - 0x16);
529  break;
530 
531  default:
532  grfmsg(1, "missing handler for extended format code");
533  break;
534  }
535  break;
536  }
537 
538  case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro
539  case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis
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;
553  default:
554  /* Validate any unhandled character */
555  if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
556  d += Utf8Encode(d, c);
557  break;
558  }
559  }
560 
561 string_end:
562  if (mapping != NULL) {
563  grfmsg(1, "choice list was incomplete, the whole list is ignored");
564  delete mapping;
565  }
566 
567  *d = '\0';
568  if (olen != NULL) *olen = d - tmp + 1;
569  tmp = ReallocT(tmp, d - tmp + 1);
570  return tmp;
571 }
572 
578 void AddGRFTextToList(GRFText **list, GRFText *text_to_add)
579 {
580  GRFText **ptext, *text;
581 
582  /* Loop through all languages and see if we can replace a string */
583  for (ptext = list; (text = *ptext) != NULL; ptext = &text->next) {
584  if (text->langid == text_to_add->langid) {
585  text_to_add->next = text->next;
586  *ptext = text_to_add;
587  delete text;
588  return;
589  }
590  }
591 
592  /* If a string wasn't replaced, then we must append the new string */
593  *ptext = text_to_add;
594 }
595 
605 void AddGRFTextToList(struct GRFText **list, byte langid, uint32 grfid, bool allow_newlines, const char *text_to_add)
606 {
607  int len;
608  char *translatedtext = TranslateTTDPatchCodes(grfid, langid, allow_newlines, text_to_add, &len);
609  GRFText *newtext = GRFText::New(langid, translatedtext, len);
610  free(translatedtext);
611 
612  AddGRFTextToList(list, newtext);
613 }
614 
621 void AddGRFTextToList(struct GRFText **list, const char *text_to_add)
622 {
623  AddGRFTextToList(list, GRFText::New(0x7F, text_to_add, strlen(text_to_add) + 1));
624 }
625 
632 {
633  GRFText *newtext = NULL;
634  GRFText **ptext = &newtext;
635  for (; orig != NULL; orig = orig->next) {
636  *ptext = GRFText::Copy(orig);
637  ptext = &(*ptext)->next;
638  }
639  return newtext;
640 }
641 
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)
646 {
647  char *translatedtext;
648  uint id;
649 
650  /* When working with the old language scheme (grf_version is less than 7) and
651  * English or American is among the set bits, simply add it as English in
652  * the new scheme, i.e. as langid = 1.
653  * If English is set, it is pretty safe to assume the translations are not
654  * actually translated.
655  */
656  if (!new_scheme) {
657  if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
658  langid_to_add = GRFLX_ENGLISH;
659  } else {
660  StringID ret = STR_EMPTY;
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);
664  return ret;
665  }
666  }
667 
668  for (id = 0; id < _num_grf_texts; id++) {
669  if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
670  break;
671  }
672  }
673 
674  /* Too many strings allocated, return empty */
675  if (id == lengthof(_grf_text)) return STR_EMPTY;
676 
677  int len;
678  translatedtext = TranslateTTDPatchCodes(grfid, langid_to_add, allow_newlines, text_to_add, &len);
679 
680  GRFText *newtext = GRFText::New(langid_to_add, translatedtext, len);
681 
682  free(translatedtext);
683 
684  /* If we didn't find our stringid and grfid in the list, allocate a new id */
685  if (id == _num_grf_texts) _num_grf_texts++;
686 
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;
691  }
692  AddGRFTextToList(&_grf_text[id].textholder, newtext);
693 
694  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));
695 
697 }
698 
702 StringID GetGRFStringID(uint32 grfid, StringID stringid)
703 {
704  for (uint id = 0; id < _num_grf_texts; id++) {
705  if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
707  }
708  }
709 
710  return STR_UNDEFINED;
711 }
712 
713 
722 {
723  const char *default_text = NULL;
724 
725  /* Search the list of lang-strings of this stringid for current lang */
726  for (; text != NULL; text = text->next) {
727  if (text->langid == _currentLangID) return text->text;
728 
729  /* If the current string is English or American, set it as the
730  * fallback language if the specific language isn't available. */
731  if (text->langid == GRFLX_UNSPECIFIED || (default_text == NULL && (text->langid == GRFLX_ENGLISH || text->langid == GRFLX_AMERICAN))) {
732  default_text = text->text;
733  }
734  }
735 
736  return default_text;
737 }
738 
742 const char *GetGRFStringPtr(uint16 stringid)
743 {
744  assert(_grf_text[stringid].grfid != 0);
745 
746  const char *str = GetGRFStringFromGRFText(_grf_text[stringid].textholder);
747  if (str != NULL) return str;
748 
749  /* Use the default string ID if the fallback string isn't available */
750  return GetStringPtr(_grf_text[stringid].def_string);
751 }
752 
761 void SetCurrentGrfLangID(byte language_id)
762 {
763  _currentLangID = language_id;
764 }
765 
766 bool CheckGrfLangID(byte lang_id, byte grf_version)
767 {
768  if (grf_version < 7) {
769  switch (_currentLangID) {
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;
774  }
775  }
776 
777  return (lang_id == _currentLangID || lang_id == GRFLX_UNSPECIFIED);
778 }
779 
784 void CleanUpGRFText(GRFText *grftext)
785 {
786  while (grftext != NULL) {
787  GRFText *grftext2 = grftext->next;
788  delete grftext;
789  grftext = grftext2;
790  }
791 }
792 
798 {
799  uint id;
800 
801  for (id = 0; id < _num_grf_texts; id++) {
802  CleanUpGRFText(_grf_text[id].textholder);
803  _grf_text[id].grfid = 0;
804  _grf_text[id].stringid = 0;
805  _grf_text[id].textholder = NULL;
806  }
807 
808  _num_grf_texts = 0;
809 }
810 
811 struct TextRefStack {
812  byte stack[0x30];
813  byte position;
814  const GRFFile *grffile;
815  bool used;
816 
817  TextRefStack() : position(0), grffile(NULL), used(false) {}
818 
819  TextRefStack(const TextRefStack &stack) :
820  position(stack.position),
821  grffile(stack.grffile),
822  used(stack.used)
823  {
824  memcpy(this->stack, stack.stack, sizeof(this->stack));
825  }
826 
827  uint8 PopUnsignedByte() { assert(this->position < lengthof(this->stack)); return this->stack[this->position++]; }
828  int8 PopSignedByte() { return (int8)this->PopUnsignedByte(); }
829 
830  uint16 PopUnsignedWord()
831  {
832  uint16 val = this->PopUnsignedByte();
833  return val | (this->PopUnsignedByte() << 8);
834  }
835  int16 PopSignedWord() { return (int32)this->PopUnsignedWord(); }
836 
837  uint32 PopUnsignedDWord()
838  {
839  uint32 val = this->PopUnsignedWord();
840  return val | (this->PopUnsignedWord() << 16);
841  }
842  int32 PopSignedDWord() { return (int32)this->PopUnsignedDWord(); }
843 
844  uint64 PopUnsignedQWord()
845  {
846  uint64 val = this->PopUnsignedDWord();
847  return val | (((uint64)this->PopUnsignedDWord()) << 32);
848  }
849  int64 PopSignedQWord() { return (int64)this->PopUnsignedQWord(); }
850 
853  {
854  byte tmp[2];
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];
858  }
859 
860  void PushWord(uint16 word)
861  {
862  if (this->position >= 2) {
863  this->position -= 2;
864  } else {
865  for (int i = lengthof(stack) - 1; i >= this->position + 2; i--) {
866  this->stack[i] = this->stack[i - 2];
867  }
868  }
869  this->stack[this->position] = GB(word, 0, 8);
870  this->stack[this->position + 1] = GB(word, 8, 8);
871  }
872 
873  void ResetStack(const GRFFile *grffile)
874  {
875  assert(grffile != NULL);
876  this->position = 0;
877  this->grffile = grffile;
878  this->used = true;
879  }
880 
881  void RewindStack() { this->position = 0; }
882 };
883 
886 
892 {
893  return _newgrf_textrefstack.used;
894 }
895 
901 {
902  return new TextRefStack(_newgrf_textrefstack);
903 }
904 
910 {
911  _newgrf_textrefstack = *backup;
912  delete backup;
913 }
914 
933 void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32 *values)
934 {
935  extern TemporaryStorageArray<int32, 0x110> _temp_store;
936 
937  _newgrf_textrefstack.ResetStack(grffile);
938 
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);
944  p++;
945  }
946  }
947 }
948 
951 {
952  _newgrf_textrefstack.used = false;
953 }
954 
955 void RewindTextRefStack()
956 {
957  _newgrf_textrefstack.RewindStack();
958 }
959 
970 uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, uint argv_size, bool modify_argv)
971 {
972  switch (scc) {
973  default: break;
974 
998  if (argv_size < 1) {
999  DEBUG(misc, 0, "Too many NewGRF string parameters.");
1000  return 0;
1001  }
1002  break;
1003 
1007  if (argv_size < 2) {
1008  DEBUG(misc, 0, "Too many NewGRF string parameters.");
1009  return 0;
1010  }
1011  break;
1012  }
1013 
1014  if (_newgrf_textrefstack.used && modify_argv) {
1015  switch (scc) {
1016  default: NOT_REACHED();
1017  case SCC_NEWGRF_PRINT_BYTE_SIGNED: *argv = _newgrf_textrefstack.PopSignedByte(); break;
1018  case SCC_NEWGRF_PRINT_QWORD_CURRENCY: *argv = _newgrf_textrefstack.PopSignedQWord(); break;
1019 
1021  case SCC_NEWGRF_PRINT_DWORD_SIGNED: *argv = _newgrf_textrefstack.PopSignedDWord(); break;
1022 
1023  case SCC_NEWGRF_PRINT_BYTE_HEX: *argv = _newgrf_textrefstack.PopUnsignedByte(); break;
1024  case SCC_NEWGRF_PRINT_QWORD_HEX: *argv = _newgrf_textrefstack.PopUnsignedQWord(); break;
1025 
1029  case SCC_NEWGRF_PRINT_WORD_SIGNED: *argv = _newgrf_textrefstack.PopSignedWord(); break;
1030 
1036  case SCC_NEWGRF_PRINT_WORD_UNSIGNED: *argv = _newgrf_textrefstack.PopUnsignedWord(); break;
1037 
1040  case SCC_NEWGRF_PRINT_DWORD_HEX: *argv = _newgrf_textrefstack.PopUnsignedDWord(); break;
1041 
1043  case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: *argv = _newgrf_textrefstack.PopUnsignedWord() + DAYS_TILL_ORIGINAL_BASE_YEAR; break;
1044 
1045  case SCC_NEWGRF_DISCARD_WORD: _newgrf_textrefstack.PopUnsignedWord(); break;
1046 
1047  case SCC_NEWGRF_ROTATE_TOP_4_WORDS: _newgrf_textrefstack.RotateTop4Words(); break;
1048  case SCC_NEWGRF_PUSH_WORD: _newgrf_textrefstack.PushWord(Utf8Consume(str)); break;
1049  case SCC_NEWGRF_UNPRINT: *buff = max(*buff - Utf8Consume(str), buf_start); break;
1050 
1054  argv[0] = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1055  argv[1] = _newgrf_textrefstack.PopUnsignedWord();
1056  break;
1057 
1059  *argv = MapGRFStringID(_newgrf_textrefstack.grffile->grfid, _newgrf_textrefstack.PopUnsignedWord());
1060  break;
1061 
1063  CargoID cargo = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1064  *argv = cargo < NUM_CARGO ? 1 << cargo : 0;
1065  break;
1066  }
1067  }
1068  } else {
1069  /* Consume additional parameter characters */
1070  switch (scc) {
1071  default: break;
1072 
1073  case SCC_NEWGRF_PUSH_WORD:
1074  case SCC_NEWGRF_UNPRINT:
1075  Utf8Consume(str);
1076  break;
1077  }
1078  }
1079 
1080  switch (scc) {
1081  default: NOT_REACHED();
1086  return SCC_COMMA;
1087 
1092  return SCC_HEX;
1093 
1096  return SCC_CURRENCY_LONG;
1097 
1100 
1103  return SCC_DATE_LONG;
1104 
1107  return SCC_DATE_SHORT;
1108 
1110  return SCC_VELOCITY;
1111 
1113  return SCC_VOLUME_LONG;
1114 
1116  return SCC_VOLUME_SHORT;
1117 
1119  return SCC_WEIGHT_LONG;
1120 
1122  return SCC_WEIGHT_SHORT;
1123 
1125  return SCC_POWER;
1126 
1128  return SCC_CARGO_LONG;
1129 
1131  return SCC_CARGO_SHORT;
1132 
1134  return SCC_CARGO_TINY;
1135 
1137  return SCC_CARGO_LIST;
1138 
1140  return SCC_STATION_NAME;
1141 
1144  case SCC_NEWGRF_PUSH_WORD:
1145  case SCC_NEWGRF_UNPRINT:
1146  return 0;
1147  }
1148 }
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.
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.
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).
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:50
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
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.
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:437
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.
Definition: math_func.hpp:26
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.
Definition: newgrf.cpp:2543
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:338
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:78
~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.
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.
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:552
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:126
byte langid
The language associated with this GRFText.
GRFBaseLanguages
Explains the newgrf shift bit positioning.
Definition: newgrf_text.cpp:43
#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:66
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:150
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:39
Maximal number of cargo types in a game.
Definition: cargo_type.h:66
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:27
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: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: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:477
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:114
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: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:88
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:104
Base for the NewGRF implementation.