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