OpenTTD
newgrf_text.cpp
Go to the documentation of this file.
1 /* $Id: newgrf_text.cpp 27707 2016-12-23 22:13:38Z frosch $ */
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 
38 #define GRFTAB 28
39 #define TABSIZE 11
40 
47  GRFLB_AMERICAN = 0x01,
48  GRFLB_ENGLISH = 0x02,
49  GRFLB_GERMAN = 0x04,
50  GRFLB_FRENCH = 0x08,
51  GRFLB_SPANISH = 0x10,
52  GRFLB_GENERIC = 0x80,
53 };
54 
55 enum GRFExtendedLanguages {
56  GRFLX_AMERICAN = 0x00,
57  GRFLX_ENGLISH = 0x01,
58  GRFLX_GERMAN = 0x02,
59  GRFLX_FRENCH = 0x03,
60  GRFLX_SPANISH = 0x04,
61  GRFLX_UNSPECIFIED = 0x7F,
62 };
63 
69 struct GRFText {
70 public:
81  static GRFText *New(byte langid, const char *text, size_t len)
82  {
83  return new (len) GRFText(langid, text, len);
84  }
85 
91  static GRFText *Copy(GRFText *orig)
92  {
93  return GRFText::New(orig->langid, orig->text, orig->len);
94  }
95 
101  void *operator new(size_t size)
102  {
103  NOT_REACHED();
104  }
105 
110  void operator delete(void *p)
111  {
112  free(p);
113  }
114 private:
121  GRFText(byte langid_, const char *text_, size_t len_) : next(NULL), len(len_), langid(langid_)
122  {
123  /* We need to use memcpy instead of strcpy due to
124  * the possibility of "choice lists" and therefore
125  * intermediate string terminators. */
126  memcpy(this->text, text_, len);
127  }
128 
135  void *operator new(size_t size, size_t extra)
136  {
137  return MallocT<byte>(size + extra);
138  }
139 
140 public:
142  size_t len;
143  byte langid;
144  char text[];
145 };
146 
147 
153 struct GRFTextEntry {
154  uint32 grfid;
155  uint16 stringid;
156  StringID def_string;
157  GRFText *textholder;
158 };
159 
160 
161 static uint _num_grf_texts = 0;
162 static GRFTextEntry _grf_text[(1 << TABSIZE) * 3];
163 static byte _currentLangID = GRFLX_ENGLISH;
164 
171 int LanguageMap::GetMapping(int newgrf_id, bool gender) const
172 {
173  const SmallVector<Mapping, 1> &map = gender ? this->gender_map : this->case_map;
174  for (const Mapping *m = map.Begin(); m != map.End(); m++) {
175  if (m->newgrf_id == newgrf_id) return m->openttd_id;
176  }
177  return -1;
178 }
179 
186 int LanguageMap::GetReverseMapping(int openttd_id, bool gender) const
187 {
188  const SmallVector<Mapping, 1> &map = gender ? this->gender_map : this->case_map;
189  for (const Mapping *m = map.Begin(); m != map.End(); m++) {
190  if (m->openttd_id == openttd_id) return m->newgrf_id;
191  }
192  return -1;
193 }
194 
199  {
200  for (SmallPair<byte, char *> *p = this->strings.Begin(); p < this->strings.End(); p++) {
201  free(p->second);
202  }
203  }
204 
212  type(type), old_d(old_d), offset(offset)
213  {
214  }
215 
217  char *old_d;
218  int offset;
219 
222 
228  char *Flush(const LanguageMap *lm)
229  {
230  if (!this->strings.Contains(0)) {
231  /* In case of a (broken) NewGRF without a default,
232  * assume an empty string. */
233  grfmsg(1, "choice list misses default value");
234  this->strings[0] = stredup("");
235  }
236 
237  char *d = old_d;
238  if (lm == NULL) {
239  /* In case there is no mapping, just ignore everything but the default.
240  * A probable cause for this happening is when the language file has
241  * been removed by the user and as such no mapping could be made. */
242  size_t len = strlen(this->strings[0]);
243  memcpy(d, this->strings[0], len);
244  return d + len;
245  }
246 
247  d += Utf8Encode(d, this->type);
248 
249  if (this->type == SCC_SWITCH_CASE) {
250  /*
251  * Format for case switch:
252  * <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
253  * Each LEN is printed using 2 bytes in big endian order.
254  */
255 
256  /* "<NUM CASES>" */
257  int count = 0;
258  for (uint8 i = 0; i < _current_language->num_cases; i++) {
259  /* Count the ones we have a mapped string for. */
260  if (this->strings.Contains(lm->GetReverseMapping(i, false))) count++;
261  }
262  *d++ = count;
263 
264  for (uint8 i = 0; i < _current_language->num_cases; i++) {
265  /* Resolve the string we're looking for. */
266  int idx = lm->GetReverseMapping(i, false);
267  if (!this->strings.Contains(idx)) continue;
268  char *str = this->strings[idx];
269 
270  /* "<CASEn>" */
271  *d++ = i + 1;
272 
273  /* "<LENn>" */
274  size_t len = strlen(str) + 1;
275  *d++ = GB(len, 8, 8);
276  *d++ = GB(len, 0, 8);
277 
278  /* "<STRINGn>" */
279  memcpy(d, str, len);
280  d += len;
281  }
282 
283  /* "<STRINGDEFAULT>" */
284  size_t len = strlen(this->strings[0]) + 1;
285  memcpy(d, this->strings[0], len);
286  d += len;
287  } else {
288  if (this->type == SCC_PLURAL_LIST) {
289  *d++ = lm->plural_form;
290  }
291 
292  /*
293  * Format for choice list:
294  * <OFFSET> <NUM CHOICES> <LENs> <STRINGs>
295  */
296 
297  /* "<OFFSET>" */
298  *d++ = this->offset - 0x80;
299 
300  /* "<NUM CHOICES>" */
301  int count = (this->type == SCC_GENDER_LIST ? _current_language->num_genders : LANGUAGE_MAX_PLURAL_FORMS);
302  *d++ = count;
303 
304  /* "<LENs>" */
305  for (int i = 0; i < count; i++) {
306  int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1);
307  const char *str = this->strings[this->strings.Contains(idx) ? idx : 0];
308  size_t len = strlen(str) + 1;
309  if (len > 0xFF) grfmsg(1, "choice list string is too long");
310  *d++ = GB(len, 0, 8);
311  }
312 
313  /* "<STRINGs>" */
314  for (int i = 0; i < count; i++) {
315  int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1);
316  const char *str = this->strings[this->strings.Contains(idx) ? idx : 0];
317  /* Limit the length of the string we copy to 0xFE. The length is written above
318  * as a byte and we need room for the final '\0'. */
319  size_t len = min<size_t>(0xFE, strlen(str));
320  memcpy(d, str, len);
321  d += len;
322  *d++ = '\0';
323  }
324  }
325  return d;
326  }
327 };
328 
339 char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_newlines, const char *str, int *olen, StringControlCode byte80)
340 {
341  char *tmp = MallocT<char>(strlen(str) * 10 + 1); // Allocate space to allow for expansion
342  char *d = tmp;
343  bool unicode = false;
344  WChar c;
345  size_t len = Utf8Decode(&c, str);
346 
347  /* Helper variable for a possible (string) mapping. */
348  UnmappedChoiceList *mapping = NULL;
349 
350  if (c == NFO_UTF8_IDENTIFIER) {
351  unicode = true;
352  str += len;
353  }
354 
355  for (;;) {
356  if (unicode && Utf8EncodedCharLen(*str) != 0) {
357  c = Utf8Consume(&str);
358  /* 'Magic' range of control codes. */
359  if (GB(c, 8, 8) == 0xE0) {
360  c = GB(c, 0, 8);
361  } else if (c >= 0x20) {
362  if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
363  d += Utf8Encode(d, c);
364  continue;
365  }
366  } else {
367  c = (byte)*str++;
368  }
369  if (c == '\0') break;
370 
371  switch (c) {
372  case 0x01:
373  if (str[0] == '\0') goto string_end;
374  d += Utf8Encode(d, ' ');
375  str++;
376  break;
377  case 0x0A: break;
378  case 0x0D:
379  if (allow_newlines) {
380  *d++ = 0x0A;
381  } else {
382  grfmsg(1, "Detected newline in string that does not allow one");
383  }
384  break;
385  case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break;
386  case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break;
387  case 0x1F:
388  if (str[0] == '\0' || str[1] == '\0') goto string_end;
389  d += Utf8Encode(d, ' ');
390  str += 2;
391  break;
392  case 0x7B:
393  case 0x7C:
394  case 0x7D:
395  case 0x7E:
396  case 0x7F: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_SIGNED + c - 0x7B); break;
397  case 0x80: d += Utf8Encode(d, byte80); break;
398  case 0x81: {
399  if (str[0] == '\0' || str[1] == '\0') goto string_end;
400  StringID string;
401  string = ((uint8)*str++);
402  string |= ((uint8)*str++) << 8;
403  d += Utf8Encode(d, SCC_NEWGRF_STRINL);
404  d += Utf8Encode(d, MapGRFStringID(grfid, string));
405  break;
406  }
407  case 0x82:
408  case 0x83:
409  case 0x84: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_DATE_LONG + c - 0x82); break;
410  case 0x85: d += Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD); break;
411  case 0x86: d += Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break;
412  case 0x87: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_VOLUME_LONG); break;
413  case 0x88: d += Utf8Encode(d, SCC_BLUE); break;
414  case 0x89: d += Utf8Encode(d, SCC_SILVER); break;
415  case 0x8A: d += Utf8Encode(d, SCC_GOLD); break;
416  case 0x8B: d += Utf8Encode(d, SCC_RED); break;
417  case 0x8C: d += Utf8Encode(d, SCC_PURPLE); break;
418  case 0x8D: d += Utf8Encode(d, SCC_LTBROWN); break;
419  case 0x8E: d += Utf8Encode(d, SCC_ORANGE); break;
420  case 0x8F: d += Utf8Encode(d, SCC_GREEN); break;
421  case 0x90: d += Utf8Encode(d, SCC_YELLOW); break;
422  case 0x91: d += Utf8Encode(d, SCC_DKGREEN); break;
423  case 0x92: d += Utf8Encode(d, SCC_CREAM); break;
424  case 0x93: d += Utf8Encode(d, SCC_BROWN); break;
425  case 0x94: d += Utf8Encode(d, SCC_WHITE); break;
426  case 0x95: d += Utf8Encode(d, SCC_LTBLUE); break;
427  case 0x96: d += Utf8Encode(d, SCC_GRAY); break;
428  case 0x97: d += Utf8Encode(d, SCC_DKBLUE); break;
429  case 0x98: d += Utf8Encode(d, SCC_BLACK); break;
430  case 0x9A: {
431  int code = *str++;
432  switch (code) {
433  case 0x00: goto string_end;
434  case 0x01: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break;
435  /* 0x02: ignore next colour byte is not supported. It works on the final
436  * string and as such hooks into the string drawing routine. At that
437  * point many things already happened, such as splitting up of strings
438  * when drawn over multiple lines or right-to-left translations, which
439  * make the behaviour peculiar, e.g. only happening at specific width
440  * of windows. Or we need to add another pass over the string to just
441  * support this. As such it is not implemented in OpenTTD. */
442  case 0x03: {
443  if (str[0] == '\0' || str[1] == '\0') goto string_end;
444  uint16 tmp = ((uint8)*str++);
445  tmp |= ((uint8)*str++) << 8;
447  d += Utf8Encode(d, tmp);
448  break;
449  }
450  case 0x04:
451  if (str[0] == '\0') goto string_end;
453  d += Utf8Encode(d, *str++);
454  break;
455  case 0x06: d += Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX); break;
456  case 0x07: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX); break;
457  case 0x08: d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX); break;
458  /* 0x09, 0x0A are TTDPatch internal use only string codes. */
459  case 0x0B: d += Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_HEX); break;
460  case 0x0C: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_STATION_NAME); break;
461  case 0x0D: d += Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG); break;
462  case 0x0E:
463  case 0x0F: {
464  if (str[0] == '\0') goto string_end;
465  const LanguageMap *lm = LanguageMap::GetLanguageMap(grfid, language_id);
466  int index = *str++;
467  int mapped = lm != NULL ? lm->GetMapping(index, code == 0x0E) : -1;
468  if (mapped >= 0) {
469  d += Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
470  d += Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
471  }
472  break;
473  }
474 
475  case 0x10:
476  case 0x11:
477  if (str[0] == '\0') goto string_end;
478  if (mapping == NULL) {
479  if (code == 0x10) str++; // Skip the index
480  grfmsg(1, "choice list %s marker found when not expected", code == 0x10 ? "next" : "default");
481  break;
482  } else {
483  /* Terminate the previous string. */
484  *d = '\0';
485  int index = (code == 0x10 ? *str++ : 0);
486  if (mapping->strings.Contains(index)) {
487  grfmsg(1, "duplicate choice list string, ignoring");
488  d++;
489  } else {
490  d = mapping->strings[index] = MallocT<char>(strlen(str) * 10 + 1);
491  }
492  }
493  break;
494 
495  case 0x12:
496  if (mapping == NULL) {
497  grfmsg(1, "choice list end marker found when not expected");
498  } else {
499  /* Terminate the previous string. */
500  *d = '\0';
501 
502  /* Now we can start flushing everything and clean everything up. */
503  d = mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id));
504  delete mapping;
505  mapping = NULL;
506  }
507  break;
508 
509  case 0x13:
510  case 0x14:
511  case 0x15:
512  if (str[0] == '\0') goto string_end;
513  if (mapping != NULL) {
514  grfmsg(1, "choice lists can't be stacked, it's going to get messy now...");
515  if (code != 0x14) str++;
516  } else {
517  static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
518  mapping = new UnmappedChoiceList(mp[code - 0x13], d, code == 0x14 ? 0 : *str++);
519  }
520  break;
521 
522  case 0x16:
523  case 0x17:
524  case 0x18:
525  case 0x19:
526  case 0x1A:
527  case 0x1B:
528  case 0x1C:
529  case 0x1D:
530  case 0x1E:
531  d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_DATE_LONG + code - 0x16);
532  break;
533 
534  default:
535  grfmsg(1, "missing handler for extended format code");
536  break;
537  }
538  break;
539  }
540 
541  case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro
542  case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis
543  case 0xA0: d += Utf8Encode(d, SCC_UP_ARROW); break;
544  case 0xAA: d += Utf8Encode(d, SCC_DOWN_ARROW); break;
545  case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK); break;
546  case 0xAD: d += Utf8Encode(d, SCC_CROSS); break;
547  case 0xAF: d += Utf8Encode(d, SCC_RIGHT_ARROW); break;
548  case 0xB4: d += Utf8Encode(d, SCC_TRAIN); break;
549  case 0xB5: d += Utf8Encode(d, SCC_LORRY); break;
550  case 0xB6: d += Utf8Encode(d, SCC_BUS); break;
551  case 0xB7: d += Utf8Encode(d, SCC_PLANE); break;
552  case 0xB8: d += Utf8Encode(d, SCC_SHIP); break;
553  case 0xB9: d += Utf8Encode(d, SCC_SUPERSCRIPT_M1); break;
554  case 0xBC: d += Utf8Encode(d, SCC_SMALL_UP_ARROW); break;
555  case 0xBD: d += Utf8Encode(d, SCC_SMALL_DOWN_ARROW); break;
556  default:
557  /* Validate any unhandled character */
558  if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
559  d += Utf8Encode(d, c);
560  break;
561  }
562  }
563 
564 string_end:
565  if (mapping != NULL) {
566  grfmsg(1, "choice list was incomplete, the whole list is ignored");
567  delete mapping;
568  }
569 
570  *d = '\0';
571  if (olen != NULL) *olen = d - tmp + 1;
572  tmp = ReallocT(tmp, d - tmp + 1);
573  return tmp;
574 }
575 
581 void AddGRFTextToList(GRFText **list, GRFText *text_to_add)
582 {
583  GRFText **ptext, *text;
584 
585  /* Loop through all languages and see if we can replace a string */
586  for (ptext = list; (text = *ptext) != NULL; ptext = &text->next) {
587  if (text->langid == text_to_add->langid) {
588  text_to_add->next = text->next;
589  *ptext = text_to_add;
590  delete text;
591  return;
592  }
593  }
594 
595  /* If a string wasn't replaced, then we must append the new string */
596  *ptext = text_to_add;
597 }
598 
608 void AddGRFTextToList(struct GRFText **list, byte langid, uint32 grfid, bool allow_newlines, const char *text_to_add)
609 {
610  int len;
611  char *translatedtext = TranslateTTDPatchCodes(grfid, langid, allow_newlines, text_to_add, &len);
612  GRFText *newtext = GRFText::New(langid, translatedtext, len);
613  free(translatedtext);
614 
615  AddGRFTextToList(list, newtext);
616 }
617 
624 void AddGRFTextToList(struct GRFText **list, const char *text_to_add)
625 {
626  AddGRFTextToList(list, GRFText::New(0x7F, text_to_add, strlen(text_to_add) + 1));
627 }
628 
635 {
636  GRFText *newtext = NULL;
637  GRFText **ptext = &newtext;
638  for (; orig != NULL; orig = orig->next) {
639  *ptext = GRFText::Copy(orig);
640  ptext = &(*ptext)->next;
641  }
642  return newtext;
643 }
644 
648 StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool new_scheme, bool allow_newlines, const char *text_to_add, StringID def_string)
649 {
650  char *translatedtext;
651  uint id;
652 
653  /* When working with the old language scheme (grf_version is less than 7) and
654  * English or American is among the set bits, simply add it as English in
655  * the new scheme, i.e. as langid = 1.
656  * If English is set, it is pretty safe to assume the translations are not
657  * actually translated.
658  */
659  if (!new_scheme) {
660  if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
661  langid_to_add = GRFLX_ENGLISH;
662  } else {
663  StringID ret = STR_EMPTY;
664  if (langid_to_add & GRFLB_GERMAN) ret = AddGRFString(grfid, stringid, GRFLX_GERMAN, true, allow_newlines, text_to_add, def_string);
665  if (langid_to_add & GRFLB_FRENCH) ret = AddGRFString(grfid, stringid, GRFLX_FRENCH, true, allow_newlines, text_to_add, def_string);
666  if (langid_to_add & GRFLB_SPANISH) ret = AddGRFString(grfid, stringid, GRFLX_SPANISH, true, allow_newlines, text_to_add, def_string);
667  return ret;
668  }
669  }
670 
671  for (id = 0; id < _num_grf_texts; id++) {
672  if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
673  break;
674  }
675  }
676 
677  /* Too many strings allocated, return empty */
678  if (id == lengthof(_grf_text)) return STR_EMPTY;
679 
680  int len;
681  translatedtext = TranslateTTDPatchCodes(grfid, langid_to_add, allow_newlines, text_to_add, &len);
682 
683  GRFText *newtext = GRFText::New(langid_to_add, translatedtext, len);
684 
685  free(translatedtext);
686 
687  /* If we didn't find our stringid and grfid in the list, allocate a new id */
688  if (id == _num_grf_texts) _num_grf_texts++;
689 
690  if (_grf_text[id].textholder == NULL) {
691  _grf_text[id].grfid = grfid;
692  _grf_text[id].stringid = stringid;
693  _grf_text[id].def_string = def_string;
694  }
695  AddGRFTextToList(&_grf_text[id].textholder, newtext);
696 
697  grfmsg(3, "Added 0x%X: grfid %08X string 0x%X lang 0x%X string '%s'", id, grfid, stringid, newtext->langid, newtext->text);
698 
699  return (GRFTAB << TABSIZE) + id;
700 }
701 
705 StringID GetGRFStringID(uint32 grfid, uint16 stringid)
706 {
707  for (uint id = 0; id < _num_grf_texts; id++) {
708  if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
709  return (GRFTAB << TABSIZE) + id;
710  }
711  }
712 
713  return STR_UNDEFINED;
714 }
715 
716 
724 const char *GetGRFStringFromGRFText(const GRFText *text)
725 {
726  const char *default_text = NULL;
727 
728  /* Search the list of lang-strings of this stringid for current lang */
729  for (; text != NULL; text = text->next) {
730  if (text->langid == _currentLangID) return text->text;
731 
732  /* If the current string is English or American, set it as the
733  * fallback language if the specific language isn't available. */
734  if (text->langid == GRFLX_UNSPECIFIED || (default_text == NULL && (text->langid == GRFLX_ENGLISH || text->langid == GRFLX_AMERICAN))) {
735  default_text = text->text;
736  }
737  }
738 
739  return default_text;
740 }
741 
745 const char *GetGRFStringPtr(uint16 stringid)
746 {
747  assert(_grf_text[stringid].grfid != 0);
748 
749  const char *str = GetGRFStringFromGRFText(_grf_text[stringid].textholder);
750  if (str != NULL) return str;
751 
752  /* Use the default string ID if the fallback string isn't available */
753  return GetStringPtr(_grf_text[stringid].def_string);
754 }
755 
764 void SetCurrentGrfLangID(byte language_id)
765 {
766  _currentLangID = language_id;
767 }
768 
769 bool CheckGrfLangID(byte lang_id, byte grf_version)
770 {
771  if (grf_version < 7) {
772  switch (_currentLangID) {
773  case GRFLX_GERMAN: return (lang_id & GRFLB_GERMAN) != 0;
774  case GRFLX_FRENCH: return (lang_id & GRFLB_FRENCH) != 0;
775  case GRFLX_SPANISH: return (lang_id & GRFLB_SPANISH) != 0;
776  default: return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
777  }
778  }
779 
780  return (lang_id == _currentLangID || lang_id == GRFLX_UNSPECIFIED);
781 }
782 
787 void CleanUpGRFText(GRFText *grftext)
788 {
789  while (grftext != NULL) {
790  GRFText *grftext2 = grftext->next;
791  delete grftext;
792  grftext = grftext2;
793  }
794 }
795 
801 {
802  uint id;
803 
804  for (id = 0; id < _num_grf_texts; id++) {
805  CleanUpGRFText(_grf_text[id].textholder);
806  _grf_text[id].grfid = 0;
807  _grf_text[id].stringid = 0;
808  _grf_text[id].textholder = NULL;
809  }
810 
811  _num_grf_texts = 0;
812 }
813 
814 struct TextRefStack {
815  byte stack[0x30];
816  byte position;
817  const GRFFile *grffile;
818  bool used;
819 
820  TextRefStack() : position(0), grffile(NULL), used(false) {}
821 
822  TextRefStack(const TextRefStack &stack) :
823  position(stack.position),
824  grffile(stack.grffile),
825  used(stack.used)
826  {
827  memcpy(this->stack, stack.stack, sizeof(this->stack));
828  }
829 
830  uint8 PopUnsignedByte() { assert(this->position < lengthof(this->stack)); return this->stack[this->position++]; }
831  int8 PopSignedByte() { return (int8)this->PopUnsignedByte(); }
832 
833  uint16 PopUnsignedWord()
834  {
835  uint16 val = this->PopUnsignedByte();
836  return val | (this->PopUnsignedByte() << 8);
837  }
838  int16 PopSignedWord() { return (int32)this->PopUnsignedWord(); }
839 
840  uint32 PopUnsignedDWord()
841  {
842  uint32 val = this->PopUnsignedWord();
843  return val | (this->PopUnsignedWord() << 16);
844  }
845  int32 PopSignedDWord() { return (int32)this->PopUnsignedDWord(); }
846 
847  uint64 PopUnsignedQWord()
848  {
849  uint64 val = this->PopUnsignedDWord();
850  return val | (((uint64)this->PopUnsignedDWord()) << 32);
851  }
852  int64 PopSignedQWord() { return (int64)this->PopUnsignedQWord(); }
853 
856  {
857  byte tmp[2];
858  for (int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6];
859  for (int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
860  for (int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i];
861  }
862 
863  void PushWord(uint16 word)
864  {
865  if (this->position >= 2) {
866  this->position -= 2;
867  } else {
868  for (int i = lengthof(stack) - 1; i >= this->position + 2; i--) {
869  this->stack[i] = this->stack[i - 2];
870  }
871  }
872  this->stack[this->position] = GB(word, 0, 8);
873  this->stack[this->position + 1] = GB(word, 8, 8);
874  }
875 
876  void ResetStack(const GRFFile *grffile)
877  {
878  assert(grffile != NULL);
879  this->position = 0;
880  this->grffile = grffile;
881  this->used = true;
882  }
883 
884  void RewindStack() { this->position = 0; }
885 };
886 
889 
895 {
896  return _newgrf_textrefstack.used;
897 }
898 
904 {
905  return new TextRefStack(_newgrf_textrefstack);
906 }
907 
913 {
914  _newgrf_textrefstack = *backup;
915  delete backup;
916 }
917 
936 void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32 *values)
937 {
938  extern TemporaryStorageArray<int32, 0x110> _temp_store;
939 
940  _newgrf_textrefstack.ResetStack(grffile);
941 
942  byte *p = _newgrf_textrefstack.stack;
943  for (uint i = 0; i < numEntries; i++) {
944  uint32 value = values != NULL ? values[i] : _temp_store.GetValue(0x100 + i);
945  for (uint j = 0; j < 32; j += 8) {
946  *p = GB(value, j, 8);
947  p++;
948  }
949  }
950 }
951 
954 {
955  _newgrf_textrefstack.used = false;
956 }
957 
958 void RewindTextRefStack()
959 {
960  _newgrf_textrefstack.RewindStack();
961 }
962 
973 uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, uint argv_size, bool modify_argv)
974 {
975  switch (scc) {
976  default: break;
977 
1001  if (argv_size < 1) {
1002  DEBUG(misc, 0, "Too many NewGRF string parameters.");
1003  return 0;
1004  }
1005  break;
1006 
1010  if (argv_size < 2) {
1011  DEBUG(misc, 0, "Too many NewGRF string parameters.");
1012  return 0;
1013  }
1014  break;
1015  }
1016 
1017  if (_newgrf_textrefstack.used && modify_argv) {
1018  switch (scc) {
1019  default: NOT_REACHED();
1020  case SCC_NEWGRF_PRINT_BYTE_SIGNED: *argv = _newgrf_textrefstack.PopSignedByte(); break;
1021  case SCC_NEWGRF_PRINT_QWORD_CURRENCY: *argv = _newgrf_textrefstack.PopSignedQWord(); break;
1022 
1024  case SCC_NEWGRF_PRINT_DWORD_SIGNED: *argv = _newgrf_textrefstack.PopSignedDWord(); break;
1025 
1026  case SCC_NEWGRF_PRINT_BYTE_HEX: *argv = _newgrf_textrefstack.PopUnsignedByte(); break;
1027  case SCC_NEWGRF_PRINT_QWORD_HEX: *argv = _newgrf_textrefstack.PopUnsignedQWord(); break;
1028 
1032  case SCC_NEWGRF_PRINT_WORD_SIGNED: *argv = _newgrf_textrefstack.PopSignedWord(); break;
1033 
1039  case SCC_NEWGRF_PRINT_WORD_UNSIGNED: *argv = _newgrf_textrefstack.PopUnsignedWord(); break;
1040 
1043  case SCC_NEWGRF_PRINT_DWORD_HEX: *argv = _newgrf_textrefstack.PopUnsignedDWord(); break;
1044 
1046  case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: *argv = _newgrf_textrefstack.PopUnsignedWord() + DAYS_TILL_ORIGINAL_BASE_YEAR; break;
1047 
1048  case SCC_NEWGRF_DISCARD_WORD: _newgrf_textrefstack.PopUnsignedWord(); break;
1049 
1050  case SCC_NEWGRF_ROTATE_TOP_4_WORDS: _newgrf_textrefstack.RotateTop4Words(); break;
1051  case SCC_NEWGRF_PUSH_WORD: _newgrf_textrefstack.PushWord(Utf8Consume(str)); break;
1052  case SCC_NEWGRF_UNPRINT: *buff = max(*buff - Utf8Consume(str), buf_start); break;
1053 
1057  argv[0] = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1058  argv[1] = _newgrf_textrefstack.PopUnsignedWord();
1059  break;
1060 
1062  *argv = MapGRFStringID(_newgrf_textrefstack.grffile->grfid, _newgrf_textrefstack.PopUnsignedWord());
1063  break;
1064 
1066  CargoID cargo = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1067  *argv = cargo < NUM_CARGO ? 1 << cargo : 0;
1068  break;
1069  }
1070  }
1071  } else {
1072  /* Consume additional parameter characters */
1073  switch (scc) {
1074  default: break;
1075 
1076  case SCC_NEWGRF_PUSH_WORD:
1077  case SCC_NEWGRF_UNPRINT:
1078  Utf8Consume(str);
1079  break;
1080  }
1081  }
1082 
1083  switch (scc) {
1084  default: NOT_REACHED();
1089  return SCC_COMMA;
1090 
1095  return SCC_HEX;
1096 
1099  return SCC_CURRENCY_LONG;
1100 
1103 
1106  return SCC_DATE_LONG;
1107 
1110  return SCC_DATE_SHORT;
1111 
1113  return SCC_VELOCITY;
1114 
1116  return SCC_VOLUME_LONG;
1117 
1119  return SCC_VOLUME_SHORT;
1120 
1122  return SCC_WEIGHT_LONG;
1123 
1125  return SCC_WEIGHT_SHORT;
1126 
1128  return SCC_POWER;
1129 
1131  return SCC_CARGO_LONG;
1132 
1134  return SCC_CARGO_SHORT;
1135 
1137  return SCC_CARGO_TINY;
1138 
1140  return SCC_CARGO_LIST;
1141 
1143  return SCC_STATION_NAME;
1144 
1147  case SCC_NEWGRF_PUSH_WORD:
1148  case SCC_NEWGRF_UNPRINT:
1149  return 0;
1150  }
1151 }