OpenTTD Source: src/strings.cpp Source File

OpenTTD Source 14.0-RC3

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