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