OpenTTD Source  14.0-beta1
strings.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 
10 #include "stdafx.h"
11 #include "currency.h"
12 #include "station_base.h"
13 #include "town.h"
14 #include "waypoint_base.h"
15 #include "depot_base.h"
16 #include "industry.h"
17 #include "newgrf_text.h"
18 #include "fileio_func.h"
19 #include "signs_base.h"
20 #include "fontdetection.h"
21 #include "error.h"
22 #include "error_func.h"
23 #include "strings_func.h"
24 #include "rev.h"
25 #include "core/endian_func.hpp"
27 #include "vehicle_base.h"
28 #include "engine_base.h"
29 #include "language.h"
30 #include "townname_func.h"
31 #include "string_func.h"
32 #include "company_base.h"
33 #include "smallmap_gui.h"
34 #include "window_func.h"
35 #include "debug.h"
36 #include "game/game_text.hpp"
38 #include "newgrf_engine.h"
39 #include "core/backup_type.hpp"
40 #include "gfx_layout.h"
41 #include <stack>
42 #include <charconv>
43 
44 #include "table/strings.h"
45 #include "table/control_codes.h"
46 #include "3rdparty/fmt/std.h"
47 
48 #include "strings_internal.h"
49 
50 #include "safeguards.h"
51 
52 std::string _config_language_file;
55 
57 
58 #ifdef WITH_ICU_I18N
59 std::unique_ptr<icu::Collator> _current_collator;
60 #endif /* WITH_ICU_I18N */
61 
62 ArrayStringParameters<20> _global_string_params;
63 
69 {
70  for (auto &param : this->parameters) param.type = 0;
71  this->offset = 0;
72 }
73 
74 
82 {
83  assert(this->next_type == 0 || (SCC_CONTROL_START <= this->next_type && this->next_type <= SCC_CONTROL_END));
84  if (this->offset >= this->parameters.size()) {
85  throw std::out_of_range("Trying to read invalid string parameter");
86  }
87 
88  auto &param = this->parameters[this->offset++];
89  if (param.type != 0 && param.type != this->next_type) {
90  this->next_type = 0;
91  throw std::out_of_range("Trying to read string parameter with wrong type");
92  }
93  param.type = this->next_type;
94  this->next_type = 0;
95  return &param;
96 }
97 
98 
104 void SetDParam(size_t n, uint64_t v)
105 {
106  _global_string_params.SetParam(n, v);
107 }
108 
114 uint64_t GetDParam(size_t n)
115 {
116  return _global_string_params.GetParam(n);
117 }
118 
127 void SetDParamMaxValue(size_t n, uint64_t max_value, uint min_count, FontSize size)
128 {
129  uint num_digits = 1;
130  while (max_value >= 10) {
131  num_digits++;
132  max_value /= 10;
133  }
134  SetDParamMaxDigits(n, std::max(min_count, num_digits), size);
135 }
136 
143 void SetDParamMaxDigits(size_t n, uint count, FontSize size)
144 {
145  uint front = 0;
146  uint next = 0;
147  GetBroadestDigit(&front, &next, size);
148  uint64_t val = count > 1 ? front : next;
149  for (; count > 1; count--) {
150  val = 10 * val + next;
151  }
152  SetDParam(n, val);
153 }
154 
159 void CopyInDParam(const std::span<const StringParameterBackup> backup)
160 {
161  for (size_t i = 0; i < backup.size(); i++) {
162  auto &value = backup[i];
163  if (value.string.has_value()) {
164  _global_string_params.SetParam(i, value.string.value());
165  } else {
166  _global_string_params.SetParam(i, value.data);
167  }
168  }
169 }
170 
176 void CopyOutDParam(std::vector<StringParameterBackup> &backup, size_t num)
177 {
178  backup.resize(num);
179  for (size_t i = 0; i < backup.size(); i++) {
180  const char *str = _global_string_params.GetParamStr(i);
181  if (str != nullptr) {
182  backup[i] = str;
183  } else {
184  backup[i] = _global_string_params.GetParam(i);
185  }
186  }
187 }
188 
194 bool HaveDParamChanged(const std::vector<StringParameterBackup> &backup)
195 {
196  bool changed = false;
197  for (size_t i = 0; !changed && i < backup.size(); i++) {
198  bool global_has_string = _global_string_params.GetParamStr(i) != nullptr;
199  if (global_has_string != backup[i].string.has_value()) return true;
200 
201  if (global_has_string) {
202  changed = backup[i].string.value() != _global_string_params.GetParamStr(i);
203  } else {
204  changed = backup[i].data != _global_string_params.GetParam(i);
205  }
206  }
207  return changed;
208 }
209 
210 static void StationGetSpecialString(StringBuilder &builder, StationFacility x);
211 static void GetSpecialTownNameString(StringBuilder &builder, int ind, uint32_t seed);
212 static void GetSpecialNameString(StringBuilder &builder, int ind, StringParameters &args);
213 
214 static void FormatString(StringBuilder &builder, const char *str, StringParameters &args, uint case_index = 0, bool game_script = false, bool dry_run = false);
215 
217  char data[]; // list of strings
218 };
219 
221  void operator()(LanguagePack *langpack)
222  {
223  /* LanguagePack is in fact reinterpreted char[], we need to reinterpret it back to free it properly. */
224  delete[] reinterpret_cast<char*>(langpack);
225  }
226 };
227 
229  std::unique_ptr<LanguagePack, LanguagePackDeleter> langpack;
230 
231  std::vector<char *> offsets;
232 
233  std::array<uint, TEXT_TAB_END> langtab_num;
234  std::array<uint, TEXT_TAB_END> langtab_start;
235 };
236 
237 static LoadedLanguagePack _langpack;
238 
239 static bool _scan_for_gender_data = false;
240 
241 
242 const char *GetStringPtr(StringID string)
243 {
244  switch (GetStringTab(string)) {
246  /* 0xD0xx and 0xD4xx IDs have been converted earlier. */
247  case TEXT_TAB_OLD_NEWGRF: NOT_REACHED();
249  default: return _langpack.offsets[_langpack.langtab_start[GetStringTab(string)] + GetStringIndex(string)];
250  }
251 }
252 
261 void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
262 {
263  if (string == 0) {
264  GetStringWithArgs(builder, STR_UNDEFINED, args);
265  return;
266  }
267 
268  uint index = GetStringIndex(string);
269  StringTab tab = GetStringTab(string);
270 
271  switch (tab) {
272  case TEXT_TAB_TOWN:
273  if (index >= 0xC0 && !game_script) {
274  GetSpecialTownNameString(builder, index - 0xC0, args.GetNextParameter<uint32_t>());
275  return;
276  }
277  break;
278 
279  case TEXT_TAB_SPECIAL:
280  if (index >= 0xE4 && !game_script) {
281  GetSpecialNameString(builder, index - 0xE4, args);
282  return;
283  }
284  break;
285 
286  case TEXT_TAB_OLD_CUSTOM:
287  /* Old table for custom names. This is no longer used */
288  if (!game_script) {
289  FatalError("Incorrect conversion of custom name string.");
290  }
291  break;
292 
294  FormatString(builder, GetGameStringPtr(index), args, case_index, true);
295  return;
296  }
297 
298  case TEXT_TAB_OLD_NEWGRF:
299  NOT_REACHED();
300 
301  case TEXT_TAB_NEWGRF_START: {
302  FormatString(builder, GetGRFStringPtr(index), args, case_index);
303  return;
304  }
305 
306  default:
307  break;
308  }
309 
310  if (index >= _langpack.langtab_num[tab]) {
311  if (game_script) {
312  return GetStringWithArgs(builder, STR_UNDEFINED, args);
313  }
314  FatalError("String 0x{:X} is invalid. You are probably using an old version of the .lng file.\n", string);
315  }
316 
317  FormatString(builder, GetStringPtr(string), args, case_index);
318 }
319 
320 
327 std::string GetString(StringID string)
328 {
329  _global_string_params.PrepareForNextRun();
330  return GetStringWithArgs(string, _global_string_params);
331 }
332 
339 std::string GetStringWithArgs(StringID string, StringParameters &args)
340 {
341  std::string result;
342  StringBuilder builder(result);
343  GetStringWithArgs(builder, string, args);
344  return result;
345 }
346 
352 void SetDParamStr(size_t n, const char *str)
353 {
354  _global_string_params.SetParam(n, str);
355 }
356 
363 void SetDParamStr(size_t n, const std::string &str)
364 {
365  _global_string_params.SetParam(n, str);
366 }
367 
375 void SetDParamStr(size_t n, std::string &&str)
376 {
377  _global_string_params.SetParam(n, std::move(str));
378 }
379 
390 static void FormatNumber(StringBuilder &builder, int64_t number, const char *separator, int zerofill = 1, int fractional_digits = 0)
391 {
392  static const int max_digits = 20;
393  uint64_t divisor = 10000000000000000000ULL;
394  zerofill += fractional_digits;
395  int thousands_offset = (max_digits - fractional_digits - 1) % 3;
396 
397  if (number < 0) {
398  builder += '-';
399  number = -number;
400  }
401 
402  uint64_t num = number;
403  uint64_t tot = 0;
404  for (int i = 0; i < max_digits; i++) {
405  if (i == max_digits - fractional_digits) {
406  const char *decimal_separator = _settings_game.locale.digit_decimal_separator.c_str();
407  if (StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator;
408  builder += decimal_separator;
409  }
410 
411  uint64_t quot = 0;
412  if (num >= divisor) {
413  quot = num / divisor;
414  num = num % divisor;
415  }
416  if ((tot |= quot) || i >= max_digits - zerofill) {
417  builder += '0' + quot; // quot is a single digit
418  if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) builder += separator;
419  }
420 
421  divisor /= 10;
422  }
423 }
424 
425 static void FormatCommaNumber(StringBuilder &builder, int64_t number, int fractional_digits = 0)
426 {
427  const char *separator = _settings_game.locale.digit_group_separator.c_str();
428  if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator;
429  FormatNumber(builder, number, separator, 1, fractional_digits);
430 }
431 
432 static void FormatNoCommaNumber(StringBuilder &builder, int64_t number)
433 {
434  FormatNumber(builder, number, "");
435 }
436 
437 static void FormatZerofillNumber(StringBuilder &builder, int64_t number, int count)
438 {
439  FormatNumber(builder, number, "", count);
440 }
441 
442 static void FormatHexNumber(StringBuilder &builder, uint64_t number)
443 {
444  fmt::format_to(builder, "0x{:X}", number);
445 }
446 
452 static void FormatBytes(StringBuilder &builder, int64_t number)
453 {
454  assert(number >= 0);
455 
456  /* 1 2^10 2^20 2^30 2^40 2^50 2^60 */
457  const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
458  uint id = 1;
459  while (number >= 1024 * 1024) {
460  number /= 1024;
461  id++;
462  }
463 
464  const char *decimal_separator = _settings_game.locale.digit_decimal_separator.c_str();
465  if (StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator;
466 
467  if (number < 1024) {
468  id = 0;
469  fmt::format_to(builder, "{}", number);
470  } else if (number < 1024 * 10) {
471  fmt::format_to(builder, "{}{}{:02}", number / 1024, decimal_separator, (number % 1024) * 100 / 1024);
472  } else if (number < 1024 * 100) {
473  fmt::format_to(builder, "{}{}{:01}", number / 1024, decimal_separator, (number % 1024) * 10 / 1024);
474  } else {
475  assert(number < 1024 * 1024);
476  fmt::format_to(builder, "{}", number / 1024);
477  }
478 
479  assert(id < lengthof(iec_prefixes));
480  fmt::format_to(builder, NBSP "{}B", iec_prefixes[id]);
481 }
482 
483 static void FormatYmdString(StringBuilder &builder, TimerGameCalendar::Date date, uint case_index)
484 {
485  TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(date);
486 
487  auto tmp_params = MakeParameters(ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year);
488  FormatString(builder, GetStringPtr(STR_FORMAT_DATE_LONG), tmp_params, case_index);
489 }
490 
491 static void FormatMonthAndYear(StringBuilder &builder, TimerGameCalendar::Date date, uint case_index)
492 {
493  TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(date);
494 
495  auto tmp_params = MakeParameters(STR_MONTH_JAN + ymd.month, ymd.year);
496  FormatString(builder, GetStringPtr(STR_FORMAT_DATE_SHORT), tmp_params, case_index);
497 }
498 
499 static void FormatTinyOrISODate(StringBuilder &builder, TimerGameCalendar::Date date, StringID str)
500 {
501  TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(date);
502 
503  /* Day and month are zero-padded with ZEROFILL_NUM, hence the two 2s. */
504  auto tmp_params = MakeParameters(ymd.day, 2, ymd.month + 1, 2, ymd.year);
505  FormatString(builder, GetStringPtr(str), tmp_params);
506 }
507 
508 static void FormatGenericCurrency(StringBuilder &builder, const CurrencySpec *spec, Money number, bool compact)
509 {
510  /* We are going to make number absolute for printing, so
511  * keep this piece of data as we need it later on */
512  bool negative = number < 0;
513 
514  number *= spec->rate;
515 
516  /* convert from negative */
517  if (number < 0) {
518  builder.Utf8Encode(SCC_PUSH_COLOUR);
519  builder.Utf8Encode(SCC_RED);
520  builder += '-';
521  number = -number;
522  }
523 
524  /* Add prefix part, following symbol_pos specification.
525  * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
526  * The only remaining value is 1 (suffix), so everything that is not 1 */
527  if (spec->symbol_pos != 1) builder += spec->prefix;
528 
529  StringID number_str = STR_NULL;
530 
531  /* For huge numbers, compact the number. */
532  if (compact) {
533  /* Take care of the thousand rounding. Having 1 000 000 k
534  * and 1 000 M is inconsistent, so always use 1 000 M. */
535  if (number >= Money(1'000'000'000'000'000) - 500'000'000) {
536  number = (number + Money(500'000'000'000)) / Money(1'000'000'000'000);
537  number_str = STR_CURRENCY_SHORT_TERA;
538  } else if (number >= Money(1'000'000'000'000) - 500'000) {
539  number = (number + 500'000'000) / 1'000'000'000;
540  number_str = STR_CURRENCY_SHORT_GIGA;
541  } else if (number >= 1'000'000'000 - 500) {
542  number = (number + 500'000) / 1'000'000;
543  number_str = STR_CURRENCY_SHORT_MEGA;
544  } else if (number >= 1'000'000) {
545  number = (number + 500) / 1'000;
546  number_str = STR_CURRENCY_SHORT_KILO;
547  }
548  }
549 
550  const char *separator = _settings_game.locale.digit_group_separator_currency.c_str();
551  if (StrEmpty(separator)) separator = _currency->separator.c_str();
552  if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator_currency;
553  FormatNumber(builder, number, separator);
554  if (number_str != STR_NULL) {
555  auto tmp_params = ArrayStringParameters<0>();
556  FormatString(builder, GetStringPtr(number_str), tmp_params);
557  }
558 
559  /* Add suffix part, following symbol_pos specification.
560  * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
561  * The only remaining value is 1 (prefix), so everything that is not 0 */
562  if (spec->symbol_pos != 0) builder += spec->suffix;
563 
564  if (negative) {
565  builder.Utf8Encode(SCC_POP_COLOUR);
566  }
567 }
568 
575 static int DeterminePluralForm(int64_t count, int plural_form)
576 {
577  /* The absolute value determines plurality */
578  uint64_t n = abs(count);
579 
580  switch (plural_form) {
581  default:
582  NOT_REACHED();
583 
584  /* Two forms: singular used for one only.
585  * Used in:
586  * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
587  * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
588  case 0:
589  return n != 1 ? 1 : 0;
590 
591  /* Only one form.
592  * Used in:
593  * Hungarian, Japanese, Turkish */
594  case 1:
595  return 0;
596 
597  /* Two forms: singular used for 0 and 1.
598  * Used in:
599  * French, Brazilian Portuguese */
600  case 2:
601  return n > 1 ? 1 : 0;
602 
603  /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
604  * Note: Cases are out of order for hysterical reasons. '0' is last.
605  * Used in:
606  * Latvian */
607  case 3:
608  return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
609 
610  /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
611  * Used in:
612  * Gaelige (Irish) */
613  case 4:
614  return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
615 
616  /* Three forms: special cases for numbers ending in 1 except when ending in 11, and 2 to 9 except when ending in 12 to 19.
617  * Used in:
618  * Lithuanian */
619  case 5:
620  return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
621 
622  /* Three forms: special cases for numbers ending in 1 except when ending in 11, and 2 to 4 except when ending in 12 to 14.
623  * Used in:
624  * Croatian, Russian, Ukrainian */
625  case 6:
626  return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
627 
628  /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
629  * Used in:
630  * Polish */
631  case 7:
632  return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
633 
634  /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
635  * Used in:
636  * Slovenian */
637  case 8:
638  return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
639 
640  /* Two forms: singular used for numbers ending in 1 except when ending in 11.
641  * Used in:
642  * Icelandic */
643  case 9:
644  return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
645 
646  /* Three forms: special cases for 1, and 2 to 4
647  * Used in:
648  * Czech, Slovak */
649  case 10:
650  return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
651 
652  /* Two forms: cases for numbers ending with a consonant, and with a vowel.
653  * Korean doesn't have the concept of plural, but depending on how a
654  * number is pronounced it needs another version of a particle.
655  * As such the plural system is misused to give this distinction.
656  */
657  case 11:
658  switch (n % 10) {
659  case 0: // yeong
660  case 1: // il
661  case 3: // sam
662  case 6: // yuk
663  case 7: // chil
664  case 8: // pal
665  return 0;
666 
667  case 2: // i
668  case 4: // sa
669  case 5: // o
670  case 9: // gu
671  return 1;
672 
673  default:
674  NOT_REACHED();
675  }
676 
677  /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
678  * Used in:
679  * Maltese */
680  case 12:
681  return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
682  /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
683  * Used in:
684  * Scottish Gaelic */
685  case 13:
686  return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
687 
688  /* Three forms: special cases for 1, 0 and numbers ending in 01 to 19.
689  * Used in:
690  * Romanian */
691  case 14:
692  return n == 1 ? 0 : (n == 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2;
693  }
694 }
695 
696 static const char *ParseStringChoice(const char *b, uint form, StringBuilder &builder)
697 {
698  /* <NUM> {Length of each string} {each string} */
699  uint n = (byte)*b++;
700  uint pos, i, mypos = 0;
701 
702  for (i = pos = 0; i != n; i++) {
703  uint len = (byte)*b++;
704  if (i == form) mypos = pos;
705  pos += len;
706  }
707 
708  builder += b + mypos;
709  return b + pos;
710 }
711 
714  double factor;
715 
722  int64_t ToDisplay(int64_t input, bool round = true) const
723  {
724  return round
725  ? (int64_t)std::round(input * this->factor)
726  : (int64_t)(input * this->factor);
727  }
728 
736  int64_t FromDisplay(int64_t input, bool round = true, int64_t divider = 1) const
737  {
738  return round
739  ? (int64_t)std::round(input / this->factor / divider)
740  : (int64_t)(input / this->factor / divider);
741  }
742 };
743 
745 struct Units {
748  unsigned int decimal_places;
749 };
750 
752 struct UnitsLong {
756  unsigned int decimal_places;
757 };
758 
760 static const Units _units_velocity_calendar[] = {
761  { { 1.0 }, STR_UNITS_VELOCITY_IMPERIAL, 0 },
762  { { 1.609344 }, STR_UNITS_VELOCITY_METRIC, 0 },
763  { { 0.44704 }, STR_UNITS_VELOCITY_SI, 0 },
764  { { 0.578125 }, STR_UNITS_VELOCITY_GAMEUNITS_DAY, 1 },
765  { { 0.868976 }, STR_UNITS_VELOCITY_KNOTS, 0 },
766 };
767 
769 static const Units _units_velocity_realtime[] = {
770  { { 1.0 }, STR_UNITS_VELOCITY_IMPERIAL, 0 },
771  { { 1.609344 }, STR_UNITS_VELOCITY_METRIC, 0 },
772  { { 0.44704 }, STR_UNITS_VELOCITY_SI, 0 },
773  { { 0.289352 }, STR_UNITS_VELOCITY_GAMEUNITS_SEC, 1 },
774  { { 0.868976 }, STR_UNITS_VELOCITY_KNOTS, 0 },
775 };
776 
778 static const Units _units_power[] = {
779  { { 1.0 }, STR_UNITS_POWER_IMPERIAL, 0 },
780  { { 1.01387 }, STR_UNITS_POWER_METRIC, 0 },
781  { { 0.745699 }, STR_UNITS_POWER_SI, 0 },
782 };
783 
785 static const Units _units_power_to_weight[] = {
786  { { 0.907185 }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_IMPERIAL, 1 },
787  { { 1.0 }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_METRIC, 1 },
788  { { 1.0 }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_SI, 1 },
789  { { 0.919768 }, STR_UNITS_POWER_METRIC_TO_WEIGHT_IMPERIAL, 1 },
790  { { 1.01387 }, STR_UNITS_POWER_METRIC_TO_WEIGHT_METRIC, 1 },
791  { { 1.01387 }, STR_UNITS_POWER_METRIC_TO_WEIGHT_SI, 1 },
792  { { 0.676487 }, STR_UNITS_POWER_SI_TO_WEIGHT_IMPERIAL, 1 },
793  { { 0.745699 }, STR_UNITS_POWER_SI_TO_WEIGHT_METRIC, 1 },
794  { { 0.745699 }, STR_UNITS_POWER_SI_TO_WEIGHT_SI, 1 },
795 };
796 
798 static const UnitsLong _units_weight[] = {
799  { { 1.102311 }, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL, 0 },
800  { { 1.0 }, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC, 0 },
801  { { 1000.0 }, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI, 0 },
802 };
803 
805 static const UnitsLong _units_volume[] = {
806  { { 264.172 }, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL, 0 },
807  { { 1000.0 }, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC, 0 },
808  { { 1.0 }, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI, 0 },
809 };
810 
812 static const Units _units_force[] = {
813  { { 0.224809 }, STR_UNITS_FORCE_IMPERIAL, 0 },
814  { { 0.101972 }, STR_UNITS_FORCE_METRIC, 0 },
815  { { 0.001 }, STR_UNITS_FORCE_SI, 0 },
816 };
817 
819 static const Units _units_height[] = {
820  { { 3.0 }, STR_UNITS_HEIGHT_IMPERIAL, 0 }, // "Wrong" conversion factor for more nicer GUI values
821  { { 1.0 }, STR_UNITS_HEIGHT_METRIC, 0 },
822  { { 1.0 }, STR_UNITS_HEIGHT_SI, 0 },
823 };
824 
827  { { 1 }, STR_UNITS_DAYS, 0 },
828  { { 2 }, STR_UNITS_SECONDS, 0 },
829 };
830 
833  { { 1 }, STR_UNITS_MONTHS, 0 },
834  { { 1 }, STR_UNITS_MINUTES, 0 },
835 };
836 
839  { { 1 }, STR_UNITS_YEARS, 0 },
840  { { 1 }, STR_UNITS_PERIODS, 0 },
841 };
842 
845  { { 1 }, STR_UNITS_YEARS, 0 },
846  { { 12 }, STR_UNITS_MINUTES, 0 },
847 };
848 
855 {
857 
858  assert(setting < lengthof(_units_velocity_calendar));
859  assert(setting < lengthof(_units_velocity_realtime));
860 
862 
863  return _units_velocity_calendar[setting];
864 }
865 
872 {
873  /* For historical reasons we don't want to mess with the
874  * conversion for speed. So, don't round it and keep the
875  * original conversion factors instead of the real ones. */
876  return GetVelocityUnits(type).c.ToDisplay(speed, false);
877 }
878 
885 {
886  return GetVelocityUnits(type).c.FromDisplay(speed);
887 }
888 
895 {
896  return GetVelocityUnits(type).c.ToDisplay(speed * 10, false) / 16;
897 }
898 
905 {
906  return GetVelocityUnits(type).c.FromDisplay(speed * 16, true, 10);
907 }
908 
916 static void FormatString(StringBuilder &builder, const char *str_arg, StringParameters &args, uint case_index, bool game_script, bool dry_run)
917 {
918  size_t orig_offset = args.GetOffset();
919 
920  if (!dry_run) {
921  /*
922  * This function is normally called with `dry_run` false, then we call this function again
923  * with `dry_run` being true. The dry run is required for the gender formatting. For the
924  * gender determination we need to format a sub string to get the gender, but for that we
925  * need to know as what string control code type the specific parameter is encoded. Since
926  * gendered words can be before the "parameter" words, this needs to be determined before
927  * the actual formatting.
928  */
929  std::string buffer;
930  StringBuilder dry_run_builder(buffer);
931  if (UsingNewGRFTextStack()) {
932  /* Values from the NewGRF text stack are only copied to the normal
933  * argv array at the time they are encountered. That means that if
934  * another string command references a value later in the string it
935  * would fail. We solve that by running FormatString twice. The first
936  * pass makes sure the argv array is correctly filled and the second
937  * pass can reference later values without problems. */
938  struct TextRefStack *backup = CreateTextRefStackBackup();
939  FormatString(dry_run_builder, str_arg, args, case_index, game_script, true);
941  } else {
942  FormatString(dry_run_builder, str_arg, args, case_index, game_script, true);
943  }
944  /* We have to restore the original offset here to to read the correct values. */
945  args.SetOffset(orig_offset);
946  }
947  char32_t b = '\0';
948  uint next_substr_case_index = 0;
949  std::stack<const char *, std::vector<const char *>> str_stack;
950  str_stack.push(str_arg);
951 
952  for (;;) {
953  try {
954  while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
955  str_stack.pop();
956  }
957  if (str_stack.empty()) break;
958  const char *&str = str_stack.top();
959 
960  if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
961  /* We need to pass some stuff as it might be modified. */
962  StringParameters remaining = args.GetRemainingParameters();
963  b = RemapNewGRFStringControlCode(b, &str, remaining, dry_run);
964  if (b == 0) continue;
965  }
966 
967  if (b < SCC_CONTROL_START || b > SCC_CONTROL_END) {
968  builder.Utf8Encode(b);
969  continue;
970  }
971 
972  args.SetTypeOfNextParameter(b);
973  switch (b) {
974  case SCC_ENCODED: {
975  ArrayStringParameters<20> sub_args;
976 
977  char *p;
978  uint32_t stringid = std::strtoul(str, &p, 16);
979  if (*p != ':' && *p != '\0') {
980  while (*p != '\0') p++;
981  str = p;
982  builder += "(invalid SCC_ENCODED)";
983  break;
984  }
985  if (stringid >= TAB_SIZE_GAMESCRIPT) {
986  while (*p != '\0') p++;
987  str = p;
988  builder += "(invalid StringID)";
989  break;
990  }
991 
992  int i = 0;
993  while (*p != '\0' && i < 20) {
994  uint64_t param;
995  const char *s = ++p;
996 
997  /* Find the next value */
998  bool instring = false;
999  bool escape = false;
1000  for (;; p++) {
1001  if (*p == '\\') {
1002  escape = true;
1003  continue;
1004  }
1005  if (*p == '"' && escape) {
1006  escape = false;
1007  continue;
1008  }
1009  escape = false;
1010 
1011  if (*p == '"') {
1012  instring = !instring;
1013  continue;
1014  }
1015  if (instring) {
1016  continue;
1017  }
1018 
1019  if (*p == ':') break;
1020  if (*p == '\0') break;
1021  }
1022 
1023  if (*s != '"') {
1024  /* Check if we want to look up another string */
1025  char32_t l;
1026  size_t len = Utf8Decode(&l, s);
1027  bool lookup = (l == SCC_ENCODED);
1028  if (lookup) s += len;
1029 
1030  param = std::strtoull(s, &p, 16);
1031 
1032  if (lookup) {
1033  if (param >= TAB_SIZE_GAMESCRIPT) {
1034  while (*p != '\0') p++;
1035  str = p;
1036  builder += "(invalid sub-StringID)";
1037  break;
1038  }
1039  param = MakeStringID(TEXT_TAB_GAMESCRIPT_START, param);
1040  }
1041 
1042  sub_args.SetParam(i++, param);
1043  } else {
1044  s++; // skip the leading \"
1045  sub_args.SetParam(i++, std::string(s, p - s - 1)); // also skip the trailing \".
1046  }
1047  }
1048  /* If we didn't error out, we can actually print the string. */
1049  if (*str != '\0') {
1050  str = p;
1051  GetStringWithArgs(builder, MakeStringID(TEXT_TAB_GAMESCRIPT_START, stringid), sub_args, true);
1052  }
1053  break;
1054  }
1055 
1056  case SCC_NEWGRF_STRINL: {
1057  StringID substr = Utf8Consume(&str);
1058  str_stack.push(GetStringPtr(substr));
1059  break;
1060  }
1061 
1063  StringID substr = args.GetNextParameter<StringID>();
1064  str_stack.push(GetStringPtr(substr));
1065  case_index = next_substr_case_index;
1066  next_substr_case_index = 0;
1067  break;
1068  }
1069 
1070 
1071  case SCC_GENDER_LIST: { // {G 0 Der Die Das}
1072  /* First read the meta data from the language file. */
1073  size_t offset = orig_offset + (byte)*str++;
1074  int gender = 0;
1075  if (!dry_run && args.GetTypeAtOffset(offset) != 0) {
1076  /* Now we need to figure out what text to resolve, i.e.
1077  * what do we need to draw? So get the actual raw string
1078  * first using the control code to get said string. */
1079  char input[4 + 1];
1080  char *p = input + Utf8Encode(input, args.GetTypeAtOffset(offset));
1081  *p = '\0';
1082 
1083  /* The gender is stored at the start of the formatted string. */
1084  bool old_sgd = _scan_for_gender_data;
1085  _scan_for_gender_data = true;
1086  std::string buffer;
1087  StringBuilder tmp_builder(buffer);
1088  StringParameters tmp_params = args.GetRemainingParameters(offset);
1089  FormatString(tmp_builder, input, tmp_params);
1090  _scan_for_gender_data = old_sgd;
1091 
1092  /* And determine the string. */
1093  const char *s = buffer.c_str();
1094  char32_t c = Utf8Consume(&s);
1095  /* Does this string have a gender, if so, set it */
1096  if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
1097  }
1098  str = ParseStringChoice(str, gender, builder);
1099  break;
1100  }
1101 
1102  /* This sets up the gender for the string.
1103  * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
1104  case SCC_GENDER_INDEX: // {GENDER 0}
1105  if (_scan_for_gender_data) {
1106  builder.Utf8Encode(SCC_GENDER_INDEX);
1107  builder += *str++;
1108  } else {
1109  str++;
1110  }
1111  break;
1112 
1113  case SCC_PLURAL_LIST: { // {P}
1114  int plural_form = *str++; // contains the plural form for this string
1115  size_t offset = orig_offset + (byte)*str++;
1116  int64_t v = args.GetParam(offset); // contains the number that determines plural
1117  str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), builder);
1118  break;
1119  }
1120 
1121  case SCC_ARG_INDEX: { // Move argument pointer
1122  args.SetOffset(orig_offset + (byte)*str++);
1123  break;
1124  }
1125 
1126  case SCC_SET_CASE: { // {SET_CASE}
1127  /* This is a pseudo command, it's outputted when someone does {STRING.ack}
1128  * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
1129  next_substr_case_index = (byte)*str++;
1130  break;
1131  }
1132 
1133  case SCC_SWITCH_CASE: { // {Used to implement case switching}
1134  /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
1135  * Each LEN is printed using 2 bytes in big endian order. */
1136  uint num = (byte)*str++;
1137  while (num) {
1138  if ((byte)str[0] == case_index) {
1139  /* Found the case, adjust str pointer and continue */
1140  str += 3;
1141  break;
1142  }
1143  /* Otherwise skip to the next case */
1144  str += 3 + (str[1] << 8) + str[2];
1145  num--;
1146  }
1147  break;
1148  }
1149 
1150  case SCC_REVISION: // {REV}
1151  builder += _openttd_revision;
1152  break;
1153 
1154  case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1155  const char *raw_string = args.GetNextParameterString();
1156  /* raw_string can be nullptr. */
1157  if (raw_string == nullptr) {
1158  builder += "(invalid RAW_STRING parameter)";
1159  break;
1160  }
1161  FormatString(builder, raw_string, args);
1162  break;
1163  }
1164 
1165  case SCC_STRING: {// {STRING}
1166  StringID string_id = args.GetNextParameter<StringID>();
1167  if (game_script && GetStringTab(string_id) != TEXT_TAB_GAMESCRIPT_START) break;
1168  /* It's prohibited for the included string to consume any arguments. */
1169  StringParameters tmp_params(args, game_script ? args.GetDataLeft() : 0);
1170  GetStringWithArgs(builder, string_id, tmp_params, next_substr_case_index, game_script);
1171  next_substr_case_index = 0;
1172  break;
1173  }
1174 
1175  case SCC_STRING1:
1176  case SCC_STRING2:
1177  case SCC_STRING3:
1178  case SCC_STRING4:
1179  case SCC_STRING5:
1180  case SCC_STRING6:
1181  case SCC_STRING7: { // {STRING1..7}
1182  /* Strings that consume arguments */
1183  StringID string_id = args.GetNextParameter<StringID>();
1184  if (game_script && GetStringTab(string_id) != TEXT_TAB_GAMESCRIPT_START) break;
1185  uint size = b - SCC_STRING1 + 1;
1186  if (game_script && size > args.GetDataLeft()) {
1187  builder += "(too many parameters)";
1188  } else {
1189  StringParameters sub_args(args, game_script ? args.GetDataLeft() : size);
1190  GetStringWithArgs(builder, string_id, sub_args, next_substr_case_index, game_script);
1191  args.AdvanceOffset(size);
1192  }
1193  next_substr_case_index = 0;
1194  break;
1195  }
1196 
1197  case SCC_COMMA: // {COMMA}
1198  FormatCommaNumber(builder, args.GetNextParameter<int64_t>());
1199  break;
1200 
1201  case SCC_DECIMAL: { // {DECIMAL}
1202  int64_t number = args.GetNextParameter<int64_t>();
1203  int digits = args.GetNextParameter<int>();
1204  FormatCommaNumber(builder, number, digits);
1205  break;
1206  }
1207 
1208  case SCC_NUM: // {NUM}
1209  FormatNoCommaNumber(builder, args.GetNextParameter<int64_t>());
1210  break;
1211 
1212  case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1213  int64_t num = args.GetNextParameter<int64_t>();
1214  FormatZerofillNumber(builder, num, args.GetNextParameter<int>());
1215  break;
1216  }
1217 
1218  case SCC_HEX: // {HEX}
1219  FormatHexNumber(builder, args.GetNextParameter<uint64_t>());
1220  break;
1221 
1222  case SCC_BYTES: // {BYTES}
1223  FormatBytes(builder, args.GetNextParameter<int64_t>());
1224  break;
1225 
1226  case SCC_CARGO_TINY: { // {CARGO_TINY}
1227  /* Tiny description of cargotypes. Layout:
1228  * param 1: cargo type
1229  * param 2: cargo count */
1230  CargoID cargo = args.GetNextParameter<CargoID>();
1231  if (cargo >= CargoSpec::GetArraySize()) break;
1232 
1233  StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1234  int64_t amount = 0;
1235  switch (cargo_str) {
1236  case STR_TONS:
1238  break;
1239 
1240  case STR_LITERS:
1242  break;
1243 
1244  default: {
1245  amount = args.GetNextParameter<int64_t>();
1246  break;
1247  }
1248  }
1249 
1250  FormatCommaNumber(builder, amount);
1251  break;
1252  }
1253 
1254  case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1255  /* Short description of cargotypes. Layout:
1256  * param 1: cargo type
1257  * param 2: cargo count */
1258  CargoID cargo = args.GetNextParameter<CargoID>();
1259  if (cargo >= CargoSpec::GetArraySize()) break;
1260 
1261  StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1262  switch (cargo_str) {
1263  case STR_TONS: {
1266  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1267  FormatString(builder, GetStringPtr(x.l), tmp_params);
1268  break;
1269  }
1270 
1271  case STR_LITERS: {
1274  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1275  FormatString(builder, GetStringPtr(x.l), tmp_params);
1276  break;
1277  }
1278 
1279  default: {
1280  auto tmp_params = MakeParameters(args.GetNextParameter<int64_t>());
1281  GetStringWithArgs(builder, cargo_str, tmp_params);
1282  break;
1283  }
1284  }
1285  break;
1286  }
1287 
1288  case SCC_CARGO_LONG: { // {CARGO_LONG}
1289  /* First parameter is cargo type, second parameter is cargo count */
1290  CargoID cargo = args.GetNextParameter<CargoID>();
1291  if (IsValidCargoID(cargo) && cargo >= CargoSpec::GetArraySize()) break;
1292 
1293  StringID cargo_str = !IsValidCargoID(cargo) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1294  auto tmp_args = MakeParameters(args.GetNextParameter<int64_t>());
1295  GetStringWithArgs(builder, cargo_str, tmp_args);
1296  break;
1297  }
1298 
1299  case SCC_CARGO_LIST: { // {CARGO_LIST}
1300  CargoTypes cmask = args.GetNextParameter<CargoTypes>();
1301  bool first = true;
1302 
1303  for (const auto &cs : _sorted_cargo_specs) {
1304  if (!HasBit(cmask, cs->Index())) continue;
1305 
1306  if (first) {
1307  first = false;
1308  } else {
1309  /* Add a comma if this is not the first item */
1310  builder += ", ";
1311  }
1312 
1313  GetStringWithArgs(builder, cs->name, args, next_substr_case_index, game_script);
1314  }
1315 
1316  /* If first is still true then no cargo is accepted */
1317  if (first) GetStringWithArgs(builder, STR_JUST_NOTHING, args, next_substr_case_index, game_script);
1318 
1319  next_substr_case_index = 0;
1320  break;
1321  }
1322 
1323  case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1324  FormatGenericCurrency(builder, _currency, args.GetNextParameter<int64_t>(), true);
1325  break;
1326 
1327  case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1328  FormatGenericCurrency(builder, _currency, args.GetNextParameter<int64_t>(), false);
1329  break;
1330 
1331  case SCC_DATE_TINY: // {DATE_TINY}
1332  FormatTinyOrISODate(builder, args.GetNextParameter<TimerGameCalendar::Date>(), STR_FORMAT_DATE_TINY);
1333  break;
1334 
1335  case SCC_DATE_SHORT: // {DATE_SHORT}
1336  FormatMonthAndYear(builder, args.GetNextParameter<TimerGameCalendar::Date>(), next_substr_case_index);
1337  next_substr_case_index = 0;
1338  break;
1339 
1340  case SCC_DATE_LONG: // {DATE_LONG}
1341  FormatYmdString(builder, args.GetNextParameter<TimerGameCalendar::Date>(), next_substr_case_index);
1342  next_substr_case_index = 0;
1343  break;
1344 
1345  case SCC_DATE_ISO: // {DATE_ISO}
1346  FormatTinyOrISODate(builder, args.GetNextParameter<TimerGameCalendar::Date>(), STR_FORMAT_DATE_ISO);
1347  break;
1348 
1349  case SCC_FORCE: { // {FORCE}
1351  const auto &x = _units_force[_settings_game.locale.units_force];
1352  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1353  FormatString(builder, GetStringPtr(x.s), tmp_params);
1354  break;
1355  }
1356 
1357  case SCC_HEIGHT: { // {HEIGHT}
1360  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1361  FormatString(builder, GetStringPtr(x.s), tmp_params);
1362  break;
1363  }
1364 
1365  case SCC_POWER: { // {POWER}
1367  const auto &x = _units_power[_settings_game.locale.units_power];
1368  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1369  FormatString(builder, GetStringPtr(x.s), tmp_params);
1370  break;
1371  }
1372 
1373  case SCC_POWER_TO_WEIGHT: { // {POWER_TO_WEIGHT}
1375  assert(setting < lengthof(_units_power_to_weight));
1376  const auto &x = _units_power_to_weight[setting];
1377  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1378  FormatString(builder, GetStringPtr(x.s), tmp_params);
1379  break;
1380  }
1381 
1382  case SCC_VELOCITY: { // {VELOCITY}
1383  int64_t arg = args.GetNextParameter<int64_t>();
1384  // Unpack vehicle type from packed argument to get desired units.
1385  VehicleType vt = static_cast<VehicleType>(GB(arg, 56, 8));
1386  const auto &x = GetVelocityUnits(vt);
1387  auto tmp_params = MakeParameters(ConvertKmhishSpeedToDisplaySpeed(GB(arg, 0, 56), vt), x.decimal_places);
1388  FormatString(builder, GetStringPtr(x.s), tmp_params);
1389  break;
1390  }
1391 
1392  case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1395  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1396  FormatString(builder, GetStringPtr(x.s), tmp_params);
1397  break;
1398  }
1399 
1400  case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1403  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1404  FormatString(builder, GetStringPtr(x.l), tmp_params);
1405  break;
1406  }
1407 
1408  case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1411  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1412  FormatString(builder, GetStringPtr(x.s), tmp_params);
1413  break;
1414  }
1415 
1416  case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1419  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1420  FormatString(builder, GetStringPtr(x.l), tmp_params);
1421  break;
1422  }
1423 
1424  case SCC_UNITS_DAYS_OR_SECONDS: { // {UNITS_DAYS_OR_SECONDS}
1425  uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1426  const auto &x = _units_time_days_or_seconds[realtime];
1427  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1428  FormatString(builder, GetStringPtr(x.s), tmp_params);
1429  break;
1430  }
1431 
1432  case SCC_UNITS_MONTHS_OR_MINUTES: { // {UNITS_MONTHS_OR_MINUTES}
1433  uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1434  const auto &x = _units_time_months_or_minutes[realtime];
1435  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1436  FormatString(builder, GetStringPtr(x.s), tmp_params);
1437  break;
1438  }
1439 
1440  case SCC_UNITS_YEARS_OR_PERIODS: { // {UNITS_YEARS_OR_PERIODS}
1441  uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1442  const auto &x = _units_time_years_or_periods[realtime];
1443  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1444  FormatString(builder, GetStringPtr(x.s), tmp_params);
1445  break;
1446  }
1447 
1448  case SCC_UNITS_YEARS_OR_MINUTES: { // {UNITS_YEARS_OR_MINUTES}
1449  uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1450  const auto &x = _units_time_years_or_minutes[realtime];
1451  auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1452  FormatString(builder, GetStringPtr(x.s), tmp_params);
1453  break;
1454  }
1455 
1456  case SCC_COMPANY_NAME: { // {COMPANY}
1458  if (c == nullptr) break;
1459 
1460  if (!c->name.empty()) {
1461  auto tmp_params = MakeParameters(c->name);
1462  GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1463  } else {
1464  auto tmp_params = MakeParameters(c->name_2);
1465  GetStringWithArgs(builder, c->name_1, tmp_params);
1466  }
1467  break;
1468  }
1469 
1470  case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1471  CompanyID company = args.GetNextParameter<CompanyID>();
1472 
1473  /* Nothing is added for AI or inactive companies */
1474  if (Company::IsValidHumanID(company)) {
1475  auto tmp_params = MakeParameters(company + 1);
1476  GetStringWithArgs(builder, STR_FORMAT_COMPANY_NUM, tmp_params);
1477  }
1478  break;
1479  }
1480 
1481  case SCC_DEPOT_NAME: { // {DEPOT}
1483  if (vt == VEH_AIRCRAFT) {
1484  auto tmp_params = MakeParameters(args.GetNextParameter<StationID>());
1485  GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_AIRCRAFT, tmp_params);
1486  break;
1487  }
1488 
1489  const Depot *d = Depot::Get(args.GetNextParameter<DepotID>());
1490  if (!d->name.empty()) {
1491  auto tmp_params = MakeParameters(d->name);
1492  GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1493  } else {
1494  auto tmp_params = MakeParameters(d->town->index, d->town_cn + 1);
1495  GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), tmp_params);
1496  }
1497  break;
1498  }
1499 
1500  case SCC_ENGINE_NAME: { // {ENGINE}
1501  int64_t arg = args.GetNextParameter<int64_t>();
1502  const Engine *e = Engine::GetIfValid(static_cast<EngineID>(arg));
1503  if (e == nullptr) break;
1504 
1505  if (!e->name.empty() && e->IsEnabled()) {
1506  auto tmp_params = MakeParameters(e->name);
1507  GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1508  break;
1509  }
1510 
1511  if (HasBit(e->info.callback_mask, CBM_VEHICLE_NAME)) {
1512  uint16_t callback = GetVehicleCallback(CBID_VEHICLE_NAME, static_cast<uint32_t>(arg >> 32), 0, e->index, nullptr);
1513  /* Not calling ErrorUnknownCallbackResult due to being inside string processing. */
1514  if (callback != CALLBACK_FAILED && callback < 0x400) {
1515  const GRFFile *grffile = e->GetGRF();
1516  assert(grffile != nullptr);
1517 
1518  StartTextRefStackUsage(grffile, 6);
1519  ArrayStringParameters<6> tmp_params;
1520  GetStringWithArgs(builder, GetGRFStringID(grffile->grfid, 0xD000 + callback), tmp_params);
1522 
1523  break;
1524  }
1525  }
1526 
1527  auto tmp_params = ArrayStringParameters<0>();
1528  GetStringWithArgs(builder, e->info.string_id, tmp_params);
1529  break;
1530  }
1531 
1532  case SCC_GROUP_NAME: { // {GROUP}
1533  const Group *g = Group::GetIfValid(args.GetNextParameter<GroupID>());
1534  if (g == nullptr) break;
1535 
1536  if (!g->name.empty()) {
1537  auto tmp_params = MakeParameters(g->name);
1538  GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1539  } else {
1540  auto tmp_params = MakeParameters(g->index);
1541  GetStringWithArgs(builder, STR_FORMAT_GROUP_NAME, tmp_params);
1542  }
1543  break;
1544  }
1545 
1546  case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1547  const Industry *i = Industry::GetIfValid(args.GetNextParameter<IndustryID>());
1548  if (i == nullptr) break;
1549 
1550  static bool use_cache = true;
1551  if (_scan_for_gender_data) {
1552  /* Gender is defined by the industry type.
1553  * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1554  auto tmp_params = ArrayStringParameters<0>();
1555  FormatString(builder, GetStringPtr(GetIndustrySpec(i->type)->name), tmp_params, next_substr_case_index);
1556  } else if (use_cache) { // Use cached version if first call
1557  AutoRestoreBackup cache_backup(use_cache, false);
1558  builder += i->GetCachedName();
1559  } else {
1560  /* First print the town name and the industry type name. */
1561  auto tmp_params = MakeParameters(i->town->index, GetIndustrySpec(i->type)->name);
1562  FormatString(builder, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), tmp_params, next_substr_case_index);
1563  }
1564  next_substr_case_index = 0;
1565  break;
1566  }
1567 
1568  case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1570  if (c == nullptr) break;
1571 
1572  if (!c->president_name.empty()) {
1573  auto tmp_params = MakeParameters(c->president_name);
1574  GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1575  } else {
1576  auto tmp_params = MakeParameters(c->president_name_2);
1577  GetStringWithArgs(builder, c->president_name_1, tmp_params);
1578  }
1579  break;
1580  }
1581 
1582  case SCC_STATION_NAME: { // {STATION}
1583  StationID sid = args.GetNextParameter<StationID>();
1584  const Station *st = Station::GetIfValid(sid);
1585 
1586  if (st == nullptr) {
1587  /* The station doesn't exist anymore. The only place where we might
1588  * be "drawing" an invalid station is in the case of cargo that is
1589  * in transit. */
1590  auto tmp_params = ArrayStringParameters<0>();
1591  GetStringWithArgs(builder, STR_UNKNOWN_STATION, tmp_params);
1592  break;
1593  }
1594 
1595  static bool use_cache = true;
1596  if (use_cache) { // Use cached version if first call
1597  AutoRestoreBackup cache_backup(use_cache, false);
1598  builder += st->GetCachedName();
1599  } else if (!st->name.empty()) {
1600  auto tmp_params = MakeParameters(st->name);
1601  GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1602  } else {
1603  StringID string_id = st->string_id;
1604  if (st->indtype != IT_INVALID) {
1605  /* Special case where the industry provides the name for the station */
1606  const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1607 
1608  /* Industry GRFs can change which might remove the station name and
1609  * thus cause very strange things. Here we check for that before we
1610  * actually set the station name. */
1611  if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1612  string_id = indsp->station_name;
1613  }
1614  }
1615 
1616  auto tmp_params = MakeParameters(STR_TOWN_NAME, st->town->index, st->index);
1617  GetStringWithArgs(builder, string_id, tmp_params);
1618  }
1619  break;
1620  }
1621 
1622  case SCC_TOWN_NAME: { // {TOWN}
1623  const Town *t = Town::GetIfValid(args.GetNextParameter<TownID>());
1624  if (t == nullptr) break;
1625 
1626  static bool use_cache = true;
1627  if (use_cache) { // Use cached version if first call
1628  AutoRestoreBackup cache_backup(use_cache, false);
1629  builder += t->GetCachedName();
1630  } else if (!t->name.empty()) {
1631  auto tmp_params = MakeParameters(t->name);
1632  GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1633  } else {
1634  GetTownName(builder, t);
1635  }
1636  break;
1637  }
1638 
1639  case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1640  Waypoint *wp = Waypoint::GetIfValid(args.GetNextParameter<StationID>());
1641  if (wp == nullptr) break;
1642 
1643  if (!wp->name.empty()) {
1644  auto tmp_params = MakeParameters(wp->name);
1645  GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1646  } else {
1647  auto tmp_params = MakeParameters(wp->town->index, wp->town_cn + 1);
1648  StringID string_id = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1649  if (wp->town_cn != 0) string_id++;
1650  GetStringWithArgs(builder, string_id, tmp_params);
1651  }
1652  break;
1653  }
1654 
1655  case SCC_VEHICLE_NAME: { // {VEHICLE}
1657  if (v == nullptr) break;
1658 
1659  if (!v->name.empty()) {
1660  auto tmp_params = MakeParameters(v->name);
1661  GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1662  } else if (v->group_id != DEFAULT_GROUP) {
1663  /* The vehicle has no name, but is member of a group, so print group name */
1664  auto tmp_params = MakeParameters(v->group_id, v->unitnumber);
1665  GetStringWithArgs(builder, STR_FORMAT_GROUP_VEHICLE_NAME, tmp_params);
1666  } else {
1667  auto tmp_params = MakeParameters(v->unitnumber);
1668 
1669  StringID string_id;
1670  switch (v->type) {
1671  default: string_id = STR_INVALID_VEHICLE; break;
1672  case VEH_TRAIN: string_id = STR_SV_TRAIN_NAME; break;
1673  case VEH_ROAD: string_id = STR_SV_ROAD_VEHICLE_NAME; break;
1674  case VEH_SHIP: string_id = STR_SV_SHIP_NAME; break;
1675  case VEH_AIRCRAFT: string_id = STR_SV_AIRCRAFT_NAME; break;
1676  }
1677 
1678  GetStringWithArgs(builder, string_id, tmp_params);
1679  }
1680  break;
1681  }
1682 
1683  case SCC_SIGN_NAME: { // {SIGN}
1684  const Sign *si = Sign::GetIfValid(args.GetNextParameter<SignID>());
1685  if (si == nullptr) break;
1686 
1687  if (!si->name.empty()) {
1688  auto tmp_params = MakeParameters(si->name);
1689  GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1690  } else {
1691  auto tmp_params = ArrayStringParameters<0>();
1692  GetStringWithArgs(builder, STR_DEFAULT_SIGN_NAME, tmp_params);
1693  }
1694  break;
1695  }
1696 
1697  case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1698  StationGetSpecialString(builder, args.GetNextParameter<StationFacility>());
1699  break;
1700  }
1701 
1702  case SCC_COLOUR: { // {COLOUR}
1703  StringControlCode scc = (StringControlCode)(SCC_BLUE + args.GetNextParameter<Colours>());
1704  if (IsInsideMM(scc, SCC_BLUE, SCC_COLOUR)) builder.Utf8Encode(scc);
1705  break;
1706  }
1707 
1708  default:
1709  builder.Utf8Encode(b);
1710  break;
1711  }
1712  } catch (std::out_of_range &e) {
1713  Debug(misc, 0, "FormatString: {}", e.what());
1714  builder += "(invalid parameter)";
1715  }
1716  }
1717 }
1718 
1719 
1720 static void StationGetSpecialString(StringBuilder &builder, StationFacility x)
1721 {
1722  if ((x & FACIL_TRAIN) != 0) builder.Utf8Encode(SCC_TRAIN);
1723  if ((x & FACIL_TRUCK_STOP) != 0) builder.Utf8Encode(SCC_LORRY);
1724  if ((x & FACIL_BUS_STOP) != 0) builder.Utf8Encode(SCC_BUS);
1725  if ((x & FACIL_DOCK) != 0) builder.Utf8Encode(SCC_SHIP);
1726  if ((x & FACIL_AIRPORT) != 0) builder.Utf8Encode(SCC_PLANE);
1727 }
1728 
1729 static void GetSpecialTownNameString(StringBuilder &builder, int ind, uint32_t seed)
1730 {
1731  GenerateTownNameString(builder, ind, seed);
1732 }
1733 
1734 static const char * const _silly_company_names[] = {
1735  "Bloggs Brothers",
1736  "Tiny Transport Ltd.",
1737  "Express Travel",
1738  "Comfy-Coach & Co.",
1739  "Crush & Bump Ltd.",
1740  "Broken & Late Ltd.",
1741  "Sam Speedy & Son",
1742  "Supersonic Travel",
1743  "Mike's Motors",
1744  "Lightning International",
1745  "Pannik & Loozit Ltd.",
1746  "Inter-City Transport",
1747  "Getout & Pushit Ltd."
1748 };
1749 
1750 static const char * const _surname_list[] = {
1751  "Adams",
1752  "Allan",
1753  "Baker",
1754  "Bigwig",
1755  "Black",
1756  "Bloggs",
1757  "Brown",
1758  "Campbell",
1759  "Gordon",
1760  "Hamilton",
1761  "Hawthorn",
1762  "Higgins",
1763  "Green",
1764  "Gribble",
1765  "Jones",
1766  "McAlpine",
1767  "MacDonald",
1768  "McIntosh",
1769  "Muir",
1770  "Murphy",
1771  "Nelson",
1772  "O'Donnell",
1773  "Parker",
1774  "Phillips",
1775  "Pilkington",
1776  "Quigley",
1777  "Sharkey",
1778  "Thomson",
1779  "Watkins"
1780 };
1781 
1782 static const char * const _silly_surname_list[] = {
1783  "Grumpy",
1784  "Dozy",
1785  "Speedy",
1786  "Nosey",
1787  "Dribble",
1788  "Mushroom",
1789  "Cabbage",
1790  "Sniffle",
1791  "Fishy",
1792  "Swindle",
1793  "Sneaky",
1794  "Nutkins"
1795 };
1796 
1797 static const char _initial_name_letters[] = {
1798  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1799  'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1800 };
1801 
1802 static void GenAndCoName(StringBuilder &builder, uint32_t arg)
1803 {
1804  const char * const *base;
1805  uint num;
1806 
1807  if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1808  base = _silly_surname_list;
1809  num = lengthof(_silly_surname_list);
1810  } else {
1811  base = _surname_list;
1812  num = lengthof(_surname_list);
1813  }
1814 
1815  builder += base[num * GB(arg, 16, 8) >> 8];
1816  builder += " & Co.";
1817 }
1818 
1819 static void GenPresidentName(StringBuilder &builder, uint32_t x)
1820 {
1821  char initial[] = "?. ";
1822  const char * const *base;
1823  uint num;
1824  uint i;
1825 
1826  initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
1827  builder += initial;
1828 
1829  i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
1830  if (i < sizeof(_initial_name_letters)) {
1831  initial[0] = _initial_name_letters[i];
1832  builder += initial;
1833  }
1834 
1835  if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1836  base = _silly_surname_list;
1837  num = lengthof(_silly_surname_list);
1838  } else {
1839  base = _surname_list;
1840  num = lengthof(_surname_list);
1841  }
1842 
1843  builder += base[num * GB(x, 16, 8) >> 8];
1844 }
1845 
1846 static void GetSpecialNameString(StringBuilder &builder, int ind, StringParameters &args)
1847 {
1848  switch (ind) {
1849  case 1: // not used
1850  builder += _silly_company_names[std::min<uint>(args.GetNextParameter<uint16_t>(), lengthof(_silly_company_names) - 1)];
1851  return;
1852 
1853  case 2: // used for Foobar & Co company names
1854  GenAndCoName(builder, args.GetNextParameter<uint32_t>());
1855  return;
1856 
1857  case 3: // President name
1858  GenPresidentName(builder, args.GetNextParameter<uint32_t>());
1859  return;
1860  }
1861 
1862  /* town name? */
1863  if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1864  GetSpecialTownNameString(builder, ind - 6, args.GetNextParameter<uint32_t>());
1865  builder += " Transport";
1866  return;
1867  }
1868 
1869  NOT_REACHED();
1870 }
1871 
1877 {
1878  return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1879  this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1880  this->plural_form < LANGUAGE_MAX_PLURAL &&
1881  this->text_dir <= 1 &&
1882  this->newgrflangid < MAX_LANG &&
1883  this->num_genders < MAX_NUM_GENDERS &&
1884  this->num_cases < MAX_NUM_CASES &&
1885  StrValid(this->name, lastof(this->name)) &&
1886  StrValid(this->own_name, lastof(this->own_name)) &&
1887  StrValid(this->isocode, lastof(this->isocode)) &&
1891 }
1892 
1897 {
1898  /* "Less than 25% missing" is "sufficiently finished". */
1899  return 4 * this->missing < LANGUAGE_TOTAL_STRINGS;
1900 }
1901 
1908 {
1909  /* Current language pack */
1910  size_t len = 0;
1911  std::unique_ptr<LanguagePack, LanguagePackDeleter> lang_pack(reinterpret_cast<LanguagePack *>(ReadFileToMem(lang->file.string(), len, 1U << 20).release()));
1912  if (!lang_pack) return false;
1913 
1914  /* End of read data (+ terminating zero added in ReadFileToMem()) */
1915  const char *end = (char *)lang_pack.get() + len + 1;
1916 
1917  /* We need at least one byte of lang_pack->data */
1918  if (end <= lang_pack->data || !lang_pack->IsValid()) {
1919  return false;
1920  }
1921 
1922  std::array<uint, TEXT_TAB_END> tab_start, tab_num;
1923 
1924  uint count = 0;
1925  for (uint i = 0; i < TEXT_TAB_END; i++) {
1926  uint16_t num = FROM_LE16(lang_pack->offsets[i]);
1927  if (num > TAB_SIZE) return false;
1928 
1929  tab_start[i] = count;
1930  tab_num[i] = num;
1931  count += num;
1932  }
1933 
1934  /* Allocate offsets */
1935  std::vector<char *> offs(count);
1936 
1937  /* Fill offsets */
1938  char *s = lang_pack->data;
1939  len = (byte)*s++;
1940  for (uint i = 0; i < count; i++) {
1941  if (s + len >= end) return false;
1942 
1943  if (len >= 0xC0) {
1944  len = ((len & 0x3F) << 8) + (byte)*s++;
1945  if (s + len >= end) return false;
1946  }
1947  offs[i] = s;
1948  s += len;
1949  len = (byte)*s;
1950  *s++ = '\0'; // zero terminate the string
1951  }
1952 
1953  _langpack.langpack = std::move(lang_pack);
1954  _langpack.offsets = std::move(offs);
1955  _langpack.langtab_num = tab_num;
1956  _langpack.langtab_start = tab_start;
1957 
1958  _current_language = lang;
1960  _config_language_file = _current_language->file.filename().string();
1962 
1963 #ifdef _WIN32
1964  extern void Win32SetCurrentLocaleName(std::string iso_code);
1965  Win32SetCurrentLocaleName(_current_language->isocode);
1966 #endif
1967 
1968 #ifdef WITH_COCOA
1969  extern void MacOSSetCurrentLocaleName(const char *iso_code);
1971 #endif
1972 
1973 #ifdef WITH_ICU_I18N
1974  /* Create a collator instance for our current locale. */
1975  UErrorCode status = U_ZERO_ERROR;
1976  _current_collator.reset(icu::Collator::createInstance(icu::Locale(_current_language->isocode), status));
1977  /* Sort number substrings by their numerical value. */
1978  if (_current_collator) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
1979  /* Avoid using the collator if it is not correctly set. */
1980  if (U_FAILURE(status)) {
1981  _current_collator.reset();
1982  }
1983 #endif /* WITH_ICU_I18N */
1984 
1986 
1987  /* Some lists need to be sorted again after a language change. */
1993  InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
1994  InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
1995  InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
1996  InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
1997  InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
1998  InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
1999  InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
2000 
2001  return true;
2002 }
2003 
2004 /* Win32 implementation in win32.cpp.
2005  * OS X implementation in os/macosx/macos.mm. */
2006 #if !(defined(_WIN32) || defined(__APPLE__))
2007 
2015 const char *GetCurrentLocale(const char *param)
2016 {
2017  const char *env;
2018 
2019  env = std::getenv("LANGUAGE");
2020  if (env != nullptr) return env;
2021 
2022  env = std::getenv("LC_ALL");
2023  if (env != nullptr) return env;
2024 
2025  if (param != nullptr) {
2026  env = std::getenv(param);
2027  if (env != nullptr) return env;
2028  }
2029 
2030  return std::getenv("LANG");
2031 }
2032 #else
2033 const char *GetCurrentLocale(const char *param);
2034 #endif /* !(defined(_WIN32) || defined(__APPLE__)) */
2035 
2041 const LanguageMetadata *GetLanguage(byte newgrflangid)
2042 {
2043  for (const LanguageMetadata &lang : _languages) {
2044  if (newgrflangid == lang.newgrflangid) return &lang;
2045  }
2046 
2047  return nullptr;
2048 }
2049 
2056 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
2057 {
2058  FILE *f = fopen(file, "rb");
2059  if (f == nullptr) return false;
2060 
2061  size_t read = fread(hdr, sizeof(*hdr), 1, f);
2062  fclose(f);
2063 
2064  bool ret = read == 1 && hdr->IsValid();
2065 
2066  /* Convert endianness for the windows language ID */
2067  if (ret) {
2068  hdr->missing = FROM_LE16(hdr->missing);
2069  hdr->winlangid = FROM_LE16(hdr->winlangid);
2070  }
2071  return ret;
2072 }
2073 
2078 static void FillLanguageList(const std::string &path)
2079 {
2080  DIR *dir = ttd_opendir(path.c_str());
2081  if (dir != nullptr) {
2082  struct dirent *dirent;
2083  while ((dirent = readdir(dir)) != nullptr) {
2084  std::string d_name = FS2OTTD(dirent->d_name);
2085  const char *extension = strrchr(d_name.c_str(), '.');
2086 
2087  /* Not a language file */
2088  if (extension == nullptr || strcmp(extension, ".lng") != 0) continue;
2089 
2090  LanguageMetadata lmd;
2091  lmd.file = path + d_name;
2092 
2093  /* Check whether the file is of the correct version */
2094  if (!GetLanguageFileHeader(lmd.file.string().c_str(), &lmd)) {
2095  Debug(misc, 3, "{} is not a valid language file", lmd.file);
2096  } else if (GetLanguage(lmd.newgrflangid) != nullptr) {
2097  Debug(misc, 3, "{}'s language ID is already known", lmd.file);
2098  } else {
2099  _languages.push_back(lmd);
2100  }
2101  }
2102  closedir(dir);
2103  }
2104 }
2105 
2111 {
2112  for (Searchpath sp : _valid_searchpaths) {
2113  FillLanguageList(FioGetDirectory(sp, LANG_DIR));
2114  }
2115  if (_languages.empty()) UserError("No available language packs (invalid versions?)");
2116 
2117  /* Acquire the locale of the current system */
2118  const char *lang = GetCurrentLocale("LC_MESSAGES");
2119  if (lang == nullptr) lang = "en_GB";
2120 
2121  const LanguageMetadata *chosen_language = nullptr;
2122  const LanguageMetadata *language_fallback = nullptr;
2123  const LanguageMetadata *en_GB_fallback = _languages.data();
2124 
2125  /* Find a proper language. */
2126  for (const LanguageMetadata &lng : _languages) {
2127  /* We are trying to find a default language. The priority is by
2128  * configuration file, local environment and last, if nothing found,
2129  * English. */
2130  if (_config_language_file == lng.file.filename()) {
2131  chosen_language = &lng;
2132  break;
2133  }
2134 
2135  if (strcmp (lng.isocode, "en_GB") == 0) en_GB_fallback = &lng;
2136 
2137  /* Only auto-pick finished translations */
2138  if (!lng.IsReasonablyFinished()) continue;
2139 
2140  if (strncmp(lng.isocode, lang, 5) == 0) chosen_language = &lng;
2141  if (strncmp(lng.isocode, lang, 2) == 0) language_fallback = &lng;
2142  }
2143 
2144  /* We haven't found the language in the config nor the one in the locale.
2145  * Now we set it to one of the fallback languages */
2146  if (chosen_language == nullptr) {
2147  chosen_language = (language_fallback != nullptr) ? language_fallback : en_GB_fallback;
2148  }
2149 
2150  if (!ReadLanguagePack(chosen_language)) UserError("Can't read language pack '{}'", chosen_language->file);
2151 }
2152 
2158 {
2159  return _langpack.langpack->isocode;
2160 }
2161 
2167 {
2168  InitFontCache(this->Monospace());
2169  const Sprite *question_mark[FS_END];
2170 
2171  for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
2172  question_mark[size] = GetGlyph(size, '?');
2173  }
2174 
2175  this->Reset();
2176  for (auto text = this->NextString(); text.has_value(); text = this->NextString()) {
2177  auto src = text->cbegin();
2178 
2179  FontSize size = this->DefaultSize();
2180  while (src != text->cend()) {
2181  char32_t c = Utf8Consume(src);
2182 
2183  if (c >= SCC_FIRST_FONT && c <= SCC_LAST_FONT) {
2184  size = (FontSize)(c - SCC_FIRST_FONT);
2185  } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
2186  /* The character is printable, but not in the normal font. This is the case we were testing for. */
2187  std::string size_name;
2188 
2189  switch (size) {
2190  case FS_NORMAL: size_name = "medium"; break;
2191  case FS_SMALL: size_name = "small"; break;
2192  case FS_LARGE: size_name = "large"; break;
2193  case FS_MONO: size_name = "mono"; break;
2194  default: NOT_REACHED();
2195  }
2196 
2197  Debug(fontcache, 0, "Font is missing glyphs to display char 0x{:X} in {} font size", (int)c, size_name);
2198  return true;
2199  }
2200  }
2201  }
2202  return false;
2203 }
2204 
2207  uint i;
2208  uint j;
2209 
2210  void Reset() override
2211  {
2212  this->i = 0;
2213  this->j = 0;
2214  }
2215 
2217  {
2218  return FS_NORMAL;
2219  }
2220 
2221  std::optional<std::string_view> NextString() override
2222  {
2223  if (this->i >= TEXT_TAB_END) return std::nullopt;
2224 
2225  const char *ret = _langpack.offsets[_langpack.langtab_start[this->i] + this->j];
2226 
2227  this->j++;
2228  while (this->i < TEXT_TAB_END && this->j >= _langpack.langtab_num[this->i]) {
2229  this->i++;
2230  this->j = 0;
2231  }
2232 
2233  return ret;
2234  }
2235 
2236  bool Monospace() override
2237  {
2238  return false;
2239  }
2240 
2241  void SetFontNames([[maybe_unused]] FontCacheSettings *settings, [[maybe_unused]] const char *font_name, [[maybe_unused]] const void *os_data) override
2242  {
2243 #if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2244  settings->small.font = font_name;
2245  settings->medium.font = font_name;
2246  settings->large.font = font_name;
2247 
2248  settings->small.os_handle = os_data;
2249  settings->medium.os_handle = os_data;
2250  settings->large.os_handle = os_data;
2251 #endif
2252  }
2253 };
2254 
2268 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2269 {
2270  static LanguagePackGlyphSearcher pack_searcher;
2271  if (searcher == nullptr) searcher = &pack_searcher;
2272  bool bad_font = !base_font || searcher->FindMissingGlyphs();
2273 #if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2274  if (bad_font) {
2275  /* We found an unprintable character... lets try whether we can find
2276  * a fallback font that can print the characters in the current language. */
2277  bool any_font_configured = !_fcsettings.medium.font.empty();
2278  FontCacheSettings backup = _fcsettings;
2279 
2280  _fcsettings.mono.os_handle = nullptr;
2281  _fcsettings.medium.os_handle = nullptr;
2282 
2283  bad_font = !SetFallbackFont(&_fcsettings, _langpack.langpack->isocode, _langpack.langpack->winlangid, searcher);
2284 
2285  _fcsettings = backup;
2286 
2287  if (!bad_font && any_font_configured) {
2288  /* If the user configured a bad font, and we found a better one,
2289  * show that we loaded the better font instead of the configured one.
2290  * The colour 'character' might change in the
2291  * future, so for safety we just Utf8 Encode it into the string,
2292  * which takes exactly three characters, so it replaces the "XXX"
2293  * with the colour marker. */
2294  static std::string err_str("XXXThe current font is missing some of the characters used in the texts for this language. Using system fallback font instead.");
2295  Utf8Encode(err_str.data(), SCC_YELLOW);
2296  SetDParamStr(0, err_str);
2297  ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2298  }
2299 
2300  if (bad_font && base_font) {
2301  /* Our fallback font does miss characters too, so keep the
2302  * user chosen font as that is more likely to be any good than
2303  * the wild guess we made */
2304  InitFontCache(searcher->Monospace());
2305  }
2306  }
2307 #endif
2308 
2309  if (bad_font) {
2310  /* All attempts have failed. Display an error. As we do not want the string to be translated by
2311  * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
2312  * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
2313  * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
2314  * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
2315  static std::string err_str("XXXThe current font is missing some of the characters used in the texts for this language. Read the readme to see how to solve this.");
2316  Utf8Encode(err_str.data(), SCC_YELLOW);
2317  SetDParamStr(0, err_str);
2318  ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2319 
2320  /* Reset the font width */
2321  LoadStringWidthTable(searcher->Monospace());
2322  return;
2323  }
2324 
2325  /* Update the font with cache */
2326  LoadStringWidthTable(searcher->Monospace());
2327 
2328 #if !(defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
2329  /*
2330  * For right-to-left languages we need the ICU library. If
2331  * we do not have support for that library we warn the user
2332  * about it with a message. As we do not want the string to
2333  * be translated by the translators, we 'force' it into the
2334  * binary and 'load' it via a BindCString. To do this
2335  * properly we have to set the colour of the string,
2336  * otherwise we end up with a lot of artifacts. The colour
2337  * 'character' might change in the future, so for safety
2338  * we just Utf8 Encode it into the string, which takes
2339  * exactly three characters, so it replaces the "XXX" with
2340  * the colour marker.
2341  */
2342  if (_current_text_dir != TD_LTR) {
2343  static std::string err_str("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with ICU + Harfbuzz enabled.");
2344  Utf8Encode(err_str.data(), SCC_YELLOW);
2345  SetDParamStr(0, err_str);
2346  ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
2347  }
2348 #endif /* !(WITH_ICU_I18N && WITH_HARFBUZZ) && !WITH_UNISCRIBE && !WITH_COCOA */
2349 }
VEH_AIRCRAFT
@ VEH_AIRCRAFT
Aircraft vehicle type.
Definition: vehicle_type.h:27
LanguagePackHeader::IsReasonablyFinished
bool IsReasonablyFinished() const
Check whether a translation is sufficiently finished to offer it to the public.
Definition: strings.cpp:1896
LoadStringWidthTable
void LoadStringWidthTable(bool monospace)
Initialize _stringwidth_table cache.
Definition: gfx.cpp:1233
StringParameters::SetOffset
void SetOffset(size_t offset)
Set the offset within the string from where to return the next result of GetInt64 or GetInt32.
Definition: strings_internal.h:62
LanguagePackHeader::text_dir
byte text_dir
default direction of the text
Definition: language.h:42
MissingGlyphSearcher::FindMissingGlyphs
bool FindMissingGlyphs()
Check whether there are glyphs missing in the current language.
Definition: strings.cpp:2166
WC_ROADVEH_LIST
@ WC_ROADVEH_LIST
Road vehicle list; Window numbers:
Definition: window_type.h:314
StringBuilder
Equivalent to the std::back_insert_iterator in function, with some convenience helpers for string con...
Definition: strings_internal.h:249
MissingGlyphSearcher::DefaultSize
virtual FontSize DefaultSize()=0
Get the default (font) size of the string.
StringParameters::GetOffset
size_t GetOffset()
Get the current offset, so it can be backed up for certain processing steps, or be used to offset the...
Definition: strings_internal.h:55
CopyInDParam
void CopyInDParam(const std::span< const StringParameterBackup > backup)
Copy the parameters from the backup into the global string parameter array.
Definition: strings.cpp:159
LanguagePack
Definition: strings.cpp:216
LanguageMetadata
Make sure the size is right.
Definition: language.h:93
Pool::PoolItem<&_depot_pool >::Get
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:335
SCC_NEWGRF_FIRST
@ SCC_NEWGRF_FIRST
The next variables are part of a NewGRF subsystem for creating text strings.
Definition: control_codes.h:130
_units_volume
static const UnitsLong _units_volume[]
Unit conversions for volume.
Definition: strings.cpp:805
GetCurrentLanguageIsoCode
const char * GetCurrentLanguageIsoCode()
Get the ISO language code of the currently loaded language.
Definition: strings.cpp:2157
SCC_NEWGRF_STRINL
@ SCC_NEWGRF_STRINL
Inline another string at the current position, StringID is encoded in the string.
Definition: control_codes.h:163
SetDParamMaxDigits
void SetDParamMaxDigits(size_t n, uint count, FontSize size)
Set DParam n to some number that is suitable for string size computations.
Definition: strings.cpp:143
MissingGlyphSearcher
A searcher for missing glyphs.
Definition: strings_func.h:115
GetGlyph
const Sprite * GetGlyph(FontSize size, char32_t key)
Get the Sprite for a glyph.
Definition: fontcache.h:188
ArrayStringParameters
Extension of StringParameters with its own statically sized buffer for the parameters.
Definition: strings_internal.h:203
SetFallbackFont
bool SetFallbackFont(struct FontCacheSettings *settings, const std::string &language_isocode, int winlangid, class MissingGlyphSearcher *callback)
We would like to have a fallback font as the current one doesn't contain all characters we need.
Definition: font_osx.cpp:27
ReconsiderGameScriptLanguage
void ReconsiderGameScriptLanguage()
Reconsider the game script language, so we use the right one.
Definition: game_text.cpp:385
LanguagePackHeader::plural_form
byte plural_form
plural form index
Definition: language.h:41
LanguagePackHeader::IDENT
static const uint32_t IDENT
Identifier for OpenTTD language files, big endian for "LANG".
Definition: language.h:25
StringParameters::PrepareForNextRun
void PrepareForNextRun()
Prepare the string parameters for the next formatting run.
Definition: strings.cpp:68
TEXT_TAB_END
@ TEXT_TAB_END
End of language files.
Definition: strings_type.h:38
IsInsideMM
constexpr bool IsInsideMM(const T x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
Definition: math_func.hpp:268
endian_func.hpp
ShowErrorMessage
void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc)
Display an error message in a window.
Definition: error_gui.cpp:367
Pool::PoolItem<&_company_pool >::GetIfValid
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:346
ttd_opendir
DIR * ttd_opendir(const char *path)
A wrapper around opendir() which will convert the string from OPENTTD encoding to that of the filesys...
Definition: fileio_func.h:111
LanguagePackHeader::num_cases
uint8_t num_cases
the number of cases of this language
Definition: language.h:54
_languages
LanguageList _languages
The actual list of language meta data.
Definition: strings.cpp:53
_units_time_years_or_minutes
static const Units _units_time_years_or_minutes[]
Unit conversions for time in calendar years or wallclock minutes.
Definition: strings.cpp:844
WL_WARNING
@ WL_WARNING
Other information.
Definition: error.h:25
TEXT_TAB_NEWGRF_START
@ TEXT_TAB_NEWGRF_START
Start of NewGRF supplied strings.
Definition: strings_type.h:40
smallmap_gui.h
GameCreationSettings::landscape
byte landscape
the landscape we're currently in
Definition: settings_type.h:356
_sorted_cargo_specs
std::vector< const CargoSpec * > _sorted_cargo_specs
Cargo specifications sorted alphabetically by name.
Definition: cargotype.cpp:161
company_base.h
LoadedLanguagePack::langtab_num
std::array< uint, TEXT_TAB_END > langtab_num
Offset into langpack offs.
Definition: strings.cpp:233
BaseStation::town
Town * town
The town this station is associated with.
Definition: base_station_base.h:73
timer_game_calendar.h
FS_BEGIN
@ FS_BEGIN
First font.
Definition: gfx_type.h:209
FACIL_TRUCK_STOP
@ FACIL_TRUCK_STOP
Station with truck stops.
Definition: station_type.h:53
TD_LTR
@ TD_LTR
Text is written left-to-right by default.
Definition: strings_type.h:23
LanguagePackGlyphSearcher::NextString
std::optional< std::string_view > NextString() override
Get the next string to search through.
Definition: strings.cpp:2221
Station
Station data structure.
Definition: station_base.h:442
currency.h
GetVelocityUnits
static const Units GetVelocityUnits(VehicleType type)
Get the correct velocity units depending on the vehicle type and whether we're using real-time units.
Definition: strings.cpp:854
StringID
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
StringParameters::GetNextParameter
T GetNextParameter()
Get the next parameter from our parameters.
Definition: strings_internal.h:93
CurrencySpec::symbol_pos
byte symbol_pos
The currency symbol is represented by two possible values, prefix and suffix Usage of one or the othe...
Definition: currency.h:90
BaseConsist::name
std::string name
Name of vehicle.
Definition: base_consist.h:18
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
_units_velocity_realtime
static const Units _units_velocity_realtime[]
Unit conversions for velocity.
Definition: strings.cpp:769
Pool::PoolItem::index
Tindex index
Index of this pool item.
Definition: pool_type.hpp:234
CargoSpec::Get
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition: cargotype.h:134
GenerateTownNameString
void GenerateTownNameString(StringBuilder &builder, size_t lang, uint32_t seed)
Generates town name from given seed.
Definition: townname.cpp:1013
StartTextRefStackUsage
void StartTextRefStackUsage(const GRFFile *grffile, byte numEntries, const uint32_t *values)
Start using the TTDP compatible string code parsing.
Definition: newgrf_text.cpp:798
_units_time_years_or_periods
static const Units _units_time_years_or_periods[]
Unit conversions for time in calendar years or economic periods.
Definition: strings.cpp:838
StringTab
StringTab
StringTabs to group StringIDs.
Definition: strings_type.h:28
Vehicle::group_id
GroupID group_id
Index of group Pool array.
Definition: vehicle_base.h:357
Searchpath
Searchpath
Types of searchpaths OpenTTD might use.
Definition: fileio_type.h:132
SortIndustryTypes
void SortIndustryTypes()
Initialize the list of sorted industry types.
Definition: industry_gui.cpp:234
Waypoint::town_cn
uint16_t town_cn
The N-1th waypoint for this town (consecutive number)
Definition: waypoint_base.h:17
MissingGlyphSearcher::Reset
virtual void Reset()=0
Reset the search, i.e.
FS_LARGE
@ FS_LARGE
Index of the large font in the font tables.
Definition: gfx_type.h:205
LocaleSettings::units_volume
byte units_volume
unit system for volume
Definition: settings_type.h:265
Waypoint
Representation of a waypoint.
Definition: waypoint_base.h:16
StringParameters::GetNextParameterPointer
StringParameter * GetNextParameterPointer()
Get the next parameter from our parameters.
Definition: strings.cpp:81
LanguageList
std::vector< LanguageMetadata > LanguageList
Type for the list of language meta data.
Definition: language.h:98
vehicle_base.h
LanguagePackHeader::num_genders
uint8_t num_genders
the number of genders of this language
Definition: language.h:53
CompanyProperties::name
std::string name
Name of the company if the user changed it.
Definition: company_base.h:59
fileio_func.h
LanguagePackHeader::newgrflangid
uint8_t newgrflangid
newgrf language id
Definition: language.h:52
StringParameter
The data required to format and validate a single parameter of a string.
Definition: strings_internal.h:17
Company::IsValidHumanID
static bool IsValidHumanID(size_t index)
Is this company a valid company, not controlled by a NoAI program?
Definition: company_base.h:150
UnitsLong::c
UnitConversion c
Conversion.
Definition: strings.cpp:753
TimerGameEconomy::UsingWallclockUnits
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
Definition: timer_game_economy.cpp:97
town.h
StringParameters::GetNextParameterString
const char * GetNextParameterString()
Get the next string parameter from our parameters.
Definition: strings_internal.h:105
IndustrySpec::station_name
StringID station_name
Default name for nearby station.
Definition: industrytype.h:130
StopTextRefStackUsage
void StopTextRefStackUsage()
Stop using the TTDP compatible string code parsing.
Definition: newgrf_text.cpp:815
Engine
Definition: engine_base.h:37
VEH_ROAD
@ VEH_ROAD
Road vehicle type.
Definition: vehicle_type.h:25
Vehicle
Vehicle data structure.
Definition: vehicle_base.h:240
LoadedLanguagePack
Definition: strings.cpp:228
Industry
Defines the internal data of a functional industry.
Definition: industry.h:68
CargoSpec::GetArraySize
static size_t GetArraySize()
Total number of cargospecs, both valid and invalid.
Definition: cargotype.h:124
CompanyProperties::president_name_2
uint32_t president_name_2
Parameter of president_name_1.
Definition: company_base.h:62
ReadLanguagePack
bool ReadLanguagePack(const LanguageMetadata *lang)
Read a particular language.
Definition: strings.cpp:1907
SignID
uint16_t SignID
The type of the IDs of signs.
Definition: signs_type.h:14
Owner
Owner
Enum for all companies/owners.
Definition: company_type.h:18
UnitsLong
Information about a specific unit system with a long variant.
Definition: strings.cpp:752
StrEmpty
bool StrEmpty(const char *s)
Check if a string buffer is empty.
Definition: string_func.h:56
LocaleSettings::digit_decimal_separator
std::string digit_decimal_separator
decimal separator
Definition: settings_type.h:270
GetStringIndex
uint GetStringIndex(StringID str)
Extract the StringIndex from a StringID.
Definition: strings_func.h:38
_units_height
static const Units _units_height[]
Unit conversions for height.
Definition: strings.cpp:819
Debug
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
NBSP
#define NBSP
A non-breaking space.
Definition: string_type.h:16
LanguagePackGlyphSearcher::i
uint i
Iterator for the primary language tables.
Definition: strings.cpp:2207
RestoreTextRefStackBackup
void RestoreTextRefStackBackup(struct TextRefStack *backup)
Restore a copy of the text stack to the used stack.
Definition: newgrf_text.cpp:774
ConvertDisplaySpeedToKmhishSpeed
uint ConvertDisplaySpeedToKmhishSpeed(uint speed, VehicleType type)
Convert the given display speed to the km/h-ish speed.
Definition: strings.cpp:904
BaseStation::string_id
StringID string_id
Default name (town area) of station.
Definition: base_station_base.h:70
GameSettings::game_creation
GameCreationSettings game_creation
settings used during the creation of a game (map)
Definition: settings_type.h:619
control_codes.h
MissingGlyphSearcher::NextString
virtual std::optional< std::string_view > NextString()=0
Get the next string to search through.
TAB_SIZE
static const uint TAB_SIZE
Number of strings per StringTab.
Definition: strings_type.h:46
CBM_VEHICLE_NAME
@ CBM_VEHICLE_NAME
Engine name.
Definition: newgrf_callbacks.h:303
GetStringWithArgs
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition: strings.cpp:261
StringParameters::GetRemainingParameters
StringParameters GetRemainingParameters()
Get a new instance of StringParameters that is a "range" into the remaining existing parameters.
Definition: strings_internal.h:119
Units
Information about a specific unit system.
Definition: strings.cpp:745
townname_func.h
Group
Group data.
Definition: group.h:72
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
StrValid
bool StrValid(const char *str, const char *last)
Checks whether the given string is valid, i.e.
Definition: string.cpp:233
UnitsLong::decimal_places
unsigned int decimal_places
Number of decimal places embedded in the value. For example, 1 if the value is in tenths,...
Definition: strings.cpp:756
MAX_NUM_CASES
static const uint8_t MAX_NUM_CASES
Maximum number of supported cases.
Definition: language.h:21
FACIL_BUS_STOP
@ FACIL_BUS_STOP
Station with bus stops.
Definition: station_type.h:54
FormatString
static void FormatString(StringBuilder &builder, const char *str, StringParameters &args, uint case_index=0, bool game_script=false, bool dry_run=false)
Parse most format codes within a string and write the result to a buffer.
Definition: strings.cpp:916
StringParameters::AdvanceOffset
void AdvanceOffset(size_t advance)
Advance the offset within the string from where to return the next result of GetInt64 or GetInt32.
Definition: strings_internal.h:80
depot_base.h
error_func.h
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
_units_time_days_or_seconds
static const Units _units_time_days_or_seconds[]
Unit conversions for time in calendar days or wallclock seconds.
Definition: strings.cpp:826
FontCacheSettings
Settings for the four different fonts.
Definition: fontcache.h:216
FillLanguageList
static void FillLanguageList(const std::string &path)
Search for the languages in the given directory and add them to the _languages list.
Definition: strings.cpp:2078
newgrf_engine.h
FS_NORMAL
@ FS_NORMAL
Index of the normal font in the font tables.
Definition: gfx_type.h:203
FS2OTTD
std::string FS2OTTD(const std::wstring &name)
Convert to OpenTTD's encoding from a wide string.
Definition: win32.cpp:462
Industry::type
IndustryType type
type of industry.
Definition: industry.h:104
CargoSpec::units_volume
StringID units_volume
Name of a single unit of cargo of this type.
Definition: cargotype.h:90
IsTextDirectionChar
bool IsTextDirectionChar(char32_t c)
Is the given character a text direction character.
Definition: string_func.h:216
WC_INDUSTRY_DIRECTORY
@ WC_INDUSTRY_DIRECTORY
Industry directory; Window numbers:
Definition: window_type.h:266
GetGameStringPtr
const char * GetGameStringPtr(uint id)
Get the string pointer of a particular game string.
Definition: game_text.cpp:320
FontCacheSubSetting::font
std::string font
The name of the font, or path to the font.
Definition: fontcache.h:208
StringParameters::GetTypeAtOffset
char32_t GetTypeAtOffset(size_t offset) const
Get the type of a specific element.
Definition: strings_internal.h:143
FS_SMALL
@ FS_SMALL
Index of the small font in the font tables.
Definition: gfx_type.h:204
UnitsLong::s
StringID s
String for the short variant of the unit.
Definition: strings.cpp:754
StringParameters::GetDataLeft
size_t GetDataLeft() const
Return the amount of elements which can still be read.
Definition: strings_internal.h:137
InitFontCache
void InitFontCache(bool monospace)
(Re)initialize the font cache related things, i.e.
Definition: fontcache.cpp:197
ReadFileToMem
std::unique_ptr< char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
Load a file into memory.
Definition: fileio.cpp:1107
MAX_NUM_GENDERS
static const uint8_t MAX_NUM_GENDERS
Maximum number of supported genders.
Definition: language.h:20
TAB_SIZE_GAMESCRIPT
static const uint TAB_SIZE_GAMESCRIPT
Number of strings for GameScripts.
Definition: strings_type.h:49
_settings_game
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:55
LoadedLanguagePack::langtab_start
std::array< uint, TEXT_TAB_END > langtab_start
Offset into langpack offs.
Definition: strings.cpp:234
CompanyProperties::name_2
uint32_t name_2
Parameter of name_1.
Definition: company_base.h:57
LanguagePackHeader::name
char name[32]
the international name of this language
Definition: language.h:29
BuildContentTypeStringList
void BuildContentTypeStringList()
Build array of all strings corresponding to the content types.
Definition: network_content_gui.cpp:1029
UnitConversion::FromDisplay
int64_t FromDisplay(int64_t input, bool round=true, int64_t divider=1) const
Convert the displayed value back into a value of OpenTTD's internal unit.
Definition: strings.cpp:736
MAX_LANG
static const uint MAX_LANG
Maximum number of languages supported by the game, and the NewGRF specs.
Definition: strings_type.h:19
industry.h
TimerGameCalendar::ConvertDateToYMD
static YearMonthDay ConvertDateToYMD(Date date)
Converts a Date to a Year, Month & Day.
Definition: timer_game_calendar.cpp:42
safeguards.h
FontCacheSettings::medium
FontCacheSubSetting medium
The normal font size.
Definition: fontcache.h:218
CopyOutDParam
void CopyOutDParam(std::vector< StringParameterBackup > &backup, size_t num)
Copy num string parameters from the global string parameter array to the backup.
Definition: strings.cpp:176
DEFAULT_GROUP
static const GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition: group_type.h:17
FormatBytes
static void FormatBytes(StringBuilder &builder, int64_t number)
Format a given number as a number of bytes with the SI prefix.
Definition: strings.cpp:452
LanguagePackGlyphSearcher::DefaultSize
FontSize DefaultSize() override
Get the default (font) size of the string.
Definition: strings.cpp:2216
WC_SHIPS_LIST
@ WC_SHIPS_LIST
Ships list; Window numbers:
Definition: window_type.h:320
settings
fluid_settings_t * settings
FluidSynth settings handle.
Definition: fluidsynth.cpp:21
fontdetection.h
MissingGlyphSearcher::Monospace
virtual bool Monospace()=0
Whether to search for a monospace font or not.
BaseStation::name
std::string name
Custom name.
Definition: base_station_base.h:69
CurrencySpec::rate
uint16_t rate
The conversion rate compared to the base currency.
Definition: currency.h:75
VehicleID
uint32_t VehicleID
The type all our vehicle IDs have.
Definition: vehicle_type.h:16
GetDParam
uint64_t GetDParam(size_t n)
Get the current string parameter at index n from the global string parameter array.
Definition: strings.cpp:114
gfx_layout.h
newgrf_text.h
CompanyProperties::president_name
std::string president_name
Name of the president if the user changed it.
Definition: company_base.h:63
error.h
LocaleSettings::units_weight
byte units_weight
unit system for weight
Definition: settings_type.h:264
WC_TRAINS_LIST
@ WC_TRAINS_LIST
Trains list; Window numbers:
Definition: window_type.h:308
UnitConversion::ToDisplay
int64_t ToDisplay(int64_t input, bool round=true) const
Convert value from OpenTTD's internal unit into the displayed value.
Definition: strings.cpp:722
language.h
FACIL_DOCK
@ FACIL_DOCK
Station with a dock.
Definition: station_type.h:56
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
VehicleType
VehicleType
Available vehicle types.
Definition: vehicle_type.h:21
IndustrySpec
Defines the data structure for constructing industry.
Definition: industrytype.h:105
LanguagePackHeader::digit_decimal_separator
char digit_decimal_separator[8]
Decimal separator.
Definition: language.h:39
LanguagePackHeader::own_name
char own_name[32]
the localized name of this language
Definition: language.h:30
CurrencySpec
Specification of a currency.
Definition: currency.h:74
LanguagePackHeader::isocode
char isocode[16]
the ISO code for the language (not country code)
Definition: language.h:31
MacOSSetCurrentLocaleName
void MacOSSetCurrentLocaleName(const char *iso_code)
Store current language locale as a CoreFoundation locale.
Definition: string_osx.cpp:313
StationFacility
StationFacility
The facilities a station might be having.
Definition: station_type.h:50
GetBroadestDigit
void GetBroadestDigit(uint *front, uint *next, FontSize size)
Determine the broadest digits for guessing the maximum width of a n-digit number.
Definition: gfx.cpp:1278
LanguagePackGlyphSearcher::Monospace
bool Monospace() override
Whether to search for a monospace font or not.
Definition: strings.cpp:2236
BuildIndustriesLegend
void BuildIndustriesLegend()
Fills an array for the industries legends.
Definition: smallmap_gui.cpp:186
LocaleSettings::units_force
byte units_force
unit system for force
Definition: settings_type.h:266
LanguagePackGlyphSearcher
Helper for searching through the language pack.
Definition: strings.cpp:2206
UnitsLong::l
StringID l
String for the long variant of the unit.
Definition: strings.cpp:755
_current_language
const LanguageMetadata * _current_language
The currently loaded language.
Definition: strings.cpp:54
Industry::town
Town * town
Nearest town.
Definition: industry.h:97
LanguageMetadata::file
std::filesystem::path file
Name of the file we read this data from.
Definition: language.h:94
_scan_for_gender_data
static bool _scan_for_gender_data
Are we scanning for the gender of the current string? (instead of formatting it)
Definition: strings.cpp:239
string_func.h
CALLBACK_FAILED
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
Definition: newgrf_callbacks.h:420
_units_power
static const Units _units_power[]
Unit conversions for power.
Definition: strings.cpp:778
DepotID
uint16_t DepotID
Type for the unique identifier of depots.
Definition: depot_type.h:13
ConvertDisplaySpeedToSpeed
uint ConvertDisplaySpeedToSpeed(uint speed, VehicleType type)
Convert the given display speed to the (internal) speed.
Definition: strings.cpp:884
LANG_DIR
@ LANG_DIR
Subdirectory for all translation files.
Definition: fileio_type.h:118
_units_velocity_calendar
static const Units _units_velocity_calendar[]
Unit conversions for velocity.
Definition: strings.cpp:760
rev.h
station_base.h
strings_func.h
LanguagePackGlyphSearcher::j
uint j
Iterator for the secondary language tables.
Definition: strings.cpp:2208
TextDirection
TextDirection
Directions a text can go to.
Definition: strings_type.h:22
Units::c
UnitConversion c
Conversion.
Definition: strings.cpp:746
LanguagePackHeader::version
uint32_t version
32-bits of auto generated version info which is basically a hash of strings.h
Definition: language.h:28
GetCurrentLocale
const char * GetCurrentLocale(const char *param)
Determine the current charset based on the environment First check some default values,...
Definition: strings.cpp:2015
StringParameters
Definition: strings_internal.h:23
LanguagePackGlyphSearcher::Reset
void Reset() override
Reset the search, i.e.
Definition: strings.cpp:2210
WC_BUILD_VEHICLE
@ WC_BUILD_VEHICLE
Build vehicle; Window numbers:
Definition: window_type.h:383
FACIL_TRAIN
@ FACIL_TRAIN
Station with train station.
Definition: station_type.h:52
LanguagePackHeader::IsValid
bool IsValid() const
Check whether the header is a valid header for OpenTTD.
Definition: strings.cpp:1876
DeterminePluralForm
static int DeterminePluralForm(int64_t count, int plural_form)
Determine the "plural" index given a plural form and a number.
Definition: strings.cpp:575
SetDParamMaxValue
void SetDParamMaxValue(size_t n, uint64_t max_value, uint min_count, FontSize size)
Set DParam n to some number that is suitable for string size computations.
Definition: strings.cpp:127
LocaleSettings::units_power
byte units_power
unit system for power
Definition: settings_type.h:263
StringParameters::offset
size_t offset
Current offset in the parameters span.
Definition: strings_internal.h:28
game_text.hpp
SetDParam
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition: strings.cpp:104
LanguagePackHeader::missing
uint16_t missing
number of missing strings.
Definition: language.h:40
_units_power_to_weight
static const Units _units_power_to_weight[]
Unit conversions for power to weight.
Definition: strings.cpp:785
StringControlCode
StringControlCode
List of string control codes used for string formatting, displaying, and by strgen to generate the la...
Definition: control_codes.h:17
LocaleSettings::units_height
byte units_height
unit system for height
Definition: settings_type.h:267
GetTownName
static void GetTownName(StringBuilder &builder, const TownNameParams *par, uint32_t townnameparts)
Fills builder with specified town name.
Definition: townname.cpp:48
InvalidateWindowClassesData
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition: window.cpp:3217
CargoSpec::quantifier
StringID quantifier
Text for multiple units of cargo of this type.
Definition: cargotype.h:91
DIR
Definition: win32.cpp:65
HaveDParamChanged
bool HaveDParamChanged(const std::vector< StringParameterBackup > &backup)
Checks whether the global string parameters have changed compared to the given backup.
Definition: strings.cpp:194
InitializeSortedCargoSpecs
void InitializeSortedCargoSpecs()
Initialize the list of sorted cargo specifications.
Definition: cargotype.cpp:194
TEXT_TAB_GAMESCRIPT_START
@ TEXT_TAB_GAMESCRIPT_START
Start of GameScript supplied strings.
Definition: strings_type.h:39
AutoRestoreBackup
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
Definition: backup_type.hpp:153
network_content_gui.h
_units_weight
static const UnitsLong _units_weight[]
Unit conversions for weight.
Definition: strings.cpp:798
FontCacheSettings::mono
FontCacheSubSetting mono
The mono space font used for license/readme viewers.
Definition: fontcache.h:220
GetString
std::string GetString(StringID string)
Resolve the given StringID into a std::string with all the associated DParam lookups and formatting.
Definition: strings.cpp:327
LanguagePackHeader::digit_group_separator_currency
char digit_group_separator_currency[8]
Thousand separator used for currencies.
Definition: language.h:37
waypoint_base.h
_units_force
static const Units _units_force[]
Unit conversions for force.
Definition: strings.cpp:812
Sign
Definition: signs_base.h:21
LanguagePackHeader::ident
uint32_t ident
32-bits identifier
Definition: language.h:27
MakeStringID
StringID MakeStringID(StringTab tab, uint index)
Create a StringID.
Definition: strings_func.h:49
SetDParamStr
void SetDParamStr(size_t n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition: strings.cpp:352
GroupID
uint16_t GroupID
Type for all group identifiers.
Definition: group_type.h:13
Vehicle::unitnumber
UnitID unitnumber
unit number, for display purposes only
Definition: vehicle_base.h:321
Group::name
std::string name
Group Name.
Definition: group.h:73
CreateTextRefStackBackup
struct TextRefStack * CreateTextRefStackBackup()
Create a backup of the current NewGRF text stack.
Definition: newgrf_text.cpp:765
WL_ERROR
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition: error.h:26
SetCurrentGrfLangID
void SetCurrentGrfLangID(byte language_id)
Equivalence Setter function between game and newgrf langID.
Definition: newgrf_text.cpp:659
FontCacheSubSetting::os_handle
const void * os_handle
Optional native OS font info. Only valid during font search.
Definition: fontcache.h:212
StringParameters::parameters
std::span< StringParameter > parameters
Array with the actual parameters.
Definition: strings_internal.h:26
CompanyProperties::president_name_1
StringID president_name_1
Name of the president if the user did not change it.
Definition: company_base.h:61
LanguagePackDeleter
Definition: strings.cpp:220
window_func.h
CheckForMissingGlyphs
void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
Check whether the currently loaded language pack uses characters that the currently loaded font does ...
Definition: strings.cpp:2268
Depot
Definition: depot_base.h:20
Town
Town data structure.
Definition: town.h:50
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:300
SpecializedStation< Station, false >::GetIfValid
static Station * GetIfValid(size_t index)
Returns station if the index is a valid index for this station type.
Definition: base_station_base.h:268
GameSettings::locale
LocaleSettings locale
settings related to used currency/unit system in the current game
Definition: settings_type.h:631
OverflowSafeInt< int64_t >
CargoID
byte CargoID
Cargo slots to indicate a cargo type within a game.
Definition: cargo_type.h:20
LanguagePackHeader::digit_group_separator
char digit_group_separator[8]
Thousand separator used for anything not currencies.
Definition: language.h:35
FS_MONO
@ FS_MONO
Index of the monospaced font in the font tables.
Definition: gfx_type.h:206
engine_base.h
GetGRFStringPtr
const char * GetGRFStringPtr(uint32_t stringid)
Get a C-string from a stringid set by a newgrf.
Definition: newgrf_text.cpp:639
IsValidCargoID
bool IsValidCargoID(CargoID t)
Test whether cargo type is not INVALID_CARGO.
Definition: cargo_type.h:90
LanguagePackHeader::winlangid
uint16_t winlangid
Windows language ID: Windows cannot and will not convert isocodes to something it can use to determin...
Definition: language.h:51
FontSize
FontSize
Available font sizes.
Definition: gfx_type.h:202
EngineID
uint16_t EngineID
Unique identification number of an engine.
Definition: engine_type.h:21
Utf8Encode
size_t Utf8Encode(T buf, char32_t c)
Encode a unicode character and place it in the buffer.
Definition: string.cpp:479
GetIndustrySpec
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
Definition: industry_cmd.cpp:123
ConvertSpeedToDisplaySpeed
uint ConvertSpeedToDisplaySpeed(uint speed, VehicleType type)
Convert the given (internal) speed to the display speed.
Definition: strings.cpp:871
VEH_TRAIN
@ VEH_TRAIN
Train vehicle type.
Definition: vehicle_type.h:24
IndustrySpec::name
StringID name
Displayed name of the industry.
Definition: industrytype.h:125
LocaleSettings::units_velocity
byte units_velocity
unit system for velocity of trains and road vehicles
Definition: settings_type.h:261
_config_language_file
std::string _config_language_file
The file (name) stored in the configuration.
Definition: strings.cpp:52
BaseVehicle::type
VehicleType type
Type of vehicle.
Definition: vehicle_type.h:51
FACIL_AIRPORT
@ FACIL_AIRPORT
Station with an airport.
Definition: station_type.h:55
GetStringTab
StringTab GetStringTab(StringID str)
Extract the StringTab from a StringID.
Definition: strings_func.h:25
Utf8Decode
size_t Utf8Decode(char32_t *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Definition: string.cpp:438
Units::s
StringID s
String for the unit.
Definition: strings.cpp:747
ConvertKmhishSpeedToDisplaySpeed
uint ConvertKmhishSpeedToDisplaySpeed(uint speed, VehicleType type)
Convert the given km/h-ish speed to the display speed.
Definition: strings.cpp:894
GetVehicleCallback
uint16_t GetVehicleCallback(CallbackID callback, uint32_t param1, uint32_t param2, EngineID engine, const Vehicle *v)
Evaluate a newgrf callback for vehicles.
Definition: newgrf_engine.cpp:1149
Layouter::Initialize
static void Initialize()
Perform initialization of layout engine.
Definition: gfx_layout.cpp:340
VEH_SHIP
@ VEH_SHIP
Ship vehicle type.
Definition: vehicle_type.h:26
_current_collator
std::unique_ptr< icu::Collator > _current_collator
Collator for the language currently in use.
Definition: strings.cpp:59
Depot::town_cn
uint16_t town_cn
The N-1th depot for this town (consecutive number)
Definition: depot_base.h:22
GetLanguage
const LanguageMetadata * GetLanguage(byte newgrflangid)
Get the language with the given NewGRF language ID.
Definition: strings.cpp:2041
Company
Definition: company_base.h:116
Town::name
std::string name
Custom town name. If empty, the town was not renamed and uses the generated name.
Definition: town.h:59
CurrencySpec::prefix
std::string prefix
Prefix to apply when formatting money in this currency.
Definition: currency.h:78
WC_AIRCRAFT_LIST
@ WC_AIRCRAFT_LIST
Aircraft list; Window numbers:
Definition: window_type.h:326
Sprite
Data structure describing a sprite.
Definition: spritecache.h:17
StringBuilder::Utf8Encode
void Utf8Encode(char32_t c)
Encode the given Utf8 character into the output buffer.
Definition: strings_internal.h:310
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:316
WC_STATION_LIST
@ WC_STATION_LIST
Station list; Window numbers:
Definition: window_type.h:302
_current_text_dir
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:56
StringParameters::next_type
char32_t next_type
The type of the next data that is retrieved.
Definition: strings_internal.h:29
LocaleSettings::digit_group_separator
std::string digit_group_separator
thousand separator for non-currencies
Definition: settings_type.h:268
UnitConversion::factor
double factor
Amount to multiply or divide upon conversion.
Definition: strings.cpp:714
CBID_VEHICLE_NAME
@ CBID_VEHICLE_NAME
Called to determine the engine name to show.
Definition: newgrf_callbacks.h:284
signs_base.h
UnitConversion
Helper for unit conversion.
Definition: strings.cpp:713
Units::decimal_places
unsigned int decimal_places
Number of decimal places embedded in the value. For example, 1 if the value is in tenths,...
Definition: strings.cpp:748
FormatNumber
static void FormatNumber(StringBuilder &builder, int64_t number, const char *separator, int zerofill=1, int fractional_digits=0)
Format a number into a string.
Definition: strings.cpp:390
GetLanguageFileHeader
static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
Reads the language file header and checks compatibility.
Definition: strings.cpp:2056
INVALID_STRING_ID
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
Definition: strings_type.h:17
GRFFile
Dynamic data of a loaded NewGRF.
Definition: newgrf.h:107
InitializeLanguagePacks
void InitializeLanguagePacks()
Make a list of the available language packs.
Definition: strings.cpp:2110
_units_time_months_or_minutes
static const Units _units_time_months_or_minutes[]
Unit conversions for time in calendar months or wallclock minutes.
Definition: strings.cpp:832
UsingNewGRFTextStack
bool UsingNewGRFTextStack()
Check whether the NewGRF text stack is in use.
Definition: newgrf_text.cpp:756
debug.h
LocaleSettings::units_velocity_nautical
byte units_velocity_nautical
unit system for velocity of ships and aircraft
Definition: settings_type.h:262
LanguagePackHeader
Header of a language file.
Definition: language.h:24
CompanyProperties::name_1
StringID name_1
Name of the company if the user did not change it.
Definition: company_base.h:58
TextRefStack
Definition: newgrf_text.cpp:687
backup_type.hpp
HasBit
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition: bitmath_func.hpp:103