OpenTTD Source  20240917-master-g9ab0a47812
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 "newgrf.h"
21 #include "strings_internal.h"
22 #include "newgrf_storage.h"
23 #include "newgrf_text.h"
24 #include "newgrf_cargo.h"
25 #include "string_func.h"
27 #include "debug.h"
28 #include "core/alloc_type.hpp"
29 #include "language.h"
30 #include <sstream>
31 
32 #include "table/strings.h"
33 #include "table/control_codes.h"
34 
35 #include "safeguards.h"
36 
43  GRFLB_AMERICAN = 0x01,
44  GRFLB_ENGLISH = 0x02,
45  GRFLB_GERMAN = 0x04,
46  GRFLB_FRENCH = 0x08,
47  GRFLB_SPANISH = 0x10,
48  GRFLB_GENERIC = 0x80,
49 };
50 
51 enum GRFExtendedLanguages {
52  GRFLX_AMERICAN = 0x00,
53  GRFLX_ENGLISH = 0x01,
54  GRFLX_GERMAN = 0x02,
55  GRFLX_FRENCH = 0x03,
56  GRFLX_SPANISH = 0x04,
57  GRFLX_UNSPECIFIED = 0x7F,
58 };
59 
60 
66 struct GRFTextEntry {
67  GRFTextList textholder;
68  StringID def_string;
69  uint32_t grfid;
70  uint16_t stringid;
71 };
72 
73 
74 static std::vector<GRFTextEntry> _grf_text;
75 static uint8_t _currentLangID = GRFLX_ENGLISH;
76 
83 int LanguageMap::GetMapping(int newgrf_id, bool gender) const
84 {
85  const std::vector<Mapping> &map = gender ? this->gender_map : this->case_map;
86  for (const Mapping &m : map) {
87  if (m.newgrf_id == newgrf_id) return m.openttd_id;
88  }
89  return -1;
90 }
91 
98 int LanguageMap::GetReverseMapping(int openttd_id, bool gender) const
99 {
100  const std::vector<Mapping> &map = gender ? this->gender_map : this->case_map;
101  for (const Mapping &m : map) {
102  if (m.openttd_id == openttd_id) return m.newgrf_id;
103  }
104  return -1;
105 }
106 
116  {
117  }
118 
120  int offset;
121 
123  std::map<uint8_t, std::stringstream> strings;
124 
130  void Flush(const LanguageMap *lm, std::ostringstream &dest)
131  {
132  if (this->strings.find(0) == this->strings.end()) {
133  /* In case of a (broken) NewGRF without a default,
134  * assume an empty string. */
135  GrfMsg(1, "choice list misses default value");
136  this->strings[0] = std::stringstream();
137  }
138 
139  std::ostreambuf_iterator<char> d(dest);
140 
141  if (lm == nullptr) {
142  /* In case there is no mapping, just ignore everything but the default.
143  * A probable cause for this happening is when the language file has
144  * been removed by the user and as such no mapping could be made. */
145  dest << this->strings[0].rdbuf();
146  return;
147  }
148 
149  Utf8Encode(d, this->type);
150 
151  if (this->type == SCC_SWITCH_CASE) {
152  /*
153  * Format for case switch:
154  * <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
155  * Each LEN is printed using 2 bytes in big endian order.
156  */
157 
158  /* "<NUM CASES>" */
159  int count = 0;
160  for (uint8_t i = 0; i < _current_language->num_cases; i++) {
161  /* Count the ones we have a mapped string for. */
162  if (this->strings.find(lm->GetReverseMapping(i, false)) != this->strings.end()) count++;
163  }
164  *d++ = count;
165 
166  for (uint8_t i = 0; i < _current_language->num_cases; i++) {
167  /* Resolve the string we're looking for. */
168  int idx = lm->GetReverseMapping(i, false);
169  if (this->strings.find(idx) == this->strings.end()) continue;
170  auto str = this->strings[idx].str();
171 
172  /* "<CASEn>" */
173  *d++ = i + 1;
174 
175  /* "<LENn>": Limit the length of the string to 0xFFFE to leave space for the '\0'. */
176  size_t len = std::min<size_t>(0xFFFE, str.size());
177  *d++ = GB(len + 1, 8, 8);
178  *d++ = GB(len + 1, 0, 8);
179 
180  /* "<STRINGn>" */
181  dest.write(str.c_str(), len);
182  *d++ = '\0';
183  }
184 
185  /* "<STRINGDEFAULT>" */
186  dest << this->strings[0].rdbuf() << '\0';
187  } else {
188  if (this->type == SCC_PLURAL_LIST) {
189  *d++ = lm->plural_form;
190  }
191 
192  /*
193  * Format for choice list:
194  * <OFFSET> <NUM CHOICES> <LENs> <STRINGs>
195  */
196 
197  /* "<OFFSET>" */
198  *d++ = this->offset - 0x80;
199 
200  /* "<NUM CHOICES>" */
201  int count = (this->type == SCC_GENDER_LIST ? _current_language->num_genders : LANGUAGE_MAX_PLURAL_FORMS);
202  *d++ = count;
203 
204  /* "<LENs>" */
205  for (int i = 0; i < count; i++) {
206  int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1);
207  const auto &str = this->strings[this->strings.find(idx) != this->strings.end() ? idx : 0].str();
208  size_t len = str.size() + 1;
209  if (len > 0xFF) GrfMsg(1, "choice list string is too long");
210  *d++ = GB(len, 0, 8);
211  }
212 
213  /* "<STRINGs>" */
214  for (int i = 0; i < count; i++) {
215  int idx = (this->type == SCC_GENDER_LIST ? lm->GetReverseMapping(i, true) : i + 1);
216  const auto &str = this->strings[this->strings.find(idx) != this->strings.end() ? idx : 0].str();
217  /* Limit the length of the string we copy to 0xFE. The length is written above
218  * as a byte and we need room for the final '\0'. */
219  size_t len = std::min<size_t>(0xFE, str.size());
220  dest.write(str.c_str(), len);
221  *d++ = '\0';
222  }
223  }
224  }
225 };
226 
236 std::string TranslateTTDPatchCodes(uint32_t grfid, uint8_t language_id, bool allow_newlines, std::string_view str, StringControlCode byte80)
237 {
238  /* Empty input string? Nothing to do here. */
239  if (str.empty()) return {};
240 
241  std::string_view::const_iterator src = str.cbegin();
242 
243  /* Is this an unicode string? */
244  bool unicode = false;
245  char32_t marker;
246  size_t len = Utf8Decode(&marker, &*src);
247 
248  if (marker == NFO_UTF8_IDENTIFIER) {
249  unicode = true;
250  src += len;
251  }
252 
253  /* Helper variable for a possible (string) mapping. */
254  UnmappedChoiceList *mapping = nullptr;
255 
256  std::ostringstream dest;
257  std::ostreambuf_iterator<char> d(dest);
258  while (src != str.cend()) {
259  char32_t c;
260 
261  if (unicode && Utf8EncodedCharLen(*src) != 0) {
262  c = Utf8Consume(src);
263  /* 'Magic' range of control codes. */
264  if (GB(c, 8, 8) == 0xE0) {
265  c = GB(c, 0, 8);
266  } else if (c >= 0x20) {
267  if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
268  Utf8Encode(d, c);
269  continue;
270  }
271  } else {
272  c = static_cast<uint8_t>(*src++);
273  }
274 
275  if (c == '\0') break;
276 
277  switch (c) {
278  case 0x01:
279  if (*src == '\0') goto string_end;
280  Utf8Encode(d, ' ');
281  src++;
282  break;
283  case 0x0A: break;
284  case 0x0D:
285  if (allow_newlines) {
286  *d++ = 0x0A;
287  } else {
288  GrfMsg(1, "Detected newline in string that does not allow one");
289  }
290  break;
291  case 0x0E: Utf8Encode(d, SCC_TINYFONT); break;
292  case 0x0F: Utf8Encode(d, SCC_BIGFONT); break;
293  case 0x1F:
294  if (src[0] == '\0' || src[1] == '\0') goto string_end;
295  Utf8Encode(d, ' ');
296  src += 2;
297  break;
298  case 0x7B:
299  case 0x7C:
300  case 0x7D:
301  case 0x7E:
302  case 0x7F: Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_SIGNED + c - 0x7B); break;
303  case 0x80: Utf8Encode(d, byte80); break;
304  case 0x81:
305  {
306  if (src[0] == '\0' || src[1] == '\0') goto string_end;
307  StringID string;
308  string = static_cast<uint8_t>(*src++);
309  string |= static_cast<uint8_t>(*src++) << 8;
311  Utf8Encode(d, MapGRFStringID(grfid, string));
312  break;
313  }
314  case 0x82:
315  case 0x83:
316  case 0x84: Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_DATE_LONG + c - 0x82); break;
317  case 0x85: Utf8Encode(d, SCC_NEWGRF_DISCARD_WORD); break;
318  case 0x86: Utf8Encode(d, SCC_NEWGRF_ROTATE_TOP_4_WORDS); break;
319  case 0x87: Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_VOLUME_LONG); break;
320  case 0x88: Utf8Encode(d, SCC_BLUE); break;
321  case 0x89: Utf8Encode(d, SCC_SILVER); break;
322  case 0x8A: Utf8Encode(d, SCC_GOLD); break;
323  case 0x8B: Utf8Encode(d, SCC_RED); break;
324  case 0x8C: Utf8Encode(d, SCC_PURPLE); break;
325  case 0x8D: Utf8Encode(d, SCC_LTBROWN); break;
326  case 0x8E: Utf8Encode(d, SCC_ORANGE); break;
327  case 0x8F: Utf8Encode(d, SCC_GREEN); break;
328  case 0x90: Utf8Encode(d, SCC_YELLOW); break;
329  case 0x91: Utf8Encode(d, SCC_DKGREEN); break;
330  case 0x92: Utf8Encode(d, SCC_CREAM); break;
331  case 0x93: Utf8Encode(d, SCC_BROWN); break;
332  case 0x94: Utf8Encode(d, SCC_WHITE); break;
333  case 0x95: Utf8Encode(d, SCC_LTBLUE); break;
334  case 0x96: Utf8Encode(d, SCC_GRAY); break;
335  case 0x97: Utf8Encode(d, SCC_DKBLUE); break;
336  case 0x98: Utf8Encode(d, SCC_BLACK); break;
337  case 0x9A:
338  {
339  int code = *src++;
340  switch (code) {
341  case 0x00: goto string_end;
342  case 0x01: Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_CURRENCY); break;
343  /* 0x02: ignore next colour byte is not supported. It works on the final
344  * string and as such hooks into the string drawing routine. At that
345  * point many things already happened, such as splitting up of strings
346  * when drawn over multiple lines or right-to-left translations, which
347  * make the behaviour peculiar, e.g. only happening at specific width
348  * of windows. Or we need to add another pass over the string to just
349  * support this. As such it is not implemented in OpenTTD. */
350  case 0x03:
351  {
352  if (src[0] == '\0' || src[1] == '\0') goto string_end;
353  uint16_t tmp = static_cast<uint8_t>(*src++);
354  tmp |= static_cast<uint8_t>(*src++) << 8;
356  Utf8Encode(d, tmp);
357  break;
358  }
359  case 0x06: Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX); break;
360  case 0x07: Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX); break;
361  case 0x08: Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX); break;
362  /* 0x09, 0x0A are TTDPatch internal use only string codes. */
363  case 0x0B: Utf8Encode(d, SCC_NEWGRF_PRINT_QWORD_HEX); break;
364  case 0x0C: Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_STATION_NAME); break;
365  case 0x0D: Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG); break;
366  case 0x0E:
367  case 0x0F:
368  {
369  if (str[0] == '\0') goto string_end;
370  const LanguageMap *lm = LanguageMap::GetLanguageMap(grfid, language_id);
371  int index = *src++;
372  int mapped = lm != nullptr ? lm->GetMapping(index, code == 0x0E) : -1;
373  if (mapped >= 0) {
374  Utf8Encode(d, code == 0x0E ? SCC_GENDER_INDEX : SCC_SET_CASE);
375  Utf8Encode(d, code == 0x0E ? mapped : mapped + 1);
376  }
377  break;
378  }
379 
380  case 0x10:
381  case 0x11:
382  if (str[0] == '\0') goto string_end;
383  if (mapping == nullptr) {
384  if (code == 0x10) src++; // Skip the index
385  GrfMsg(1, "choice list {} marker found when not expected", code == 0x10 ? "next" : "default");
386  break;
387  } else {
388  int index = (code == 0x10 ? *src++ : 0);
389  if (mapping->strings.find(index) != mapping->strings.end()) {
390  GrfMsg(1, "duplicate choice list string, ignoring");
391  } else {
392  d = std::ostreambuf_iterator<char>(mapping->strings[index]);
393  }
394  }
395  break;
396 
397  case 0x12:
398  if (mapping == nullptr) {
399  GrfMsg(1, "choice list end marker found when not expected");
400  } else {
401  /* Now we can start flushing everything and clean everything up. */
402  mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id), dest);
403  delete mapping;
404  mapping = nullptr;
405 
406  d = std::ostreambuf_iterator<char>(dest);
407  }
408  break;
409 
410  case 0x13:
411  case 0x14:
412  case 0x15:
413  if (src[0] == '\0') goto string_end;
414  if (mapping != nullptr) {
415  GrfMsg(1, "choice lists can't be stacked, it's going to get messy now...");
416  if (code != 0x14) src++;
417  } else {
418  static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE, SCC_PLURAL_LIST };
419  mapping = new UnmappedChoiceList(mp[code - 0x13], code == 0x14 ? 0 : *src++);
420  }
421  break;
422 
423  case 0x16:
424  case 0x17:
425  case 0x18:
426  case 0x19:
427  case 0x1A:
428  case 0x1B:
429  case 0x1C:
430  case 0x1D:
431  case 0x1E:
433  break;
434 
435  case 0x1F: Utf8Encode(d, SCC_PUSH_COLOUR); break;
436  case 0x20: Utf8Encode(d, SCC_POP_COLOUR); break;
437 
438  case 0x21: Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_FORCE); break;
439 
440  default:
441  GrfMsg(1, "missing handler for extended format code");
442  break;
443  }
444  break;
445  }
446 
447  case 0x9E: Utf8Encode(d, 0x20AC); break; // Euro
448  case 0x9F: Utf8Encode(d, 0x0178); break; // Y with diaeresis
449  case 0xA0: Utf8Encode(d, SCC_UP_ARROW); break;
450  case 0xAA: Utf8Encode(d, SCC_DOWN_ARROW); break;
451  case 0xAC: Utf8Encode(d, SCC_CHECKMARK); break;
452  case 0xAD: Utf8Encode(d, SCC_CROSS); break;
453  case 0xAF: Utf8Encode(d, SCC_RIGHT_ARROW); break;
454  case 0xB4: Utf8Encode(d, SCC_TRAIN); break;
455  case 0xB5: Utf8Encode(d, SCC_LORRY); break;
456  case 0xB6: Utf8Encode(d, SCC_BUS); break;
457  case 0xB7: Utf8Encode(d, SCC_PLANE); break;
458  case 0xB8: Utf8Encode(d, SCC_SHIP); break;
459  case 0xB9: Utf8Encode(d, SCC_SUPERSCRIPT_M1); break;
460  case 0xBC: Utf8Encode(d, SCC_SMALL_UP_ARROW); break;
461  case 0xBD: Utf8Encode(d, SCC_SMALL_DOWN_ARROW); break;
462  default:
463  /* Validate any unhandled character */
464  if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
465  Utf8Encode(d, c);
466  break;
467  }
468  }
469 
470 string_end:
471  if (mapping != nullptr) {
472  GrfMsg(1, "choice list was incomplete, the whole list is ignored");
473  delete mapping;
474  }
475 
476  return dest.str();
477 }
478 
485 static void AddGRFTextToList(GRFTextList &list, uint8_t langid, std::string_view text_to_add)
486 {
487  /* Loop through all languages and see if we can replace a string */
488  for (auto &text : list) {
489  if (text.langid == langid) {
490  text.text = text_to_add;
491  return;
492  }
493  }
494 
495  /* If a string wasn't replaced, then we must append the new string */
496  list.push_back(GRFText{ langid, std::string(text_to_add) });
497 }
498 
508 void AddGRFTextToList(GRFTextList &list, uint8_t langid, uint32_t grfid, bool allow_newlines, std::string_view text_to_add)
509 {
510  AddGRFTextToList(list, langid, TranslateTTDPatchCodes(grfid, langid, allow_newlines, text_to_add));
511 }
512 
522 void AddGRFTextToList(GRFTextWrapper &list, uint8_t langid, uint32_t grfid, bool allow_newlines, std::string_view text_to_add)
523 {
524  if (!list) list.reset(new GRFTextList());
525  AddGRFTextToList(*list, langid, grfid, allow_newlines, text_to_add);
526 }
527 
534 void AddGRFTextToList(GRFTextWrapper &list, std::string_view text_to_add)
535 {
536  if (!list) list.reset(new GRFTextList());
537  AddGRFTextToList(*list, GRFLX_UNSPECIFIED, text_to_add);
538 }
539 
543 StringID AddGRFString(uint32_t grfid, uint16_t stringid, uint8_t langid_to_add, bool new_scheme, bool allow_newlines, std::string_view text_to_add, StringID def_string)
544 {
545  /* When working with the old language scheme (grf_version is less than 7) and
546  * English or American is among the set bits, simply add it as English in
547  * the new scheme, i.e. as langid = 1.
548  * If English is set, it is pretty safe to assume the translations are not
549  * actually translated.
550  */
551  if (!new_scheme) {
552  if (langid_to_add & (GRFLB_AMERICAN | GRFLB_ENGLISH)) {
553  langid_to_add = GRFLX_ENGLISH;
554  } else {
555  StringID ret = STR_EMPTY;
556  if (langid_to_add & GRFLB_GERMAN) ret = AddGRFString(grfid, stringid, GRFLX_GERMAN, true, allow_newlines, text_to_add, def_string);
557  if (langid_to_add & GRFLB_FRENCH) ret = AddGRFString(grfid, stringid, GRFLX_FRENCH, true, allow_newlines, text_to_add, def_string);
558  if (langid_to_add & GRFLB_SPANISH) ret = AddGRFString(grfid, stringid, GRFLX_SPANISH, true, allow_newlines, text_to_add, def_string);
559  return ret;
560  }
561  }
562 
563  auto it = std::find_if(std::begin(_grf_text), std::end(_grf_text), [&grfid, &stringid](const GRFTextEntry &grf_text) { return grf_text.grfid == grfid && grf_text.stringid == stringid; });
564  if (it == std::end(_grf_text)) {
565  /* Too many strings allocated, return empty. */
566  if (_grf_text.size() == TAB_SIZE_NEWGRF) return STR_EMPTY;
567 
568  /* We didn't find our stringid and grfid in the list, allocate a new id. */
569  it = _grf_text.emplace(std::end(_grf_text));
570  it->grfid = grfid;
571  it->stringid = stringid;
572  it->def_string = def_string;
573  }
574  uint id = static_cast<uint>(it - std::begin(_grf_text));
575 
576  std::string newtext = TranslateTTDPatchCodes(grfid, langid_to_add, allow_newlines, text_to_add);
577  AddGRFTextToList(it->textholder, langid_to_add, newtext);
578 
579  GrfMsg(3, "Added 0x{:X} grfid {:08X} string 0x{:X} lang 0x{:X} string '{}' ({:X})", id, grfid, stringid, langid_to_add, newtext, MakeStringID(TEXT_TAB_NEWGRF_START, id));
580 
582 }
583 
587 StringID GetGRFStringID(uint32_t grfid, StringID stringid)
588 {
589  auto it = std::find_if(std::begin(_grf_text), std::end(_grf_text), [&grfid, &stringid](const GRFTextEntry &grf_text) { return grf_text.grfid == grfid && grf_text.stringid == stringid; });
590  if (it != std::end(_grf_text)) {
591  uint id = static_cast<uint>(it - std::begin(_grf_text));
593  }
594 
595  return STR_UNDEFINED;
596 }
597 
598 
606 const char *GetGRFStringFromGRFText(const GRFTextList &text_list)
607 {
608  const char *default_text = nullptr;
609 
610  /* Search the list of lang-strings of this stringid for current lang */
611  for (const auto &text : text_list) {
612  if (text.langid == _currentLangID) return text.text.c_str();
613 
614  /* If the current string is English or American, set it as the
615  * fallback language if the specific language isn't available. */
616  if (text.langid == GRFLX_UNSPECIFIED || (default_text == nullptr && (text.langid == GRFLX_ENGLISH || text.langid == GRFLX_AMERICAN))) {
617  default_text = text.text.c_str();
618  }
619  }
620 
621  return default_text;
622 }
623 
631 const char *GetGRFStringFromGRFText(const GRFTextWrapper &text)
632 {
633  return text ? GetGRFStringFromGRFText(*text) : nullptr;
634 }
635 
639 const char *GetGRFStringPtr(uint32_t stringid)
640 {
641  assert(stringid < _grf_text.size());
642  assert(_grf_text[stringid].grfid != 0);
643 
644  const char *str = GetGRFStringFromGRFText(_grf_text[stringid].textholder);
645  if (str != nullptr) return str;
646 
647  /* Use the default string ID if the fallback string isn't available */
648  return GetStringPtr(_grf_text[stringid].def_string);
649 }
650 
659 void SetCurrentGrfLangID(uint8_t language_id)
660 {
661  _currentLangID = language_id;
662 }
663 
664 bool CheckGrfLangID(uint8_t lang_id, uint8_t grf_version)
665 {
666  if (grf_version < 7) {
667  switch (_currentLangID) {
668  case GRFLX_GERMAN: return (lang_id & GRFLB_GERMAN) != 0;
669  case GRFLX_FRENCH: return (lang_id & GRFLB_FRENCH) != 0;
670  case GRFLX_SPANISH: return (lang_id & GRFLB_SPANISH) != 0;
671  default: return (lang_id & (GRFLB_ENGLISH | GRFLB_AMERICAN)) != 0;
672  }
673  }
674 
675  return (lang_id == _currentLangID || lang_id == GRFLX_UNSPECIFIED);
676 }
677 
683 {
684  _grf_text.clear();
685 }
686 
687 struct TextRefStack {
688  std::array<uint8_t, 0x30> stack;
689  uint8_t position;
690  const GRFFile *grffile;
691  bool used;
692 
693  TextRefStack() : position(0), grffile(nullptr), used(false) {}
694 
695  uint8_t PopUnsignedByte() { assert(this->position < this->stack.size()); return this->stack[this->position++]; }
696  int8_t PopSignedByte() { return (int8_t)this->PopUnsignedByte(); }
697 
698  uint16_t PopUnsignedWord()
699  {
700  uint16_t val = this->PopUnsignedByte();
701  return val | (this->PopUnsignedByte() << 8);
702  }
703  int16_t PopSignedWord() { return (int32_t)this->PopUnsignedWord(); }
704 
705  uint32_t PopUnsignedDWord()
706  {
707  uint32_t val = this->PopUnsignedWord();
708  return val | (this->PopUnsignedWord() << 16);
709  }
710  int32_t PopSignedDWord() { return (int32_t)this->PopUnsignedDWord(); }
711 
712  uint64_t PopUnsignedQWord()
713  {
714  uint64_t val = this->PopUnsignedDWord();
715  return val | (((uint64_t)this->PopUnsignedDWord()) << 32);
716  }
717  int64_t PopSignedQWord() { return (int64_t)this->PopUnsignedQWord(); }
718 
721  {
722  uint8_t tmp[2];
723  for (int i = 0; i < 2; i++) tmp[i] = this->stack[this->position + i + 6];
724  for (int i = 5; i >= 0; i--) this->stack[this->position + i + 2] = this->stack[this->position + i];
725  for (int i = 0; i < 2; i++) this->stack[this->position + i] = tmp[i];
726  }
727 
728  void PushWord(uint16_t word)
729  {
730  if (this->position >= 2) {
731  this->position -= 2;
732  } else {
733  // Rotate right 2 positions
734  std::rotate(this->stack.rbegin(), this->stack.rbegin() + 2, this->stack.rend());
735  }
736  this->stack[this->position] = GB(word, 0, 8);
737  this->stack[this->position + 1] = GB(word, 8, 8);
738  }
739 
740  void ResetStack(const GRFFile *grffile)
741  {
742  assert(grffile != nullptr);
743  this->position = 0;
744  this->grffile = grffile;
745  this->used = true;
746  }
747 };
748 
751 
757 {
758  return _newgrf_textrefstack.used;
759 }
760 
766 {
768 }
769 
775 {
776  _newgrf_textrefstack = *backup;
777  delete backup;
778 }
779 
798 void StartTextRefStackUsage(const GRFFile *grffile, uint8_t numEntries, const uint32_t *values)
799 {
800  extern TemporaryStorageArray<int32_t, 0x110> _temp_store;
801 
802  _newgrf_textrefstack.ResetStack(grffile);
803 
804  auto stack_it = _newgrf_textrefstack.stack.begin();
805  for (uint i = 0; i < numEntries; i++) {
806  uint32_t value = values != nullptr ? values[i] : _temp_store.GetValue(0x100 + i);
807  for (uint j = 0; j < 32; j += 8) {
808  *stack_it = GB(value, j, 8);
809  stack_it++;
810  }
811  }
812 }
813 
816 {
817  _newgrf_textrefstack.used = false;
818 }
819 
828 uint RemapNewGRFStringControlCode(uint scc, const char **str, StringParameters &parameters, bool modify_parameters)
829 {
830  switch (scc) {
831  default: break;
832 
833  /* These control codes take one string parameter, check there are at least that many available. */
858  if (parameters.GetDataLeft() < 1) {
859  Debug(misc, 0, "Too many NewGRF string parameters.");
860  return 0;
861  }
862  break;
863 
864  /* These string code take two string parameters, check there are at least that many available. */
868  if (parameters.GetDataLeft() < 2) {
869  Debug(misc, 0, "Too many NewGRF string parameters.");
870  return 0;
871  }
872  break;
873  }
874 
875  if (_newgrf_textrefstack.used && modify_parameters) {
876  /* There is data on the NewGRF text stack, and we want to move them to OpenTTD's string stack.
877  * After this call, a new call is made with `modify_parameters` set to false when the string is finally formatted. */
878  switch (scc) {
879  default: NOT_REACHED();
880  case SCC_NEWGRF_PRINT_BYTE_SIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopSignedByte()); break;
881  case SCC_NEWGRF_PRINT_QWORD_CURRENCY: parameters.SetParam(0, _newgrf_textrefstack.PopSignedQWord()); break;
882 
884  case SCC_NEWGRF_PRINT_DWORD_SIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopSignedDWord()); break;
885 
886  case SCC_NEWGRF_PRINT_BYTE_HEX: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedByte()); break;
887  case SCC_NEWGRF_PRINT_QWORD_HEX: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedQWord()); break;
888 
892  case SCC_NEWGRF_PRINT_WORD_SIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopSignedWord()); break;
893 
899  case SCC_NEWGRF_PRINT_WORD_UNSIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedWord()); break;
900 
904  case SCC_NEWGRF_PRINT_DWORD_HEX: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedDWord()); break;
905 
906  /* Dates from NewGRFs have 1920-01-01 as their zero point, convert it to OpenTTD's epoch. */
908  case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedWord() + CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR); break;
909 
910  case SCC_NEWGRF_DISCARD_WORD: _newgrf_textrefstack.PopUnsignedWord(); break;
911 
913  case SCC_NEWGRF_PUSH_WORD: _newgrf_textrefstack.PushWord(Utf8Consume(str)); break;
914 
918  parameters.SetParam(0, GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile));
919  parameters.SetParam(1, _newgrf_textrefstack.PopUnsignedWord());
920  break;
921 
923  parameters.SetParam(0, MapGRFStringID(_newgrf_textrefstack.grffile->grfid, _newgrf_textrefstack.PopUnsignedWord()));
924  break;
925 
927  CargoID cargo = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile);
928  parameters.SetParam(0, cargo < NUM_CARGO ? 1ULL << cargo : 0);
929  break;
930  }
931  }
932  } else {
933  /* Consume additional parameter characters that follow the NewGRF string code. */
934  switch (scc) {
935  default: break;
936 
938  Utf8Consume(str);
939  break;
940  }
941  }
942 
943  /* Emit OpenTTD's internal string code for the different NewGRF variants. */
944  switch (scc) {
945  default: NOT_REACHED();
950  return SCC_COMMA;
951 
956  return SCC_HEX;
957 
960  return SCC_CURRENCY_LONG;
961 
964 
967  return SCC_DATE_LONG;
968 
971  return SCC_DATE_SHORT;
972 
974  return SCC_VELOCITY;
975 
977  return SCC_VOLUME_LONG;
978 
980  return SCC_VOLUME_SHORT;
981 
983  return SCC_WEIGHT_LONG;
984 
986  return SCC_WEIGHT_SHORT;
987 
989  return SCC_POWER;
990 
992  return SCC_FORCE;
993 
995  return SCC_CARGO_LONG;
996 
998  return SCC_CARGO_SHORT;
999 
1001  return SCC_CARGO_TINY;
1002 
1004  return SCC_CARGO_LIST;
1005 
1007  return SCC_STATION_NAME;
1008 
1009  /* These NewGRF string codes modify the NewGRF stack or otherwise do not map to OpenTTD string codes. */
1012  case SCC_NEWGRF_PUSH_WORD:
1013  return 0;
1014  }
1015 }
SCC_NEWGRF_ROTATE_TOP_4_WORDS
@ SCC_NEWGRF_ROTATE_TOP_4_WORDS
86: Rotate the top 4 words of the stack (W4 W1 W2 W3)
Definition: control_codes.h:160
GRFText
A GRF text with associated language ID.
Definition: newgrf_text_type.h:17
LanguageMap::case_map
std::vector< Mapping > case_map
Mapping of NewGRF and OpenTTD IDs for cases.
Definition: newgrf_text_type.h:42
TimerGameConst< struct Calendar >::DAYS_TILL_ORIGINAL_BASE_YEAR
static constexpr TimerGame< struct Calendar >::Date DAYS_TILL_ORIGINAL_BASE_YEAR
The date of the first day of the original base year.
Definition: timer_game_common.h:184
SCC_NEWGRF_STRINL
@ SCC_NEWGRF_STRINL
Inline another string at the current position, StringID is encoded in the string.
Definition: control_codes.h:163
SCC_NEWGRF_PRINT_WORD_SPEED
@ SCC_NEWGRF_PRINT_WORD_SPEED
84: Read 2 bytes from the stack as signed speed
Definition: control_codes.h:139
SCC_NEWGRF_PRINT_WORD_CARGO_SHORT
@ SCC_NEWGRF_PRINT_WORD_CARGO_SHORT
9A 1C: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount
Definition: control_codes.h:154
LanguagePackHeader::num_cases
uint8_t num_cases
the number of cases of this language
Definition: language.h:54
TEXT_TAB_NEWGRF_START
@ TEXT_TAB_NEWGRF_START
Start of NewGRF supplied strings.
Definition: strings_type.h:40
timer_game_calendar.h
StringID
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
SetCurrentGrfLangID
void SetCurrentGrfLangID(uint8_t language_id)
Equivalence Setter function between game and newgrf langID.
Definition: newgrf_text.cpp:659
LanguageMap::plural_form
int plural_form
The plural form used for this language.
Definition: newgrf_text_type.h:43
SCC_BIGFONT
@ SCC_BIGFONT
Switch to large font.
Definition: control_codes.h:31
GB
constexpr static debug_inline uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
Definition: bitmath_func.hpp:32
SCC_NEWGRF_PRINT_WORD_HEX
@ SCC_NEWGRF_PRINT_WORD_HEX
9A 07: Read 2 bytes from the stack and print it as hex
Definition: control_codes.h:145
_currentLangID
static uint8_t _currentLangID
by default, english is used.
Definition: newgrf_text.cpp:75
SCC_NEWGRF_PRINT_DWORD_FORCE
@ SCC_NEWGRF_PRINT_DWORD_FORCE
9A 21: Read 4 bytes from the stack as unsigned force
Definition: control_codes.h:157
GRFTextEntry
Holder of the above structure.
Definition: newgrf_text.cpp:66
LanguagePackHeader::num_genders
uint8_t num_genders
the number of genders of this language
Definition: language.h:53
StopTextRefStackUsage
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
Definition: newgrf_text.cpp:815
UnmappedChoiceList::strings
std::map< uint8_t, std::stringstream > strings
Mapping of NewGRF supplied ID to the different strings in the choice list.
Definition: newgrf_text.cpp:123
TAB_SIZE_NEWGRF
static const uint TAB_SIZE_NEWGRF
Number of strings for NewGRFs.
Definition: strings_type.h:52
SCC_NEWGRF_PRINT_DWORD_DATE_LONG
@ SCC_NEWGRF_PRINT_DWORD_DATE_LONG
9A 16: Read 4 bytes from the stack as base 0 date
Definition: control_codes.h:148
GRFBaseLanguages
GRFBaseLanguages
Explains the newgrf shift bit positioning.
Definition: newgrf_text.cpp:42
Debug
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
RestoreTextRefStackBackup
void RestoreTextRefStackBackup(struct TextRefStack *backup)
Restore a copy of the text stack to the used stack.
Definition: newgrf_text.cpp:774
alloc_type.hpp
NFO_UTF8_IDENTIFIER
static const char32_t NFO_UTF8_IDENTIFIER
This character (thorn) indicates a unicode string to NFO.
Definition: newgrf_text_type.h:14
control_codes.h
SCC_NEWGRF_DISCARD_WORD
@ SCC_NEWGRF_DISCARD_WORD
85: Discard the next two bytes
Definition: control_codes.h:159
RemapNewGRFStringControlCode
uint RemapNewGRFStringControlCode(uint scc, const char **str, StringParameters &parameters, bool modify_parameters)
FormatString for NewGRF specific "magic" string control codes.
Definition: newgrf_text.cpp:828
SCC_NEWGRF_PUSH_WORD
@ SCC_NEWGRF_PUSH_WORD
9A 03: Pushes 2 bytes onto the stack
Definition: control_codes.h:158
SCC_NEWGRF_PRINT_WORD_STRING_ID
@ SCC_NEWGRF_PRINT_WORD_STRING_ID
81: Read 2 bytes from the stack as String ID
Definition: control_codes.h:136
UnmappedChoiceList::Flush
void Flush(const LanguageMap *lm, std::ostringstream &dest)
Flush this choice list into the destination string.
Definition: newgrf_text.cpp:130
SCC_TINYFONT
@ SCC_TINYFONT
Switch to small font.
Definition: control_codes.h:30
TextRefStack::RotateTop4Words
void RotateTop4Words()
Rotate the top four words down: W1, W2, W3, W4 -> W4, W1, W2, W3.
Definition: newgrf_text.cpp:720
StringParameters::GetDataLeft
size_t GetDataLeft() const
Return the amount of elements which can still be read.
Definition: strings_internal.h:140
GetGRFStringFromGRFText
const char * GetGRFStringFromGRFText(const GRFTextList &text_list)
Get a C-string from a GRFText-list.
Definition: newgrf_text.cpp:606
SCC_NEWGRF_PRINT_WORD_CARGO_NAME
@ SCC_NEWGRF_PRINT_WORD_CARGO_NAME
9A 1E: Read 2 bytes from the stack as cargo name
Definition: control_codes.h:156
CleanUpStrings
void CleanUpStrings()
House cleaning.
Definition: newgrf_text.cpp:682
SCC_NEWGRF_PRINT_WORD_CARGO_TINY
@ SCC_NEWGRF_PRINT_WORD_CARGO_TINY
9A 1D: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount
Definition: control_codes.h:155
LanguageMap::gender_map
std::vector< Mapping > gender_map
Mapping of NewGRF and OpenTTD IDs for genders.
Definition: newgrf_text_type.h:41
SCC_NEWGRF_PRINT_DWORD_SIGNED
@ SCC_NEWGRF_PRINT_DWORD_SIGNED
7B: Read 4 bytes from the stack
Definition: control_codes.h:131
_current_language
const LanguageMetadata * _current_language
The currently loaded language.
Definition: strings.cpp:54
safeguards.h
GRFTextList
std::vector< GRFText > GRFTextList
A GRF text with a list of translations.
Definition: newgrf_text_type.h:23
newgrf_text.h
UnmappedChoiceList
Helper structure for mapping choice lists.
Definition: newgrf_text.cpp:108
language.h
GetGRFStringID
StringID GetGRFStringID(uint32_t grfid, StringID stringid)
Returns the index for this stringid associated with its grfID.
Definition: newgrf_text.cpp:587
stdafx.h
SCC_NEWGRF_PRINT_WORD_STATION_NAME
@ SCC_NEWGRF_PRINT_WORD_STATION_NAME
9A 0C: Read 2 bytes from the stack as station name
Definition: control_codes.h:142
CS_ALPHANUMERAL
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
Definition: string_type.h:25
UnmappedChoiceList::UnmappedChoiceList
UnmappedChoiceList(StringControlCode type, int offset)
Initialise the mapping.
Definition: newgrf_text.cpp:114
LanguageMap::Mapping
Mapping between NewGRF and OpenTTD IDs.
Definition: newgrf_text_type.h:30
SCC_NEWGRF_PRINT_WORD_DATE_LONG
@ SCC_NEWGRF_PRINT_WORD_DATE_LONG
82: Read 2 bytes from the stack as base 1920 date
Definition: control_codes.h:137
StartTextRefStackUsage
void StartTextRefStackUsage(const GRFFile *grffile, uint8_t numEntries, const uint32_t *values)
Start using the TTDP compatible string code parsing.
Definition: newgrf_text.cpp:798
string_func.h
Utf8EncodedCharLen
int8_t Utf8EncodedCharLen(char c)
Return the length of an UTF-8 encoded value based on a single char.
Definition: string_func.h:124
SCC_NEWGRF_PRINT_QWORD_CURRENCY
@ SCC_NEWGRF_PRINT_QWORD_CURRENCY
9A 01: Read 8 bytes from the stack as currency
Definition: control_codes.h:143
UnmappedChoiceList::offset
int offset
The offset for the plural/gender form.
Definition: newgrf_text.cpp:120
IsValidChar
bool IsValidChar(char32_t key, CharSetFilter afilter)
Only allow certain keys.
Definition: string.cpp:396
LanguageMap
Mapping of language data between a NewGRF and OpenTTD.
Definition: newgrf_text_type.h:28
StringParameters
Definition: strings_internal.h:22
LanguageMap::GetMapping
int GetMapping(int newgrf_id, bool gender) const
Get the mapping from the NewGRF supplied ID to OpenTTD's internal ID.
Definition: newgrf_text.cpp:83
StringControlCode
StringControlCode
List of string control codes used for string formatting, displaying, and by strgen to generate the la...
Definition: control_codes.h:17
SCC_NEWGRF_PRINT_WORD_SIGNED
@ SCC_NEWGRF_PRINT_WORD_SIGNED
7C: Read 2 bytes from the stack as signed value
Definition: control_codes.h:132
SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT
@ SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT
9A 19: Read 2 bytes from the stack as short signed volume
Definition: control_codes.h:151
SCC_NEWGRF_PRINT_QWORD_HEX
@ SCC_NEWGRF_PRINT_QWORD_HEX
9A 0B: Read 8 bytes from the stack and print it as hex
Definition: control_codes.h:147
newgrf.h
LanguageMap::GetLanguageMap
static const LanguageMap * GetLanguageMap(uint32_t grfid, uint8_t language_id)
Get the language map associated with a given NewGRF and language.
Definition: newgrf.cpp:2677
SCC_NEWGRF_PRINT_DWORD_DATE_SHORT
@ SCC_NEWGRF_PRINT_DWORD_DATE_SHORT
9A 17: Read 4 bytes from the stack as base 0 date
Definition: control_codes.h:149
SCC_NEWGRF_PRINT_DWORD_CURRENCY
@ SCC_NEWGRF_PRINT_DWORD_CURRENCY
8F: Read 4 bytes from the stack as currency
Definition: control_codes.h:135
SCC_NEWGRF_PRINT_BYTE_HEX
@ SCC_NEWGRF_PRINT_BYTE_HEX
9A 06: Read 1 byte from the stack and print it as hex
Definition: control_codes.h:144
UnmappedChoiceList::type
StringControlCode type
The type of choice list.
Definition: newgrf_text.cpp:119
CargoID
uint8_t CargoID
Cargo slots to indicate a cargo type within a game.
Definition: cargo_type.h:22
MakeStringID
StringID MakeStringID(StringTab tab, uint index)
Create a StringID.
Definition: strings_func.h:49
MapGRFStringID
StringID MapGRFStringID(uint32_t grfid, StringID str)
Used when setting an object's property to map to the GRF's strings while taking in consideration the ...
Definition: newgrf.cpp:559
SCC_NEWGRF_PRINT_WORD_CARGO_LONG
@ SCC_NEWGRF_PRINT_WORD_CARGO_LONG
9A 1B: Read 2 + 2 bytes from the stack as cargo type (translated) and unsigned cargo amount
Definition: control_codes.h:153
CreateTextRefStackBackup
struct TextRefStack * CreateTextRefStackBackup()
Create a backup of the current NewGRF text stack.
Definition: newgrf_text.cpp:765
SCC_NEWGRF_PRINT_WORD_DATE_SHORT
@ SCC_NEWGRF_PRINT_WORD_DATE_SHORT
83: Read 2 bytes from the stack as base 1920 date
Definition: control_codes.h:138
SCC_NEWGRF_PRINT_WORD_VOLUME_LONG
@ SCC_NEWGRF_PRINT_WORD_VOLUME_LONG
87: Read 2 bytes from the stack as long signed volume
Definition: control_codes.h:140
LanguageMap::GetReverseMapping
int GetReverseMapping(int openttd_id, bool gender) const
Get the mapping from OpenTTD's internal ID to the NewGRF supplied ID.
Definition: newgrf_text.cpp:98
SCC_NEWGRF_PRINT_WORD_UNSIGNED
@ SCC_NEWGRF_PRINT_WORD_UNSIGNED
7E: Read 2 bytes from the stack as unsigned value
Definition: control_codes.h:134
TemporaryStorageArray
Class for temporary storage of data.
Definition: newgrf_storage.h:133
SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG
@ SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG
9A 0D: Read 2 bytes from the stack as long unsigned weight
Definition: control_codes.h:141
GetGRFStringPtr
const char * GetGRFStringPtr(uint32_t stringid)
Get a C-string from a stringid set by a newgrf.
Definition: newgrf_text.cpp:639
Utf8Encode
size_t Utf8Encode(T buf, char32_t c)
Encode a unicode character and place it in the buffer.
Definition: string.cpp:460
TranslateTTDPatchCodes
std::string TranslateTTDPatchCodes(uint32_t grfid, uint8_t language_id, bool allow_newlines, std::string_view str, StringControlCode byte80)
Translate TTDPatch string codes into something OpenTTD can handle (better).
Definition: newgrf_text.cpp:236
GRFTextWrapper
std::shared_ptr< GRFTextList > GRFTextWrapper
Reference counted wrapper around a GRFText pointer.
Definition: newgrf_text_type.h:25
Utf8Decode
size_t Utf8Decode(char32_t *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Definition: string.cpp:419
NUM_CARGO
static const CargoID NUM_CARGO
Maximum number of cargo types in a game.
Definition: cargo_type.h:74
SCC_NEWGRF_PRINT_DWORD_HEX
@ SCC_NEWGRF_PRINT_DWORD_HEX
9A 08: Read 4 bytes from the stack and print it as hex
Definition: control_codes.h:146
GetCargoTranslation
CargoID GetCargoTranslation(uint8_t cargo, const GRFFile *grffile, bool usebit)
Translate a GRF-local cargo slot/bitnum into a CargoID.
Definition: newgrf_cargo.cpp:79
SCC_NEWGRF_PRINT_WORD_POWER
@ SCC_NEWGRF_PRINT_WORD_POWER
9A 18: Read 2 bytes from the stack as unsigned power
Definition: control_codes.h:150
SCC_NEWGRF_PRINT_BYTE_SIGNED
@ SCC_NEWGRF_PRINT_BYTE_SIGNED
7D: Read 1 byte from the stack as signed value
Definition: control_codes.h:133
newgrf_storage.h
newgrf_cargo.h
_newgrf_textrefstack
static TextRefStack _newgrf_textrefstack
The stack that is used for TTDP compatible string code parsing.
Definition: newgrf_text.cpp:750
AddGRFTextToList
static void AddGRFTextToList(GRFTextList &list, uint8_t langid, std::string_view text_to_add)
Add a new text to a GRFText list.
Definition: newgrf_text.cpp:485
AddGRFString
StringID AddGRFString(uint32_t grfid, uint16_t stringid, uint8_t langid_to_add, bool new_scheme, bool allow_newlines, std::string_view text_to_add, StringID def_string)
Add the new read string into our structure.
Definition: newgrf_text.cpp:543
GRFFile
Dynamic data of a loaded NewGRF.
Definition: newgrf.h:108
UsingNewGRFTextStack
bool UsingNewGRFTextStack()
Check whether the NewGRF text stack is in use.
Definition: newgrf_text.cpp:756
debug.h
SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT
@ SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT
9A 1A: Read 2 bytes from the stack as short unsigned weight
Definition: control_codes.h:152
TextRefStack
Definition: newgrf_text.cpp:687