OpenTTD Source 15.0-beta2
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 "core/utf8.hpp"
42#include <stack>
43#include <charconv>
44
45#include "table/strings.h"
46#include "table/control_codes.h"
47#include "3rdparty/fmt/std.h"
48
49#include "strings_internal.h"
50
51#include "safeguards.h"
52
56
58
59#ifdef WITH_ICU_I18N
60std::unique_ptr<icu::Collator> _current_collator;
61#endif /* WITH_ICU_I18N */
62
70{
71 assert(this->next_type == 0 || (SCC_CONTROL_START <= this->next_type && this->next_type <= SCC_CONTROL_END));
72 if (this->offset >= this->parameters.size()) {
73 throw std::out_of_range("Trying to read invalid string parameter");
74 }
75
76 auto &param = this->parameters[this->offset++];
77 if (param.type != 0 && param.type != this->next_type) {
78 this->next_type = 0;
79 throw std::out_of_range("Trying to read string parameter with wrong type");
80 }
81 param.type = this->next_type;
82 this->next_type = 0;
83 return param;
84}
85
95
103EncodedString GetEncodedStringWithArgs(StringID str, std::span<const StringParameter> params)
104{
105 std::string result;
106 StringBuilder builder(result);
108 builder.PutIntegerBase(str, 16);
109
110 struct visitor {
111 StringBuilder &builder;
112
113 void operator()(const std::monostate &) {}
114
115 void operator()(const uint64_t &arg)
117 this->builder.PutUtf8(SCC_ENCODED_NUMERIC);
118 this->builder.PutIntegerBase(arg, 16);
119 }
120
121 void operator()(const std::string &value)
122 {
123#ifdef WITH_ASSERT
124 /* Don't allow an encoded string to contain another encoded string. */
125 {
126 auto [len, c] = DecodeUtf8(value);
127 assert(len == 0 || (c != SCC_ENCODED && c != SCC_ENCODED_INTERNAL && c != SCC_RECORD_SEPARATOR));
128 }
129#endif /* WITH_ASSERT */
130 this->builder.PutUtf8(SCC_ENCODED_STRING);
131 this->builder += value;
132 }
133 };
134
135 visitor v{builder};
136 for (const auto &param : params) {
137 builder.PutUtf8(SCC_RECORD_SEPARATOR);
138 std::visit(v, param.data);
139 }
140
141 return EncodedString{std::move(result)};
142}
143
152{
153 if (this->empty()) return {};
154
155 std::vector<StringParameter> params;
156
157 /* We need char * for std::from_chars. Iterate the underlying data, as string's own iterators may interfere. */
158 const char *p = this->string.data();
159 const char *e = this->string.data() + this->string.length();
160
161 char32_t c = Utf8Consume(p);
162 if (c != SCC_ENCODED_INTERNAL) return {};
163
164 StringID str;
165 auto result = std::from_chars(p, e, str, 16);
166 if (result.ec != std::errc()) return {};
167 if (result.ptr != e && *result.ptr != SCC_RECORD_SEPARATOR) return {};
168 p = result.ptr;
169
170 while (p != e) {
171 auto s = ++p;
172
173 /* Find end of the parameter. */
174 for (; p != e && *p != SCC_RECORD_SEPARATOR; ++p) {}
175
176 if (s == p) {
177 /* This is an empty parameter. */
178 params.emplace_back(std::monostate{});
179 continue;
180 }
181
182 /* Get the parameter type. */
183 char32_t parameter_type;
184 size_t len = Utf8Decode(&parameter_type, s);
185 s += len;
186
187 switch (parameter_type) {
188 case SCC_ENCODED_NUMERIC: {
189 uint64_t value;
190 result = std::from_chars(s, p, value, 16);
191 if (result.ec != std::errc() || result.ptr != p) return {};
192 params.emplace_back(value);
193 break;
194 }
195
196 case SCC_ENCODED_STRING: {
197 params.emplace_back(std::string(s, p));
198 break;
199 }
200
201 default:
202 /* Unknown parameter, make it blank. */
203 params.emplace_back(std::monostate{});
204 break;
205 }
206 }
207
208 if (param >= std::size(params)) return {};
209 params[param] = data;
210 return GetEncodedStringWithArgs(str, params);
211}
212
218{
219 return GetString(STR_JUST_RAW_STRING, this->string);
220}
221
228uint64_t GetParamMaxDigits(uint count, FontSize size)
229{
230 auto [front, next] = GetBroadestDigit(size);
231 uint64_t val = count > 1 ? front : next;
232 for (; count > 1; count--) {
233 val = 10 * val + next;
234 }
235 return val;
236}
237
246uint64_t GetParamMaxValue(uint64_t max_value, uint min_count, FontSize size)
247{
248 uint num_digits = 1;
249 while (max_value >= 10) {
250 num_digits++;
251 max_value /= 10;
252 }
253 return GetParamMaxDigits(std::max(min_count, num_digits), size);
254}
255
256static void StationGetSpecialString(StringBuilder &builder, StationFacilities x);
257static bool GetSpecialNameString(StringBuilder &builder, StringID string, StringParameters &args);
258
259static void FormatString(StringBuilder &builder, std::string_view str, StringParameters &args, uint case_index = 0, bool game_script = false, bool dry_run = false);
260
271static void FormatString(StringBuilder &builder, std::string_view str, std::span<StringParameter> params, uint case_index = 0, bool game_script = false, bool dry_run = false)
272{
273 StringParameters tmp_params{params};
274 FormatString(builder, str, tmp_params, case_index, game_script, dry_run);
275}
276
278 char data[]; // list of strings
279};
280
282 void operator()(LanguagePack *langpack)
283 {
284 /* LanguagePack is in fact reinterpreted char[], we need to reinterpret it back to free it properly. */
285 delete[] reinterpret_cast<char*>(langpack);
286 }
287};
288
290 std::unique_ptr<LanguagePack, LanguagePackDeleter> langpack;
291
292 std::vector<std::string_view> strings;
293
294 std::array<uint, TEXT_TAB_END> langtab_num;
295 std::array<uint, TEXT_TAB_END> langtab_start;
296
297 std::string list_separator;
298};
299
300static LoadedLanguagePack _langpack;
301
302static bool _scan_for_gender_data = false;
303
308std::string_view GetListSeparator()
309{
310 return _langpack.list_separator;
311}
312
313std::string_view GetStringPtr(StringID string)
314{
315 switch (GetStringTab(string)) {
317 /* 0xD0xx and 0xD4xx IDs have been converted earlier. */
318 case TEXT_TAB_OLD_NEWGRF: NOT_REACHED();
320 default: {
321 const size_t offset = _langpack.langtab_start[GetStringTab(string)] + GetStringIndex(string).base();
322 if (offset < _langpack.strings.size()) return _langpack.strings[offset];
323 return "(undefined string)";
324 }
325 }
326}
327
336void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters &args, uint case_index, bool game_script)
337{
338 if (string == 0) {
339 GetStringWithArgs(builder, STR_UNDEFINED, args);
340 return;
341 }
342
343 StringIndexInTab index = GetStringIndex(string);
344 StringTab tab = GetStringTab(string);
345
346 switch (tab) {
347 case TEXT_TAB_TOWN:
348 if (IsInsideMM(string, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_END) && !game_script) {
349 try {
350 GenerateTownNameString(builder, string - SPECSTR_TOWNNAME_START, args.GetNextParameter<uint32_t>());
351 } catch (const std::runtime_error &e) {
352 Debug(misc, 0, "GetStringWithArgs: {}", e.what());
353 builder += "(invalid string parameter)";
354 }
355 return;
356 }
357 break;
358
359 case TEXT_TAB_SPECIAL:
360 if (!game_script) {
361 try {
362 if (GetSpecialNameString(builder, string, args)) return;
363 } catch (const std::runtime_error &e) {
364 Debug(misc, 0, "GetStringWithArgs: {}", e.what());
365 builder += "(invalid string parameter)";
366 return;
367 }
368 }
369 break;
370
371 case TEXT_TAB_OLD_CUSTOM:
372 /* Old table for custom names. This is no longer used */
373 if (!game_script) {
374 FatalError("Incorrect conversion of custom name string.");
375 }
376 break;
377
379 FormatString(builder, GetGameStringPtr(index), args, case_index, true);
380 return;
381 }
382
383 case TEXT_TAB_OLD_NEWGRF:
384 NOT_REACHED();
385
387 FormatString(builder, GetGRFStringPtr(index), args, case_index);
388 return;
389 }
390
391 default:
392 break;
393 }
394
395 if (index >= _langpack.langtab_num[tab]) {
396 if (game_script) {
397 return GetStringWithArgs(builder, STR_UNDEFINED, args);
398 }
399 FatalError("String 0x{:X} is invalid. You are probably using an old version of the .lng file.\n", string);
400 }
401
402 FormatString(builder, GetStringPtr(string), args, case_index);
403}
404
413void GetStringWithArgs(StringBuilder &builder, StringID string, std::span<StringParameter> params, uint case_index, bool game_script)
414{
415 StringParameters tmp_params{params};
416 GetStringWithArgs(builder, string, tmp_params, case_index, game_script);
417}
418
424std::string GetString(StringID string)
425{
426 return GetStringWithArgs(string, {});
427}
428
434void AppendStringInPlace(std::string &result, StringID string)
435{
436 StringBuilder builder(result);
437 GetStringWithArgs(builder, string, {});
438}
439
440void AppendStringWithArgsInPlace(std::string &result, StringID string, std::span<StringParameter> params)
441{
442 StringParameters tmp_params{params};
443 StringBuilder builder(result);
444 GetStringWithArgs(builder, string, tmp_params);
445}
446
454{
455 std::string result;
456 StringBuilder builder(result);
457 GetStringWithArgs(builder, string, args);
458 return result;
459}
460
461std::string GetStringWithArgs(StringID string, std::span<StringParameter> args)
462{
463 std::string result;
464 StringBuilder builder(result);
465 GetStringWithArgs(builder, string, args);
466 return result;
467}
468
469static const char *GetDecimalSeparator()
470{
471 const char *decimal_separator = _settings_game.locale.digit_decimal_separator.c_str();
472 if (StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator;
473 return decimal_separator;
474}
475
482static void FormatNumber(StringBuilder &builder, int64_t number, const char *separator)
483{
484 static const int max_digits = 20;
485 uint64_t divisor = 10000000000000000000ULL;
486 int thousands_offset = (max_digits - 1) % 3;
487
488 if (number < 0) {
489 builder.PutChar('-');
490 number = -number;
491 }
492
493 uint64_t num = number;
494 uint64_t tot = 0;
495 for (int i = 0; i < max_digits; i++) {
496 uint64_t quot = 0;
497 if (num >= divisor) {
498 quot = num / divisor;
499 num = num % divisor;
500 }
501 if ((tot |= quot) || i == max_digits - 1) {
502 builder.PutChar('0' + quot); // quot is a single digit
503 if ((i % 3) == thousands_offset && i < max_digits - 1) builder += separator;
504 }
505
506 divisor /= 10;
507 }
508}
509
510static void FormatCommaNumber(StringBuilder &builder, int64_t number)
511{
512 const char *separator = _settings_game.locale.digit_group_separator.c_str();
513 if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator;
514 FormatNumber(builder, number, separator);
515}
516
517static void FormatNoCommaNumber(StringBuilder &builder, int64_t number)
518{
519 fmt::format_to(builder.back_inserter(), "{}", number);
520}
521
522static void FormatZerofillNumber(StringBuilder &builder, int64_t number, int count)
523{
524 fmt::format_to(builder.back_inserter(), "{:0{}d}", number, count);
525}
526
527static void FormatHexNumber(StringBuilder &builder, uint64_t number)
528{
529 fmt::format_to(builder.back_inserter(), "0x{:X}", number);
530}
531
537static void FormatBytes(StringBuilder &builder, int64_t number)
538{
539 assert(number >= 0);
540
541 /* 1 2^10 2^20 2^30 2^40 2^50 2^60 */
542 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
543 uint id = 1;
544 while (number >= 1024 * 1024) {
545 number /= 1024;
546 id++;
547 }
548
549 if (number < 1024) {
550 id = 0;
551 fmt::format_to(builder.back_inserter(), "{}", number);
552 } else if (number < 1024 * 10) {
553 fmt::format_to(builder.back_inserter(), "{}{}{:02}", number / 1024, GetDecimalSeparator(), (number % 1024) * 100 / 1024);
554 } else if (number < 1024 * 100) {
555 fmt::format_to(builder.back_inserter(), "{}{}{:01}", number / 1024, GetDecimalSeparator(), (number % 1024) * 10 / 1024);
556 } else {
557 assert(number < 1024 * 1024);
558 fmt::format_to(builder.back_inserter(), "{}", number / 1024);
559 }
560
561 assert(id < lengthof(iec_prefixes));
562 fmt::format_to(builder.back_inserter(), NBSP "{}B", iec_prefixes[id]);
563}
564
565static void FormatYmdString(StringBuilder &builder, TimerGameCalendar::Date date, uint case_index)
566{
567 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(date);
568
569 auto tmp_params = MakeParameters(STR_DAY_NUMBER_1ST + ymd.day - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year);
570 FormatString(builder, GetStringPtr(STR_FORMAT_DATE_LONG), tmp_params, case_index);
571}
572
573static void FormatMonthAndYear(StringBuilder &builder, TimerGameCalendar::Date date, uint case_index)
574{
575 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(date);
576
577 auto tmp_params = MakeParameters(STR_MONTH_JAN + ymd.month, ymd.year);
578 FormatString(builder, GetStringPtr(STR_FORMAT_DATE_SHORT), tmp_params, case_index);
579}
580
581static void FormatTinyOrISODate(StringBuilder &builder, TimerGameCalendar::Date date, StringID str)
582{
583 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(date);
584
585 /* Day and month are zero-padded with ZEROFILL_NUM, hence the two 2s. */
586 auto tmp_params = MakeParameters(ymd.day, 2, ymd.month + 1, 2, ymd.year);
587 FormatString(builder, GetStringPtr(str), tmp_params);
588}
589
590static void FormatGenericCurrency(StringBuilder &builder, const CurrencySpec *spec, Money number, bool compact)
591{
592 /* We are going to make number absolute for printing, so
593 * keep this piece of data as we need it later on */
594 bool negative = number < 0;
595
596 number *= spec->rate;
597
598 /* convert from negative */
599 if (number < 0) {
600 builder.PutUtf8(SCC_PUSH_COLOUR);
601 builder.PutUtf8(SCC_RED);
602 builder.PutChar('-');
603 number = -number;
604 }
605
606 /* Add prefix part, following symbol_pos specification.
607 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
608 * The only remaining value is 1 (suffix), so everything that is not 1 */
609 if (spec->symbol_pos != 1) builder += spec->prefix;
610
611 StringID number_str = STR_NULL;
612
613 /* For huge numbers, compact the number. */
614 if (compact) {
615 /* Take care of the thousand rounding. Having 1 000 000 k
616 * and 1 000 M is inconsistent, so always use 1 000 M. */
617 if (number >= Money(1'000'000'000'000'000) - 500'000'000) {
618 number = (number + Money(500'000'000'000)) / Money(1'000'000'000'000);
619 number_str = STR_CURRENCY_SHORT_TERA;
620 } else if (number >= Money(1'000'000'000'000) - 500'000) {
621 number = (number + 500'000'000) / 1'000'000'000;
622 number_str = STR_CURRENCY_SHORT_GIGA;
623 } else if (number >= 1'000'000'000 - 500) {
624 number = (number + 500'000) / 1'000'000;
625 number_str = STR_CURRENCY_SHORT_MEGA;
626 } else if (number >= 1'000'000) {
627 number = (number + 500) / 1'000;
628 number_str = STR_CURRENCY_SHORT_KILO;
629 }
630 }
631
632 const char *separator = _settings_game.locale.digit_group_separator_currency.c_str();
633 if (StrEmpty(separator)) separator = GetCurrency().separator.c_str();
634 if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator_currency;
635 FormatNumber(builder, number, separator);
636 if (number_str != STR_NULL) {
637 FormatString(builder, GetStringPtr(number_str), {});
638 }
639
640 /* Add suffix part, following symbol_pos specification.
641 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
642 * The only remaining value is 1 (prefix), so everything that is not 0 */
643 if (spec->symbol_pos != 0) builder += spec->suffix;
644
645 if (negative) {
646 builder.PutUtf8(SCC_POP_COLOUR);
647 }
648}
649
656static int DeterminePluralForm(int64_t count, int plural_form)
657{
658 /* The absolute value determines plurality */
659 uint64_t n = abs(count);
660
661 switch (plural_form) {
662 default:
663 NOT_REACHED();
664
665 /* Two forms: singular used for one only.
666 * Used in:
667 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
668 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
669 case 0:
670 return n != 1 ? 1 : 0;
671
672 /* Only one form.
673 * Used in:
674 * Hungarian, Japanese, Turkish */
675 case 1:
676 return 0;
677
678 /* Two forms: singular used for 0 and 1.
679 * Used in:
680 * French, Brazilian Portuguese */
681 case 2:
682 return n > 1 ? 1 : 0;
683
684 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
685 * Note: Cases are out of order for hysterical reasons. '0' is last.
686 * Used in:
687 * Latvian */
688 case 3:
689 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
690
691 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
692 * Used in:
693 * Gaelige (Irish) */
694 case 4:
695 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
696
697 /* 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.
698 * Used in:
699 * Lithuanian */
700 case 5:
701 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
702
703 /* 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.
704 * Used in:
705 * Croatian, Russian, Ukrainian */
706 case 6:
707 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
708
709 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
710 * Used in:
711 * Polish */
712 case 7:
713 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
714
715 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
716 * Used in:
717 * Slovenian */
718 case 8:
719 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
720
721 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
722 * Used in:
723 * Icelandic */
724 case 9:
725 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
726
727 /* Three forms: special cases for 1, and 2 to 4
728 * Used in:
729 * Czech, Slovak */
730 case 10:
731 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
732
733 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
734 * Korean doesn't have the concept of plural, but depending on how a
735 * number is pronounced it needs another version of a particle.
736 * As such the plural system is misused to give this distinction.
737 */
738 case 11:
739 switch (n % 10) {
740 case 0: // yeong
741 case 1: // il
742 case 3: // sam
743 case 6: // yuk
744 case 7: // chil
745 case 8: // pal
746 return 0;
747
748 case 2: // i
749 case 4: // sa
750 case 5: // o
751 case 9: // gu
752 return 1;
753
754 default:
755 NOT_REACHED();
756 }
757
758 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
759 * Used in:
760 * Maltese */
761 case 12:
762 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
763 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
764 * Used in:
765 * Scottish Gaelic */
766 case 13:
767 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
768
769 /* Three forms: special cases for 1, 0 and numbers ending in 01 to 19.
770 * Used in:
771 * Romanian */
772 case 14:
773 return n == 1 ? 0 : (n == 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2;
774 }
775}
776
777static const char *ParseStringChoice(const char *b, uint form, StringBuilder &builder)
778{
779 /* <NUM> {Length of each string} {each string} */
780 uint n = (uint8_t)*b++;
781 size_t form_offset = 0, form_len = 0, total_len = 0;
782 for (uint i = 0; i != n; i++) {
783 uint len = (uint8_t)*b++;
784 if (i == form) {
785 form_offset = total_len;
786 form_len = len;
787 }
788 total_len += len;
789 }
790
791 builder += std::string_view(b + form_offset, form_len);
792 return b + total_len;
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, std::string_view str_arg, StringParameters &args, uint orig_case_index, bool game_script, bool dry_run)
1079{
1080 size_t orig_first_param_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, orig_case_index, game_script, true);
1094 /* We have to restore the original offset here to to read the correct values. */
1095 args.SetOffset(orig_first_param_offset);
1096 }
1097 uint next_substr_case_index = 0;
1098 struct StrStackItem {
1099 const char *str;
1100 const char *end;
1101 size_t first_param_offset;
1102 uint case_index;
1103
1104 StrStackItem(std::string_view view, size_t first_param_offset, uint case_index)
1105 : str(view.data()), end(view.data() + view.size()), first_param_offset(first_param_offset), case_index(case_index)
1106 {}
1107 };
1108 std::stack<StrStackItem, std::vector<StrStackItem>> str_stack;
1109 str_stack.emplace(str_arg, orig_first_param_offset, orig_case_index);
1110
1111 for (;;) {
1112 try {
1113 while (!str_stack.empty() && str_stack.top().str >= str_stack.top().end) {
1114 str_stack.pop();
1115 }
1116 if (str_stack.empty()) break;
1117 const char *&str = str_stack.top().str;
1118 const size_t ref_param_offset = str_stack.top().first_param_offset;
1119 const uint case_index = str_stack.top().case_index;
1120 char32_t b = Utf8Consume(&str);
1121 assert(b != 0);
1122
1123 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
1124 /* We need to pass some stuff as it might be modified. */
1125 b = RemapNewGRFStringControlCode(b, &str);
1126 if (b == 0) continue;
1127 }
1128
1129 if (b < SCC_CONTROL_START || b > SCC_CONTROL_END) {
1130 builder.PutUtf8(b);
1131 continue;
1132 }
1133
1134 args.SetTypeOfNextParameter(b);
1135 switch (b) {
1136 case SCC_ENCODED:
1138 str = DecodeEncodedString(str, b == SCC_ENCODED, builder);
1139 break;
1140
1141 case SCC_NEWGRF_STRINL: {
1142 StringID substr = Utf8Consume(&str);
1143 std::string_view ptr = GetStringPtr(substr);
1144 str_stack.emplace(ptr, args.GetOffset(), next_substr_case_index); // this may invalidate "str"
1145 next_substr_case_index = 0;
1146 break;
1147 }
1148
1150 StringID substr = args.GetNextParameter<StringID>();
1151 std::string_view ptr = GetStringPtr(substr);
1152 str_stack.emplace(ptr, args.GetOffset(), next_substr_case_index); // this may invalidate "str"
1153 next_substr_case_index = 0;
1154 break;
1155 }
1156
1157 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
1158 /* First read the meta data from the language file. */
1159 size_t offset = ref_param_offset + (uint8_t)*str++;
1160 int gender = 0;
1161 if (offset >= args.GetNumParameters()) {
1162 /* The offset may come from an external NewGRF, and be invalid. */
1163 builder += "(invalid GENDER parameter)";
1164 } else if (!dry_run && args.GetTypeAtOffset(offset) != 0) {
1165 /* Now we need to figure out what text to resolve, i.e.
1166 * what do we need to draw? So get the actual raw string
1167 * first using the control code to get said string. */
1168 std::string input;
1169 {
1170 StringBuilder tmp_builder(input);
1171 tmp_builder.PutUtf8(args.GetTypeAtOffset(offset));
1172 }
1173
1174 std::string buffer;
1175 {
1176 AutoRestoreBackup sgd_backup(_scan_for_gender_data, true);
1177 StringBuilder tmp_builder(buffer);
1178 StringParameters tmp_params = args.GetRemainingParameters(offset);
1179 FormatString(tmp_builder, input, tmp_params);
1180 }
1181
1182 /* The gender is stored at the start of the formatted string. */
1183 const char *s = buffer.c_str();
1184 char32_t c = Utf8Consume(&s);
1185 /* Does this string have a gender, if so, set it */
1186 if (c == SCC_GENDER_INDEX) gender = (uint8_t)s[0];
1187 }
1188 str = ParseStringChoice(str, gender, builder);
1189 break;
1190 }
1191
1192 /* This sets up the gender for the string.
1193 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
1194 case SCC_GENDER_INDEX: // {GENDER 0}
1196 builder.PutUtf8(SCC_GENDER_INDEX);
1197 builder.PutUint8(*str++);
1198 } else {
1199 str++;
1200 }
1201 break;
1202
1203 case SCC_PLURAL_LIST: { // {P}
1204 int plural_form = *str++; // contains the plural form for this string
1205 size_t offset = ref_param_offset + (uint8_t)*str++;
1206 const uint64_t *v = nullptr;
1207 /* The offset may come from an external NewGRF, and be invalid. */
1208 if (offset < args.GetNumParameters()) {
1209 v = std::get_if<uint64_t>(&args.GetParam(offset)); // contains the number that determines plural
1210 }
1211 if (v != nullptr) {
1212 str = ParseStringChoice(str, DeterminePluralForm(static_cast<int64_t>(*v), plural_form), builder);
1213 } else {
1214 builder += "(invalid PLURAL parameter)";
1215 }
1216 break;
1217 }
1218
1219 case SCC_ARG_INDEX: { // Move argument pointer
1220 args.SetOffset(ref_param_offset + (uint8_t)*str++);
1221 break;
1222 }
1223
1224 case SCC_SET_CASE: { // {SET_CASE}
1225 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
1226 * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
1227 next_substr_case_index = (uint8_t)*str++;
1228 break;
1229 }
1230
1231 case SCC_SWITCH_CASE: { // {Used to implement case switching}
1232 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <LENDEFAULT> <STRINGDEFAULT>
1233 * Each LEN is printed using 2 bytes in little endian order. */
1234 uint num = (uint8_t)*str++;
1235 std::optional<std::string_view> found;
1236 for (; num > 0; --num) {
1237 uint8_t index = static_cast<uint8_t>(str[0]);
1238 uint16_t len = static_cast<uint8_t>(str[1]) + (static_cast<uint8_t>(str[2]) << 8);
1239 str += 3;
1240 if (index == case_index) {
1241 /* Found the case */
1242 found.emplace(str, len);
1243 }
1244 str += len;
1245 }
1246 uint16_t default_len = static_cast<uint8_t>(str[0]) + (static_cast<uint8_t>(str[1]) << 8);
1247 str += 2;
1248 if (!found.has_value()) found.emplace(str, default_len);
1249 str += default_len;
1250 assert(str <= str_stack.top().end);
1251 str_stack.emplace(*found, ref_param_offset, case_index); // this may invalidate "str"
1252 break;
1253 }
1254
1255 case SCC_REVISION: // {REV}
1256 builder += _openttd_revision;
1257 break;
1258
1259 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1260 const char *raw_string = args.GetNextParameterString();
1261 /* raw_string can be nullptr. */
1262 if (raw_string == nullptr) {
1263 builder += "(invalid RAW_STRING parameter)";
1264 break;
1265 }
1266 FormatString(builder, raw_string, args);
1267 break;
1268 }
1269
1270 case SCC_STRING: {// {STRING}
1271 StringID string_id = args.GetNextParameter<StringID>();
1272 if (game_script && GetStringTab(string_id) != TEXT_TAB_GAMESCRIPT_START) break;
1273 /* It's prohibited for the included string to consume any arguments. */
1274 StringParameters tmp_params(args, game_script ? args.GetDataLeft() : 0);
1275 GetStringWithArgs(builder, string_id, tmp_params, next_substr_case_index, game_script);
1276 next_substr_case_index = 0;
1277 break;
1278 }
1279
1280 case SCC_STRING1:
1281 case SCC_STRING2:
1282 case SCC_STRING3:
1283 case SCC_STRING4:
1284 case SCC_STRING5:
1285 case SCC_STRING6:
1286 case SCC_STRING7: { // {STRING1..7}
1287 /* Strings that consume arguments */
1288 StringID string_id = args.GetNextParameter<StringID>();
1289 if (game_script && GetStringTab(string_id) != TEXT_TAB_GAMESCRIPT_START) break;
1290 uint size = b - SCC_STRING1 + 1;
1291 if (size > args.GetDataLeft()) {
1292 builder += "(consumed too many parameters)";
1293 } else {
1294 StringParameters sub_args(args, game_script ? args.GetDataLeft() : size);
1295 GetStringWithArgs(builder, string_id, sub_args, next_substr_case_index, game_script);
1296 args.AdvanceOffset(size);
1297 }
1298 next_substr_case_index = 0;
1299 break;
1300 }
1301
1302 case SCC_COMMA: // {COMMA}
1303 FormatCommaNumber(builder, args.GetNextParameter<int64_t>());
1304 break;
1305
1306 case SCC_DECIMAL: { // {DECIMAL}
1307 int64_t number = args.GetNextParameter<int64_t>();
1308 int digits = args.GetNextParameter<int>();
1309 if (digits == 0) {
1310 FormatCommaNumber(builder, number);
1311 break;
1312 }
1313
1314 int64_t divisor = PowerOfTen(digits);
1315 int64_t fractional = number % divisor;
1316 number /= divisor;
1317 FormatCommaNumber(builder, number);
1318 fmt::format_to(builder.back_inserter(), "{}{:0{}d}", GetDecimalSeparator(), fractional, digits);
1319 break;
1320 }
1321
1322 case SCC_NUM: // {NUM}
1323 FormatNoCommaNumber(builder, args.GetNextParameter<int64_t>());
1324 break;
1325
1326 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1327 int64_t num = args.GetNextParameter<int64_t>();
1328 FormatZerofillNumber(builder, num, args.GetNextParameter<int>());
1329 break;
1330 }
1331
1332 case SCC_HEX: // {HEX}
1333 FormatHexNumber(builder, args.GetNextParameter<uint64_t>());
1334 break;
1335
1336 case SCC_BYTES: // {BYTES}
1337 FormatBytes(builder, args.GetNextParameter<int64_t>());
1338 break;
1339
1340 case SCC_CARGO_TINY: { // {CARGO_TINY}
1341 /* Tiny description of cargotypes. Layout:
1342 * param 1: cargo type
1343 * param 2: cargo count */
1344 CargoType cargo = args.GetNextParameter<CargoType>();
1345 if (cargo >= CargoSpec::GetArraySize()) break;
1346
1347 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1348 int64_t amount = 0;
1349 switch (cargo_str) {
1350 case STR_TONS:
1352 break;
1353
1354 case STR_LITERS:
1356 break;
1357
1358 default: {
1359 amount = args.GetNextParameter<int64_t>();
1360 break;
1361 }
1362 }
1363
1364 FormatCommaNumber(builder, amount);
1365 break;
1366 }
1367
1368 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1369 /* Short description of cargotypes. Layout:
1370 * param 1: cargo type
1371 * param 2: cargo count */
1372 CargoType cargo = args.GetNextParameter<CargoType>();
1373 if (cargo >= CargoSpec::GetArraySize()) break;
1374
1375 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1376 switch (cargo_str) {
1377 case STR_TONS: {
1380 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1381 FormatString(builder, GetStringPtr(x.l), tmp_params);
1382 break;
1383 }
1384
1385 case STR_LITERS: {
1388 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1389 FormatString(builder, GetStringPtr(x.l), tmp_params);
1390 break;
1391 }
1392
1393 default: {
1394 auto tmp_params = MakeParameters(args.GetNextParameter<int64_t>());
1395 GetStringWithArgs(builder, cargo_str, tmp_params);
1396 break;
1397 }
1398 }
1399 break;
1400 }
1401
1402 case SCC_CARGO_LONG: { // {CARGO_LONG}
1403 /* First parameter is cargo type, second parameter is cargo count */
1404 CargoType cargo = args.GetNextParameter<CargoType>();
1405 if (IsValidCargoType(cargo) && cargo >= CargoSpec::GetArraySize()) break;
1406
1407 StringID cargo_str = !IsValidCargoType(cargo) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1408 auto tmp_args = MakeParameters(args.GetNextParameter<int64_t>());
1409 GetStringWithArgs(builder, cargo_str, tmp_args);
1410 break;
1411 }
1412
1413 case SCC_CARGO_LIST: { // {CARGO_LIST}
1414 CargoTypes cmask = args.GetNextParameter<CargoTypes>();
1415 bool first = true;
1416
1417 std::string_view list_separator = GetListSeparator();
1418 for (const auto &cs : _sorted_cargo_specs) {
1419 if (!HasBit(cmask, cs->Index())) continue;
1420
1421 if (first) {
1422 first = false;
1423 } else {
1424 /* Add a comma if this is not the first item */
1425 builder += list_separator;
1426 }
1427
1428 GetStringWithArgs(builder, cs->name, args, next_substr_case_index, game_script);
1429 }
1430
1431 /* If first is still true then no cargo is accepted */
1432 if (first) GetStringWithArgs(builder, STR_JUST_NOTHING, args, next_substr_case_index, game_script);
1433
1434 next_substr_case_index = 0;
1435 break;
1436 }
1437
1438 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1439 FormatGenericCurrency(builder, &GetCurrency(), args.GetNextParameter<int64_t>(), true);
1440 break;
1441
1442 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1443 FormatGenericCurrency(builder, &GetCurrency(), args.GetNextParameter<int64_t>(), false);
1444 break;
1445
1446 case SCC_DATE_TINY: // {DATE_TINY}
1447 FormatTinyOrISODate(builder, args.GetNextParameter<TimerGameCalendar::Date>(), STR_FORMAT_DATE_TINY);
1448 break;
1449
1450 case SCC_DATE_SHORT: // {DATE_SHORT}
1451 FormatMonthAndYear(builder, args.GetNextParameter<TimerGameCalendar::Date>(), next_substr_case_index);
1452 next_substr_case_index = 0;
1453 break;
1454
1455 case SCC_DATE_LONG: // {DATE_LONG}
1456 FormatYmdString(builder, args.GetNextParameter<TimerGameCalendar::Date>(), next_substr_case_index);
1457 next_substr_case_index = 0;
1458 break;
1459
1460 case SCC_DATE_ISO: // {DATE_ISO}
1461 FormatTinyOrISODate(builder, args.GetNextParameter<TimerGameCalendar::Date>(), STR_FORMAT_DATE_ISO);
1462 break;
1463
1464 case SCC_FORCE: { // {FORCE}
1467 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1468 FormatString(builder, GetStringPtr(x.s), tmp_params);
1469 break;
1470 }
1471
1472 case SCC_HEIGHT: { // {HEIGHT}
1475 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1476 FormatString(builder, GetStringPtr(x.s), tmp_params);
1477 break;
1478 }
1479
1480 case SCC_POWER: { // {POWER}
1483 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1484 FormatString(builder, GetStringPtr(x.s), tmp_params);
1485 break;
1486 }
1487
1488 case SCC_POWER_TO_WEIGHT: { // {POWER_TO_WEIGHT}
1490 assert(setting < lengthof(_units_power_to_weight));
1491 const auto &x = _units_power_to_weight[setting];
1492 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1493 FormatString(builder, GetStringPtr(x.s), tmp_params);
1494 break;
1495 }
1496
1497 case SCC_VELOCITY: { // {VELOCITY}
1498 int64_t arg = args.GetNextParameter<int64_t>();
1499 // Unpack vehicle type from packed argument to get desired units.
1500 VehicleType vt = static_cast<VehicleType>(GB(arg, 56, 8));
1501 const auto &x = GetVelocityUnits(vt);
1502 auto tmp_params = MakeParameters(ConvertKmhishSpeedToDisplaySpeed(GB(arg, 0, 56), vt), x.decimal_places);
1503 FormatString(builder, GetStringPtr(x.s), tmp_params);
1504 break;
1505 }
1506
1507 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1510 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1511 FormatString(builder, GetStringPtr(x.s), tmp_params);
1512 break;
1513 }
1514
1515 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1518 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1519 FormatString(builder, GetStringPtr(x.l), tmp_params);
1520 break;
1521 }
1522
1523 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1526 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1527 FormatString(builder, GetStringPtr(x.s), tmp_params);
1528 break;
1529 }
1530
1531 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1534 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1535 FormatString(builder, GetStringPtr(x.l), tmp_params);
1536 break;
1537 }
1538
1539 case SCC_UNITS_DAYS_OR_SECONDS: { // {UNITS_DAYS_OR_SECONDS}
1540 uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1541 const auto &x = _units_time_days_or_seconds[realtime];
1542 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1543 FormatString(builder, GetStringPtr(x.s), tmp_params);
1544 break;
1545 }
1546
1547 case SCC_UNITS_MONTHS_OR_MINUTES: { // {UNITS_MONTHS_OR_MINUTES}
1548 uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1549 const auto &x = _units_time_months_or_minutes[realtime];
1550 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1551 FormatString(builder, GetStringPtr(x.s), tmp_params);
1552 break;
1553 }
1554
1555 case SCC_UNITS_YEARS_OR_PERIODS: { // {UNITS_YEARS_OR_PERIODS}
1556 uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1557 const auto &x = _units_time_years_or_periods[realtime];
1558 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1559 FormatString(builder, GetStringPtr(x.s), tmp_params);
1560 break;
1561 }
1562
1563 case SCC_UNITS_YEARS_OR_MINUTES: { // {UNITS_YEARS_OR_MINUTES}
1564 uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU);
1565 const auto &x = _units_time_years_or_minutes[realtime];
1566 auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter<int64_t>()), x.decimal_places);
1567 FormatString(builder, GetStringPtr(x.s), tmp_params);
1568 break;
1569 }
1570
1571 case SCC_COMPANY_NAME: { // {COMPANY}
1573 if (c == nullptr) break;
1574
1575 if (!c->name.empty()) {
1576 auto tmp_params = MakeParameters(c->name);
1577 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1578 } else {
1579 auto tmp_params = MakeParameters(c->name_2);
1580 GetStringWithArgs(builder, c->name_1, tmp_params);
1581 }
1582 break;
1583 }
1584
1585 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1586 CompanyID company = args.GetNextParameter<CompanyID>();
1587
1588 /* Nothing is added for AI or inactive companies */
1589 if (Company::IsValidHumanID(company)) {
1590 auto tmp_params = MakeParameters(company + 1);
1591 GetStringWithArgs(builder, STR_FORMAT_COMPANY_NUM, tmp_params);
1592 }
1593 break;
1594 }
1595
1596 case SCC_DEPOT_NAME: { // {DEPOT}
1598 if (vt == VEH_AIRCRAFT) {
1599 auto tmp_params = MakeParameters(args.GetNextParameter<StationID>());
1600 GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_AIRCRAFT, tmp_params);
1601 break;
1602 }
1603
1604 const Depot *d = Depot::Get(args.GetNextParameter<DepotID>());
1605 if (!d->name.empty()) {
1606 auto tmp_params = MakeParameters(d->name);
1607 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1608 } else {
1609 auto tmp_params = MakeParameters(d->town->index, d->town_cn + 1);
1610 GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), tmp_params);
1611 }
1612 break;
1613 }
1614
1615 case SCC_ENGINE_NAME: { // {ENGINE}
1616 int64_t arg = args.GetNextParameter<int64_t>();
1617 const Engine *e = Engine::GetIfValid(static_cast<EngineID>(arg));
1618 if (e == nullptr) break;
1619
1620 if (!e->name.empty() && e->IsEnabled()) {
1621 auto tmp_params = MakeParameters(e->name);
1622 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1623 break;
1624 }
1625
1626 if (e->info.callback_mask.Test(VehicleCallbackMask::Name)) {
1627 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_NAME, static_cast<uint32_t>(arg >> 32), 0, e->index, nullptr);
1628 /* Not calling ErrorUnknownCallbackResult due to being inside string processing. */
1629 if (callback != CALLBACK_FAILED && callback < 0x400) {
1630 const GRFFile *grffile = e->GetGRF();
1631 assert(grffile != nullptr);
1632
1633 builder += GetGRFStringWithTextStack(grffile, GRFSTR_MISC_GRF_TEXT + callback, 6);
1634 break;
1635 }
1636 }
1637
1638 GetStringWithArgs(builder, e->info.string_id, {});
1639 break;
1640 }
1641
1642 case SCC_GROUP_NAME: { // {GROUP}
1643 const Group *g = Group::GetIfValid(args.GetNextParameter<GroupID>());
1644 if (g == nullptr) break;
1645
1646 if (!g->name.empty()) {
1647 auto tmp_params = MakeParameters(g->name);
1648 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1649 } else {
1650 auto tmp_params = MakeParameters(g->number);
1651 GetStringWithArgs(builder, STR_FORMAT_GROUP_NAME, tmp_params);
1652 }
1653 break;
1654 }
1655
1656 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1658 if (i == nullptr) break;
1659
1660 static bool use_cache = true;
1662 /* Gender is defined by the industry type.
1663 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1664 FormatString(builder, GetStringPtr(GetIndustrySpec(i->type)->name), {}, next_substr_case_index);
1665 } else if (use_cache) { // Use cached version if first call
1666 AutoRestoreBackup cache_backup(use_cache, false);
1667 builder += i->GetCachedName();
1668 } else {
1669 /* First print the town name and the industry type name. */
1670 auto tmp_params = MakeParameters(i->town->index, GetIndustrySpec(i->type)->name);
1671 FormatString(builder, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), tmp_params, next_substr_case_index);
1672 }
1673 next_substr_case_index = 0;
1674 break;
1675 }
1676
1677 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1679 if (c == nullptr) break;
1680
1681 if (!c->president_name.empty()) {
1682 auto tmp_params = MakeParameters(c->president_name);
1683 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1684 } else {
1685 auto tmp_params = MakeParameters(c->president_name_2);
1686 GetStringWithArgs(builder, c->president_name_1, tmp_params);
1687 }
1688 break;
1689 }
1690
1691 case SCC_STATION_NAME: { // {STATION}
1692 StationID sid = args.GetNextParameter<StationID>();
1693 const Station *st = Station::GetIfValid(sid);
1694
1695 if (st == nullptr) {
1696 /* The station doesn't exist anymore. The only place where we might
1697 * be "drawing" an invalid station is in the case of cargo that is
1698 * in transit. */
1699 GetStringWithArgs(builder, STR_UNKNOWN_STATION, {});
1700 break;
1701 }
1702
1703 static bool use_cache = true;
1704 if (use_cache) { // Use cached version if first call
1705 AutoRestoreBackup cache_backup(use_cache, false);
1706 builder += st->GetCachedName();
1707 } else if (!st->name.empty()) {
1708 auto tmp_params = MakeParameters(st->name);
1709 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1710 } else {
1711 StringID string_id = st->string_id;
1712 if (st->indtype != IT_INVALID) {
1713 /* Special case where the industry provides the name for the station */
1714 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1715
1716 /* Industry GRFs can change which might remove the station name and
1717 * thus cause very strange things. Here we check for that before we
1718 * actually set the station name. */
1719 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1720 string_id = indsp->station_name;
1721 }
1722 }
1723
1724 auto tmp_params = MakeParameters(STR_TOWN_NAME, st->town->index, st->index);
1725 GetStringWithArgs(builder, string_id, tmp_params);
1726 }
1727 break;
1728 }
1729
1730 case SCC_TOWN_NAME: { // {TOWN}
1731 const Town *t = Town::GetIfValid(args.GetNextParameter<TownID>());
1732 if (t == nullptr) break;
1733
1734 static bool use_cache = true;
1735 if (use_cache) { // Use cached version if first call
1736 AutoRestoreBackup cache_backup(use_cache, false);
1737 builder += t->GetCachedName();
1738 } else if (!t->name.empty()) {
1739 auto tmp_params = MakeParameters(t->name);
1740 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1741 } else {
1742 GetTownName(builder, t);
1743 }
1744 break;
1745 }
1746
1747 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1749 if (wp == nullptr) break;
1750
1751 if (!wp->name.empty()) {
1752 auto tmp_params = MakeParameters(wp->name);
1753 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1754 } else {
1755 auto tmp_params = MakeParameters(wp->town->index, wp->town_cn + 1);
1756 StringID string_id = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1757 if (wp->town_cn != 0) string_id++;
1758 GetStringWithArgs(builder, string_id, tmp_params);
1759 }
1760 break;
1761 }
1762
1763 case SCC_VEHICLE_NAME: { // {VEHICLE}
1765 if (v == nullptr) break;
1766
1767 if (!v->name.empty()) {
1768 auto tmp_params = MakeParameters(v->name);
1769 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1770 } else if (v->group_id != DEFAULT_GROUP) {
1771 /* The vehicle has no name, but is member of a group, so print group name */
1772 auto tmp_params = MakeParameters(v->group_id, v->unitnumber);
1773 GetStringWithArgs(builder, STR_FORMAT_GROUP_VEHICLE_NAME, tmp_params);
1774 } else {
1775 auto tmp_params = MakeParameters(v->unitnumber);
1776
1777 StringID string_id;
1778 switch (v->type) {
1779 default: string_id = STR_INVALID_VEHICLE; break;
1780 case VEH_TRAIN: string_id = STR_SV_TRAIN_NAME; break;
1781 case VEH_ROAD: string_id = STR_SV_ROAD_VEHICLE_NAME; break;
1782 case VEH_SHIP: string_id = STR_SV_SHIP_NAME; break;
1783 case VEH_AIRCRAFT: string_id = STR_SV_AIRCRAFT_NAME; break;
1784 }
1785
1786 GetStringWithArgs(builder, string_id, tmp_params);
1787 }
1788 break;
1789 }
1790
1791 case SCC_SIGN_NAME: { // {SIGN}
1792 const Sign *si = Sign::GetIfValid(args.GetNextParameter<SignID>());
1793 if (si == nullptr) break;
1794
1795 if (!si->name.empty()) {
1796 auto tmp_params = MakeParameters(si->name);
1797 GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params);
1798 } else {
1799 GetStringWithArgs(builder, STR_DEFAULT_SIGN_NAME, {});
1800 }
1801 break;
1802 }
1803
1804 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1805 StationGetSpecialString(builder, args.GetNextParameter<StationFacilities>());
1806 break;
1807 }
1808
1809 case SCC_COLOUR: { // {COLOUR}
1810 StringControlCode scc = (StringControlCode)(SCC_BLUE + args.GetNextParameter<Colours>());
1811 if (IsInsideMM(scc, SCC_BLUE, SCC_COLOUR)) builder.PutUtf8(scc);
1812 break;
1813 }
1814
1815 default:
1816 builder.PutUtf8(b);
1817 break;
1818 }
1819 } catch (std::out_of_range &e) {
1820 Debug(misc, 0, "FormatString: {}", e.what());
1821 builder += "(invalid parameter)";
1822 }
1823 }
1824}
1825
1826
1827static void StationGetSpecialString(StringBuilder &builder, StationFacilities x)
1828{
1829 if (x.Test(StationFacility::Train)) builder.PutUtf8(SCC_TRAIN);
1830 if (x.Test(StationFacility::TruckStop)) builder.PutUtf8(SCC_LORRY);
1831 if (x.Test(StationFacility::BusStop)) builder.PutUtf8(SCC_BUS);
1832 if (x.Test(StationFacility::Dock)) builder.PutUtf8(SCC_SHIP);
1833 if (x.Test(StationFacility::Airport)) builder.PutUtf8(SCC_PLANE);
1834}
1835
1836static const char * const _silly_company_names[] = {
1837 "Bloggs Brothers",
1838 "Tiny Transport Ltd.",
1839 "Express Travel",
1840 "Comfy-Coach & Co.",
1841 "Crush & Bump Ltd.",
1842 "Broken & Late Ltd.",
1843 "Sam Speedy & Son",
1844 "Supersonic Travel",
1845 "Mike's Motors",
1846 "Lightning International",
1847 "Pannik & Loozit Ltd.",
1848 "Inter-City Transport",
1849 "Getout & Pushit Ltd."
1850};
1851
1852static const char * const _surname_list[] = {
1853 "Adams",
1854 "Allan",
1855 "Baker",
1856 "Bigwig",
1857 "Black",
1858 "Bloggs",
1859 "Brown",
1860 "Campbell",
1861 "Gordon",
1862 "Hamilton",
1863 "Hawthorn",
1864 "Higgins",
1865 "Green",
1866 "Gribble",
1867 "Jones",
1868 "McAlpine",
1869 "MacDonald",
1870 "McIntosh",
1871 "Muir",
1872 "Murphy",
1873 "Nelson",
1874 "O'Donnell",
1875 "Parker",
1876 "Phillips",
1877 "Pilkington",
1878 "Quigley",
1879 "Sharkey",
1880 "Thomson",
1881 "Watkins"
1882};
1883
1884static const char * const _silly_surname_list[] = {
1885 "Grumpy",
1886 "Dozy",
1887 "Speedy",
1888 "Nosey",
1889 "Dribble",
1890 "Mushroom",
1891 "Cabbage",
1892 "Sniffle",
1893 "Fishy",
1894 "Swindle",
1895 "Sneaky",
1896 "Nutkins"
1897};
1898
1899static const char _initial_name_letters[] = {
1900 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1901 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1902};
1903
1904static std::span<const char * const> GetSurnameOptions()
1905{
1906 if (_settings_game.game_creation.landscape == LandscapeType::Toyland) return _silly_surname_list;
1907 return _surname_list;
1908}
1909
1915static const char *GetSurname(uint32_t seed)
1916{
1917 auto surname_options = GetSurnameOptions();
1918 return surname_options[surname_options.size() * GB(seed, 16, 8) >> 8];
1919}
1920
1921static void GenAndCoName(StringBuilder &builder, uint32_t seed)
1922{
1923 builder += GetSurname(seed);
1924 builder += " & Co.";
1925}
1926
1927static void GenPresidentName(StringBuilder &builder, uint32_t seed)
1928{
1929 builder.PutChar(_initial_name_letters[std::size(_initial_name_letters) * GB(seed, 0, 8) >> 8]);
1930 builder += ". ";
1931
1932 /* The second initial is optional. */
1933 size_t index = (std::size(_initial_name_letters) + 35) * GB(seed, 8, 8) >> 8;
1934 if (index < std::size(_initial_name_letters)) {
1935 builder.PutChar(_initial_name_letters[index]);
1936 builder += ". ";
1937 }
1938
1939 builder += GetSurname(seed);
1940}
1941
1942static bool GetSpecialNameString(StringBuilder &builder, StringID string, StringParameters &args)
1943{
1944 switch (string) {
1945 case SPECSTR_SILLY_NAME: // Not used in new companies, but retained for old-loader savegames
1946 builder += _silly_company_names[std::min<size_t>(args.GetNextParameter<uint16_t>(), std::size(_silly_company_names) - 1)];
1947 return true;
1948
1949 case SPECSTR_ANDCO_NAME: // used for Foobar & Co company names
1950 GenAndCoName(builder, args.GetNextParameter<uint32_t>());
1951 return true;
1952
1953 case SPECSTR_PRESIDENT_NAME: // President name
1954 GenPresidentName(builder, args.GetNextParameter<uint32_t>());
1955 return true;
1956 }
1957
1958 /* TownName Transport company names, with the appropriate town name. */
1959 if (IsInsideMM(string, SPECSTR_COMPANY_NAME_START, SPECSTR_COMPANY_NAME_END)) {
1960 GenerateTownNameString(builder, string - SPECSTR_COMPANY_NAME_START, args.GetNextParameter<uint32_t>());
1961 builder += " Transport";
1962 return true;
1963 }
1964
1965 return false;
1966}
1967
1973{
1974 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1975 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1976 this->plural_form < LANGUAGE_MAX_PLURAL &&
1977 this->text_dir <= 1 &&
1978 this->newgrflangid < MAX_LANG &&
1979 this->num_genders < MAX_NUM_GENDERS &&
1980 this->num_cases < MAX_NUM_CASES &&
1981 StrValid(this->name) &&
1982 StrValid(this->own_name) &&
1983 StrValid(this->isocode) &&
1987}
1988
1993{
1994 /* "Less than 25% missing" is "sufficiently finished". */
1995 return 4 * this->missing < LANGUAGE_TOTAL_STRINGS;
1996}
1997
2004{
2005 /* Current language pack */
2006 size_t total_len = 0;
2007 std::unique_ptr<LanguagePack, LanguagePackDeleter> lang_pack(reinterpret_cast<LanguagePack *>(ReadFileToMem(FS2OTTD(lang->file), total_len, 1U << 20).release()));
2008 if (!lang_pack) return false;
2009
2010 /* End of read data (+ terminating zero added in ReadFileToMem()) */
2011 const char *end = (char *)lang_pack.get() + total_len + 1;
2012
2013 /* We need at least one byte of lang_pack->data */
2014 if (end <= lang_pack->data || !lang_pack->IsValid()) {
2015 return false;
2016 }
2017
2018 std::array<uint, TEXT_TAB_END> tab_start, tab_num;
2019
2020 uint count = 0;
2021 for (uint i = 0; i < TEXT_TAB_END; i++) {
2022 uint16_t num = FROM_LE16(lang_pack->offsets[i]);
2023 if (num > TAB_SIZE) return false;
2024
2025 tab_start[i] = count;
2026 tab_num[i] = num;
2027 count += num;
2028 }
2029
2030 /* Allocate offsets */
2031 std::vector<std::string_view> strings;
2032
2033 /* Fill offsets */
2034 char *s = lang_pack->data;
2035 for (uint i = 0; i < count; i++) {
2036 size_t len = static_cast<uint8_t>(*s++);
2037 if (s + len >= end) return false;
2038
2039 if (len >= 0xC0) {
2040 len = ((len & 0x3F) << 8) + static_cast<uint8_t>(*s++);
2041 if (s + len >= end) return false;
2042 }
2043 strings.emplace_back(s, len);
2044 s += len;
2045 }
2046 assert(strings.size() == count);
2047
2048 _langpack.langpack = std::move(lang_pack);
2049 _langpack.strings = std::move(strings);
2050 _langpack.langtab_num = tab_num;
2051 _langpack.langtab_start = tab_start;
2052
2053 _current_language = lang;
2057 _langpack.list_separator = GetString(STR_LIST_SEPARATOR);
2058
2059#ifdef _WIN32
2060 extern void Win32SetCurrentLocaleName(std::string iso_code);
2061 Win32SetCurrentLocaleName(_current_language->isocode);
2062#endif
2063
2064#ifdef WITH_COCOA
2065 extern void MacOSSetCurrentLocaleName(const char *iso_code);
2067#endif
2068
2069#ifdef WITH_ICU_I18N
2070 /* Create a collator instance for our current locale. */
2071 UErrorCode status = U_ZERO_ERROR;
2072 _current_collator.reset(icu::Collator::createInstance(icu::Locale(_current_language->isocode), status));
2073 /* Sort number substrings by their numerical value. */
2074 if (_current_collator) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
2075 /* Avoid using the collator if it is not correctly set. */
2076 if (U_FAILURE(status)) {
2077 _current_collator.reset();
2078 }
2079#endif /* WITH_ICU_I18N */
2080
2082
2083 /* Some lists need to be sorted again after a language change. */
2089 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
2090 InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
2091 InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
2092 InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
2093 InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
2094 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
2095 InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
2096
2097 return true;
2098}
2099
2100/* Win32 implementation in win32.cpp.
2101 * OS X implementation in os/macosx/macos.mm. */
2102#if !(defined(_WIN32) || defined(__APPLE__))
2111const char *GetCurrentLocale(const char *param)
2112{
2113 const char *env;
2114
2115 env = std::getenv("LANGUAGE");
2116 if (env != nullptr) return env;
2117
2118 env = std::getenv("LC_ALL");
2119 if (env != nullptr) return env;
2120
2121 if (param != nullptr) {
2122 env = std::getenv(param);
2123 if (env != nullptr) return env;
2124 }
2125
2126 return std::getenv("LANG");
2127}
2128#else
2129const char *GetCurrentLocale(const char *param);
2130#endif /* !(defined(_WIN32) || defined(__APPLE__)) */
2131
2137const LanguageMetadata *GetLanguage(uint8_t newgrflangid)
2138{
2139 for (const LanguageMetadata &lang : _languages) {
2140 if (newgrflangid == lang.newgrflangid) return &lang;
2141 }
2142
2143 return nullptr;
2144}
2145
2152static bool GetLanguageFileHeader(const std::string &file, LanguagePackHeader *hdr)
2153{
2154 auto f = FileHandle::Open(file, "rb");
2155 if (!f.has_value()) return false;
2156
2157 size_t read = fread(hdr, sizeof(*hdr), 1, *f);
2158
2159 bool ret = read == 1 && hdr->IsValid();
2160
2161 /* Convert endianness for the windows language ID */
2162 if (ret) {
2163 hdr->missing = FROM_LE16(hdr->missing);
2164 hdr->winlangid = FROM_LE16(hdr->winlangid);
2165 }
2166 return ret;
2167}
2168
2173static void FillLanguageList(const std::string &path)
2174{
2175 std::error_code error_code;
2176 for (const auto &dir_entry : std::filesystem::directory_iterator(OTTD2FS(path), error_code)) {
2177 if (!dir_entry.is_regular_file()) continue;
2178 if (dir_entry.path().extension() != ".lng") continue;
2179
2180 LanguageMetadata lmd;
2181 lmd.file = dir_entry.path();
2182
2183 /* Check whether the file is of the correct version */
2184 if (!GetLanguageFileHeader(FS2OTTD(lmd.file), &lmd)) {
2185 Debug(misc, 3, "{} is not a valid language file", FS2OTTD(lmd.file));
2186 } else if (GetLanguage(lmd.newgrflangid) != nullptr) {
2187 Debug(misc, 3, "{}'s language ID is already known", FS2OTTD(lmd.file));
2188 } else {
2189 _languages.push_back(std::move(lmd));
2190 }
2191 }
2192 if (error_code) {
2193 Debug(misc, 9, "Unable to open directory {}: {}", path, error_code.message());
2194 }
2195}
2196
2202{
2203 for (Searchpath sp : _valid_searchpaths) {
2204 FillLanguageList(FioGetDirectory(sp, LANG_DIR));
2205 }
2206 if (_languages.empty()) UserError("No available language packs (invalid versions?)");
2207
2208 /* Acquire the locale of the current system */
2209 const char *lang = GetCurrentLocale("LC_MESSAGES");
2210 if (lang == nullptr) lang = "en_GB";
2211
2212 const LanguageMetadata *chosen_language = nullptr;
2213 const LanguageMetadata *language_fallback = nullptr;
2214 const LanguageMetadata *en_GB_fallback = _languages.data();
2215
2216 /* Find a proper language. */
2217 for (const LanguageMetadata &lng : _languages) {
2218 /* We are trying to find a default language. The priority is by
2219 * configuration file, local environment and last, if nothing found,
2220 * English. */
2221 if (_config_language_file == FS2OTTD(lng.file.filename())) {
2222 chosen_language = &lng;
2223 break;
2224 }
2225
2226 if (strcmp (lng.isocode, "en_GB") == 0) en_GB_fallback = &lng;
2227
2228 /* Only auto-pick finished translations */
2229 if (!lng.IsReasonablyFinished()) continue;
2230
2231 if (strncmp(lng.isocode, lang, 5) == 0) chosen_language = &lng;
2232 if (strncmp(lng.isocode, lang, 2) == 0) language_fallback = &lng;
2233 }
2234
2235 /* We haven't found the language in the config nor the one in the locale.
2236 * Now we set it to one of the fallback languages */
2237 if (chosen_language == nullptr) {
2238 chosen_language = (language_fallback != nullptr) ? language_fallback : en_GB_fallback;
2239 }
2240
2241 if (!ReadLanguagePack(chosen_language)) UserError("Can't read language pack '{}'", FS2OTTD(chosen_language->file));
2242}
2243
2249{
2250 return _langpack.langpack->isocode;
2251}
2252
2258{
2259 InitFontCache(this->Monospace());
2260
2261 this->Reset();
2262 for (auto text = this->NextString(); text.has_value(); text = this->NextString()) {
2263 FontSize size = this->DefaultSize();
2264 FontCache *fc = FontCache::Get(size);
2265 for (char32_t c : Utf8View(*text)) {
2266 if (c >= SCC_FIRST_FONT && c <= SCC_LAST_FONT) {
2267 size = (FontSize)(c - SCC_FIRST_FONT);
2268 fc = FontCache::Get(size);
2269 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && fc->MapCharToGlyph(c, false) == 0) {
2270 /* The character is printable, but not in the normal font. This is the case we were testing for. */
2271 std::string size_name;
2272
2273 switch (size) {
2274 case FS_NORMAL: size_name = "medium"; break;
2275 case FS_SMALL: size_name = "small"; break;
2276 case FS_LARGE: size_name = "large"; break;
2277 case FS_MONO: size_name = "mono"; break;
2278 default: NOT_REACHED();
2279 }
2280
2281 Debug(fontcache, 0, "Font is missing glyphs to display char 0x{:X} in {} font size", (int)c, size_name);
2282 return true;
2283 }
2284 }
2285 }
2286 return false;
2287}
2288
2291 uint i;
2292 uint j;
2293
2294 void Reset() override
2295 {
2296 this->i = 0;
2297 this->j = 0;
2298 }
2299
2301 {
2302 return FS_NORMAL;
2303 }
2304
2305 std::optional<std::string_view> NextString() override
2306 {
2307 if (this->i >= TEXT_TAB_END) return std::nullopt;
2308
2309 std::string_view ret = _langpack.strings[_langpack.langtab_start[this->i] + this->j];
2310
2311 this->j++;
2312 while (this->i < TEXT_TAB_END && this->j >= _langpack.langtab_num[this->i]) {
2313 this->i++;
2314 this->j = 0;
2315 }
2316
2317 return ret;
2318 }
2319
2320 bool Monospace() override
2321 {
2322 return false;
2323 }
2324
2325 void SetFontNames([[maybe_unused]] FontCacheSettings *settings, [[maybe_unused]] const char *font_name, [[maybe_unused]] const void *os_data) override
2326 {
2327#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2328 settings->small.font = font_name;
2329 settings->medium.font = font_name;
2330 settings->large.font = font_name;
2331
2332 settings->small.os_handle = os_data;
2333 settings->medium.os_handle = os_data;
2334 settings->large.os_handle = os_data;
2335#endif
2336 }
2337};
2338
2352void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2353{
2354 static LanguagePackGlyphSearcher pack_searcher;
2355 if (searcher == nullptr) searcher = &pack_searcher;
2356 bool bad_font = !base_font || searcher->FindMissingGlyphs();
2357#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2358 if (bad_font) {
2359 /* We found an unprintable character... lets try whether we can find
2360 * a fallback font that can print the characters in the current language. */
2361 bool any_font_configured = !_fcsettings.medium.font.empty();
2362 FontCacheSettings backup = _fcsettings;
2363
2364 _fcsettings.mono.os_handle = nullptr;
2365 _fcsettings.medium.os_handle = nullptr;
2366
2367 bad_font = !SetFallbackFont(&_fcsettings, _langpack.langpack->isocode, searcher);
2368
2369 _fcsettings = std::move(backup);
2370
2371 if (!bad_font && any_font_configured) {
2372 /* If the user configured a bad font, and we found a better one,
2373 * show that we loaded the better font instead of the configured one.
2374 */
2375 std::string err_str;
2376 StringBuilder builder(err_str);
2377 builder.PutUtf8(SCC_YELLOW);
2378 builder.Put("The current font is missing some of the characters used in the texts for this language. Using system fallback font instead.");
2379 ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_WARNING);
2380 }
2381
2382 if (bad_font && base_font) {
2383 /* Our fallback font does miss characters too, so keep the
2384 * user chosen font as that is more likely to be any good than
2385 * the wild guess we made */
2386 InitFontCache(searcher->Monospace());
2387 }
2388 }
2389#endif
2390
2391 if (bad_font) {
2392 /* All attempts have failed. Display an error. As we do not want the string to be translated by
2393 * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
2394 * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
2395 */
2396 std::string err_str;
2397 StringBuilder builder(err_str);
2398 builder.PutUtf8(SCC_YELLOW);
2399 builder.Put("The 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.");
2400 ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_WARNING);
2401
2402 /* Reset the font width */
2403 LoadStringWidthTable(searcher->Monospace());
2404 return;
2405 }
2406
2407 /* Update the font with cache */
2408 LoadStringWidthTable(searcher->Monospace());
2409
2410#if !(defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
2411 /*
2412 * For right-to-left languages we need the ICU library. If
2413 * we do not have support for that library we warn the user
2414 * about it with a message. As we do not want the string to
2415 * be translated by the translators, we 'force' it into the
2416 * binary and 'load' it via a BindCString. To do this
2417 * properly we have to set the colour of the string,
2418 * otherwise we end up with a lot of artifacts.
2419 */
2420 if (_current_text_dir != TD_LTR) {
2421 std::string err_str;
2422 StringBuilder builder(err_str);
2423 builder.PutUtf8(SCC_YELLOW);
2424 builder.Put("This version of OpenTTD does not support right-to-left languages. Recompile with ICU + Harfbuzz enabled.");
2425 ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_ERROR);
2426 }
2427#endif /* !(WITH_ICU_I18N && WITH_HARFBUZZ) && !WITH_UNISCRIBE && !WITH_COCOA */
2428}
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 cargo)
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.
void PutUtf8(char32_t c)
Append UTF.8 char.
void Put(std::string_view str)
Append string.
void PutIntegerBase(T value, int base)
Append integer 'value' in given number 'base'.
void PutChar(char c)
Append 8-bit char.
void PutUint8(uint8_t value)
Append binary uint8.
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:103
std::string GetDecodedString() const
Decode the encoded string.
Definition strings.cpp:217
EncodedString ReplaceParam(size_t param, StringParameter &&value) const
Replace a parameter of this EncodedString.
Definition strings.cpp:151
static std::optional< FileHandle > Open(const std::string &filename, const std::string &mode)
Open an RAII file handle if possible.
Definition fileio.cpp:1169
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:2290
bool Monospace() override
Whether to search for a monospace font or not.
Definition strings.cpp:2320
uint j
Iterator for the secondary language tables.
Definition strings.cpp:2292
std::optional< std::string_view > NextString() override
Get the next string to search through.
Definition strings.cpp:2305
FontSize DefaultSize() override
Get the default (font) size of the string.
Definition strings.cpp:2300
void SetFontNames(FontCacheSettings *settings, const char *font_name, const void *os_data) override
Set the right font names.
Definition strings.cpp:2325
uint i
Iterator for the primary language tables.
Definition strings.cpp:2291
void Reset() override
Reset the search, i.e.
Definition strings.cpp:2294
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:2257
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.
Compose data into a growing std::string.
back_insert_iterator back_inserter()
Create a back-insert-iterator.
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:69
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 GetNumParameters() const
Return the number of 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.
Constant span of UTF-8 encoded data.
Definition utf8.hpp:24
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.
std::pair< size_t, char32_t > DecodeUtf8(std::string_view buf)
Decode a character from UTF-8.
Definition utf8.cpp:48
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, 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:1024
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.
std::string_view 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:244
@ FS_MONO
Index of the monospaced font in the font tables.
Definition gfx_type.h:248
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:246
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:245
@ FS_LARGE
Index of the large font in the font tables.
Definition gfx_type.h:247
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.
std::string_view GetGRFStringPtr(StringIndexInTab stringid)
Get a C-string from a stringid set by a newgrf.
void SetCurrentGrfLangID(uint8_t language_id)
Equivalence Setter function between game and newgrf langID.
char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str)
Emit OpenTTD's internal string code for the different NewGRF string codes.
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:59
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:271
bool StrValid(std::span< const char > str)
Checks whether the given string is valid, i.e.
Definition string.cpp:248
size_t Utf8Decode(char32_t *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Definition string.cpp:441
Functions related to low-level strings.
bool StrEmpty(const char *s)
Check if a string buffer is empty.
Definition string_func.h:68
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:246
LanguageList _languages
The actual list of language meta data.
Definition strings.cpp:54
EncodedString GetEncodedStringWithArgs(StringID str, std::span< const StringParameter > params)
Encode a string with its parameters into an encoded string.
Definition strings.cpp:103
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:336
std::string_view GetListSeparator()
Get the list separator string for the current language.
Definition strings.cpp:308
static const Units _units_height[]
Unit conversions for height.
Definition strings.cpp:902
const LanguageMetadata * _current_language
The currently loaded language.
Definition strings.cpp:55
static void FormatBytes(StringBuilder &builder, int64_t number)
Format a given number as a number of bytes with the SI prefix.
Definition strings.cpp:537
const char * GetCurrentLanguageIsoCode()
Get the ISO language code of the currently loaded language.
Definition strings.cpp:2248
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:434
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:2352
std::string _config_language_file
The file (name) stored in the configuration.
Definition strings.cpp:53
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:91
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:482
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
static bool _scan_for_gender_data
Are we scanning for the gender of the current string? (instead of formatting it)
Definition strings.cpp:302
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:60
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:2173
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:2201
const LanguageMetadata * GetLanguage(uint8_t newgrflangid)
Get the language with the given NewGRF language ID.
Definition strings.cpp:2137
static const char * GetSurname(uint32_t seed)
Get the surname of the president with the given seed.
Definition strings.cpp:1915
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:57
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:2003
static bool GetLanguageFileHeader(const std::string &file, LanguagePackHeader *hdr)
Reads the language file header and checks compatibility.
Definition strings.cpp:2152
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 void FormatString(StringBuilder &builder, std::string_view 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 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:2111
uint64_t GetParamMaxDigits(uint count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:228
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:656
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:113
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:1972
bool IsReasonablyFinished() const
Check whether a translation is sufficiently finished to offer it to the public.
Definition strings.cpp:1992
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:297
std::array< uint, TEXT_TAB_END > langtab_num
Offset into langpack offs.
Definition strings.cpp:294
std::array< uint, TEXT_TAB_END > langtab_start
Offset into langpack offs.
Definition strings.cpp:295
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.
Handling of UTF-8 encoded data.
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: