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