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