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