OpenTTD Source: src/strings.cpp Source File

OpenTTD Source 14.0-beta3

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