OpenTTD Source 20250311-master-g40ddc03423
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
69{
70 assert(this->next_type == 0 || (SCC_CONTROL_START <= this->next_type && this->next_type <= SCC_CONTROL_END));
71 if (this->offset >= this->parameters.size()) {
72 throw std::out_of_range("Trying to read invalid string parameter");
73 }
74
75 auto &param = this->parameters[this->offset++];
76 if (param.type != 0 && param.type != this->next_type) {
77 this->next_type = 0;
78 throw std::out_of_range("Trying to read string parameter with wrong type");
79 }
80 param.type = this->next_type;
81 this->next_type = 0;
82 return param;
83}
84
94
102EncodedString GetEncodedStringWithArgs(StringID str, std::span<const StringParameter> params)
103{
104 std::string result;
105 auto output = std::back_inserter(result);
107 fmt::format_to(output, "{:X}", str);
108
109 struct visitor {
110 std::back_insert_iterator<std::string> &output;
111
112 void operator()(const std::monostate &) {}
113
114 void operator()(const uint64_t &arg)
115 {
117 fmt::format_to(this->output, "{:X}", arg);
118 }
119
120 void operator()(const std::string &value)
121 {
122#ifdef WITH_ASSERT
123 /* Don't allow an encoded string to contain another encoded string. */
124 if (!value.empty()) {
125 char32_t c;
126 const char *p = value.data();
127 if (Utf8Decode(&c, p)) {
128 assert(c != SCC_ENCODED && c != SCC_ENCODED_INTERNAL);
129 }
130 }
131#endif /* WITH_ASSERT */
133 fmt::format_to(this->output, "{}", value);
134 }
135 };
136
137 visitor v{output};
138 for (const auto &param : params) {
139 *output = SCC_RECORD_SEPARATOR;
140 std::visit(v, param.data);
141 }
142
143 return EncodedString{std::move(result)};
144}
145
154{
155 if (this->empty()) return {};
156
157 std::vector<StringParameter> params;
158
159 /* We need char * for std::from_chars. Iterate the underlying data, as string's own iterators may interfere. */
160 const char *p = this->string.data();
161 const char *e = this->string.data() + this->string.length();
162
163 char32_t c = Utf8Consume(p);
164 if (c != SCC_ENCODED_INTERNAL) return {};
165
166 StringID str;
167 auto result = std::from_chars(p, e, str, 16);
168 if (result.ec != std::errc()) return {};
169 if (result.ptr != e && *result.ptr != SCC_RECORD_SEPARATOR) return {};
170 p = result.ptr;
171
172 while (p != e) {
173 auto s = ++p;
174
175 /* Find end of the parameter. */
176 for (; p != e && *p != SCC_RECORD_SEPARATOR; ++p) {}
177
178 if (s == p) {
179 /* This is an empty parameter. */
180 params.emplace_back(std::monostate{});
181 continue;
182 }
183
184 /* Get the parameter type. */
185 char32_t parameter_type;
186 size_t len = Utf8Decode(&parameter_type, s);
187 s += len;
188
189 switch (parameter_type) {
190 case SCC_ENCODED_NUMERIC: {
191 uint64_t value;
192 result = std::from_chars(s, p, value, 16);
193 if (result.ec != std::errc() || result.ptr != p) return {};
194 params.emplace_back(value);
195 break;
196 }
197
198 case SCC_ENCODED_STRING: {
199 params.emplace_back(std::string(s, p));
200 break;
201 }
202
203 default:
204 /* Unknown parameter, make it blank. */
205 params.emplace_back(std::monostate{});
206 break;
207 }
208 }
209
210 if (param >= std::size(params)) return {};
211 params[param] = data;
212 return GetEncodedStringWithArgs(str, params);
213}
214
220{
221 return GetString(STR_JUST_RAW_STRING, this->string);
222}
223
230uint64_t GetParamMaxDigits(uint count, FontSize size)
231{
232 auto [front, next] = GetBroadestDigit(size);
233 uint64_t val = count > 1 ? front : next;
234 for (; count > 1; count--) {
235 val = 10 * val + next;
236 }
237 return val;
238}
239
248uint64_t GetParamMaxValue(uint64_t max_value, uint min_count, FontSize size)
249{
250 uint num_digits = 1;
251 while (max_value >= 10) {
252 num_digits++;
253 max_value /= 10;
254 }
255 return GetParamMaxDigits(std::max(min_count, num_digits), size);
256}
257
258static void StationGetSpecialString(StringBuilder &builder, StationFacilities x);
259static bool GetSpecialNameString(StringBuilder &builder, StringID string, StringParameters &args);
260
261static void FormatString(StringBuilder &builder, const char *str, StringParameters &args, uint case_index = 0, bool game_script = false, bool dry_run = false);
262
273static void FormatString(StringBuilder &builder, const char *str, std::span<StringParameter> params, uint case_index = 0, bool game_script = false, bool dry_run = false)
274{
275 StringParameters tmp_params{params};
276 FormatString(builder, str, tmp_params, case_index, game_script, dry_run);
277}
278
280 char data[]; // list of strings
281};
282
284 void operator()(LanguagePack *langpack)
285 {
286 /* LanguagePack is in fact reinterpreted char[], we need to reinterpret it back to free it properly. */
287 delete[] reinterpret_cast<char*>(langpack);
288 }
289};
290
292 std::unique_ptr<LanguagePack, LanguagePackDeleter> langpack;
293
294 std::vector<char *> offsets;
295
296 std::array<uint, TEXT_TAB_END> langtab_num;
297 std::array<uint, TEXT_TAB_END> langtab_start;
298
299 std::string list_separator;
300};
301
302static LoadedLanguagePack _langpack;
303
304static bool _scan_for_gender_data = false;
305
310std::string_view GetListSeparator()
311{
312 return _langpack.list_separator;
313}
314
315const char *GetStringPtr(StringID string)
316{
317 switch (GetStringTab(string)) {
319 /* 0xD0xx and 0xD4xx IDs have been converted earlier. */
320 case TEXT_TAB_OLD_NEWGRF: NOT_REACHED();
322 default: {
323 const size_t offset = _langpack.langtab_start[GetStringTab(string)] + GetStringIndex(string).base();
324 if (offset < _langpack.offsets.size()) return _langpack.offsets[offset];
325 return nullptr;
326 }
327 }
328}
329
338void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
339{
340 if (string == 0) {
341 GetStringWithArgs(builder, STR_UNDEFINED, args);
342 return;
343 }
344
345 StringIndexInTab index = GetStringIndex(string);
346 StringTab tab = GetStringTab(string);
347
348 switch (tab) {
349 case TEXT_TAB_TOWN:
350 if (IsInsideMM(string, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_END) && !game_script) {
351 try {
352 GenerateTownNameString(builder, string - SPECSTR_TOWNNAME_START, args.GetNextParameter<uint32_t>());
353 } catch (const std::runtime_error &e) {
354 Debug(misc, 0, "GetStringWithArgs: {}", e.what());
355 builder += "(invalid string parameter)";
356 }
357 return;
358 }
359 break;
360
361 case TEXT_TAB_SPECIAL:
362 if (!game_script) {
363 try {
364 if (GetSpecialNameString(builder, string, args)) return;
365 } catch (const std::runtime_error &e) {
366 Debug(misc, 0, "GetStringWithArgs: {}", e.what());
367 builder += "(invalid string parameter)";
368 return;
369 }
370 }
371 break;
372
373 case TEXT_TAB_OLD_CUSTOM:
374 /* Old table for custom names. This is no longer used */
375 if (!game_script) {
376 FatalError("Incorrect conversion of custom name string.");
377 }
378 break;
379
381 FormatString(builder, GetGameStringPtr(index), args, case_index, true);
382 return;
383 }
384
385 case TEXT_TAB_OLD_NEWGRF:
386 NOT_REACHED();
387
389 FormatString(builder, GetGRFStringPtr(index), args, case_index);
390 return;
391 }
392
393 default:
394 break;
395 }
396
397 if (index >= _langpack.langtab_num[tab]) {
398 if (game_script) {
399 return GetStringWithArgs(builder, STR_UNDEFINED, args);
400 }
401 FatalError("String 0x{:X} is invalid. You are probably using an old version of the .lng file.\n", string);
402 }
403
404 FormatString(builder, GetStringPtr(string), args, case_index);
405}
406
415void GetStringWithArgs(StringBuilder &builder, StringID string, std::span<StringParameter> params, uint case_index, bool game_script)
416{
417 StringParameters tmp_params{params};
418 GetStringWithArgs(builder, string, tmp_params, case_index, game_script);
419}
420
426std::string GetString(StringID string)
427{
428 return GetStringWithArgs(string, {});
429}
430
436void AppendStringInPlace(std::string &result, StringID string)
437{
438 StringBuilder builder(result);
439 GetStringWithArgs(builder, string, {});
440}
441
442void AppendStringWithArgsInPlace(std::string &result, StringID string, std::span<StringParameter> params)
443{
444 StringParameters tmp_params{params};
445 StringBuilder builder(result);
446 GetStringWithArgs(builder, string, tmp_params);
447}
448
456{
457 std::string result;
458 StringBuilder builder(result);
459 GetStringWithArgs(builder, string, args);
460 return result;
461}
462
463std::string GetStringWithArgs(StringID string, std::span<StringParameter> args)
464{
465 std::string result;
466 StringBuilder builder(result);
467 GetStringWithArgs(builder, string, args);
468 return result;
469}
470
471static const char *GetDecimalSeparator()
472{
473 const char *decimal_separator = _settings_game.locale.digit_decimal_separator.c_str();
474 if (StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator;
475 return decimal_separator;
476}
477
484static void FormatNumber(StringBuilder &builder, int64_t number, const char *separator)
485{
486 static const int max_digits = 20;
487 uint64_t divisor = 10000000000000000000ULL;
488 int thousands_offset = (max_digits - 1) % 3;
489
490 if (number < 0) {
491 builder += '-';
492 number = -number;
493 }
494
495 uint64_t num = number;
496 uint64_t tot = 0;
497 for (int i = 0; i < max_digits; i++) {
498 uint64_t quot = 0;
499 if (num >= divisor) {
500 quot = num / divisor;
501 num = num % divisor;
502 }
503 if ((tot |= quot) || i == max_digits - 1) {
504 builder += '0' + quot; // quot is a single digit
505 if ((i % 3) == thousands_offset && i < max_digits - 1) builder += separator;
506 }
507
508 divisor /= 10;
509 }
510}
511
512static void FormatCommaNumber(StringBuilder &builder, int64_t number)
513{
514 const char *separator = _settings_game.locale.digit_group_separator.c_str();
515 if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator;
516 FormatNumber(builder, number, separator);
517}
518
519static void FormatNoCommaNumber(StringBuilder &builder, int64_t number)
520{
521 fmt::format_to(builder, "{}", number);
522}
523
524static void FormatZerofillNumber(StringBuilder &builder, int64_t number, int count)
525{
526 fmt::format_to(builder, "{:0{}d}", number, count);
527}
528
529static void FormatHexNumber(StringBuilder &builder, uint64_t number)
530{
531 fmt::format_to(builder, "0x{:X}", number);
532}
533
539static void FormatBytes(StringBuilder &builder, int64_t number)
540{
541 assert(number >= 0);
542
543 /* 1 2^10 2^20 2^30 2^40 2^50 2^60 */
544 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
545 uint id = 1;
546 while (number >= 1024 * 1024) {
547 number /= 1024;
548 id++;
549 }
550
551 if (number < 1024) {
552 id = 0;
553 fmt::format_to(builder, "{}", number);
554 } else if (number < 1024 * 10) {
555 fmt::format_to(builder, "{}{}{:02}", number / 1024, GetDecimalSeparator(), (number % 1024) * 100 / 1024);
556 } else if (number < 1024 * 100) {
557 fmt::format_to(builder, "{}{}{:01}", number / 1024, GetDecimalSeparator(), (number % 1024) * 10 / 1024);
558 } else {
559 assert(number < 1024 * 1024);
560 fmt::format_to(builder, "{}", number / 1024);
561 }
562
563 assert(id < lengthof(iec_prefixes));
564 fmt::format_to(builder, NBSP "{}B", iec_prefixes[id]);
565}
566
567static void FormatYmdString(StringBuilder &builder, TimerGameCalendar::Date date, uint case_index)
568{
569 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(date);
570
571 auto tmp_params = MakeParameters(STR_DAY_NUMBER_1ST + ymd.day - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year);
572 FormatString(builder, GetStringPtr(STR_FORMAT_DATE_LONG), tmp_params, case_index);
573}
574
575static void FormatMonthAndYear(StringBuilder &builder, TimerGameCalendar::Date date, uint case_index)
576{
577 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(date);
578
579 auto tmp_params = MakeParameters(STR_MONTH_JAN + ymd.month, ymd.year);
580 FormatString(builder, GetStringPtr(STR_FORMAT_DATE_SHORT), tmp_params, case_index);
581}
582
583static void FormatTinyOrISODate(StringBuilder &builder, TimerGameCalendar::Date date, StringID str)
584{
585 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(date);
586
587 /* Day and month are zero-padded with ZEROFILL_NUM, hence the two 2s. */
588 auto tmp_params = MakeParameters(ymd.day, 2, ymd.month + 1, 2, ymd.year);
589 FormatString(builder, GetStringPtr(str), tmp_params);
590}
591
592static void FormatGenericCurrency(StringBuilder &builder, const CurrencySpec *spec, Money number, bool compact)
593{
594 /* We are going to make number absolute for printing, so
595 * keep this piece of data as we need it later on */
596 bool negative = number < 0;
597
598 number *= spec->rate;
599
600 /* convert from negative */
601 if (number < 0) {
602 builder.Utf8Encode(SCC_PUSH_COLOUR);
603 builder.Utf8Encode(SCC_RED);
604 builder += '-';
605 number = -number;
606 }
607
608 /* Add prefix part, following symbol_pos specification.
609 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
610 * The only remaining value is 1 (suffix), so everything that is not 1 */
611 if (spec->symbol_pos != 1) builder += spec->prefix;
612
613 StringID number_str = STR_NULL;
614
615 /* For huge numbers, compact the number. */
616 if (compact) {
617 /* Take care of the thousand rounding. Having 1 000 000 k
618 * and 1 000 M is inconsistent, so always use 1 000 M. */
619 if (number >= Money(1'000'000'000'000'000) - 500'000'000) {
620 number = (number + Money(500'000'000'000)) / Money(1'000'000'000'000);
621 number_str = STR_CURRENCY_SHORT_TERA;
622 } else if (number >= Money(1'000'000'000'000) - 500'000) {
623 number = (number + 500'000'000) / 1'000'000'000;
624 number_str = STR_CURRENCY_SHORT_GIGA;
625 } else if (number >= 1'000'000'000 - 500) {
626 number = (number + 500'000) / 1'000'000;
627 number_str = STR_CURRENCY_SHORT_MEGA;
628 } else if (number >= 1'000'000) {
629 number = (number + 500) / 1'000;
630 number_str = STR_CURRENCY_SHORT_KILO;
631 }
632 }
633
634 const char *separator = _settings_game.locale.digit_group_separator_currency.c_str();
635 if (StrEmpty(separator)) separator = GetCurrency().separator.c_str();
636 if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator_currency;
637 FormatNumber(builder, number, separator);
638 if (number_str != STR_NULL) {
639 FormatString(builder, GetStringPtr(number_str), {});
640 }
641
642 /* Add suffix part, following symbol_pos specification.
643 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
644 * The only remaining value is 1 (prefix), so everything that is not 0 */
645 if (spec->symbol_pos != 0) builder += spec->suffix;
646
647 if (negative) {
648 builder.Utf8Encode(SCC_POP_COLOUR);
649 }
650}
651
658static int DeterminePluralForm(int64_t count, int plural_form)
659{
660 /* The absolute value determines plurality */
661 uint64_t n = abs(count);
662
663 switch (plural_form) {
664 default:
665 NOT_REACHED();
666
667 /* Two forms: singular used for one only.
668 * Used in:
669 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
670 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
671 case 0:
672 return n != 1 ? 1 : 0;
673
674 /* Only one form.
675 * Used in:
676 * Hungarian, Japanese, Turkish */
677 case 1:
678 return 0;
679
680 /* Two forms: singular used for 0 and 1.
681 * Used in:
682 * French, Brazilian Portuguese */
683 case 2:
684 return n > 1 ? 1 : 0;
685
686 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
687 * Note: Cases are out of order for hysterical reasons. '0' is last.
688 * Used in:
689 * Latvian */
690 case 3:
691 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
692
693 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
694 * Used in:
695 * Gaelige (Irish) */
696 case 4:
697 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
698
699 /* 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.
700 * Used in:
701 * Lithuanian */
702 case 5:
703 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
704
705 /* 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.
706 * Used in:
707 * Croatian, Russian, Ukrainian */
708 case 6:
709 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
710
711 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
712 * Used in:
713 * Polish */
714 case 7:
715 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
716
717 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
718 * Used in:
719 * Slovenian */
720 case 8:
721 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
722
723 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
724 * Used in:
725 * Icelandic */
726 case 9:
727 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
728
729 /* Three forms: special cases for 1, and 2 to 4
730 * Used in:
731 * Czech, Slovak */
732 case 10:
733 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
734
735 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
736 * Korean doesn't have the concept of plural, but depending on how a
737 * number is pronounced it needs another version of a particle.
738 * As such the plural system is misused to give this distinction.
739 */
740 case 11:
741 switch (n % 10) {
742 case 0: // yeong
743 case 1: // il
744 case 3: // sam
745 case 6: // yuk
746 case 7: // chil
747 case 8: // pal
748 return 0;
749
750 case 2: // i
751 case 4: // sa
752 case 5: // o
753 case 9: // gu
754 return 1;
755
756 default:
757 NOT_REACHED();
758 }
759
760 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
761 * Used in:
762 * Maltese */
763 case 12:
764 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
765 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
766 * Used in:
767 * Scottish Gaelic */
768 case 13:
769 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
770
771 /* Three forms: special cases for 1, 0 and numbers ending in 01 to 19.
772 * Used in:
773 * Romanian */
774 case 14:
775 return n == 1 ? 0 : (n == 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2;
776 }
777}
778
779static const char *ParseStringChoice(const char *b, uint form, StringBuilder &builder)
780{
781 /* <NUM> {Length of each string} {each string} */
782 uint n = (uint8_t)*b++;
783 uint pos, i, mypos = 0;
784
785 for (i = pos = 0; i != n; i++) {
786 uint len = (uint8_t)*b++;
787 if (i == form) mypos = pos;
788 pos += len;
789 }
790
791 builder += b + mypos;
792 return b + pos;
793}
794
797 double factor;
798
805 int64_t ToDisplay(int64_t input, bool round = true) const
806 {
807 return round
808 ? (int64_t)std::round(input * this->factor)
809 : (int64_t)(input * this->factor);
810 }
811
819 int64_t FromDisplay(int64_t input, bool round = true, int64_t divider = 1) const
820 {
821 return round
822 ? (int64_t)std::round(input / this->factor / divider)
823 : (int64_t)(input / this->factor / divider);
824 }
825};
826
833
841
844 { { 1.0 }, STR_UNITS_VELOCITY_IMPERIAL, 0 },
845 { { 1.609344 }, STR_UNITS_VELOCITY_METRIC, 0 },
846 { { 0.44704 }, STR_UNITS_VELOCITY_SI, 0 },
847 { { 0.578125 }, STR_UNITS_VELOCITY_GAMEUNITS_DAY, 1 },
848 { { 0.868976 }, STR_UNITS_VELOCITY_KNOTS, 0 },
849};
850
853 { { 1.0 }, STR_UNITS_VELOCITY_IMPERIAL, 0 },
854 { { 1.609344 }, STR_UNITS_VELOCITY_METRIC, 0 },
855 { { 0.44704 }, STR_UNITS_VELOCITY_SI, 0 },
856 { { 0.289352 }, STR_UNITS_VELOCITY_GAMEUNITS_SEC, 1 },
857 { { 0.868976 }, STR_UNITS_VELOCITY_KNOTS, 0 },
858};
859
861static const Units _units_power[] = {
862 { { 1.0 }, STR_UNITS_POWER_IMPERIAL, 0 },
863 { { 1.01387 }, STR_UNITS_POWER_METRIC, 0 },
864 { { 0.745699 }, STR_UNITS_POWER_SI, 0 },
865};
866
869 { { 0.907185 }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_IMPERIAL, 1 },
870 { { 1.0 }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_METRIC, 1 },
871 { { 1.0 }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_SI, 1 },
872 { { 0.919768 }, STR_UNITS_POWER_METRIC_TO_WEIGHT_IMPERIAL, 1 },
873 { { 1.01387 }, STR_UNITS_POWER_METRIC_TO_WEIGHT_METRIC, 1 },
874 { { 1.01387 }, STR_UNITS_POWER_METRIC_TO_WEIGHT_SI, 1 },
875 { { 0.676487 }, STR_UNITS_POWER_SI_TO_WEIGHT_IMPERIAL, 1 },
876 { { 0.745699 }, STR_UNITS_POWER_SI_TO_WEIGHT_METRIC, 1 },
877 { { 0.745699 }, STR_UNITS_POWER_SI_TO_WEIGHT_SI, 1 },
878};
879
881static const UnitsLong _units_weight[] = {
882 { { 1.102311 }, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL, 0 },
883 { { 1.0 }, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC, 0 },
884 { { 1000.0 }, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI, 0 },
885};
886
888static const UnitsLong _units_volume[] = {
889 { { 264.172 }, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL, 0 },
890 { { 1000.0 }, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC, 0 },
891 { { 1.0 }, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI, 0 },
892};
893
895static const Units _units_force[] = {
896 { { 0.224809 }, STR_UNITS_FORCE_IMPERIAL, 0 },
897 { { 0.101972 }, STR_UNITS_FORCE_METRIC, 0 },
898 { { 0.001 }, STR_UNITS_FORCE_SI, 0 },
899};
900
902static const Units _units_height[] = {
903 { { 3.0 }, STR_UNITS_HEIGHT_IMPERIAL, 0 }, // "Wrong" conversion factor for more nicer GUI values
904 { { 1.0 }, STR_UNITS_HEIGHT_METRIC, 0 },
905 { { 1.0 }, STR_UNITS_HEIGHT_SI, 0 },
906};
907
910 { { 1 }, STR_UNITS_DAYS, 0 },
911 { { 2 }, STR_UNITS_SECONDS, 0 },
912};
913
916 { { 1 }, STR_UNITS_MONTHS, 0 },
917 { { 1 }, STR_UNITS_MINUTES, 0 },
918};
919
922 { { 1 }, STR_UNITS_YEARS, 0 },
923 { { 1 }, STR_UNITS_PERIODS, 0 },
924};
925
928 { { 1 }, STR_UNITS_YEARS, 0 },
929 { { 12 }, STR_UNITS_MINUTES, 0 },
930};
931
938{
940
941 assert(setting < lengthof(_units_velocity_calendar));
942 assert(setting < lengthof(_units_velocity_realtime));
943
945
946 return _units_velocity_calendar[setting];
947}
948
955{
956 /* For historical reasons we don't want to mess with the
957 * conversion for speed. So, don't round it and keep the
958 * original conversion factors instead of the real ones. */
959 return GetVelocityUnits(type).c.ToDisplay(speed, false);
960}
961
968{
969 return GetVelocityUnits(type).c.FromDisplay(speed);
970}
971
978{
979 return GetVelocityUnits(type).c.ToDisplay(speed * 10, false) / 16;
980}
981
988{
989 return GetVelocityUnits(type).c.FromDisplay(speed * 16, true, 10);
990}
991
999static const char *DecodeEncodedString(const char *str, bool game_script, StringBuilder &builder)
1000{
1001 std::vector<StringParameter> sub_args;
1002
1003 char *p;
1004 StringIndexInTab id(std::strtoul(str, &p, 16));
1005 if (*p != SCC_RECORD_SEPARATOR && *p != '\0') {
1006 while (*p != '\0') p++;
1007 builder += "(invalid SCC_ENCODED)";
1008 return p;
1009 }
1010 if (game_script && id >= TAB_SIZE_GAMESCRIPT) {
1011 while (*p != '\0') p++;
1012 builder += "(invalid StringID)";
1013 return p;
1014 }
1015
1016 while (*p != '\0') {
1017 /* The start of parameter. */
1018 const char *s = ++p;
1019
1020 /* Find end of the parameter. */
1021 for (; *p != '\0' && *p != SCC_RECORD_SEPARATOR; ++p) {}
1022
1023 if (s == p) {
1024 /* This is an empty parameter. */
1025 sub_args.emplace_back(std::monostate{});
1026 continue;
1027 }
1028
1029 /* Get the parameter type. */
1030 char32_t parameter_type;
1031 size_t len = Utf8Decode(&parameter_type, s);
1032 s += len;
1033
1034 switch (parameter_type) {
1035 case SCC_ENCODED: {
1036 uint64_t param = std::strtoull(s, &p, 16);
1037 if (param >= TAB_SIZE_GAMESCRIPT) {
1038 while (*p != '\0') p++;
1039 builder += "(invalid sub-StringID)";
1040 return p;
1041 }
1043 sub_args.emplace_back(param);
1044 break;
1045 }
1046
1047 case SCC_ENCODED_NUMERIC: {
1048 uint64_t param = std::strtoull(s, &p, 16);
1049 sub_args.emplace_back(param);
1050 break;
1051 }
1052
1053 case SCC_ENCODED_STRING: {
1054 sub_args.emplace_back(std::string(s, p - s));
1055 break;
1056 }
1057
1058 default:
1059 /* Unknown parameter, make it blank. */
1060 sub_args.emplace_back(std::monostate{});
1061 break;
1062 }
1063 }
1064
1065 StringID stringid = game_script ? MakeStringID(TEXT_TAB_GAMESCRIPT_START, id) : StringID{id.base()};
1066 GetStringWithArgs(builder, stringid, sub_args, true);
1067
1068 return p;
1069}
1070
1078static void FormatString(StringBuilder &builder, const char *str_arg, StringParameters &args, uint case_index, bool game_script, bool dry_run)
1079{
1080 size_t orig_offset = args.GetOffset();
1081
1082 if (!dry_run) {
1083 /*
1084 * This function is normally called with `dry_run` false, then we call this function again
1085 * with `dry_run` being true. The dry run is required for the gender formatting. For the
1086 * gender determination we need to format a sub string to get the gender, but for that we
1087 * need to know as what string control code type the specific parameter is encoded. Since
1088 * gendered words can be before the "parameter" words, this needs to be determined before
1089 * the actual formatting.
1090 */
1091 std::string buffer;
1092 StringBuilder dry_run_builder(buffer);
1093 FormatString(dry_run_builder, str_arg, args, case_index, game_script, true);
1094 /* We have to restore the original offset here to to read the correct values. */
1095 args.SetOffset(orig_offset);
1096 }
1097 char32_t b = '\0';
1098 uint next_substr_case_index = 0;
1099 std::stack<const char *, std::vector<const char *>> str_stack;
1100 str_stack.push(str_arg);
1101
1102 for (;;) {
1103 try {
1104 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
1105 str_stack.pop();
1106 }
1107 if (str_stack.empty()) break;
1108 const char *&str = str_stack.top();
1109
1110 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
1111 /* We need to pass some stuff as it might be modified. */
1112 b = RemapNewGRFStringControlCode(b, &str);
1113 if (b == 0) continue;
1114 }
1115
1116 if (b < SCC_CONTROL_START || b > SCC_CONTROL_END) {
1117 builder.Utf8Encode(b);
1118 continue;
1119 }
1120
1121 args.SetTypeOfNextParameter(b);
1122 switch (b) {
1123 case SCC_ENCODED:
1125 str = DecodeEncodedString(str, b == SCC_ENCODED, builder);
1126 break;
1127
1128 case SCC_NEWGRF_STRINL: {
1129 StringID substr = Utf8Consume(&str);
1130 const char *ptr = GetStringPtr(substr);
1131 if (ptr == nullptr) {
1132 builder += "(invalid NewGRF string)";
1133 } else {
1134 str_stack.push(ptr);
1135 }
1136 break;
1137 }
1138
1140 StringID substr = args.GetNextParameter<StringID>();
1141 const char *ptr = GetStringPtr(substr);
1142 if (ptr == nullptr) {
1143 builder += "(invalid NewGRF string)";
1144 } else {
1145 str_stack.push(ptr);
1146 }
1147 case_index = next_substr_case_index;
1148 next_substr_case_index = 0;
1149 break;
1150 }
1151
1152
1153 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
1154 /* First read the meta data from the language file. */
1155 size_t offset = orig_offset + (uint8_t)*str++;
1156 int gender = 0;
1157 if (!dry_run && args.GetTypeAtOffset(offset) != 0) {
1158 /* Now we need to figure out what text to resolve, i.e.
1159 * what do we need to draw? So get the actual raw string
1160 * first using the control code to get said string. */
1161 char input[4 + 1];
1162 char *p = input + Utf8Encode(input, args.GetTypeAtOffset(offset));
1163 *p = '\0';
1164
1165 /* The gender is stored at the start of the formatted string. */
1166 bool old_sgd = _scan_for_gender_data;
1167 _scan_for_gender_data = true;
1168 std::string buffer;
1169 StringBuilder tmp_builder(buffer);
1170 StringParameters tmp_params = args.GetRemainingParameters(offset);
1171 FormatString(tmp_builder, input, tmp_params);
1172 _scan_for_gender_data = old_sgd;
1173
1174 /* And determine the string. */
1175 const char *s = buffer.c_str();
1176 char32_t c = Utf8Consume(&s);
1177 /* Does this string have a gender, if so, set it */
1178 if (c == SCC_GENDER_INDEX) gender = (uint8_t)s[0];
1179 }
1180 str = ParseStringChoice(str, gender, builder);
1181 break;
1182 }
1183
1184 /* This sets up the gender for the string.
1185 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
1186 case SCC_GENDER_INDEX: // {GENDER 0}
1188 builder.Utf8Encode(SCC_GENDER_INDEX);
1189 builder += *str++;
1190 } else {
1191 str++;
1192 }
1193 break;
1194
1195 case SCC_PLURAL_LIST: { // {P}
1196 int plural_form = *str++; // contains the plural form for this string
1197 size_t offset = orig_offset + (uint8_t)*str++;
1198 const uint64_t *v = std::get_if<uint64_t>(&args.GetParam(offset)); // contains the number that determines plural
1199 if (v != nullptr) {
1200 str = ParseStringChoice(str, DeterminePluralForm(static_cast<int64_t>(*v), plural_form), builder);
1201 } else {
1202 builder += "(invalid PLURAL parameter)";
1203 }
1204 break;
1205 }
1206
1207 case SCC_ARG_INDEX: { // Move argument pointer
1208 args.SetOffset(orig_offset + (uint8_t)*str++);
1209 break;
1210 }
1211
1212 case SCC_SET_CASE: { // {SET_CASE}
1213 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
1214 * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
1215 next_substr_case_index = (uint8_t)*str++;
1216 break;
1217 }
1218
1219 case SCC_SWITCH_CASE: { // {Used to implement case switching}
1220 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
1221 * Each LEN is printed using 2 bytes in big endian order. */
1222 uint num = (uint8_t)*str++;
1223 while (num) {
1224 if ((uint8_t)str[0] == case_index) {
1225 /* Found the case, adjust str pointer and continue */
1226 str += 3;
1227 break;
1228 }
1229 /* Otherwise skip to the next case */
1230 str += 3 + (str[1] << 8) + str[2];
1231 num--;
1232 }
1233 break;
1234 }
1235
1236 case SCC_REVISION: // {REV}
1237 builder += _openttd_revision;
1238 break;
1239
1240 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1241 const char *raw_string = args.GetNextParameterString();
1242 /* raw_string can be nullptr. */
1243 if (raw_string == nullptr) {
1244 builder += "(invalid RAW_STRING parameter)";
1245 break;
1246 }
1247 FormatString(builder, raw_string, args);
1248 break;
1249 }
1250
1251 case SCC_STRING: {// {STRING}
1252 StringID string_id = args.GetNextParameter<StringID>();
1253 if (game_script && GetStringTab(string_id) != TEXT_TAB_GAMESCRIPT_START) break;
1254 /* It's prohibited for the included string to consume any arguments. */
1255 StringParameters tmp_params(args, game_script ? args.GetDataLeft() : 0);
1256 GetStringWithArgs(builder, string_id, tmp_params, next_substr_case_index, game_script);
1257 next_substr_case_index = 0;
1258 break;
1259 }
1260
1261 case SCC_STRING1:
1262 case SCC_STRING2:
1263 case SCC_STRING3:
1264 case SCC_STRING4:
1265 case SCC_STRING5:
1266 case SCC_STRING6:
1267 case SCC_STRING7: { // {STRING1..7}
1268 /* Strings that consume arguments */
1269 StringID string_id = args.GetNextParameter<StringID>();
1270 if (game_script && GetStringTab(string_id) != TEXT_TAB_GAMESCRIPT_START) break;
1271 uint size = b - SCC_STRING1 + 1;
1272 if (size > args.GetDataLeft()) {
1273 builder += "(consumed too many parameters)";
1274 } else {
1275 StringParameters sub_args(args, game_script ? args.GetDataLeft() : size);
1276 GetStringWithArgs(builder, string_id, sub_args, next_substr_case_index, game_script);
1277 args.AdvanceOffset(size);
1278 }
1279 next_substr_case_index = 0;
1280 break;
1281 }
1282
1283 case SCC_COMMA: // {COMMA}
1284 FormatCommaNumber(builder, args.GetNextParameter<int64_t>());
1285 break;
1286
1287 case SCC_DECIMAL: { // {DECIMAL}
1288 int64_t number = args.GetNextParameter<int64_t>();
1289 int digits = args.GetNextParameter<int>();
1290 if (digits == 0) {
1291 FormatCommaNumber(builder, number);
1292 break;
1293 }
1294
1295 int64_t divisor = PowerOfTen(digits);
1296 int64_t fractional = number % divisor;
1297 number /= divisor;
1298 FormatCommaNumber(builder, number);
1299 fmt::format_to(builder, "{}{:0{}d}", GetDecimalSeparator(), fractional, digits);
1300 break;
1301 }
1302
1303 case SCC_NUM: // {NUM}
1304 FormatNoCommaNumber(builder, args.GetNextParameter<int64_t>());
1305 break;
1306
1307 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1308 int64_t num = args.GetNextParameter<int64_t>();
1309 FormatZerofillNumber(builder, num, args.GetNextParameter<int>());
1310 break;
1311 }
1312
1313 case SCC_HEX: // {HEX}
1314 FormatHexNumber(builder, args.GetNextParameter<uint64_t>());
1315 break;
1316
1317 case SCC_BYTES: // {BYTES}
1318 FormatBytes(builder, args.GetNextParameter<int64_t>());
1319 break;
1320
1321 case SCC_CARGO_TINY: { // {CARGO_TINY}
1322 /* Tiny description of cargotypes. Layout:
1323 * param 1: cargo type
1324 * param 2: cargo count */
1325 CargoType cargo = args.GetNextParameter<CargoType>();
1326 if (cargo >= CargoSpec::GetArraySize()) break;
1327
1328 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1329 int64_t amount = 0;
1330 switch (cargo_str) {
1331 case STR_TONS:
1333 break;
1334
1335 case STR_LITERS:
1337 break;
1338
1339 default: {
1340 amount = args.GetNextParameter<int64_t>();
1341 break;
1342 }
1343 }
1344
1345 FormatCommaNumber(builder, amount);
1346 break;
1347 }
1348
1349 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1350 /* Short description of cargotypes. Layout:
1351 * param 1: cargo type
1352 * param 2: cargo count */
1353 CargoType cargo = args.GetNextParameter<CargoType>();
1354 if (cargo >= CargoSpec::GetArraySize()) break;
1355
1356 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1357 switch (cargo_str) {
1358 case STR_TONS: {
1361 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1362 FormatString(builder, GetStringPtr(x.l), tmp_params);
1363 break;
1364 }
1365
1366 case STR_LITERS: {
1369 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1370 FormatString(builder, GetStringPtr(x.l), tmp_params);
1371 break;
1372 }
1373
1374 default: {
1375 auto tmp_params = MakeParameters(args.GetNextParameter<int64_t>());
1376 GetStringWithArgs(builder, cargo_str, tmp_params);
1377 break;
1378 }
1379 }
1380 break;
1381 }
1382
1383 case SCC_CARGO_LONG: { // {CARGO_LONG}
1384 /* First parameter is cargo type, second parameter is cargo count */
1385 CargoType cargo = args.GetNextParameter<CargoType>();
1386 if (IsValidCargoType(cargo) && cargo >= CargoSpec::GetArraySize()) break;
1387
1388 StringID cargo_str = !IsValidCargoType(cargo) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1389 auto tmp_args = MakeParameters(args.GetNextParameter<int64_t>());
1390 GetStringWithArgs(builder, cargo_str, tmp_args);
1391 break;
1392 }
1393
1394 case SCC_CARGO_LIST: { // {CARGO_LIST}
1395 CargoTypes cmask = args.GetNextParameter<CargoTypes>();
1396 bool first = true;
1397
1398 std::string_view list_separator = GetListSeparator();
1399 for (const auto &cs : _sorted_cargo_specs) {
1400 if (!HasBit(cmask, cs->Index())) continue;
1401
1402 if (first) {
1403 first = false;
1404 } else {
1405 /* Add a comma if this is not the first item */
1406 builder += list_separator;
1407 }
1408
1409 GetStringWithArgs(builder, cs->name, args, next_substr_case_index, game_script);
1410 }
1411
1412 /* If first is still true then no cargo is accepted */
1413 if (first) GetStringWithArgs(builder, STR_JUST_NOTHING, args, next_substr_case_index, game_script);
1414
1415 next_substr_case_index = 0;
1416 break;
1417 }
1418
1419 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1420 FormatGenericCurrency(builder, &GetCurrency(), args.GetNextParameter<int64_t>(), true);
1421 break;
1422
1423 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1424 FormatGenericCurrency(builder, &GetCurrency(), args.GetNextParameter<int64_t>(), false);
1425 break;
1426
1427 case SCC_DATE_TINY: // {DATE_TINY}
1428 FormatTinyOrISODate(builder, args.GetNextParameter<TimerGameCalendar::Date>(), STR_FORMAT_DATE_TINY);
1429 break;
1430
1431 case SCC_DATE_SHORT: // {DATE_SHORT}
1432 FormatMonthAndYear(builder, args.GetNextParameter<TimerGameCalendar::Date>(), next_substr_case_index);
1433 next_substr_case_index = 0;
1434 break;
1435
1436 case SCC_DATE_LONG: // {DATE_LONG}
1437 FormatYmdString(builder, args.GetNextParameter<TimerGameCalendar::Date>(), next_substr_case_index);
1438 next_substr_case_index = 0;
1439 break;
1440
1441 case SCC_DATE_ISO: // {DATE_ISO}
1442 FormatTinyOrISODate(builder, args.GetNextParameter<TimerGameCalendar::Date>(), STR_FORMAT_DATE_ISO);
1443 break;
1444
1445 case SCC_FORCE: { // {FORCE}
1448 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1449 FormatString(builder, GetStringPtr(x.s), tmp_params);
1450 break;
1451 }
1452
1453 case SCC_HEIGHT: { // {HEIGHT}
1456 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1457 FormatString(builder, GetStringPtr(x.s), tmp_params);
1458 break;
1459 }
1460
1461 case SCC_POWER: { // {POWER}
1464 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1465 FormatString(builder, GetStringPtr(x.s), tmp_params);
1466 break;
1467 }
1468
1469 case SCC_POWER_TO_WEIGHT: { // {POWER_TO_WEIGHT}
1471 assert(setting < lengthof(_units_power_to_weight));
1472 const auto &x = _units_power_to_weight[setting];
1473 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1474 FormatString(builder, GetStringPtr(x.s), tmp_params);
1475 break;
1476 }
1477
1478 case SCC_VELOCITY: { // {VELOCITY}
1479 int64_t arg = args.GetNextParameter<int64_t>();
1480 // Unpack vehicle type from packed argument to get desired units.
1481 VehicleType vt = static_cast<VehicleType>(GB(arg, 56, 8));
1482 const auto &x = GetVelocityUnits(vt);
1483 auto tmp_params = MakeParameters(ConvertKmhishSpeedToDisplaySpeed(GB(arg, 0, 56), vt), x.decimal_places);
1484 FormatString(builder, GetStringPtr(x.s), tmp_params);
1485 break;
1486 }
1487
1488 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1491 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1492 FormatString(builder, GetStringPtr(x.s), tmp_params);
1493 break;
1494 }
1495
1496 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1499 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1500 FormatString(builder, GetStringPtr(x.l), tmp_params);
1501 break;
1502 }
1503
1504 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1507 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1508 FormatString(builder, GetStringPtr(x.s), tmp_params);
1509 break;
1510 }
1511
1512 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1515 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1516 FormatString(builder, GetStringPtr(x.l), tmp_params);
1517 break;
1518 }
1519
1520 case SCC_UNITS_DAYS_OR_SECONDS: { // {UNITS_DAYS_OR_SECONDS}
1521 uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1522 const auto &x = _units_time_days_or_seconds[realtime];
1523 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1524 FormatString(builder, GetStringPtr(x.s), tmp_params);
1525 break;
1526 }
1527
1528 case SCC_UNITS_MONTHS_OR_MINUTES: { // {UNITS_MONTHS_OR_MINUTES}
1529 uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1530 const auto &x = _units_time_months_or_minutes[realtime];
1531 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1532 FormatString(builder, GetStringPtr(x.s), tmp_params);
1533 break;
1534 }
1535
1536 case SCC_UNITS_YEARS_OR_PERIODS: { // {UNITS_YEARS_OR_PERIODS}
1537 uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1538 const auto &x = _units_time_years_or_periods[realtime];
1539 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1540 FormatString(builder, GetStringPtr(x.s), tmp_params);
1541 break;
1542 }
1543
1544 case SCC_UNITS_YEARS_OR_MINUTES: { // {UNITS_YEARS_OR_MINUTES}
1545 uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1546 const auto &x = _units_time_years_or_minutes[realtime];
1547 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1548 FormatString(builder, GetStringPtr(x.s), tmp_params);
1549 break;
1550 }
1551
1552 case SCC_COMPANY_NAME: { // {COMPANY}
1554 if (c == nullptr) break;
1555
1556 if (!c->name.empty()) {
1557 auto tmp_params = MakeParameters(c->name);
1558 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1559 } else {
1560 auto tmp_params = MakeParameters(c->name_2);
1561 GetStringWithArgs(builder, c->name_1, tmp_params);
1562 }
1563 break;
1564 }
1565
1566 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1567 CompanyID company = args.GetNextParameter<CompanyID>();
1568
1569 /* Nothing is added for AI or inactive companies */
1570 if (Company::IsValidHumanID(company)) {
1571 auto tmp_params = MakeParameters(company + 1);
1572 GetStringWithArgs(builder, STR_FORMAT_COMPANY_NUM, tmp_params);
1573 }
1574 break;
1575 }
1576
1577 case SCC_DEPOT_NAME: { // {DEPOT}
1579 if (vt == VEH_AIRCRAFT) {
1580 auto tmp_params = MakeParameters(args.GetNextParameter<StationID>());
1581 GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_AIRCRAFT, tmp_params);
1582 break;
1583 }
1584
1585 const Depot *d = Depot::Get(args.GetNextParameter<DepotID>());
1586 if (!d->name.empty()) {
1587 auto tmp_params = MakeParameters(d->name);
1588 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1589 } else {
1590 auto tmp_params = MakeParameters(d->town->index, d->town_cn + 1);
1591 GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), tmp_params);
1592 }
1593 break;
1594 }
1595
1596 case SCC_ENGINE_NAME: { // {ENGINE}
1597 int64_t arg = args.GetNextParameter<int64_t>();
1598 const Engine *e = Engine::GetIfValid(static_cast<EngineID>(arg));
1599 if (e == nullptr) break;
1600
1601 if (!e->name.empty() && e->IsEnabled()) {
1602 auto tmp_params = MakeParameters(e->name);
1603 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1604 break;
1605 }
1606
1607 if (e->info.callback_mask.Test(VehicleCallbackMask::Name)) {
1608 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_NAME, static_cast<uint32_t>(arg >> 32), 0, e->index, nullptr);
1609 /* Not calling ErrorUnknownCallbackResult due to being inside string processing. */
1610 if (callback != CALLBACK_FAILED && callback < 0x400) {
1611 const GRFFile *grffile = e->GetGRF();
1612 assert(grffile != nullptr);
1613
1614 builder += GetGRFStringWithTextStack(grffile, GRFSTR_MISC_GRF_TEXT + callback, 6);
1615 break;
1616 }
1617 }
1618
1619 GetStringWithArgs(builder, e->info.string_id, {});
1620 break;
1621 }
1622
1623 case SCC_GROUP_NAME: { // {GROUP}
1624 const Group *g = Group::GetIfValid(args.GetNextParameter<GroupID>());
1625 if (g == nullptr) break;
1626
1627 if (!g->name.empty()) {
1628 auto tmp_params = MakeParameters(g->name);
1629 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1630 } else {
1631 auto tmp_params = MakeParameters(g->number);
1632 GetStringWithArgs(builder, STR_FORMAT_GROUP_NAME, tmp_params);
1633 }
1634 break;
1635 }
1636
1637 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1639 if (i == nullptr) break;
1640
1641 static bool use_cache = true;
1643 /* Gender is defined by the industry type.
1644 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1645 FormatString(builder, GetStringPtr(GetIndustrySpec(i->type)->name), {}, next_substr_case_index);
1646 } else if (use_cache) { // Use cached version if first call
1647 AutoRestoreBackup cache_backup(use_cache, false);
1648 builder += i->GetCachedName();
1649 } else {
1650 /* First print the town name and the industry type name. */
1651 auto tmp_params = MakeParameters(i->town->index, GetIndustrySpec(i->type)->name);
1652 FormatString(builder, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), tmp_params, next_substr_case_index);
1653 }
1654 next_substr_case_index = 0;
1655 break;
1656 }
1657
1658 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1660 if (c == nullptr) break;
1661
1662 if (!c->president_name.empty()) {
1663 auto tmp_params = MakeParameters(c->president_name);
1664 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1665 } else {
1666 auto tmp_params = MakeParameters(c->president_name_2);
1667 GetStringWithArgs(builder, c->president_name_1, tmp_params);
1668 }
1669 break;
1670 }
1671
1672 case SCC_STATION_NAME: { // {STATION}
1673 StationID sid = args.GetNextParameter<StationID>();
1674 const Station *st = Station::GetIfValid(sid);
1675
1676 if (st == nullptr) {
1677 /* The station doesn't exist anymore. The only place where we might
1678 * be "drawing" an invalid station is in the case of cargo that is
1679 * in transit. */
1680 GetStringWithArgs(builder, STR_UNKNOWN_STATION, {});
1681 break;
1682 }
1683
1684 static bool use_cache = true;
1685 if (use_cache) { // Use cached version if first call
1686 AutoRestoreBackup cache_backup(use_cache, false);
1687 builder += st->GetCachedName();
1688 } else if (!st->name.empty()) {
1689 auto tmp_params = MakeParameters(st->name);
1690 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1691 } else {
1692 StringID string_id = st->string_id;
1693 if (st->indtype != IT_INVALID) {
1694 /* Special case where the industry provides the name for the station */
1695 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1696
1697 /* Industry GRFs can change which might remove the station name and
1698 * thus cause very strange things. Here we check for that before we
1699 * actually set the station name. */
1700 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1701 string_id = indsp->station_name;
1702 }
1703 }
1704
1705 auto tmp_params = MakeParameters(STR_TOWN_NAME, st->town->index, st->index);
1706 GetStringWithArgs(builder, string_id, tmp_params);
1707 }
1708 break;
1709 }
1710
1711 case SCC_TOWN_NAME: { // {TOWN}
1712 const Town *t = Town::GetIfValid(args.GetNextParameter<TownID>());
1713 if (t == nullptr) break;
1714
1715 static bool use_cache = true;
1716 if (use_cache) { // Use cached version if first call
1717 AutoRestoreBackup cache_backup(use_cache, false);
1718 builder += t->GetCachedName();
1719 } else if (!t->name.empty()) {
1720 auto tmp_params = MakeParameters(t->name);
1721 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1722 } else {
1723 GetTownName(builder, t);
1724 }
1725 break;
1726 }
1727
1728 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1730 if (wp == nullptr) break;
1731
1732 if (!wp->name.empty()) {
1733 auto tmp_params = MakeParameters(wp->name);
1734 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1735 } else {
1736 auto tmp_params = MakeParameters(wp->town->index, wp->town_cn + 1);
1737 StringID string_id = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1738 if (wp->town_cn != 0) string_id++;
1739 GetStringWithArgs(builder, string_id, tmp_params);
1740 }
1741 break;
1742 }
1743
1744 case SCC_VEHICLE_NAME: { // {VEHICLE}
1746 if (v == nullptr) break;
1747
1748 if (!v->name.empty()) {
1749 auto tmp_params = MakeParameters(v->name);
1750 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1751 } else if (v->group_id != DEFAULT_GROUP) {
1752 /* The vehicle has no name, but is member of a group, so print group name */
1753 auto tmp_params = MakeParameters(v->group_id, v->unitnumber);
1754 GetStringWithArgs(builder, STR_FORMAT_GROUP_VEHICLE_NAME, tmp_params);
1755 } else {
1756 auto tmp_params = MakeParameters(v->unitnumber);
1757
1758 StringID string_id;
1759 switch (v->type) {
1760 default: string_id = STR_INVALID_VEHICLE; break;
1761 case VEH_TRAIN: string_id = STR_SV_TRAIN_NAME; break;
1762 case VEH_ROAD: string_id = STR_SV_ROAD_VEHICLE_NAME; break;
1763 case VEH_SHIP: string_id = STR_SV_SHIP_NAME; break;
1764 case VEH_AIRCRAFT: string_id = STR_SV_AIRCRAFT_NAME; break;
1765 }
1766
1767 GetStringWithArgs(builder, string_id, tmp_params);
1768 }
1769 break;
1770 }
1771
1772 case SCC_SIGN_NAME: { // {SIGN}
1773 const Sign *si = Sign::GetIfValid(args.GetNextParameter<SignID>());
1774 if (si == nullptr) break;
1775
1776 if (!si->name.empty()) {
1777 auto tmp_params = MakeParameters(si->name);
1778 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1779 } else {
1780 GetStringWithArgs(builder, STR_DEFAULT_SIGN_NAME, {});
1781 }
1782 break;
1783 }
1784
1785 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1786 StationGetSpecialString(builder, args.GetNextParameter<StationFacilities>());
1787 break;
1788 }
1789
1790 case SCC_COLOUR: { // {COLOUR}
1791 StringControlCode scc = (StringControlCode)(SCC_BLUE + args.GetNextParameter<Colours>());
1792 if (IsInsideMM(scc, SCC_BLUE, SCC_COLOUR)) builder.Utf8Encode(scc);
1793 break;
1794 }
1795
1796 default:
1797 builder.Utf8Encode(b);
1798 break;
1799 }
1800 } catch (std::out_of_range &e) {
1801 Debug(misc, 0, "FormatString: {}", e.what());
1802 builder += "(invalid parameter)";
1803 }
1804 }
1805}
1806
1807
1808static void StationGetSpecialString(StringBuilder &builder, StationFacilities x)
1809{
1810 if (x.Test(StationFacility::Train)) builder.Utf8Encode(SCC_TRAIN);
1811 if (x.Test(StationFacility::TruckStop)) builder.Utf8Encode(SCC_LORRY);
1812 if (x.Test(StationFacility::BusStop)) builder.Utf8Encode(SCC_BUS);
1813 if (x.Test(StationFacility::Dock)) builder.Utf8Encode(SCC_SHIP);
1814 if (x.Test(StationFacility::Airport)) builder.Utf8Encode(SCC_PLANE);
1815}
1816
1817static const char * const _silly_company_names[] = {
1818 "Bloggs Brothers",
1819 "Tiny Transport Ltd.",
1820 "Express Travel",
1821 "Comfy-Coach & Co.",
1822 "Crush & Bump Ltd.",
1823 "Broken & Late Ltd.",
1824 "Sam Speedy & Son",
1825 "Supersonic Travel",
1826 "Mike's Motors",
1827 "Lightning International",
1828 "Pannik & Loozit Ltd.",
1829 "Inter-City Transport",
1830 "Getout & Pushit Ltd."
1831};
1832
1833static const char * const _surname_list[] = {
1834 "Adams",
1835 "Allan",
1836 "Baker",
1837 "Bigwig",
1838 "Black",
1839 "Bloggs",
1840 "Brown",
1841 "Campbell",
1842 "Gordon",
1843 "Hamilton",
1844 "Hawthorn",
1845 "Higgins",
1846 "Green",
1847 "Gribble",
1848 "Jones",
1849 "McAlpine",
1850 "MacDonald",
1851 "McIntosh",
1852 "Muir",
1853 "Murphy",
1854 "Nelson",
1855 "O'Donnell",
1856 "Parker",
1857 "Phillips",
1858 "Pilkington",
1859 "Quigley",
1860 "Sharkey",
1861 "Thomson",
1862 "Watkins"
1863};
1864
1865static const char * const _silly_surname_list[] = {
1866 "Grumpy",
1867 "Dozy",
1868 "Speedy",
1869 "Nosey",
1870 "Dribble",
1871 "Mushroom",
1872 "Cabbage",
1873 "Sniffle",
1874 "Fishy",
1875 "Swindle",
1876 "Sneaky",
1877 "Nutkins"
1878};
1879
1880static const char _initial_name_letters[] = {
1881 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1882 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1883};
1884
1885static std::span<const char * const> GetSurnameOptions()
1886{
1887 if (_settings_game.game_creation.landscape == LandscapeType::Toyland) return _silly_surname_list;
1888 return _surname_list;
1889}
1890
1896static const char *GetSurname(uint32_t seed)
1897{
1898 auto surname_options = GetSurnameOptions();
1899 return surname_options[surname_options.size() * GB(seed, 16, 8) >> 8];
1900}
1901
1902static void GenAndCoName(StringBuilder &builder, uint32_t seed)
1903{
1904 builder += GetSurname(seed);
1905 builder += " & Co.";
1906}
1907
1908static void GenPresidentName(StringBuilder &builder, uint32_t seed)
1909{
1910 builder += _initial_name_letters[std::size(_initial_name_letters) * GB(seed, 0, 8) >> 8];
1911 builder += ". ";
1912
1913 /* The second initial is optional. */
1914 size_t index = (std::size(_initial_name_letters) + 35) * GB(seed, 8, 8) >> 8;
1915 if (index < std::size(_initial_name_letters)) {
1916 builder += _initial_name_letters[index];
1917 builder += ". ";
1918 }
1919
1920 builder += GetSurname(seed);
1921}
1922
1923static bool GetSpecialNameString(StringBuilder &builder, StringID string, StringParameters &args)
1924{
1925 switch (string) {
1926 case SPECSTR_SILLY_NAME: // Not used in new companies, but retained for old-loader savegames
1927 builder += _silly_company_names[std::min<size_t>(args.GetNextParameter<uint16_t>(), std::size(_silly_company_names) - 1)];
1928 return true;
1929
1930 case SPECSTR_ANDCO_NAME: // used for Foobar & Co company names
1931 GenAndCoName(builder, args.GetNextParameter<uint32_t>());
1932 return true;
1933
1934 case SPECSTR_PRESIDENT_NAME: // President name
1935 GenPresidentName(builder, args.GetNextParameter<uint32_t>());
1936 return true;
1937 }
1938
1939 /* TownName Transport company names, with the appropriate town name. */
1940 if (IsInsideMM(string, SPECSTR_COMPANY_NAME_START, SPECSTR_COMPANY_NAME_END)) {
1941 GenerateTownNameString(builder, string - SPECSTR_COMPANY_NAME_START, args.GetNextParameter<uint32_t>());
1942 builder += " Transport";
1943 return true;
1944 }
1945
1946 return false;
1947}
1948
1954{
1955 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1956 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1957 this->plural_form < LANGUAGE_MAX_PLURAL &&
1958 this->text_dir <= 1 &&
1959 this->newgrflangid < MAX_LANG &&
1960 this->num_genders < MAX_NUM_GENDERS &&
1961 this->num_cases < MAX_NUM_CASES &&
1962 StrValid(this->name) &&
1963 StrValid(this->own_name) &&
1964 StrValid(this->isocode) &&
1968}
1969
1974{
1975 /* "Less than 25% missing" is "sufficiently finished". */
1976 return 4 * this->missing < LANGUAGE_TOTAL_STRINGS;
1977}
1978
1985{
1986 /* Current language pack */
1987 size_t len = 0;
1988 std::unique_ptr<LanguagePack, LanguagePackDeleter> lang_pack(reinterpret_cast<LanguagePack *>(ReadFileToMem(FS2OTTD(lang->file), len, 1U << 20).release()));
1989 if (!lang_pack) return false;
1990
1991 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1992 const char *end = (char *)lang_pack.get() + len + 1;
1993
1994 /* We need at least one byte of lang_pack->data */
1995 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1996 return false;
1997 }
1998
1999 std::array<uint, TEXT_TAB_END> tab_start, tab_num;
2000
2001 uint count = 0;
2002 for (uint i = 0; i < TEXT_TAB_END; i++) {
2003 uint16_t num = FROM_LE16(lang_pack->offsets[i]);
2004 if (num > TAB_SIZE) return false;
2005
2006 tab_start[i] = count;
2007 tab_num[i] = num;
2008 count += num;
2009 }
2010
2011 /* Allocate offsets */
2012 std::vector<char *> offs(count);
2013
2014 /* Fill offsets */
2015 char *s = lang_pack->data;
2016 len = (uint8_t)*s++;
2017 for (uint i = 0; i < count; i++) {
2018 if (s + len >= end) return false;
2019
2020 if (len >= 0xC0) {
2021 len = ((len & 0x3F) << 8) + (uint8_t)*s++;
2022 if (s + len >= end) return false;
2023 }
2024 offs[i] = s;
2025 s += len;
2026 len = (uint8_t)*s;
2027 *s++ = '\0'; // zero terminate the string
2028 }
2029
2030 _langpack.langpack = std::move(lang_pack);
2031 _langpack.offsets = std::move(offs);
2032 _langpack.langtab_num = tab_num;
2033 _langpack.langtab_start = tab_start;
2034
2035 _current_language = lang;
2039 _langpack.list_separator = GetString(STR_LIST_SEPARATOR);
2040
2041#ifdef _WIN32
2042 extern void Win32SetCurrentLocaleName(std::string iso_code);
2043 Win32SetCurrentLocaleName(_current_language->isocode);
2044#endif
2045
2046#ifdef WITH_COCOA
2047 extern void MacOSSetCurrentLocaleName(const char *iso_code);
2049#endif
2050
2051#ifdef WITH_ICU_I18N
2052 /* Create a collator instance for our current locale. */
2053 UErrorCode status = U_ZERO_ERROR;
2054 _current_collator.reset(icu::Collator::createInstance(icu::Locale(_current_language->isocode), status));
2055 /* Sort number substrings by their numerical value. */
2056 if (_current_collator) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
2057 /* Avoid using the collator if it is not correctly set. */
2058 if (U_FAILURE(status)) {
2059 _current_collator.reset();
2060 }
2061#endif /* WITH_ICU_I18N */
2062
2064
2065 /* Some lists need to be sorted again after a language change. */
2071 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
2072 InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
2073 InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
2074 InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
2075 InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
2076 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
2077 InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
2078
2079 return true;
2080}
2081
2082/* Win32 implementation in win32.cpp.
2083 * OS X implementation in os/macosx/macos.mm. */
2084#if !(defined(_WIN32) || defined(__APPLE__))
2093const char *GetCurrentLocale(const char *param)
2094{
2095 const char *env;
2096
2097 env = std::getenv("LANGUAGE");
2098 if (env != nullptr) return env;
2099
2100 env = std::getenv("LC_ALL");
2101 if (env != nullptr) return env;
2102
2103 if (param != nullptr) {
2104 env = std::getenv(param);
2105 if (env != nullptr) return env;
2106 }
2107
2108 return std::getenv("LANG");
2109}
2110#else
2111const char *GetCurrentLocale(const char *param);
2112#endif /* !(defined(_WIN32) || defined(__APPLE__)) */
2113
2119const LanguageMetadata *GetLanguage(uint8_t newgrflangid)
2120{
2121 for (const LanguageMetadata &lang : _languages) {
2122 if (newgrflangid == lang.newgrflangid) return &lang;
2123 }
2124
2125 return nullptr;
2126}
2127
2134static bool GetLanguageFileHeader(const std::string &file, LanguagePackHeader *hdr)
2135{
2136 auto f = FileHandle::Open(file, "rb");
2137 if (!f.has_value()) return false;
2138
2139 size_t read = fread(hdr, sizeof(*hdr), 1, *f);
2140
2141 bool ret = read == 1 && hdr->IsValid();
2142
2143 /* Convert endianness for the windows language ID */
2144 if (ret) {
2145 hdr->missing = FROM_LE16(hdr->missing);
2146 hdr->winlangid = FROM_LE16(hdr->winlangid);
2147 }
2148 return ret;
2149}
2150
2155static void FillLanguageList(const std::string &path)
2156{
2157 std::error_code error_code;
2158 for (const auto &dir_entry : std::filesystem::directory_iterator(OTTD2FS(path), error_code)) {
2159 if (!dir_entry.is_regular_file()) continue;
2160 if (dir_entry.path().extension() != ".lng") continue;
2161
2162 LanguageMetadata lmd;
2163 lmd.file = dir_entry.path();
2164
2165 /* Check whether the file is of the correct version */
2166 if (!GetLanguageFileHeader(FS2OTTD(lmd.file), &lmd)) {
2167 Debug(misc, 3, "{} is not a valid language file", FS2OTTD(lmd.file));
2168 } else if (GetLanguage(lmd.newgrflangid) != nullptr) {
2169 Debug(misc, 3, "{}'s language ID is already known", FS2OTTD(lmd.file));
2170 } else {
2171 _languages.push_back(lmd);
2172 }
2173 }
2174 if (error_code) {
2175 Debug(misc, 9, "Unable to open directory {}: {}", path, error_code.message());
2176 }
2177}
2178
2184{
2185 for (Searchpath sp : _valid_searchpaths) {
2186 FillLanguageList(FioGetDirectory(sp, LANG_DIR));
2187 }
2188 if (_languages.empty()) UserError("No available language packs (invalid versions?)");
2189
2190 /* Acquire the locale of the current system */
2191 const char *lang = GetCurrentLocale("LC_MESSAGES");
2192 if (lang == nullptr) lang = "en_GB";
2193
2194 const LanguageMetadata *chosen_language = nullptr;
2195 const LanguageMetadata *language_fallback = nullptr;
2196 const LanguageMetadata *en_GB_fallback = _languages.data();
2197
2198 /* Find a proper language. */
2199 for (const LanguageMetadata &lng : _languages) {
2200 /* We are trying to find a default language. The priority is by
2201 * configuration file, local environment and last, if nothing found,
2202 * English. */
2203 if (_config_language_file == FS2OTTD(lng.file.filename())) {
2204 chosen_language = &lng;
2205 break;
2206 }
2207
2208 if (strcmp (lng.isocode, "en_GB") == 0) en_GB_fallback = &lng;
2209
2210 /* Only auto-pick finished translations */
2211 if (!lng.IsReasonablyFinished()) continue;
2212
2213 if (strncmp(lng.isocode, lang, 5) == 0) chosen_language = &lng;
2214 if (strncmp(lng.isocode, lang, 2) == 0) language_fallback = &lng;
2215 }
2216
2217 /* We haven't found the language in the config nor the one in the locale.
2218 * Now we set it to one of the fallback languages */
2219 if (chosen_language == nullptr) {
2220 chosen_language = (language_fallback != nullptr) ? language_fallback : en_GB_fallback;
2221 }
2222
2223 if (!ReadLanguagePack(chosen_language)) UserError("Can't read language pack '{}'", FS2OTTD(chosen_language->file));
2224}
2225
2231{
2232 return _langpack.langpack->isocode;
2233}
2234
2240{
2241 InitFontCache(this->Monospace());
2242
2243 this->Reset();
2244 for (auto text = this->NextString(); text.has_value(); text = this->NextString()) {
2245 auto src = text->cbegin();
2246
2247 FontSize size = this->DefaultSize();
2248 FontCache *fc = FontCache::Get(size);
2249 while (src != text->cend()) {
2250 char32_t c = Utf8Consume(src);
2251
2252 if (c >= SCC_FIRST_FONT && c <= SCC_LAST_FONT) {
2253 size = (FontSize)(c - SCC_FIRST_FONT);
2254 fc = FontCache::Get(size);
2255 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && fc->MapCharToGlyph(c, false) == 0) {
2256 /* The character is printable, but not in the normal font. This is the case we were testing for. */
2257 std::string size_name;
2258
2259 switch (size) {
2260 case FS_NORMAL: size_name = "medium"; break;
2261 case FS_SMALL: size_name = "small"; break;
2262 case FS_LARGE: size_name = "large"; break;
2263 case FS_MONO: size_name = "mono"; break;
2264 default: NOT_REACHED();
2265 }
2266
2267 Debug(fontcache, 0, "Font is missing glyphs to display char 0x{:X} in {} font size", (int)c, size_name);
2268 return true;
2269 }
2270 }
2271 }
2272 return false;
2273}
2274
2277 uint i;
2278 uint j;
2279
2280 void Reset() override
2281 {
2282 this->i = 0;
2283 this->j = 0;
2284 }
2285
2287 {
2288 return FS_NORMAL;
2289 }
2290
2291 std::optional<std::string_view> NextString() override
2292 {
2293 if (this->i >= TEXT_TAB_END) return std::nullopt;
2294
2295 const char *ret = _langpack.offsets[_langpack.langtab_start[this->i] + this->j];
2296
2297 this->j++;
2298 while (this->i < TEXT_TAB_END && this->j >= _langpack.langtab_num[this->i]) {
2299 this->i++;
2300 this->j = 0;
2301 }
2302
2303 return ret;
2304 }
2305
2306 bool Monospace() override
2307 {
2308 return false;
2309 }
2310
2311 void SetFontNames([[maybe_unused]] FontCacheSettings *settings, [[maybe_unused]] const char *font_name, [[maybe_unused]] const void *os_data) override
2312 {
2313#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2314 settings->small.font = font_name;
2315 settings->medium.font = font_name;
2316 settings->large.font = font_name;
2317
2318 settings->small.os_handle = os_data;
2319 settings->medium.os_handle = os_data;
2320 settings->large.os_handle = os_data;
2321#endif
2322 }
2323};
2324
2338void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2339{
2340 static LanguagePackGlyphSearcher pack_searcher;
2341 if (searcher == nullptr) searcher = &pack_searcher;
2342 bool bad_font = !base_font || searcher->FindMissingGlyphs();
2343#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2344 if (bad_font) {
2345 /* We found an unprintable character... lets try whether we can find
2346 * a fallback font that can print the characters in the current language. */
2347 bool any_font_configured = !_fcsettings.medium.font.empty();
2348 FontCacheSettings backup = _fcsettings;
2349
2350 _fcsettings.mono.os_handle = nullptr;
2351 _fcsettings.medium.os_handle = nullptr;
2352
2353 bad_font = !SetFallbackFont(&_fcsettings, _langpack.langpack->isocode, searcher);
2354
2355 _fcsettings = backup;
2356
2357 if (!bad_font && any_font_configured) {
2358 /* If the user configured a bad font, and we found a better one,
2359 * show that we loaded the better font instead of the configured one.
2360 * The colour 'character' might change in the
2361 * future, so for safety we just Utf8 Encode it into the string,
2362 * which takes exactly three characters, so it replaces the "XXX"
2363 * with the colour marker. */
2364 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.");
2365 Utf8Encode(err_str.data(), SCC_YELLOW);
2366 ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, err_str), {}, WL_WARNING);
2367 }
2368
2369 if (bad_font && base_font) {
2370 /* Our fallback font does miss characters too, so keep the
2371 * user chosen font as that is more likely to be any good than
2372 * the wild guess we made */
2373 InitFontCache(searcher->Monospace());
2374 }
2375 }
2376#endif
2377
2378 if (bad_font) {
2379 /* All attempts have failed. Display an error. As we do not want the string to be translated by
2380 * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
2381 * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
2382 * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
2383 * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
2384 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.");
2385 Utf8Encode(err_str.data(), SCC_YELLOW);
2386 ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, err_str), {}, WL_WARNING);
2387
2388 /* Reset the font width */
2389 LoadStringWidthTable(searcher->Monospace());
2390 return;
2391 }
2392
2393 /* Update the font with cache */
2394 LoadStringWidthTable(searcher->Monospace());
2395
2396#if !(defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
2397 /*
2398 * For right-to-left languages we need the ICU library. If
2399 * we do not have support for that library we warn the user
2400 * about it with a message. As we do not want the string to
2401 * be translated by the translators, we 'force' it into the
2402 * binary and 'load' it via a BindCString. To do this
2403 * properly we have to set the colour of the string,
2404 * otherwise we end up with a lot of artifacts. The colour
2405 * 'character' might change in the future, so for safety
2406 * we just Utf8 Encode it into the string, which takes
2407 * exactly three characters, so it replaces the "XXX" with
2408 * the colour marker.
2409 */
2410 if (_current_text_dir != TD_LTR) {
2411 static std::string err_str("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with ICU + Harfbuzz enabled.");
2412 Utf8Encode(err_str.data(), SCC_YELLOW);
2413 ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, err_str), {}, WL_ERROR);
2414 }
2415#endif /* !(WITH_ICU_I18N && WITH_HARFBUZZ) && !WITH_UNISCRIBE && !WITH_COCOA */
2416}
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:23
bool IsValidCargoType(CargoType t)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:106
std::vector< const CargoSpec * > _sorted_cargo_specs
Cargo specifications sorted alphabetically by name.
void InitializeSortedCargoSpecs()
Initialize the list of sorted cargo specifications.
Container for an encoded string, created by GetEncodedString.
friend EncodedString GetEncodedStringWithArgs(StringID str, std::span< const StringParameter > params)
Encode a string with its parameters into an encoded string.
Definition strings.cpp:102
std::string GetDecodedString() const
Decode the encoded string.
Definition strings.cpp:219
EncodedString ReplaceParam(size_t param, StringParameter &&value) const
Replace a parameter of this EncodedString.
Definition strings.cpp:153
static std::optional< FileHandle > Open(const std::string &filename, const std::string &mode)
Open an RAII file handle if possible.
Definition fileio.cpp:1170
Font cache for basic fonts.
Definition fontcache.h:21
virtual GlyphID MapCharToGlyph(char32_t key, bool fallback=true)=0
Map a character into a glyph.
static FontCache * Get(FontSize fs)
Get the font cache of a given font size.
Definition fontcache.h:129
Helper for searching through the language pack.
Definition strings.cpp:2276
bool Monospace() override
Whether to search for a monospace font or not.
Definition strings.cpp:2306
uint j
Iterator for the secondary language tables.
Definition strings.cpp:2278
std::optional< std::string_view > NextString() override
Get the next string to search through.
Definition strings.cpp:2291
FontSize DefaultSize() override
Get the default (font) size of the string.
Definition strings.cpp:2286
void SetFontNames(FontCacheSettings *settings, const char *font_name, const void *os_data) override
Set the right font names.
Definition strings.cpp:2311
uint i
Iterator for the primary language tables.
Definition strings.cpp:2277
void Reset() override
Reset the search, i.e.
Definition strings.cpp:2280
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:2239
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.
const StringParameter & GetNextParameterReference()
Get the next parameter from our parameters.
Definition strings.cpp:68
void AdvanceOffset(size_t advance)
Advance the offset within the string from where to return the next result of GetInt64 or GetInt32.
uint64_t GetNextParameter()
Get the next parameter from our parameters.
size_t offset
Current offset in the parameters span.
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.
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_ENCODED
Encoded string marker and sub-string parameter.
@ SCC_ENCODED_NUMERIC
Encoded numeric parameter.
@ SCC_ENCODED_STRING
Encoded string parameter.
@ 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
@ SCC_ENCODED_INTERNAL
Encoded text from OpenTTD.
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,...)
Output a line of debugging information.
Definition debug.h:37
Base for all depots (except hangars)
Function to handling different endian machines.
Base class for engines.
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(EncodedString &&summary_msg, int x, int y, const 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:1025
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.
std::pair< uint8_t, uint8_t > GetBroadestDigit(FontSize size)
Determine the broadest digits for guessing the maximum width of a n-digit number.
Definition gfx.cpp:1257
void LoadStringWidthTable(bool monospace)
Initialize _stringwidth_table cache.
Definition gfx.cpp:1211
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
static constexpr GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition group_type.h:18
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.
const char * GetGRFStringPtr(StringIndexInTab stringid)
Get a C-string from a stringid set by a newgrf.
static void RemapNewGRFStringControlCode(char32_t scc, const char **str, TextRefStack &stack, std::vector< StringParameter > &params)
Process NewGRF string control code instructions.
void SetCurrentGrfLangID(uint8_t language_id)
Equivalence Setter function between game and newgrf langID.
std::string GetGRFStringWithTextStack(const struct GRFFile *grffile, GRFStringID grfstringid, uint8_t num_entries)
Format a GRF string using the text ref stack for parameters.
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:58
Base class for signs.
void BuildIndustriesLegend()
Fills an array for the industries legends.
Smallmap GUI functions.
Base classes/functions for stations.
@ Dock
Station with a dock.
@ TruckStop
Station with truck stops.
@ Train
Station with train station.
@ Airport
Station with an airport.
@ BusStop
Station with bus stops.
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:245
size_t Utf8Decode(char32_t *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Definition string.cpp:437
size_t Utf8Encode(T buf, char32_t c)
Encode a unicode character and place it in the buffer.
Definition string.cpp:478
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
uint64_t GetParamMaxValue(uint64_t max_value, uint min_count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:248
LanguageList _languages
The actual list of language meta data.
Definition strings.cpp:53
EncodedString GetEncodedStringWithArgs(StringID str, std::span< const StringParameter > params)
Encode a string with its parameters into an encoded string.
Definition strings.cpp:102
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:338
std::string_view GetListSeparator()
Get the list separator string for the current language.
Definition strings.cpp:310
static const Units _units_height[]
Unit conversions for height.
Definition strings.cpp:902
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:539
const char * GetCurrentLanguageIsoCode()
Get the ISO language code of the currently loaded language.
Definition strings.cpp:2230
void AppendStringInPlace(std::string &result, StringID string)
Resolve the given StringID and append in place into an existing std::string with formatting but no pa...
Definition strings.cpp:436
uint ConvertSpeedToDisplaySpeed(uint speed, VehicleType type)
Convert the given (internal) speed to the display speed.
Definition strings.cpp:954
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:2338
std::string _config_language_file
The file (name) stored in the configuration.
Definition strings.cpp:52
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:937
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
uint ConvertDisplaySpeedToKmhishSpeed(uint speed, VehicleType type)
Convert the given display speed to the km/h-ish speed.
Definition strings.cpp:987
static const Units _units_power[]
Unit conversions for power.
Definition strings.cpp:861
static void FormatNumber(StringBuilder &builder, int64_t number, const char *separator)
Format a number into a string.
Definition strings.cpp:484
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:426
static bool _scan_for_gender_data
Are we scanning for the gender of the current string? (instead of formatting it)
Definition strings.cpp:304
static const UnitsLong _units_volume[]
Unit conversions for volume.
Definition strings.cpp:888
static const UnitsLong _units_weight[]
Unit conversions for weight.
Definition strings.cpp:881
static const Units _units_velocity_calendar[]
Unit conversions for velocity.
Definition strings.cpp:843
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:1078
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:2155
uint ConvertKmhishSpeedToDisplaySpeed(uint speed, VehicleType type)
Convert the given km/h-ish speed to the display speed.
Definition strings.cpp:977
static const Units _units_force[]
Unit conversions for force.
Definition strings.cpp:895
void InitializeLanguagePacks()
Make a list of the available language packs.
Definition strings.cpp:2183
const LanguageMetadata * GetLanguage(uint8_t newgrflangid)
Get the language with the given NewGRF language ID.
Definition strings.cpp:2119
static const char * GetSurname(uint32_t seed)
Get the surname of the president with the given seed.
Definition strings.cpp:1896
static const Units _units_time_days_or_seconds[]
Unit conversions for time in calendar days or wallclock seconds.
Definition strings.cpp:909
uint ConvertDisplaySpeedToSpeed(uint speed, VehicleType type)
Convert the given display speed to the (internal) speed.
Definition strings.cpp:967
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:852
bool ReadLanguagePack(const LanguageMetadata *lang)
Read a particular language.
Definition strings.cpp:1984
static bool GetLanguageFileHeader(const std::string &file, LanguagePackHeader *hdr)
Reads the language file header and checks compatibility.
Definition strings.cpp:2134
static const Units _units_time_months_or_minutes[]
Unit conversions for time in calendar months or wallclock minutes.
Definition strings.cpp:915
static const char * DecodeEncodedString(const char *str, bool game_script, StringBuilder &builder)
Decodes an encoded string during FormatString.
Definition strings.cpp:999
static const Units _units_time_years_or_minutes[]
Unit conversions for time in calendar years or wallclock minutes.
Definition strings.cpp:927
static const Units _units_power_to_weight[]
Unit conversions for power to weight.
Definition strings.cpp:868
const char * GetCurrentLocale(const char *param)
Determine the current charset based on the environment First check some default values,...
Definition strings.cpp:2093
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:230
static const Units _units_time_years_or_periods[]
Unit conversions for time in calendar years or economic periods.
Definition strings.cpp:921
static int DeterminePluralForm(int64_t count, int plural_form)
Determine the "plural" index given a plural form and a number.
Definition strings.cpp:658
Functions related to OTTD's strings.
StringTab GetStringTab(StringID str)
Extract the StringTab from a StringID.
auto MakeParameters(Args &&... args)
Helper to create the StringParameters with its own buffer with the given parameter values.
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 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:93
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
StringID quantifier
Text for multiple units of cargo of this type.
Definition cargotype.h:94
static size_t GetArraySize()
Total number of cargospecs, both valid and invalid.
Definition cargotype.h:127
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(auto 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:111
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:63
IndustryType type
type of industry.
Definition industry.h:99
Town * town
Nearest town.
Definition industry.h:92
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:1953
bool IsReasonablyFinished() const
Check whether a translation is sufficiently finished to offer it to the public.
Definition strings.cpp:1973
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:299
std::array< uint, TEXT_TAB_END > langtab_num
Offset into langpack offs.
Definition strings.cpp:296
std::array< uint, TEXT_TAB_END > langtab_start
Offset into langpack offs.
Definition strings.cpp:297
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
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
static Titem * GetIfValid(auto index)
Returns Titem with given index.
static Station * GetIfValid(auto 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:796
double factor
Amount to multiply or divide upon conversion.
Definition strings.cpp:797
int64_t ToDisplay(int64_t input, bool round=true) const
Convert value from OpenTTD's internal unit into the displayed value.
Definition strings.cpp:805
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:819
Information about a specific unit system with a long variant.
Definition strings.cpp:835
StringID s
String for the short variant of the unit.
Definition strings.cpp:837
StringID l
String for the long variant of the unit.
Definition strings.cpp:838
unsigned int decimal_places
Number of decimal places embedded in the value. For example, 1 if the value is in tenths,...
Definition strings.cpp:839
UnitConversion c
Conversion.
Definition strings.cpp:836
Information about a specific unit system.
Definition strings.cpp:828
StringID s
String for the unit.
Definition strings.cpp:830
unsigned int decimal_places
Number of decimal places embedded in the value. For example, 1 if the value is in tenths,...
Definition strings.cpp:831
UnitConversion c
Conversion.
Definition strings.cpp:829
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.
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:3241
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: