OpenTTD
newgrf_text.cpp
Go to the documentation of this file.
1 /* $Id: newgrf_text.cpp 26713 2014-08-03 11:59:07Z 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  d += Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_DATE_LONG + code - 0x16);
531  break;
532 
533  default:
534  grfmsg(1, "missing handler for extended format code");
535  break;
536  }
537  break;
538  }
539 
540  case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro
541  case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis
542  case 0xA0: d += Utf8Encode(d, SCC_UP_ARROW); break;
543  case 0xAA: d += Utf8Encode(d, SCC_DOWN_ARROW); break;
544  case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK); break;
545  case 0xAD: d += Utf8Encode(d, SCC_CROSS); break;
546  case 0xAF: d += Utf8Encode(d, SCC_RIGHT_ARROW); break;
547  case 0xB4: d += Utf8Encode(d, SCC_TRAIN); break;
548  case 0xB5: d += Utf8Encode(d, SCC_LORRY); break;
549  case 0xB6: d += Utf8Encode(d, SCC_BUS); break;
550  case 0xB7: d += Utf8Encode(d, SCC_PLANE); break;
551  case 0xB8: d += Utf8Encode(d, SCC_SHIP); break;
552  case 0xB9: d += Utf8Encode(d, SCC_SUPERSCRIPT_M1); break;
553  case 0xBC: d += Utf8Encode(d, SCC_SMALL_UP_ARROW); break;
554  case 0xBD: d += Utf8Encode(d, SCC_SMALL_DOWN_ARROW); break;
555  default:
556  /* Validate any unhandled character */
557  if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
558  d += Utf8Encode(d, c);
559  break;
560  }
561  }
562 
563 string_end:
564  if (mapping != NULL) {
565  grfmsg(1, "choice list was incomplete, the whole list is ignored");
566  delete mapping;
567  }
568 
569  *d = '\0';
570  if (olen != NULL) *olen = d - tmp + 1;
571  tmp = ReallocT(tmp, d - tmp + 1);
572  return tmp;
573 }
574 
580 void AddGRFTextToList(GRFText **list, GRFText *text_to_add)
581 {
582  GRFText **ptext, *text;
583 
584  /* Loop through all languages and see if we can replace a string */
585  for (ptext = list; (text = *ptext) != NULL; ptext = &text->next) {
586  if (text->langid == text_to_add->langid) {
587  text_to_add->next = text->next;
588  *ptext = text_to_add;
589  delete text;
590  return;
591  }
592  }
593 
594  /* If a string wasn't replaced, then we must append the new string */
595  *ptext = text_to_add;
596 }
597 
607 void AddGRFTextToList(struct GRFText **list, byte langid, uint32 grfid, bool allow_newlines, const char *text_to_add)
608 {
609  int len;
610  char *translatedtext = TranslateTTDPatchCodes(grfid, langid, allow_newlines, text_to_add, &len);
611  GRFText *newtext = GRFText::New(langid, translatedtext, len);
612  free(translatedtext);
613 
614  AddGRFTextToList(list, newtext);
615 }
616 
623 void AddGRFTextToList(struct GRFText **list, const char *text_to_add)
624 {
625  AddGRFTextToList(list, GRFText::New(0x7F, text_to_add, strlen(text_to_add) + 1));
626 }
627 
634 {
635  GRFText *newtext = NULL;
636  GRFText **ptext = &newtext;
637  for (; orig != NULL; orig = orig->next) {
638  *ptext = GRFText::Copy(orig);
639  ptext = &(*ptext)->next;
640  }
641  return newtext;
642 }
643 
647 StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool new_scheme, bool allow_newlines, const char *text_to_add, StringID def_string)
648 {
649  char *translatedtext;
650  uint id;
651 
652  /* When working with the old language scheme (grf_version is less than 7) and
653  * English or American is among the set bits, simply add it as English in
654  * the new scheme, i.e. as langid = 1.
655  * If English is set, it is pretty safe to assume the translations are not
656  * actually translated.
657  */
658  if (!new_scheme) {
659  if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
660  langid_to_add = GRFLX_ENGLISH;
661  } else {
662  StringID ret = STR_EMPTY;
663  if (langid_to_add & GRFLB_GERMAN) ret = AddGRFString(grfid, stringid, GRFLX_GERMAN, true, allow_newlines, text_to_add, def_string);
664  if (langid_to_add & GRFLB_FRENCH) ret = AddGRFString(grfid, stringid, GRFLX_FRENCH, true, allow_newlines, text_to_add, def_string);
665  if (langid_to_add & GRFLB_SPANISH) ret = AddGRFString(grfid, stringid, GRFLX_SPANISH, true, allow_newlines, text_to_add, def_string);
666  return ret;
667  }
668  }
669 
670  for (id = 0; id < _num_grf_texts; id++) {
671  if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
672  break;
673  }
674  }
675 
676  /* Too many strings allocated, return empty */
677  if (id == lengthof(_grf_text)) return STR_EMPTY;
678 
679  int len;
680  translatedtext = TranslateTTDPatchCodes(grfid, langid_to_add, allow_newlines, text_to_add, &len);
681 
682  GRFText *newtext = GRFText::New(langid_to_add, translatedtext, len);
683 
684  free(translatedtext);
685 
686  /* If we didn't find our stringid and grfid in the list, allocate a new id */
687  if (id == _num_grf_texts) _num_grf_texts++;
688 
689  if (_grf_text[id].textholder == NULL) {
690  _grf_text[id].grfid = grfid;
691  _grf_text[id].stringid = stringid;
692  _grf_text[id].def_string = def_string;
693  }
694  AddGRFTextToList(&_grf_text[id].textholder, newtext);
695 
696  grfmsg(3, "Added 0x%X: grfid %08X string 0x%X lang 0x%X string '%s'", id, grfid, stringid, newtext->langid, newtext->text);
697 
698  return (GRFTAB << TABSIZE) + id;
699 }
700 
704 StringID GetGRFStringID(uint32 grfid, uint16 stringid)
705 {
706  for (uint id = 0; id < _num_grf_texts; id++) {
707  if (_grf_text[id].grfid == grfid && _grf_text[id].stringid == stringid) {
708  return (GRFTAB << TABSIZE) + id;
709  }
710  }
711 
712  return STR_UNDEFINED;
713 }
714 
715 
723 const char *GetGRFStringFromGRFText(const GRFText *text)
724 {
725  const char *default_text = NULL;
726 
727  /* Search the list of lang-strings of this stringid for current lang */
728  for (; text != NULL; text = text->next) {
729  if (text->langid == _currentLangID) return text->text;
730 
731  /* If the current string is English or American, set it as the
732  * fallback language if the specific language isn't available. */
733  if (text->langid == GRFLX_UNSPECIFIED || (default_text == NULL && (text->langid == GRFLX_ENGLISH || text->langid == GRFLX_AMERICAN))) {
734  default_text = text->text;
735  }
736  }
737 
738  return default_text;
739 }
740 
744 const char *GetGRFStringPtr(uint16 stringid)
745 {
746  assert(_grf_text[stringid].grfid != 0);
747 
748  const char *str = GetGRFStringFromGRFText(_grf_text[stringid].textholder);
749  if (str != NULL) return str;
750 
751  /* Use the default string ID if the fallback string isn't available */
752  return GetStringPtr(_grf_text[stringid].def_string);
753 }
754 
763 void SetCurrentGrfLangID(byte language_id)
764 {
765  _currentLangID = language_id;
766 }
767 
768 bool CheckGrfLangID(byte lang_id, byte grf_version)
769 {
770  if (grf_version < 7) {
771  switch (_currentLangID) {
772  case GRFLX_GERMAN: return (lang_id & GRFLB_GERMAN) != 0;
773  case GRFLX_FRENCH: return (lang_id & GRFLB_FRENCH) != 0;
774  case GRFLX_SPANISH: return (lang_id & GRFLB_SPANISH) != 0;
775  default: return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
776  }
777  }
778 
779  return (lang_id == _currentLangID || lang_id == GRFLX_UNSPECIFIED);
780 }
781 
786 void CleanUpGRFText(GRFText *grftext)
787 {
788  while (grftext != NULL) {
789  GRFText *grftext2 = grftext->next;
790  delete grftext;
791  grftext = grftext2;
792  }
793 }
794 
800 {
801  uint id;
802 
803  for (id = 0; id < _num_grf_texts; id++) {
804  CleanUpGRFText(_grf_text[id].textholder);
805  _grf_text[id].grfid = 0;
806  _grf_text[id].stringid = 0;
807  _grf_text[id].textholder = NULL;
808  }
809 
810  _num_grf_texts = 0;
811 }
812 
813 struct TextRefStack {
814  byte stack[0x30];
815  byte position;
816  const GRFFile *grffile;
817  bool used;
818 
819  TextRefStack() : position(0), grffile(NULL), used(false) {}
820 
821  TextRefStack(const TextRefStack &stack) :
822  position(stack.position),
823  grffile(stack.grffile),
824  used(stack.used)
825  {
826  memcpy(this->stack, stack.stack, sizeof(this->stack));
827  }
828 
829  uint8 PopUnsignedByte() { assert(this->position < lengthof(this->stack)); return this->stack[this->position++]; }
830  int8 PopSignedByte() { return (int8)this->PopUnsignedByte(); }
831 
832  uint16 PopUnsignedWord()
833  {
834  uint16 val = this->PopUnsignedByte();
835  return val | (this->PopUnsignedByte() << 8);
836  }
837  int16 PopSignedWord() { return (int32)this->PopUnsignedWord(); }
838 
839  uint32 PopUnsignedDWord()
840  {
841  uint32 val = this->PopUnsignedWord();
842  return val | (this->PopUnsignedWord() << 16);
843  }
844  int32 PopSignedDWord() { return (int32)this->PopUnsignedDWord(); }
845 
846  uint64 PopUnsignedQWord()
847  {
848  uint64 val = this->PopUnsignedDWord();
849  return val | (((uint64)this->PopUnsignedDWord()) << 32);
850  }
851  int64 PopSignedQWord() { return (int64)this->PopUnsignedQWord(); }
852 
855  {
856  byte tmp[2];
857  for (int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6];
858  for (int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
859  for (int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i];
860  }
861 
862  void PushWord(uint16 word)
863  {
864  if (this->position >= 2) {
865  this->position -= 2;
866  } else {
867  for (int i = lengthof(stack) - 1; i >= this->position + 2; i--) {
868  this->stack[i] = this->stack[i - 2];
869  }
870  }
871  this->stack[this->position] = GB(word, 0, 8);
872  this->stack[this->position + 1] = GB(word, 8, 8);
873  }
874 
875  void ResetStack(const GRFFile *grffile)
876  {
877  assert(grffile != NULL);
878  this->position = 0;
879  this->grffile = grffile;
880  this->used = true;
881  }
882 
883  void RewindStack() { this->position = 0; }
884 };
885 
888 
894 {
895  return _newgrf_textrefstack.used;
896 }
897 
903 {
904  return new TextRefStack(_newgrf_textrefstack);
905 }
906 
912 {
913  _newgrf_textrefstack = *backup;
914  delete backup;
915 }
916 
935 void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32 *values)
936 {
937  extern TemporaryStorageArray<int32, 0x110> _temp_store;
938 
939  _newgrf_textrefstack.ResetStack(grffile);
940 
941  byte *p = _newgrf_textrefstack.stack;
942  for (uint i = 0; i < numEntries; i++) {
943  uint32 value = values != NULL ? values[i] : _temp_store.GetValue(0x100 + i);
944  for (uint j = 0; j < 32; j += 8) {
945  *p = GB(value, j, 8);
946  p++;
947  }
948  }
949 }
950 
953 {
954  _newgrf_textrefstack.used = false;
955 }
956 
957 void RewindTextRefStack()
958 {
959  _newgrf_textrefstack.RewindStack();
960 }
961 
972 uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, uint argv_size, bool modify_argv)
973 {
974  switch (scc) {
975  default: break;
976 
999  if (argv_size < 1) {
1000  DEBUG(misc, 0, "Too many NewGRF string parameters.");
1001  return 0;
1002  }
1003  break;
1004 
1008  if (argv_size < 2) {
1009  DEBUG(misc, 0, "Too many NewGRF string parameters.");
1010  return 0;
1011  }
1012  break;
1013  }
1014 
1015  if (_newgrf_textrefstack.used && modify_argv) {
1016  switch (scc) {
1017  default: NOT_REACHED();
1018  case SCC_NEWGRF_PRINT_BYTE_SIGNED: *argv = _newgrf_textrefstack.PopSignedByte(); break;
1019  case SCC_NEWGRF_PRINT_QWORD_CURRENCY: *argv = _newgrf_textrefstack.PopSignedQWord(); break;
1020 
1022  case SCC_NEWGRF_PRINT_DWORD_SIGNED: *argv = _newgrf_textrefstack.PopSignedDWord(); break;
1023 
1024  case SCC_NEWGRF_PRINT_BYTE_HEX: *argv = _newgrf_textrefstack.PopUnsignedByte(); break;
1025  case SCC_NEWGRF_PRINT_QWORD_HEX: *argv = _newgrf_textrefstack.PopUnsignedQWord(); break;
1026 
1030  case SCC_NEWGRF_PRINT_WORD_SIGNED: *argv = _newgrf_textrefstack.PopSignedWord(); break;
1031 
1037  case SCC_NEWGRF_PRINT_WORD_UNSIGNED: *argv = _newgrf_textrefstack.PopUnsignedWord(); break;
1038 
1041  case SCC_NEWGRF_PRINT_DWORD_HEX: *argv = _newgrf_textrefstack.PopUnsignedDWord(); break;
1042 
1044  case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: *argv = _newgrf_textrefstack.PopUnsignedWord() + DAYS_TILL_ORIGINAL_BASE_YEAR; break;
1045 
1046  case SCC_NEWGRF_DISCARD_WORD: _newgrf_textrefstack.PopUnsignedWord(); break;
1047 
1048  case SCC_NEWGRF_ROTATE_TOP_4_WORDS: _newgrf_textrefstack.RotateTop4Words(); break;
1049  case SCC_NEWGRF_PUSH_WORD: _newgrf_textrefstack.PushWord(Utf8Consume(str)); break;
1050  case SCC_NEWGRF_UNPRINT: *buff = max(*buff - Utf8Consume(str), buf_start); break;
1051 
1055  argv[0] = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
1056  argv[1] = _newgrf_textrefstack.PopUnsignedWord();
1057  break;
1058 
1060  *argv = MapGRFStringID(_newgrf_textrefstack.grffile->grfid, _newgrf_textrefstack.PopUnsignedWord());
1061  break;
1062  }
1063  } else {
1064  /* Consume additional parameter characters */
1065  switch (scc) {
1066  default: break;
1067 
1068  case SCC_NEWGRF_PUSH_WORD:
1069  case SCC_NEWGRF_UNPRINT:
1070  Utf8Consume(str);
1071  break;
1072  }
1073  }
1074 
1075  switch (scc) {
1076  default: NOT_REACHED();
1081  return SCC_COMMA;
1082 
1087  return SCC_HEX;
1088 
1091  return SCC_CURRENCY_LONG;
1092 
1095 
1098  return SCC_DATE_LONG;
1099 
1102  return SCC_DATE_SHORT;
1103 
1105  return SCC_VELOCITY;
1106 
1108  return SCC_VOLUME_LONG;
1109 
1111  return SCC_VOLUME_SHORT;
1112 
1114  return SCC_WEIGHT_LONG;
1115 
1117  return SCC_WEIGHT_SHORT;
1118 
1120  return SCC_POWER;
1121 
1123  return SCC_CARGO_LONG;
1124 
1126  return SCC_CARGO_SHORT;
1127 
1129  return SCC_CARGO_TINY;
1130 
1132  return SCC_STATION_NAME;
1133 
1136  case SCC_NEWGRF_PUSH_WORD:
1137  case SCC_NEWGRF_UNPRINT:
1138  return 0;
1139  }
1140 }