OpenTTD Source: src/strings.cpp Source File

OpenTTD Source 12.2

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 "strings_func.h"
23 #include "rev.h"
24 #include "core/endian_func.hpp"
25 #include "date_func.h"
26 #include "vehicle_base.h"
27 #include "engine_base.h"
28 #include "language.h"
29 #include "townname_func.h"
30 #include "string_func.h"
31 #include "company_base.h"
32 #include "smallmap_gui.h"
33 #include "window_func.h"
34 #include "debug.h"
35 #include "game/game_text.hpp"
36 #include "network/network_content_gui.h"
37 #include <stack>
38
39 #include "table/strings.h"
40 #include "table/control_codes.h"
41
42 #include "safeguards.h"
43
44 std::string _config_language_file;
45 LanguageList _languages;
46 const LanguageMetadata *_current_language = nullptr;
47
48 TextDirection _current_text_dir;
49
50 #ifdef WITH_ICU_I18N
51 std::unique_ptr<icu::Collator> _current_collator;
52 #endif /* WITH_ICU_I18N */
53
54 static uint64 _global_string_params_data[20];
55 static WChar _global_string_params_type[20];
56 StringParameters _global_string_params(_global_string_params_data, 20, _global_string_params_type);
57
59 void StringParameters::ClearTypeInformation()
60 {
61 assert(this->type != nullptr);
62 MemSetT(this->type, 0, this->num_param);
63 }
64
65
70 int64 StringParameters::GetInt64(WChar type)
71 {
72 if (this->offset >= this->num_param) {
73 Debug(misc, 0, "Trying to read invalid string parameter");
74 return 0;
75 }
76 if (this->type != nullptr) {
77 if (this->type[this->offset] != 0 && this->type[this->offset] != type) {
78 Debug(misc, 0, "Trying to read string parameter with wrong type");
79 return 0;
80 }
81 this->type[this->offset] = type;
82 }
83 return this->data[this->offset++];
84 }
85
94 void SetDParamMaxValue(uint n, uint64 max_value, uint min_count, FontSize size)
95 {
96 uint num_digits = 1;
97 while (max_value >= 10) {
98 num_digits++;
99 max_value /= 10;
100 }
101 SetDParamMaxDigits(n, std::max(min_count, num_digits), size);
102 }
103
110 void SetDParamMaxDigits(uint n, uint count, FontSize size)
111 {
112 uint front = 0;
113 uint next = 0;
114 GetBroadestDigit(&front, &next, size);
115 uint64 val = count > 1 ? front : next;
116 for (; count > 1; count--) {
117 val = 10 * val + next;
118 }
119 SetDParam(n, val);
120 }
121
128 void CopyInDParam(int offs, const uint64 *src, int num)
129 {
130 MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
131 }
132
139 void CopyOutDParam(uint64 *dst, int offs, int num)
140 {
141 MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
142 }
143
152 void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
153 {
154 char buf[DRAW_STRING_BUFFER];
155 GetString(buf, string, lastof(buf));
156
157 MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num);
158 for (int i = 0; i < num; i++) {
159 if (_global_string_params.HasTypeInformation() && _global_string_params.GetTypeAtOffset(i) == SCC_RAW_STRING_POINTER) {
160 strings[i] = stredup((const char *)(size_t)_global_string_params.GetParam(i));
161 dst[i] = (size_t)strings[i];
162 } else {
163 strings[i] = nullptr;
164 }
165 }
166 }
167
168 static char *StationGetSpecialString(char *buff, int x, const char *last);
169 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
170 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
171
172 static char *FormatString(char *buff, const char *str, StringParameters *args, const char *last, uint case_index = 0, bool game_script = false, bool dry_run = false);
173
174 struct LanguagePack : public LanguagePackHeader {
175 char data[]; // list of strings
176 };
177
178 struct LanguagePackDeleter {
179 void operator()(LanguagePack *langpack)
180 {
181 /* LanguagePack is in fact reinterpreted char[], we need to reinterpret it back to free it properly. */
182 delete[] reinterpret_cast<char*>(langpack);
183 }
184 };
185
186 struct LoadedLanguagePack {
187 std::unique_ptr<LanguagePack, LanguagePackDeleter> langpack;
188
189 std::vector<char *> offsets;
190
191 std::array<uint, TEXT_TAB_END> langtab_num;
192 std::array<uint, TEXT_TAB_END> langtab_start;
193 };
194
195 static LoadedLanguagePack _langpack;
196
197 static bool _scan_for_gender_data = false;
198
199
200 const char *GetStringPtr(StringID string)
201 {
202 switch (GetStringTab(string)) {
203 case TEXT_TAB_GAMESCRIPT_START: return GetGameStringPtr(GetStringIndex(string));
204 /* 0xD0xx and 0xD4xx IDs have been converted earlier. */
205 case TEXT_TAB_OLD_NEWGRF: NOT_REACHED();
206 case TEXT_TAB_NEWGRF_START: return GetGRFStringPtr(GetStringIndex(string));
207 default: return _langpack.offsets[_langpack.langtab_start[GetStringTab(string)] + GetStringIndex(string)];
208 }
209 }
210
221 char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
222 {
223 if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
224
225 uint index = GetStringIndex(string);
226 StringTab tab = GetStringTab(string);
227
228 switch (tab) {
229 case TEXT_TAB_TOWN:
230 if (index >= 0xC0 && !game_script) {
231 return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
232 }
233 break;
234
235 case TEXT_TAB_SPECIAL:
236 if (index >= 0xE4 && !game_script) {
237 return GetSpecialNameString(buffr, index - 0xE4, args, last);
238 }
239 break;
240
241 case TEXT_TAB_OLD_CUSTOM:
242 /* Old table for custom names. This is no longer used */
243 if (!game_script) {
244 error("Incorrect conversion of custom name string.");
245 }
246 break;
247
248 case TEXT_TAB_GAMESCRIPT_START:
249 return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
250
251 case TEXT_TAB_OLD_NEWGRF:
252 NOT_REACHED();
253
254 case TEXT_TAB_NEWGRF_START:
255 return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
256
257 default:
258 break;
259 }
260
261 if (index >= _langpack.langtab_num[tab]) {
262 if (game_script) {
263 return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
264 }
265 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
266 }
267
268 return FormatString(buffr, GetStringPtr(string), args, last, case_index);
269 }
270
271 char *GetString(char *buffr, StringID string, const char *last)
272 {
273 _global_string_params.ClearTypeInformation();
274 _global_string_params.offset = 0;
275 return GetStringWithArgs(buffr, string, &_global_string_params, last);
276 }
277
284 std::string GetString(StringID string)
285 {
286 char buffer[DRAW_STRING_BUFFER];
287 GetString(buffer, string, lastof(buffer));
288 return buffer;
289 }
290
296 void SetDParamStr(uint n, const char *str)
297 {
298 SetDParam(n, (uint64)(size_t)str);
299 }
300
307 void SetDParamStr(uint n, const std::string &str)
308 {
309 SetDParamStr(n, str.c_str());
310 }
311
323 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
324 {
325 static const int max_digits = 20;
326 uint64 divisor = 10000000000000000000ULL;
327 zerofill += fractional_digits;
328 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
329
330 if (number < 0) {
331 buff += seprintf(buff, last, "-");
332 number = -number;
333 }
334
335 uint64 num = number;
336 uint64 tot = 0;
337 for (int i = 0; i < max_digits; i++) {
338 if (i == max_digits - fractional_digits) {
339 const char *decimal_separator = _settings_game.locale.digit_decimal_separator.c_str();
340 if (StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator;
341 buff += seprintf(buff, last, "%s", decimal_separator);
342 }
343
344 uint64 quot = 0;
345 if (num >= divisor) {
346 quot = num / divisor;
347 num = num % divisor;
348 }
349 if ((tot |= quot) || i >= max_digits - zerofill) {
350 buff += seprintf(buff, last, "%i", (int)quot);
351 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
352 }
353
354 divisor /= 10;
355 }
356
357 *buff = '\0';
358
359 return buff;
360 }
361
362 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
363 {
364 const char *separator = _settings_game.locale.digit_group_separator.c_str();
365 if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator;
366 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
367 }
368
369 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
370 {
371 return FormatNumber(buff, number, last, "");
372 }
373
374 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
375 {
376 return FormatNumber(buff, number, last, "", count);
377 }
378
379 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
380 {
381 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
382 }
383
391 static char *FormatBytes(char *buff, int64 number, const char *last)
392 {
393 assert(number >= 0);
394
395 /* 1 2^10 2^20 2^30 2^40 2^50 2^60 */
396 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
397 uint id = 1;
398 while (number >= 1024 * 1024) {
399 number /= 1024;
400 id++;
401 }
402
403 const char *decimal_separator = _settings_game.locale.digit_decimal_separator.c_str();
404 if (StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator;
405
406 if (number < 1024) {
407 id = 0;
408 buff += seprintf(buff, last, "%i", (int)number);
409 } else if (number < 1024 * 10) {
410 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
411 } else if (number < 1024 * 100) {
412 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
413 } else {
414 assert(number < 1024 * 1024);
415 buff += seprintf(buff, last, "%i", (int)number / 1024);
416 }
417
418 assert(id < lengthof(iec_prefixes));
419 buff += seprintf(buff, last, NBSP "%sB", iec_prefixes[id]);
420
421 return buff;
422 }
423
424 static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
425 {
426 YearMonthDay ymd;
427 ConvertDateToYMD(date, &ymd);
428
429 int64 args[] = {ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
430 StringParameters tmp_params(args);
431 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
432 }
433
434 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
435 {
436 YearMonthDay ymd;
437 ConvertDateToYMD(date, &ymd);
438
439 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
440 StringParameters tmp_params(args);
441 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
442 }
443
444 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
445 {
446 YearMonthDay ymd;
447 ConvertDateToYMD(date, &ymd);
448
449 char day[3];
450 char month[3];
451 /* We want to zero-pad the days and months */
452 seprintf(day, lastof(day), "%02i", ymd.day);
453 seprintf(month, lastof(month), "%02i", ymd.month + 1);
454
455 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
456 StringParameters tmp_params(args);
457 return FormatString(buff, GetStringPtr(str), &tmp_params, last);
458 }
459
460 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
461 {
462 /* We are going to make number absolute for printing, so
463 * keep this piece of data as we need it later on */
464 bool negative = number < 0;
465 const char *multiplier = "";
466
467 number *= spec->rate;
468
469 /* convert from negative */
470 if (number < 0) {
471 if (buff + Utf8CharLen(SCC_PUSH_COLOUR) > last) return buff;
472 buff += Utf8Encode(buff, SCC_PUSH_COLOUR);
473 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
474 buff += Utf8Encode(buff, SCC_RED);
475 buff = strecpy(buff, "-", last);
476 number = -number;
477 }
478
479 /* Add prefix part, following symbol_pos specification.
480 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
481 * The only remaining value is 1 (suffix), so everything that is not 1 */
482 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix.c_str(), last);
483
484 /* for huge numbers, compact the number into k or M */
485 if (compact) {
486 /* Take care of the 'k' rounding. Having 1 000 000 k
487 * and 1 000 M is inconsistent, so always use 1 000 M. */
488 if (number >= 1000000000 - 500) {
489 number = (number + 500000) / 1000000;
490 multiplier = NBSP "M";
491 } else if (number >= 1000000) {
492 number = (number + 500) / 1000;
493 multiplier = NBSP "k";
494 }
495 }
496
497 const char *separator = _settings_game.locale.digit_group_separator_currency.c_str();
498 if (StrEmpty(separator)) separator = _currency->separator.c_str();
499 if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator_currency;
500 buff = FormatNumber(buff, number, last, separator);
501 buff = strecpy(buff, multiplier, last);
502
503 /* Add suffix part, following symbol_pos specification.
504 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
505 * The only remaining value is 1 (prefix), so everything that is not 0 */
506 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix.c_str(), last);
507
508 if (negative) {
509 if (buff + Utf8CharLen(SCC_POP_COLOUR) > last) return buff;
510 buff += Utf8Encode(buff, SCC_POP_COLOUR);
511 *buff = '\0';
512 }
513
514 return buff;
515 }
516
523 static int DeterminePluralForm(int64 count, int plural_form)
524 {
525 /* The absolute value determines plurality */
526 uint64 n = abs(count);
527
528 switch (plural_form) {
529 default:
530 NOT_REACHED();
531
532 /* Two forms: singular used for one only.
533 * Used in:
534 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
535 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
536 case 0:
537 return n != 1 ? 1 : 0;
538
539 /* Only one form.
540 * Used in:
541 * Hungarian, Japanese, Turkish */
542 case 1:
543 return 0;
544
545 /* Two forms: singular used for 0 and 1.
546 * Used in:
547 * French, Brazilian Portuguese */
548 case 2:
549 return n > 1 ? 1 : 0;
550
551 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
552 * Note: Cases are out of order for hysterical reasons. '0' is last.
553 * Used in:
554 * Latvian */
555 case 3:
556 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
557
558 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
559 * Used in:
560 * Gaelige (Irish) */
561 case 4:
562 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
563
564 /* 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.
565 * Used in:
566 * Lithuanian */
567 case 5:
568 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
569
570 /* 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.
571 * Used in:
572 * Croatian, Russian, Ukrainian */
573 case 6:
574 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
575
576 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
577 * Used in:
578 * Polish */
579 case 7:
580 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
581
582 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
583 * Used in:
584 * Slovenian */
585 case 8:
586 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
587
588 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
589 * Used in:
590 * Icelandic */
591 case 9:
592 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
593
594 /* Three forms: special cases for 1, and 2 to 4
595 * Used in:
596 * Czech, Slovak */
597 case 10:
598 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
599
600 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
601 * Korean doesn't have the concept of plural, but depending on how a
602 * number is pronounced it needs another version of a particle.
603 * As such the plural system is misused to give this distinction.
604 */
605 case 11:
606 switch (n % 10) {
607 case 0: // yeong
608 case 1: // il
609 case 3: // sam
610 case 6: // yuk
611 case 7: // chil
612 case 8: // pal
613 return 0;
614
615 case 2: // i
616 case 4: // sa
617 case 5: // o
618 case 9: // gu
619 return 1;
620
621 default:
622 NOT_REACHED();
623 }
624
625 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
626 * Used in:
627 * Maltese */
628 case 12:
629 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
630 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
631 * Used in:
632 * Scottish Gaelic */
633 case 13:
634 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
635
636 /* Three forms: special cases for 1, 0 and numbers ending in 01 to 19.
637 * Used in:
638 * Romanian */
639 case 14:
640 return n == 1 ? 0 : (n == 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2;
641 }
642 }
643
644 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
645 {
646 /* <NUM> {Length of each string} {each string} */
647 uint n = (byte)*b++;
648 uint pos, i, mypos = 0;
649
650 for (i = pos = 0; i != n; i++) {
651 uint len = (byte)*b++;
652 if (i == form) mypos = pos;
653 pos += len;
654 }
655
656 *dst += seprintf(*dst, last, "%s", b + mypos);
657 return b + pos;
658 }
659
661 struct UnitConversion {
662 int multiplier;
663 int shift;
664
671 int64 ToDisplay(int64 input, bool round = true) const
672 {
673 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
674 }
675
683 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
684 {
685 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
686 }
687 };
688
690 struct Units {
691 UnitConversion c;
692 StringID s;
693 unsigned int decimal_places;
694 };
695
697 struct UnitsLong {
698 UnitConversion c;
699 StringID s;
700 StringID l;
701 };
702
704 static const Units _units_velocity[] = {
705 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL, 0 },
706 { { 103, 6}, STR_UNITS_VELOCITY_METRIC, 0 },
707 { { 1831, 12}, STR_UNITS_VELOCITY_SI, 0 },
708 { {37888, 16}, STR_UNITS_VELOCITY_GAMEUNITS, 1 },
709 };
710
712 static const Units _units_power[] = {
713 { { 1, 0}, STR_UNITS_POWER_IMPERIAL, 0 },
714 { {4153, 12}, STR_UNITS_POWER_METRIC, 0 },
715 { {6109, 13}, STR_UNITS_POWER_SI, 0 },
716 };
717
719 static const UnitsLong _units_weight[] = {
720 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
721 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC },
722 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI },
723 };
724
726 static const UnitsLong _units_volume[] = {
727 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
728 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC },
729 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI },
730 };
731
733 static const Units _units_force[] = {
734 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL, 0 },
735 { {3263, 5}, STR_UNITS_FORCE_METRIC, 0 },
736 { { 1, 0}, STR_UNITS_FORCE_SI, 0 },
737 };
738
740 static const Units _units_height[] = {
741 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL, 0 }, // "Wrong" conversion factor for more nicer GUI values
742 { { 1, 0}, STR_UNITS_HEIGHT_METRIC, 0 },
743 { { 1, 0}, STR_UNITS_HEIGHT_SI, 0 },
744 };
745
751 uint ConvertSpeedToDisplaySpeed(uint speed)
752 {
753 /* For historical reasons we don't want to mess with the
754 * conversion for speed. So, don't round it and keep the
755 * original conversion factors instead of the real ones. */
756 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed, false);
757 }
758
764 uint ConvertDisplaySpeedToSpeed(uint speed)
765 {
766 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed);
767 }
768
774 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
775 {
776 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed * 10, false) / 16;
777 }
778
784 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
785 {
786 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed * 16, true, 10);
787 }
796 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
797 {
798 uint orig_offset = args->offset;
799
800 /* When there is no array with types there is no need to do a dry run. */
801 if (args->HasTypeInformation() && !dry_run) {
802 if (UsingNewGRFTextStack()) {
803 /* Values from the NewGRF text stack are only copied to the normal
804 * argv array at the time they are encountered. That means that if
805 * another string command references a value later in the string it
806 * would fail. We solve that by running FormatString twice. The first
807 * pass makes sure the argv array is correctly filled and the second
808 * pass can reference later values without problems. */
809 struct TextRefStack *backup = CreateTextRefStackBackup();
810 FormatString(buff, str_arg, args, last, case_index, game_script, true);
811 RestoreTextRefStackBackup(backup);
812 } else {
813 FormatString(buff, str_arg, args, last, case_index, game_script, true);
814 }
815 /* We have to restore the original offset here to to read the correct values. */
816 args->offset = orig_offset;
817 }
818 WChar b = '\0';
819 uint next_substr_case_index = 0;
820 char *buf_start = buff;
821 std::stack<const char *, std::vector<const char *>> str_stack;
822 str_stack.push(str_arg);
823
824 for (;;) {
825 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
826 str_stack.pop();
827 }
828 if (str_stack.empty()) break;
829 const char *&str = str_stack.top();
830
831 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
832 /* We need to pass some stuff as it might be modified. */
833 //todo: should argve be passed here too?
834 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), args->GetDataLeft(), dry_run);
835 if (b == 0) continue;
836 }
837
838 switch (b) {
839 case SCC_ENCODED: {
840 uint64 sub_args_data[20];
841 WChar sub_args_type[20];
842 bool sub_args_need_free[20];
843 StringParameters sub_args(sub_args_data, 20, sub_args_type);
844
845 sub_args.ClearTypeInformation();
846 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
847
848 char *p;
849 uint32 stringid = strtoul(str, &p, 16);
850 if (*p != ':' && *p != '\0') {
851 while (*p != '\0') p++;
852 str = p;
853 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
854 break;
855 }
856 if (stringid >= TAB_SIZE_GAMESCRIPT) {
857 while (*p != '\0') p++;
858 str = p;
859 buff = strecat(buff, "(invalid StringID)", last);
860 break;
861 }
862
863 int i = 0;
864 while (*p != '\0' && i < 20) {
865 uint64 param;
866 const char *s = ++p;
867
868 /* Find the next value */
869 bool instring = false;
870 bool escape = false;
871 for (;; p++) {
872 if (*p == '\\') {
873 escape = true;
874 continue;
875 }
876 if (*p == '"' && escape) {
877 escape = false;
878 continue;
879 }
880 escape = false;
881
882 if (*p == '"') {
883 instring = !instring;
884 continue;
885 }
886 if (instring) {
887 continue;
888 }
889
890 if (*p == ':') break;
891 if (*p == '\0') break;
892 }
893
894 if (*s != '"') {
895 /* Check if we want to look up another string */
896 WChar l;
897 size_t len = Utf8Decode(&l, s);
898 bool lookup = (l == SCC_ENCODED);
899 if (lookup) s += len;
900
901 param = strtoull(s, &p, 16);
902
903 if (lookup) {
904 if (param >= TAB_SIZE_GAMESCRIPT) {
905 while (*p != '\0') p++;
906 str = p;
907 buff = strecat(buff, "(invalid sub-StringID)", last);
908 break;
909 }
910 param = MakeStringID(TEXT_TAB_GAMESCRIPT_START, param);
911 }
912
913 sub_args.SetParam(i++, param);
914 } else {
915 char *g = stredup(s);
916 g[p - s] = '\0';
917
918 sub_args_need_free[i] = true;
919 sub_args.SetParam(i++, (uint64)(size_t)g);
920 }
921 }
922 /* If we didn't error out, we can actually print the string. */
923 if (*str != '\0') {
924 str = p;
925 buff = GetStringWithArgs(buff, MakeStringID(TEXT_TAB_GAMESCRIPT_START, stringid), &sub_args, last, true);
926 }
927
928 for (int i = 0; i < 20; i++) {
929 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
930 }
931 break;
932 }
933
934 case SCC_NEWGRF_STRINL: {
935 StringID substr = Utf8Consume(&str);
936 str_stack.push(GetStringPtr(substr));
937 break;
938 }
939
940 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
941 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
942 str_stack.push(GetStringPtr(substr));
943 case_index = next_substr_case_index;
944 next_substr_case_index = 0;
945 break;
946 }
947
948
949 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
950 /* First read the meta data from the language file. */
951 uint offset = orig_offset + (byte)*str++;
952 int gender = 0;
953 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
954 /* Now we need to figure out what text to resolve, i.e.
955 * what do we need to draw? So get the actual raw string
956 * first using the control code to get said string. */
957 char input[4 + 1];
958 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
959 *p = '\0';
960
961 /* Now do the string formatting. */
962 char buf[256];
963 bool old_sgd = _scan_for_gender_data;
964 _scan_for_gender_data = true;
965 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, nullptr);
966 p = FormatString(buf, input, &tmp_params, lastof(buf));
967 _scan_for_gender_data = old_sgd;
968 *p = '\0';
969
970 /* And determine the string. */
971 const char *s = buf;
972 WChar c = Utf8Consume(&s);
973 /* Does this string have a gender, if so, set it */
974 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
975 }
976 str = ParseStringChoice(str, gender, &buff, last);
977 break;
978 }
979
980 /* This sets up the gender for the string.
981 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
982 case SCC_GENDER_INDEX: // {GENDER 0}
983 if (_scan_for_gender_data) {
984 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
985 *buff++ = *str++;
986 } else {
987 str++;
988 }
989 break;
990
991 case SCC_PLURAL_LIST: { // {P}
992 int plural_form = *str++; // contains the plural form for this string
993 uint offset = orig_offset + (byte)*str++;
994 int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
995 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
996 break;
997 }
998
999 case SCC_ARG_INDEX: { // Move argument pointer
1000 args->offset = orig_offset + (byte)*str++;
1001 break;
1002 }
1003
1004 case SCC_SET_CASE: { // {SET_CASE}
1005 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
1006 * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
1007 next_substr_case_index = (byte)*str++;
1008 break;
1009 }
1010
1011 case SCC_SWITCH_CASE: { // {Used to implement case switching}
1012 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
1013 * Each LEN is printed using 2 bytes in big endian order. */
1014 uint num = (byte)*str++;
1015 while (num) {
1016 if ((byte)str[0] == case_index) {
1017 /* Found the case, adjust str pointer and continue */
1018 str += 3;
1019 break;
1020 }
1021 /* Otherwise skip to the next case */
1022 str += 3 + (str[1] << 8) + str[2];
1023 num--;
1024 }
1025 break;
1026 }
1027
1028 case SCC_REVISION: // {REV}
1029 buff = strecpy(buff, _openttd_revision, last);
1030 break;
1031
1032 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1033 if (game_script) break;
1034 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
1035 buff = FormatString(buff, str, args, last);
1036 break;
1037 }
1038
1039 case SCC_STRING: {// {STRING}
1040 StringID str = args->GetInt32(SCC_STRING);
1041 if (game_script && GetStringTab(str) != TEXT_TAB_GAMESCRIPT_START) break;
1042 /* WARNING. It's prohibited for the included string to consume any arguments.
1043 * For included strings that consume argument, you should use STRING1, STRING2 etc.
1044 * To debug stuff you can set argv to nullptr and it will tell you */
1045 StringParameters tmp_params(args->GetDataPointer(), args->GetDataLeft(), nullptr);
1046 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
1047 next_substr_case_index = 0;
1048 break;
1049 }
1050
1051 case SCC_STRING1:
1052 case SCC_STRING2:
1053 case SCC_STRING3:
1054 case SCC_STRING4:
1055 case SCC_STRING5:
1056 case SCC_STRING6:
1057 case SCC_STRING7: { // {STRING1..7}
1058 /* Strings that consume arguments */
1059 StringID str = args->GetInt32(b);
1060 if (game_script && GetStringTab(str) != TEXT_TAB_GAMESCRIPT_START) break;
1061 uint size = b - SCC_STRING1 + 1;
1062 if (game_script && size > args->GetDataLeft()) {
1063 buff = strecat(buff, "(too many parameters)", last);
1064 } else {
1065 StringParameters sub_args(*args, size);
1066 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
1067 }
1068 next_substr_case_index = 0;
1069 break;
1070 }
1071
1072 case SCC_COMMA: // {COMMA}
1073 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
1074 break;
1075
1076 case SCC_DECIMAL: {// {DECIMAL}
1077 int64 number = args->GetInt64(SCC_DECIMAL);
1078 int digits = args->GetInt32(SCC_DECIMAL);
1079 buff = FormatCommaNumber(buff, number, last, digits);
1080 break;
1081 }
1082
1083 case SCC_NUM: // {NUM}
1084 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
1085 break;
1086
1087 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1088 int64 num = args->GetInt64();
1089 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
1090 break;
1091 }
1092
1093 case SCC_HEX: // {HEX}
1094 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
1095 break;
1096
1097 case SCC_BYTES: // {BYTES}
1098 buff = FormatBytes(buff, args->GetInt64(), last);
1099 break;
1100
1101 case SCC_CARGO_TINY: { // {CARGO_TINY}
1102 /* Tiny description of cargotypes. Layout:
1103 * param 1: cargo type
1104 * param 2: cargo count */
1105 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
1106 if (cargo >= CargoSpec::GetArraySize()) break;
1107
1108 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1109 int64 amount = 0;
1110 switch (cargo_str) {
1111 case STR_TONS:
1112 amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64());
1113 break;
1114
1115 case STR_LITERS:
1116 amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64());
1117 break;
1118
1119 default: {
1120 amount = args->GetInt64();
1121 break;
1122 }
1123 }
1124
1125 buff = FormatCommaNumber(buff, amount, last);
1126 break;
1127 }
1128
1129 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1130 /* Short description of cargotypes. Layout:
1131 * param 1: cargo type
1132 * param 2: cargo count */
1133 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
1134 if (cargo >= CargoSpec::GetArraySize()) break;
1135
1136 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1137 switch (cargo_str) {
1138 case STR_TONS: {
1139 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1140 int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1141 StringParameters tmp_params(args_array);
1142 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1143 break;
1144 }
1145
1146 case STR_LITERS: {
1147 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1148 int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1149 StringParameters tmp_params(args_array);
1150 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1151 break;
1152 }
1153
1154 default: {
1155 StringParameters tmp_params(*args, 1);
1156 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
1157 break;
1158 }
1159 }
1160 break;
1161 }
1162
1163 case SCC_CARGO_LONG: { // {CARGO_LONG}
1164 /* First parameter is cargo type, second parameter is cargo count */
1165 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
1166 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
1167
1168 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1169 StringParameters tmp_args(*args, 1);
1170 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
1171 break;
1172 }
1173
1174 case SCC_CARGO_LIST: { // {CARGO_LIST}
1175 CargoTypes cmask = args->GetInt64(SCC_CARGO_LIST);
1176 bool first = true;
1177
1178 for (const auto &cs : _sorted_cargo_specs) {
1179 if (!HasBit(cmask, cs->Index())) continue;
1180
1181 if (buff >= last - 2) break; // ',' and ' '
1182
1183 if (first) {
1184 first = false;
1185 } else {
1186 /* Add a comma if this is not the first item */
1187 *buff++ = ',';
1188 *buff++ = ' ';
1189 }
1190
1191 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
1192 }
1193
1194 /* If first is still true then no cargo is accepted */
1195 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
1196
1197 *buff = '\0';
1198 next_substr_case_index = 0;
1199
1200 /* Make sure we detect any buffer overflow */
1201 assert(buff < last);
1202 break;
1203 }
1204
1205 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1206 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
1207 break;
1208
1209 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1210 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
1211 break;
1212
1213 case SCC_DATE_TINY: // {DATE_TINY}
1214 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
1215 break;
1216
1217 case SCC_DATE_SHORT: // {DATE_SHORT}
1218 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
1219 next_substr_case_index = 0;
1220 break;
1221
1222 case SCC_DATE_LONG: // {DATE_LONG}
1223 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
1224 next_substr_case_index = 0;
1225 break;
1226
1227 case SCC_DATE_ISO: // {DATE_ISO}
1228 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
1229 break;
1230
1231 case SCC_FORCE: { // {FORCE}
1232 assert(_settings_game.locale.units_force < lengthof(_units_force));
1233 int64 args_array[1] = {_units_force[_settings_game.locale.units_force].c.ToDisplay(args->GetInt64())};
1234 StringParameters tmp_params(args_array);
1235 buff = FormatString(buff, GetStringPtr(_units_force[_settings_game.locale.units_force].s), &tmp_params, last);
1236 break;
1237 }
1238
1239 case SCC_HEIGHT: { // {HEIGHT}
1240 assert(_settings_game.locale.units_height < lengthof(_units_height));
1241 int64 args_array[] = {_units_height[_settings_game.locale.units_height].c.ToDisplay(args->GetInt64())};
1242 StringParameters tmp_params(args_array);
1243 buff = FormatString(buff, GetStringPtr(_units_height[_settings_game.locale.units_height].s), &tmp_params, last);
1244 break;
1245 }
1246
1247 case SCC_POWER: { // {POWER}
1248 assert(_settings_game.locale.units_power < lengthof(_units_power));
1249 int64 args_array[1] = {_units_power[_settings_game.locale.units_power].c.ToDisplay(args->GetInt64())};
1250 StringParameters tmp_params(args_array);
1251 buff = FormatString(buff, GetStringPtr(_units_power[_settings_game.locale.units_power].s), &tmp_params, last);
1252 break;
1253 }
1254
1255 case SCC_VELOCITY: { // {VELOCITY}
1256 assert(_settings_game.locale.units_velocity < lengthof(_units_velocity));
1257 unsigned int decimal_places = _units_velocity[_settings_game.locale.units_velocity].decimal_places;
1258 uint64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY)), decimal_places};
1259 StringParameters tmp_params(args_array, decimal_places ? 2 : 1, nullptr);
1260 buff = FormatString(buff, GetStringPtr(_units_velocity[_settings_game.locale.units_velocity].s), &tmp_params, last);
1261 break;
1262 }
1263
1264 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1265 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1266 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1267 StringParameters tmp_params(args_array);
1268 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].s), &tmp_params, last);
1269 break;
1270 }
1271
1272 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1273 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1274 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
1275 StringParameters tmp_params(args_array);
1276 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1277 break;
1278 }
1279
1280 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1281 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1282 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1283 StringParameters tmp_params(args_array);
1284 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].s), &tmp_params, last);
1285 break;
1286 }
1287
1288 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1289 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1290 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
1291 StringParameters tmp_params(args_array);
1292 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1293 break;
1294 }
1295
1296 case SCC_COMPANY_NAME: { // {COMPANY}
1297 const Company *c = Company::GetIfValid(args->GetInt32());
1298 if (c == nullptr) break;
1299
1300 if (!c->name.empty()) {
1301 int64 args_array[] = {(int64)(size_t)c->name.c_str()};
1302 StringParameters tmp_params(args_array);
1303 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1304 } else {
1305 int64 args_array[] = {c->name_2};
1306 StringParameters tmp_params(args_array);
1307 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
1308 }
1309 break;
1310 }
1311
1312 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1313 CompanyID company = (CompanyID)args->GetInt32();
1314
1315 /* Nothing is added for AI or inactive companies */
1316 if (Company::IsValidHumanID(company)) {
1317 int64 args_array[] = {company + 1};
1318 StringParameters tmp_params(args_array);
1319 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
1320 }
1321 break;
1322 }
1323
1324 case SCC_DEPOT_NAME: { // {DEPOT}
1325 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
1326 if (vt == VEH_AIRCRAFT) {
1327 uint64 args_array[] = {(uint64)args->GetInt32()};
1328 WChar types_array[] = {SCC_STATION_NAME};
1329 StringParameters tmp_params(args_array, 1, types_array);
1330 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
1331 break;
1332 }
1333
1334 const Depot *d = Depot::Get(args->GetInt32());
1335 if (!d->name.empty()) {
1336 int64 args_array[] = {(int64)(size_t)d->name.c_str()};
1337 StringParameters tmp_params(args_array);
1338 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1339 } else {
1340 int64 args_array[] = {d->town->index, d->town_cn + 1};
1341 StringParameters tmp_params(args_array);
1342 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
1343 }
1344 break;
1345 }
1346
1347 case SCC_ENGINE_NAME: { // {ENGINE}
1348 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
1349 if (e == nullptr) break;
1350
1351 if (!e->name.empty() && e->IsEnabled()) {
1352 int64 args_array[] = {(int64)(size_t)e->name.c_str()};
1353 StringParameters tmp_params(args_array);
1354 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1355 } else {
1356 StringParameters tmp_params(nullptr, 0, nullptr);
1357 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
1358 }
1359 break;
1360 }
1361
1362 case SCC_GROUP_NAME: { // {GROUP}
1363 const Group *g = Group::GetIfValid(args->GetInt32());
1364 if (g == nullptr) break;
1365
1366 if (!g->name.empty()) {
1367 int64 args_array[] = {(int64)(size_t)g->name.c_str()};
1368 StringParameters tmp_params(args_array);
1369 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1370 } else {
1371 int64 args_array[] = {g->index};
1372 StringParameters tmp_params(args_array);
1373
1374 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
1375 }
1376 break;
1377 }
1378
1379 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1380 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
1381 if (i == nullptr) break;
1382
1383 if (_scan_for_gender_data) {
1384 /* Gender is defined by the industry type.
1385 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1386 StringParameters tmp_params(nullptr, 0, nullptr);
1387 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
1388 } else {
1389 /* First print the town name and the industry type name. */
1390 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
1391 StringParameters tmp_params(args_array);
1392
1393 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
1394 }
1395 next_substr_case_index = 0;
1396 break;
1397 }
1398
1399 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1400 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
1401 if (c == nullptr) break;
1402
1403 if (!c->president_name.empty()) {
1404 int64 args_array[] = {(int64)(size_t)c->president_name.c_str()};
1405 StringParameters tmp_params(args_array);
1406 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1407 } else {
1408 int64 args_array[] = {c->president_name_2};
1409 StringParameters tmp_params(args_array);
1410 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
1411 }
1412 break;
1413 }
1414
1415 case SCC_STATION_NAME: { // {STATION}
1416 StationID sid = args->GetInt32(SCC_STATION_NAME);
1417 const Station *st = Station::GetIfValid(sid);
1418
1419 if (st == nullptr) {
1420 /* The station doesn't exist anymore. The only place where we might
1421 * be "drawing" an invalid station is in the case of cargo that is
1422 * in transit. */
1423 StringParameters tmp_params(nullptr, 0, nullptr);
1424 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
1425 break;
1426 }
1427
1428 if (!st->name.empty()) {
1429 int64 args_array[] = {(int64)(size_t)st->name.c_str()};
1430 StringParameters tmp_params(args_array);
1431 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1432 } else {
1433 StringID str = st->string_id;
1434 if (st->indtype != IT_INVALID) {
1435 /* Special case where the industry provides the name for the station */
1436 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1437
1438 /* Industry GRFs can change which might remove the station name and
1439 * thus cause very strange things. Here we check for that before we
1440 * actually set the station name. */
1441 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1442 str = indsp->station_name;
1443 }
1444 }
1445
1446 uint64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
1447 WChar types_array[] = {0, SCC_TOWN_NAME, SCC_NUM};
1448 StringParameters tmp_params(args_array, 3, types_array);
1449 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1450 }
1451 break;
1452 }
1453
1454 case SCC_TOWN_NAME: { // {TOWN}
1455 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
1456 if (t == nullptr) break;
1457
1458 if (!t->name.empty()) {
1459 int64 args_array[] = {(int64)(size_t)t->name.c_str()};
1460 StringParameters tmp_params(args_array);
1461 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1462 } else {
1463 buff = GetTownName(buff, t, last);
1464 }
1465 break;
1466 }
1467
1468 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1469 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
1470 if (wp == nullptr) break;
1471
1472 if (!wp->name.empty()) {
1473 int64 args_array[] = {(int64)(size_t)wp->name.c_str()};
1474 StringParameters tmp_params(args_array);
1475 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1476 } else {
1477 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
1478 StringParameters tmp_params(args_array);
1479 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1480 if (wp->town_cn != 0) str++;
1481 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1482 }
1483 break;
1484 }
1485
1486 case SCC_VEHICLE_NAME: { // {VEHICLE}
1487 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
1488 if (v == nullptr) break;
1489
1490 if (!v->name.empty()) {
1491 int64 args_array[] = {(int64)(size_t)v->name.c_str()};
1492 StringParameters tmp_params(args_array);
1493 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1494 } else if (v->group_id != DEFAULT_GROUP) {
1495 /* The vehicle has no name, but is member of a group, so print group name */
1496 int64 args_array[] = {v->group_id, v->unitnumber};
1497 StringParameters tmp_params(args_array);
1498 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_VEHICLE_NAME, &tmp_params, last);
1499 } else {
1500 int64 args_array[] = {v->unitnumber};
1501 StringParameters tmp_params(args_array);
1502
1503 StringID str;
1504 switch (v->type) {
1505 default: str = STR_INVALID_VEHICLE; break;
1506 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
1507 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
1508 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
1509 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
1510 }
1511
1512 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1513 }
1514 break;
1515 }
1516
1517 case SCC_SIGN_NAME: { // {SIGN}
1518 const Sign *si = Sign::GetIfValid(args->GetInt32());
1519 if (si == nullptr) break;
1520
1521 if (!si->name.empty()) {
1522 int64 args_array[] = {(int64)(size_t)si->name.c_str()};
1523 StringParameters tmp_params(args_array);
1524 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1525 } else {
1526 StringParameters tmp_params(nullptr, 0, nullptr);
1527 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
1528 }
1529 break;
1530 }
1531
1532 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1533 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
1534 break;
1535 }
1536
1537 default:
1538 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
1539 break;
1540 }
1541 }
1542 *buff = '\0';
1543 return buff;
1544 }
1545
1546
1547 static char *StationGetSpecialString(char *buff, int x, const char *last)
1548 {
1549 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
1550 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
1551 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
1552 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
1553 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
1554 *buff = '\0';
1555 return buff;
1556 }
1557
1558 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
1559 {
1560 return GenerateTownNameString(buff, last, ind, seed);
1561 }
1562
1563 static const char * const _silly_company_names[] = {
1564 "Bloggs Brothers",
1565 "Tiny Transport Ltd.",
1566 "Express Travel",
1567 "Comfy-Coach & Co.",
1568 "Crush & Bump Ltd.",
1569 "Broken & Late Ltd.",
1570 "Sam Speedy & Son",
1571 "Supersonic Travel",
1572 "Mike's Motors",
1573 "Lightning International",
1574 "Pannik & Loozit Ltd.",
1575 "Inter-City Transport",
1576 "Getout & Pushit Ltd."
1577 };
1578
1579 static const char * const _surname_list[] = {
1580 "Adams",
1581 "Allan",
1582 "Baker",
1583 "Bigwig",
1584 "Black",
1585 "Bloggs",
1586 "Brown",
1587 "Campbell",
1588 "Gordon",
1589 "Hamilton",
1590 "Hawthorn",
1591 "Higgins",
1592 "Green",
1593 "Gribble",
1594 "Jones",
1595 "McAlpine",
1596 "MacDonald",
1597 "McIntosh",
1598 "Muir",
1599 "Murphy",
1600 "Nelson",
1601 "O'Donnell",
1602 "Parker",
1603 "Phillips",
1604 "Pilkington",
1605 "Quigley",
1606 "Sharkey",
1607 "Thomson",
1608 "Watkins"
1609 };
1610
1611 static const char * const _silly_surname_list[] = {
1612 "Grumpy",
1613 "Dozy",
1614 "Speedy",
1615 "Nosey",
1616 "Dribble",
1617 "Mushroom",
1618 "Cabbage",
1619 "Sniffle",
1620 "Fishy",
1621 "Swindle",
1622 "Sneaky",
1623 "Nutkins"
1624 };
1625
1626 static const char _initial_name_letters[] = {
1627 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1628 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1629 };
1630
1631 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
1632 {
1633 const char * const *base;
1634 uint num;
1635
1636 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1637 base = _silly_surname_list;
1638 num = lengthof(_silly_surname_list);
1639 } else {
1640 base = _surname_list;
1641 num = lengthof(_surname_list);
1642 }
1643
1644 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
1645 buff = strecpy(buff, " & Co.", last);
1646
1647 return buff;
1648 }
1649
1650 static char *GenPresidentName(char *buff, uint32 x, const char *last)
1651 {
1652 char initial[] = "?. ";
1653 const char * const *base;
1654 uint num;
1655 uint i;
1656
1657 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
1658 buff = strecpy(buff, initial, last);
1659
1660 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
1661 if (i < sizeof(_initial_name_letters)) {
1662 initial[0] = _initial_name_letters[i];
1663 buff = strecpy(buff, initial, last);
1664 }
1665
1666 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1667 base = _silly_surname_list;
1668 num = lengthof(_silly_surname_list);
1669 } else {
1670 base = _surname_list;
1671 num = lengthof(_surname_list);
1672 }
1673
1674 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
1675
1676 return buff;
1677 }
1678
1679 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
1680 {
1681 switch (ind) {
1682 case 1: // not used
1683 return strecpy(buff, _silly_company_names[std::min<uint>(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
1684
1685 case 2: // used for Foobar & Co company names
1686 return GenAndCoName(buff, args->GetInt32(), last);
1687
1688 case 3: // President name
1689 return GenPresidentName(buff, args->GetInt32(), last);
1690 }
1691
1692 /* town name? */
1693 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1694 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
1695 return strecpy(buff, " Transport", last);
1696 }
1697
1698 NOT_REACHED();
1699 }
1700
1705 bool LanguagePackHeader::IsValid() const
1706 {
1707 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1708 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1709 this->plural_form < LANGUAGE_MAX_PLURAL &&
1710 this->text_dir <= 1 &&
1711 this->newgrflangid < MAX_LANG &&
1712 this->num_genders < MAX_NUM_GENDERS &&
1713 this->num_cases < MAX_NUM_CASES &&
1714 StrValid(this->name, lastof(this->name)) &&
1715 StrValid(this->own_name, lastof(this->own_name)) &&
1716 StrValid(this->isocode, lastof(this->isocode)) &&
1717 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
1718 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
1719 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
1720 }
1721
1725 bool LanguagePackHeader::IsReasonablyFinished() const
1726 {
1727 /* "Less than 25% missing" is "sufficiently finished". */
1728 return 4 * this->missing < LANGUAGE_TOTAL_STRINGS;
1729 }
1730
1736 bool ReadLanguagePack(const LanguageMetadata *lang)
1737 {
1738 /* Current language pack */
1739 size_t len = 0;
1740 std::unique_ptr<LanguagePack, LanguagePackDeleter> lang_pack(reinterpret_cast<LanguagePack *>(ReadFileToMem(lang->file, len, 1U << 20).release()));
1741 if (!lang_pack) return false;
1742
1743 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1744 const char *end = (char *)lang_pack.get() + len + 1;
1745
1746 /* We need at least one byte of lang_pack->data */
1747 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1748 return false;
1749 }
1750
1751 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1752 for (uint i = 0; i < TEXT_TAB_END; i++) {
1753 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
1754 }
1755 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
1756
1757 std::array<uint, TEXT_TAB_END> tab_start, tab_num;
1758
1759 uint count = 0;
1760 for (uint i = 0; i < TEXT_TAB_END; i++) {
1761 uint16 num = lang_pack->offsets[i];
1762 if (num > TAB_SIZE) return false;
1763
1764 tab_start[i] = count;
1765 tab_num[i] = num;
1766 count += num;
1767 }
1768
1769 /* Allocate offsets */
1770 std::vector<char *> offs(count);
1771
1772 /* Fill offsets */
1773 char *s = lang_pack->data;
1774 len = (byte)*s++;
1775 for (uint i = 0; i < count; i++) {
1776 if (s + len >= end) return false;
1777
1778 if (len >= 0xC0) {
1779 len = ((len & 0x3F) << 8) + (byte)*s++;
1780 if (s + len >= end) return false;
1781 }
1782 offs[i] = s;
1783 s += len;
1784 len = (byte)*s;
1785 *s++ = '\0'; // zero terminate the string
1786 }
1787
1788 _langpack.langpack = std::move(lang_pack);
1789 _langpack.offsets = std::move(offs);
1790 _langpack.langtab_num = tab_num;
1791 _langpack.langtab_start = tab_start;
1792
1793 _current_language = lang;
1794 _current_text_dir = (TextDirection)_current_language->text_dir;
1795 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
1796 _config_language_file = c_file;
1797 SetCurrentGrfLangID(_current_language->newgrflangid);
1798
1799 #ifdef _WIN32
1800 extern void Win32SetCurrentLocaleName(const char *iso_code);
1801 Win32SetCurrentLocaleName(_current_language->isocode);
1802 #endif
1803
1804 #ifdef WITH_COCOA
1805 extern void MacOSSetCurrentLocaleName(const char *iso_code);
1806 MacOSSetCurrentLocaleName(_current_language->isocode);
1807 #endif
1808
1809 #ifdef WITH_ICU_I18N
1810 /* Create a collator instance for our current locale. */
1811 UErrorCode status = U_ZERO_ERROR;
1812 _current_collator.reset(icu::Collator::createInstance(icu::Locale(_current_language->isocode), status));
1813 /* Sort number substrings by their numerical value. */
1814 if (_current_collator) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
1815 /* Avoid using the collator if it is not correctly set. */
1816 if (U_FAILURE(status)) {
1817 _current_collator.reset();
1818 }
1819 #endif /* WITH_ICU_I18N */
1820
1821 /* Some lists need to be sorted again after a language change. */
1822 ReconsiderGameScriptLanguage();
1823 InitializeSortedCargoSpecs();
1824 SortIndustryTypes();
1825 BuildIndustriesLegend();
1826 BuildContentTypeStringList();
1827 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
1828 InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
1829 InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
1830 InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
1831 InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
1832 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
1833 InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
1834
1835 return true;
1836 }
1837
1838 /* Win32 implementation in win32.cpp.
1839 * OS X implementation in os/macosx/macos.mm. */
1840 #if !(defined(_WIN32) || defined(__APPLE__))
1841
1849 const char *GetCurrentLocale(const char *param)
1850 {
1851 const char *env;
1852
1853 env = std::getenv("LANGUAGE");
1854 if (env != nullptr) return env;
1855
1856 env = std::getenv("LC_ALL");
1857 if (env != nullptr) return env;
1858
1859 if (param != nullptr) {
1860 env = std::getenv(param);
1861 if (env != nullptr) return env;
1862 }
1863
1864 return std::getenv("LANG");
1865 }
1866 #else
1867 const char *GetCurrentLocale(const char *param);
1868 #endif /* !(defined(_WIN32) || defined(__APPLE__)) */
1869
1870 bool StringIDSorter(const StringID &a, const StringID &b)
1871 {
1872 char stra[512];
1873 char strb[512];
1874 GetString(stra, a, lastof(stra));
1875 GetString(strb, b, lastof(strb));
1876
1877 return strnatcmp(stra, strb) < 0;
1878 }
1879
1885 const LanguageMetadata *GetLanguage(byte newgrflangid)
1886 {
1887 for (const LanguageMetadata &lang : _languages) {
1888 if (newgrflangid == lang.newgrflangid) return &lang;
1889 }
1890
1891 return nullptr;
1892 }
1893
1900 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
1901 {
1902 FILE *f = fopen(file, "rb");
1903 if (f == nullptr) return false;
1904
1905 size_t read = fread(hdr, sizeof(*hdr), 1, f);
1906 fclose(f);
1907
1908 bool ret = read == 1 && hdr->IsValid();
1909
1910 /* Convert endianness for the windows language ID */
1911 if (ret) {
1912 hdr->missing = FROM_LE16(hdr->missing);
1913 hdr->winlangid = FROM_LE16(hdr->winlangid);
1914 }
1915 return ret;
1916 }
1917
1922 static void GetLanguageList(const char *path)
1923 {
1924 DIR *dir = ttd_opendir(path);
1925 if (dir != nullptr) {
1926 struct dirent *dirent;
1927 while ((dirent = readdir(dir)) != nullptr) {
1928 std::string d_name = FS2OTTD(dirent->d_name);
1929 const char *extension = strrchr(d_name.c_str(), '.');
1930
1931 /* Not a language file */
1932 if (extension == nullptr || strcmp(extension, ".lng") != 0) continue;
1933
1934 LanguageMetadata lmd;
1935 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name.c_str());
1936
1937 /* Check whether the file is of the correct version */
1938 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
1939 Debug(misc, 3, "{} is not a valid language file", lmd.file);
1940 } else if (GetLanguage(lmd.newgrflangid) != nullptr) {
1941 Debug(misc, 3, "{}'s language ID is already known", lmd.file);
1942 } else {
1943 _languages.push_back(lmd);
1944 }
1945 }
1946 closedir(dir);
1947 }
1948 }
1949
1954 void InitializeLanguagePacks()
1955 {
1956 for (Searchpath sp : _valid_searchpaths) {
1957 std::string path = FioGetDirectory(sp, LANG_DIR);
1958 GetLanguageList(path.c_str());
1959 }
1960 if (_languages.size() == 0) usererror("No available language packs (invalid versions?)");
1961
1962 /* Acquire the locale of the current system */
1963 const char *lang = GetCurrentLocale("LC_MESSAGES");
1964 if (lang == nullptr) lang = "en_GB";
1965
1966 const LanguageMetadata *chosen_language = nullptr;
1967 const LanguageMetadata *language_fallback = nullptr;
1968 const LanguageMetadata *en_GB_fallback = _languages.data();
1969
1970 /* Find a proper language. */
1971 for (const LanguageMetadata &lng : _languages) {
1972 /* We are trying to find a default language. The priority is by
1973 * configuration file, local environment and last, if nothing found,
1974 * English. */
1975 const char *lang_file = strrchr(lng.file, PATHSEPCHAR) + 1;
1976 if (_config_language_file == lang_file) {
1977 chosen_language = &lng;
1978 break;
1979 }
1980
1981 if (strcmp (lng.isocode, "en_GB") == 0) en_GB_fallback = &lng;
1982
1983 /* Only auto-pick finished translations */
1984 if (!lng.IsReasonablyFinished()) continue;
1985
1986 if (strncmp(lng.isocode, lang, 5) == 0) chosen_language = &lng;
1987 if (strncmp(lng.isocode, lang, 2) == 0) language_fallback = &lng;
1988 }
1989
1990 /* We haven't found the language in the config nor the one in the locale.
1991 * Now we set it to one of the fallback languages */
1992 if (chosen_language == nullptr) {
1993 chosen_language = (language_fallback != nullptr) ? language_fallback : en_GB_fallback;
1994 }
1995
1996 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
1997 }
1998
2003 const char *GetCurrentLanguageIsoCode()
2004 {
2005 return _langpack.langpack->isocode;
2006 }
2007
2012 bool MissingGlyphSearcher::FindMissingGlyphs()
2013 {
2014 InitFreeType(this->Monospace());
2015 const Sprite *question_mark[FS_END];
2016
2017 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
2018 question_mark[size] = GetGlyph(size, '?');
2019 }
2020
2021 this->Reset();
2022 for (const char *text = this->NextString(); text != nullptr; text = this->NextString()) {
2023 FontSize size = this->DefaultSize();
2024 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
2025 if (c >= SCC_FIRST_FONT && c <= SCC_LAST_FONT) {
2026 size = (FontSize)(c - SCC_FIRST_FONT);
2027 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
2028 /* The character is printable, but not in the normal font. This is the case we were testing for. */
2029 std::string size_name;
2030
2031 switch (size) {
2032 case 0: size_name = "medium"; break;
2033 case 1: size_name = "small"; break;
2034 case 2: size_name = "large"; break;
2035 case 3: size_name = "mono"; break;
2036 default: NOT_REACHED();
2037 }
2038
2039 Debug(freetype, 0, "Font is missing glyphs to display char 0x{:X} in {} font size", (int)c, size_name);
2040 return true;
2041 }
2042 }
2043 }
2044 return false;
2045 }
2046
2048 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
2049 uint i;
2050 uint j;
2051
2052 void Reset() override
2053 {
2054 this->i = 0;
2055 this->j = 0;
2056 }
2057
2058 FontSize DefaultSize() override
2059 {
2060 return FS_NORMAL;
2061 }
2062
2063 const char *NextString() override
2064 {
2065 if (this->i >= TEXT_TAB_END) return nullptr;
2066
2067 const char *ret = _langpack.offsets[_langpack.langtab_start[this->i] + this->j];
2068
2069 this->j++;
2070 while (this->i < TEXT_TAB_END && this->j >= _langpack.langtab_num[this->i]) {
2071 this->i++;
2072 this->j = 0;
2073 }
2074
2075 return ret;
2076 }
2077
2078 bool Monospace() override
2079 {
2080 return false;
2081 }
2082
2083 void SetFontNames(FreeTypeSettings *settings, const char *font_name, const void *os_data) override
2084 {
2085 #if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2086 settings->small.font = font_name;
2087 settings->medium.font = font_name;
2088 settings->large.font = font_name;
2089
2090 settings->small.os_handle = os_data;
2091 settings->medium.os_handle = os_data;
2092 settings->large.os_handle = os_data;
2093 #endif
2094 }
2095 };
2096
2110 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2111 {
2112 static LanguagePackGlyphSearcher pack_searcher;
2113 if (searcher == nullptr) searcher = &pack_searcher;
2114 bool bad_font = !base_font || searcher->FindMissingGlyphs();
2115 #if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2116 if (bad_font) {
2117 /* We found an unprintable character... lets try whether we can find
2118 * a fallback font that can print the characters in the current language. */
2119 bool any_font_configured = !_freetype.medium.font.empty();
2120 FreeTypeSettings backup = _freetype;
2121
2122 _freetype.mono.os_handle = nullptr;
2123 _freetype.medium.os_handle = nullptr;
2124
2125 bad_font = !SetFallbackFont(&_freetype, _langpack.langpack->isocode, _langpack.langpack->winlangid, searcher);
2126
2127 _freetype = backup;
2128
2129 if (!bad_font && any_font_configured) {
2130 /* If the user configured a bad font, and we found a better one,
2131 * show that we loaded the better font instead of the configured one.
2132 * The colour 'character' might change in the
2133 * future, so for safety we just Utf8 Encode it into the string,
2134 * which takes exactly three characters, so it replaces the "XXX"
2135 * with the colour marker. */
2136 static char *err_str = stredup("XXXThe current font is missing some of the characters used in the texts for this language. Using system fallback font instead.");
2137 Utf8Encode(err_str, SCC_YELLOW);
2138 SetDParamStr(0, err_str);
2139 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2140 }
2141
2142 if (bad_font && base_font) {
2143 /* Our fallback font does miss characters too, so keep the
2144 * user chosen font as that is more likely to be any good than
2145 * the wild guess we made */
2146 InitFreeType(searcher->Monospace());
2147 }
2148 }
2149 #endif
2150
2151 if (bad_font) {
2152 /* All attempts have failed. Display an error. As we do not want the string to be translated by
2153 * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
2154 * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
2155 * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
2156 * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
2157 static char *err_str = stredup("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.");
2158 Utf8Encode(err_str, SCC_YELLOW);
2159 SetDParamStr(0, err_str);
2160 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2161
2162 /* Reset the font width */
2163 LoadStringWidthTable(searcher->Monospace());
2164 return;
2165 }
2166
2167 /* Update the font with cache */
2168 LoadStringWidthTable(searcher->Monospace());
2169
2170 #if !defined(WITH_ICU_LX) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
2171 /*
2172 * For right-to-left languages we need the ICU library. If
2173 * we do not have support for that library we warn the user
2174 * about it with a message. As we do not want the string to
2175 * be translated by the translators, we 'force' it into the
2176 * binary and 'load' it via a BindCString. To do this
2177 * properly we have to set the colour of the string,
2178 * otherwise we end up with a lot of artifacts. The colour
2179 * 'character' might change in the future, so for safety
2180 * we just Utf8 Encode it into the string, which takes
2181 * exactly three characters, so it replaces the "XXX" with
2182 * the colour marker.
2183 */
2184 if (_current_text_dir != TD_LTR) {
2185 static char *err_str = stredup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
2186 Utf8Encode(err_str, SCC_YELLOW);
2187 SetDParamStr(0, err_str);
2188 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
2189 }
2190 #endif /* !WITH_ICU_LX */
2191 }
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:1725
LoadStringWidthTable
void LoadStringWidthTable(bool monospace)
Initialize _stringwidth_table cache.
Definition: gfx.cpp:1430
LanguagePackHeader::missing
uint16 missing
number of missing strings.
Definition: language.h:40
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:2012
YearMonthDay::day
Day day
Day (1..31)
Definition: date_type.h:107
MAX_NUM_GENDERS
static const uint8 MAX_NUM_GENDERS
Maximum number of supported genders.
Definition: language.h:20
WC_ROADVEH_LIST
@ WC_ROADVEH_LIST
Road vehicle list; Window numbers:
Definition: window_type.h:306
GetStringTab
static StringTab GetStringTab(StringID str)
Extract the StringTab from a StringID.
Definition: strings_func.h:23
LanguagePackHeader::version
uint32 version
32-bits of auto generated version info which is basically a hash of strings.h
Definition: language.h:28
MissingGlyphSearcher::DefaultSize
virtual FontSize DefaultSize()=0
Get the default (font) size of the string.
LanguagePack
Definition: strings.cpp:174
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:337
SCC_NEWGRF_FIRST
@ SCC_NEWGRF_FIRST
The next variables are part of a NewGRF subsystem for creating text strings.
Definition: control_codes.h:123
_units_volume
static const UnitsLong _units_volume[]
Unit conversions for volume.
Definition: strings.cpp:726
WChar
char32_t WChar
Type for wide characters, i.e.
Definition: string_type.h:35
GenerateTownNameString
char * GenerateTownNameString(char *buf, const char *last, size_t lang, uint32 seed)
Generates town name from given seed.
Definition: townname.cpp:1052
CopyOutDParam
void CopyOutDParam(uint64 *dst, int offs, int num)
Copy num string parameters from the global string parameter array to the dst array.
Definition: strings.cpp:139
GetCurrentLanguageIsoCode
const char * GetCurrentLanguageIsoCode()
Get the ISO language code of the currently loaded language.
Definition: strings.cpp:2003
usererror
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
Definition: openttd.cpp:103
SCC_NEWGRF_STRINL
@ SCC_NEWGRF_STRINL
Inline another string at the current position, StringID is encoded in the string.
Definition: control_codes.h:156
GB
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
Definition: bitmath_func.hpp:32
MissingGlyphSearcher
A searcher for missing glyphs.
Definition: strings_func.h:242
ReconsiderGameScriptLanguage
void ReconsiderGameScriptLanguage()
Reconsider the game script language, so we use the right one.
Definition: game_text.cpp:345
LanguagePackHeader::plural_form
byte plural_form
plural form index
Definition: language.h:41
TEXT_TAB_END
@ TEXT_TAB_END
End of language files.
Definition: strings_type.h:38
endian_func.hpp
Pool::PoolItem<&_company_pool >::GetIfValid
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:348
MissingGlyphSearcher::NextString
virtual const char * NextString()=0
Get the next string to search through.
_languages
LanguageList _languages
The actual list of language meta data.
Definition: strings.cpp:45
WL_WARNING
@ WL_WARNING
Other information.
Definition: error.h:23
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:321
_sorted_cargo_specs
std::vector< const CargoSpec * > _sorted_cargo_specs
Cargo specifications sorted alphabetically by name.
Definition: cargotype.cpp:152
MakeStringID
static StringID MakeStringID(StringTab tab, uint index)
Create a StringID.
Definition: strings_func.h:47
company_base.h
LoadedLanguagePack::langtab_num
std::array< uint, TEXT_TAB_END > langtab_num
Offset into langpack offs.
Definition: strings.cpp:191
BaseStation::town
Town * town
The town this station is associated with.
Definition: base_station_base.h:61
FS_BEGIN
@ FS_BEGIN
First font.
Definition: gfx_type.h:213
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
Station
Station data structure.
Definition: station_base.h:447
currency.h
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:87
BaseConsist::name
std::string name
Name of vehicle.
Definition: base_consist.h:19
FreeTypeSettings::mono
FreeTypeSubSetting mono
The mono space font used for license/readme viewers.
Definition: fontcache.h:229
Utf8CharLen
static int8 Utf8CharLen(WChar c)
Return the length of a UTF-8 encoded character.
Definition: string_func.h:113
Waypoint::town_cn
uint16 town_cn
The N-1th waypoint for this town (consecutive number)
Definition: waypoint_base.h:17
Pool::PoolItem::index
Tindex index
Index of this pool item.
Definition: pool_type.hpp:235
CargoSpec::Get
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition: cargotype.h:119
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:337
Searchpath
Searchpath
Types of searchpaths OpenTTD might use.
Definition: fileio_type.h:131
SortIndustryTypes
void SortIndustryTypes()
Initialize the list of sorted industry types.
Definition: industry_gui.cpp:208
HasBit
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
Definition: bitmath_func.hpp:103
MissingGlyphSearcher::Reset
virtual void Reset()=0
Reset the search, i.e.
_global_string_params_type
static WChar _global_string_params_type[20]
Type of parameters stored in #_global_string_params.
Definition: strings.cpp:55
LocaleSettings::units_volume
byte units_volume
unit system for volume
Definition: settings_type.h:234
Waypoint
Representation of a waypoint.
Definition: waypoint_base.h:16
Utf8Encode
size_t Utf8Encode(T buf, WChar c)
Encode a unicode character and place it in the buffer.
Definition: string.cpp:616
LanguageList
std::vector< LanguageMetadata > LanguageList
Type for the list of language meta data.
Definition: language.h:98
vehicle_base.h
CompanyProperties::name
std::string name
Name of the company if the user changed it.
Definition: company_base.h:58
fileio_func.h
Company::IsValidHumanID
static bool IsValidHumanID(size_t index)
Is this company a valid company, not controlled by a NoAI program?
Definition: company_base.h:147
UnitConversion::multiplier
int multiplier
Amount to multiply upon conversion.
Definition: strings.cpp:662
UnitsLong::c
UnitConversion c
Conversion.
Definition: strings.cpp:698
LanguagePackHeader::num_cases
uint8 num_cases
the number of cases of this language
Definition: language.h:54
town.h
UnitConversion::shift
int shift
Amount to shift upon conversion.
Definition: strings.cpp:663
LanguagePackGlyphSearcher::NextString
const char * NextString() override
Get the next string to search through.
Definition: strings.cpp:2063
IndustrySpec::station_name
StringID station_name
Default name for nearby station.
Definition: industrytype.h:132
FreeTypeSubSetting::os_handle
const void * os_handle
Optional native OS font info. Only valid during font search.
Definition: fontcache.h:221
Engine
Definition: engine_base.h:27
VEH_ROAD
@ VEH_ROAD
Road vehicle type.
Definition: vehicle_type.h:25
Vehicle
Vehicle data structure.
Definition: vehicle_base.h:221
LoadedLanguagePack
Definition: strings.cpp:186
Industry
Defines the internal data of a functional industry.
Definition: industry.h:66
CargoSpec::GetArraySize
static size_t GetArraySize()
Total number of cargospecs, both valid and invalid.
Definition: cargotype.h:109
ReadLanguagePack
bool ReadLanguagePack(const LanguageMetadata *lang)
Read a particular language.
Definition: strings.cpp:1736
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:697
MemCpyT
static void MemCpyT(T *destination, const T *source, size_t num=1)
Type-safe version of memcpy().
Definition: mem_func.hpp:23
GetLanguageList
static void GetLanguageList(const char *path)
Gets a list of languages from the given directory.
Definition: strings.cpp:1922
FreeTypeSubSetting::font
std::string font
The name of the font, or path to the font.
Definition: fontcache.h:217
StringParameters::GetTypeAtOffset
WChar GetTypeAtOffset(uint offset) const
Get the type of a specific element.
Definition: strings_func.h:151
SetDParam
static void SetDParam(uint n, uint64 v)
Set a string parameter v at index n in the global string parameter array.
Definition: strings_func.h:196
LocaleSettings::digit_decimal_separator
std::string digit_decimal_separator
decimal separator
Definition: settings_type.h:239
_units_height
static const Units _units_height[]
Unit conversions for height.
Definition: strings.cpp:740
NBSP
#define NBSP
A non-breaking space.
Definition: string_type.h:18
LanguagePackGlyphSearcher::i
uint i
Iterator for the primary language tables.
Definition: strings.cpp:2049
CompanyProperties::name_2
uint32 name_2
Parameter of name_1.
Definition: company_base.h:56
RestoreTextRefStackBackup
void RestoreTextRefStackBackup(struct TextRefStack *backup)
Restore a copy of the text stack to the used stack.
Definition: newgrf_text.cpp:797
ShowErrorMessage
void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x=0, int y=0, const GRFFile *textref_stack_grffile=nullptr, uint textref_stack_size=0, const uint32 *textref_stack=nullptr)
Display an error message in a window.
Definition: error_gui.cpp:383
BaseStation::string_id
StringID string_id
Default name (town area) of station.
Definition: base_station_base.h:58
GameSettings::game_creation
GameCreationSettings game_creation
settings used during the creation of a game (map)
Definition: settings_type.h:577
LanguagePackHeader::IDENT
static const uint32 IDENT
Identifier for OpenTTD language files, big endian for "LANG".
Definition: language.h:25
control_codes.h
IsInsideMM
static bool IsInsideMM(const T x, const size_t min, const size_t max)
Checks if a value is in an interval.
Definition: math_func.hpp:204
TAB_SIZE
static const uint TAB_SIZE
Number of strings per StringTab.
Definition: strings_type.h:46
StringParameters::GetInt64
int64 GetInt64(WChar type=0)
Read an int64 from the argument array.
Definition: strings.cpp:70
GetStringWithArgs
char * GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
Get a parsed string with most special stringcodes replaced by the string parameters.
Definition: strings.cpp:221
Units
Information about a specific unit system.
Definition: strings.cpp:690
townname_func.h
Group
Group data.
Definition: group.h:72
UnitConversion::FromDisplay
int64 FromDisplay(int64 input, bool round=true, int64 divider=1) const
Convert the displayed value back into a value of OpenTTD's internal unit.
Definition: strings.cpp:683
ConvertDisplaySpeedToKmhishSpeed
uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
Convert the given display speed to the km/h-ish speed.
Definition: strings.cpp:784
StrValid
bool StrValid(const char *str, const char *last)
Checks whether the given string is valid, i.e.
Definition: string.cpp:300
FACIL_BUS_STOP
@ FACIL_BUS_STOP
Station with bus stops.
Definition: station_type.h:54
depot_base.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:129
LanguagePackHeader::newgrflangid
uint8 newgrflangid
newgrf language id
Definition: language.h:52
_units_velocity
static const Units _units_velocity[]
Unit conversions for velocity.
Definition: strings.cpp:704
DRAW_STRING_BUFFER
static const int DRAW_STRING_BUFFER
Size of the buffer used for drawing strings.
Definition: gfx_func.h:86
GetTownName
char * GetTownName(char *buff, const TownNameParams *par, uint32 townnameparts, const char *last)
Fills buffer with specified town name.
Definition: townname.cpp:49
FS_NORMAL
@ FS_NORMAL
Index of the normal font in the font tables.
Definition: gfx_type.h:207
RemapNewGRFStringControlCode
uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, uint argv_size, bool modify_argv)
FormatString for NewGRF specific "magic" string control codes.
Definition: newgrf_text.cpp:858
FS2OTTD
std::string FS2OTTD(const std::wstring &name)
Convert to OpenTTD's encoding from a wide string.
Definition: win32.cpp:542
UnitConversion::ToDisplay
int64 ToDisplay(int64 input, bool round=true) const
Convert value from OpenTTD's internal unit into the displayed value.
Definition: strings.cpp:671
CurrencySpec::suffix
std::string suffix
Suffix to apply when formatting money in this currency.
Definition: currency.h:77
Industry::type
IndustryType type
type of industry.
Definition: industry.h:83
StringParameters::GetDataLeft
uint GetDataLeft() const
Return the amount of elements which can still be read.
Definition: strings_func.h:132
YearMonthDay::month
Month month
Month (0..11)
Definition: date_type.h:106
CargoSpec::units_volume
StringID units_volume
Name of a single unit of cargo of this type.
Definition: cargotype.h:74
WC_INDUSTRY_DIRECTORY
@ WC_INDUSTRY_DIRECTORY
Industry directory; Window numbers:
Definition: window_type.h:258
GetGameStringPtr
const char * GetGameStringPtr(uint id)
Get the string pointer of a particular game string.
Definition: game_text.cpp:308
UnitsLong::s
StringID s
String for the short variant of the unit.
Definition: strings.cpp:699
Date
int32 Date
The type to store our dates in.
Definition: date_type.h:14
StringParameters::offset
uint offset
Current offset in the data/type arrays.
Definition: strings_func.h:66
ReadFileToMem
std::unique_ptr< char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
Load a file into memory.
Definition: fileio.cpp:1111
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:53
Station::indtype
IndustryType indtype
Industry type to get the name from.
Definition: station_base.h:465
Depot::town_cn
uint16 town_cn
The N-1th depot for this town (consecutive number)
Definition: depot_base.h:24
LoadedLanguagePack::langtab_start
std::array< uint, TEXT_TAB_END > langtab_start
Offset into langpack offs.
Definition: strings.cpp:192
LanguagePackHeader::name
char name[32]
the international name of this language
Definition: language.h:29
ConvertDateToYMD
void ConvertDateToYMD(Date date, YearMonthDay *ymd)
Converts a Date to a Year, Month & Day.
Definition: date.cpp:94
BuildContentTypeStringList
void BuildContentTypeStringList()
Build array of all strings corresponding to the content types.
Definition: network_content_gui.cpp:1014
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
safeguards.h
GetGRFStringPtr
const char * GetGRFStringPtr(uint16 stringid)
Get a C-string from a stringid set by a newgrf.
Definition: newgrf_text.cpp:653
DEFAULT_GROUP
static const GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition: group_type.h:17
LanguagePackGlyphSearcher::DefaultSize
FontSize DefaultSize() override
Get the default (font) size of the string.
Definition: strings.cpp:2058
WC_SHIPS_LIST
@ WC_SHIPS_LIST
Ships list; Window numbers:
Definition: window_type.h:312
StrEmpty
static bool StrEmpty(const char *s)
Check if a string buffer is empty.
Definition: string_func.h:64
settings
fluid_settings_t * settings
FluidSynth settings handle.
Definition: fluidsynth.cpp:21
fontdetection.h
StringParameters::type
WChar * type
Array with type information about the data. Can be nullptr when no type information is needed....
Definition: strings_func.h:63
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:57
StringParameters::GetPointerToOffset
uint64 * GetPointerToOffset(uint offset) const
Get a pointer to a specific element in the data array.
Definition: strings_func.h:138
newgrf_text.h
CompanyProperties::president_name
std::string president_name
Name of the president if the user changed it.
Definition: company_base.h:62
LanguageMetadata::file
char file[MAX_PATH]
Name of the file we read this data from.
Definition: language.h:94
error.h
LocaleSettings::units_weight
byte units_weight
unit system for weight
Definition: settings_type.h:233
WC_TRAINS_LIST
@ WC_TRAINS_LIST
Trains list; Window numbers:
Definition: window_type.h:300
StringParameters::GetDataPointer
uint64 * GetDataPointer() const
Get a pointer to the current element in the data array.
Definition: strings_func.h:126
language.h
FACIL_DOCK
@ FACIL_DOCK
Station with a dock.
Definition: station_type.h:56
date_func.h
stdafx.h
VehicleType
VehicleType
Available vehicle types.
Definition: vehicle_type.h:21
IndustrySpec
Defines the data structure for constructing industry.
Definition: industrytype.h:107
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:72
StringParameters::num_param
uint num_param
Length of the data array.
Definition: strings_func.h:67
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:308
CompanyProperties::president_name_2
uint32 president_name_2
Parameter of president_name_1.
Definition: company_base.h:61
ConvertSpeedToDisplaySpeed
uint ConvertSpeedToDisplaySpeed(uint speed)
Convert the given (internal) speed to the display speed.
Definition: strings.cpp:751
Utf8Decode
size_t Utf8Decode(WChar *c, const char *s)
Decode and consume the next UTF-8 encoded character.
Definition: string.cpp:574
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:1477
LanguagePackGlyphSearcher::Monospace
bool Monospace() override
Whether to search for a monospace font or not.
Definition: strings.cpp:2078
BuildIndustriesLegend
void BuildIndustriesLegend()
Fills an array for the industries legends.
Definition: smallmap_gui.cpp:169
LocaleSettings::units_force
byte units_force
unit system for force
Definition: settings_type.h:235
LanguagePackGlyphSearcher::SetFontNames
void SetFontNames(FreeTypeSettings *settings, const char *font_name, const void *os_data) override
Set the right font names.
Definition: strings.cpp:2083
LanguagePackGlyphSearcher
Helper for searching through the language pack.
Definition: strings.cpp:2048
UnitsLong::l
StringID l
String for the long variant of the unit.
Definition: strings.cpp:700
_current_language
const LanguageMetadata * _current_language
The currently loaded language.
Definition: strings.cpp:46
FormatNumber
static char * FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill=1, int fractional_digits=0)
Format a number into a string.
Definition: strings.cpp:323
Industry::town
Town * town
Nearest town.
Definition: industry.h:68
_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:197
YearMonthDay::year
Year year
Year (0...)
Definition: date_type.h:105
string_func.h
_units_power
static const Units _units_power[]
Unit conversions for velocity.
Definition: strings.cpp:712
StringID
uint32 StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
LANG_DIR
@ LANG_DIR
Subdirectory for all translation files.
Definition: fileio_type.h:118
rev.h
Engine::name
std::string name
Custom name of engine.
Definition: engine_base.h:28
station_base.h
FormatBytes
static char * FormatBytes(char *buff, int64 number, const char *last)
Format a given number as a number of bytes with the SI prefix.
Definition: strings.cpp:391
StringParameters::data
uint64 * data
Array with the actual data.
Definition: strings_func.h:62
strings_func.h
FormatString
static char * FormatString(char *buff, const char *str, StringParameters *args, const char *last, 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:796
ConvertKmhishSpeedToDisplaySpeed
uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
Convert the given km/h-ish speed to the display speed.
Definition: strings.cpp:774
LanguagePackGlyphSearcher::j
uint j
Iterator for the secondary language tables.
Definition: strings.cpp:2050
StringParameters::GetInt32
int32 GetInt32(WChar type=0)
Read an int32 from the argument array.
Definition: strings_func.h:120
TextDirection
TextDirection
Directions a text can go to.
Definition: strings_type.h:22
Units::c
UnitConversion c
Conversion.
Definition: strings.cpp:691
GetCurrentLocale
const char * GetCurrentLocale(const char *param)
Determine the current charset based on the environment First check some default values,...
Definition: strings.cpp:1849
StringParameters
Definition: strings_func.h:60
LanguagePackGlyphSearcher::Reset
void Reset() override
Reset the search, i.e.
Definition: strings.cpp:2052
WC_BUILD_VEHICLE
@ WC_BUILD_VEHICLE
Build vehicle; Window numbers:
Definition: window_type.h:375
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:1705
LocaleSettings::units_power
byte units_power
unit system for power
Definition: settings_type.h:232
game_text.hpp
LocaleSettings::units_height
byte units_height
unit system for height
Definition: settings_type.h:236
SetDParamMaxValue
void SetDParamMaxValue(uint n, uint64 max_value, uint min_count, FontSize size)
Set DParam n to some number that is suitable for string size computations.
Definition: strings.cpp:94
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:3251
CargoSpec::quantifier
StringID quantifier
Text for multiple units of cargo of this type.
Definition: cargotype.h:75
DIR
Definition: win32.cpp:68
FreeTypeSettings::medium
FreeTypeSubSetting medium
The normal font size.
Definition: fontcache.h:227
GetGlyph
static const Sprite * GetGlyph(FontSize size, WChar key)
Get the Sprite for a glyph.
Definition: fontcache.h:197
InitializeSortedCargoSpecs
void InitializeSortedCargoSpecs()
Initialize the list of sorted cargo specifications.
Definition: cargotype.cpp:188
CurrencySpec::rate
uint16 rate
The conversion rate compared to the base currency.
Definition: currency.h:73
TEXT_TAB_GAMESCRIPT_START
@ TEXT_TAB_GAMESCRIPT_START
Start of GameScript supplied strings.
Definition: strings_type.h:39
network_content_gui.h
_units_weight
static const UnitsLong _units_weight[]
Unit conversions for weight.
Definition: strings.cpp:719
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:284
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:733
LanguagePackHeader::winlangid
uint16 winlangid
Windows language ID: Windows cannot and will not convert isocodes to something it can use to determin...
Definition: language.h:51
Sign
Definition: signs_base.h:22
_global_string_params_data
static uint64 _global_string_params_data[20]
Global array of string parameters. To access, use SetDParam.
Definition: strings.cpp:54
StringParameters::ClearTypeInformation
void ClearTypeInformation()
Reset the type array.
Definition: strings.cpp:59
seprintf
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:535
Vehicle::unitnumber
UnitID unitnumber
unit number, for display purposes only
Definition: vehicle_base.h:302
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:788
WL_ERROR
@ WL_ERROR
Errors (eg. saving/loading failed)
Definition: error.h:24
LocaleSettings::digit_group_separator_currency
std::string digit_group_separator_currency
thousand separator for currencies
Definition: settings_type.h:238
InitFreeType
void InitFreeType(bool monospace)
(Re)initialize the freetype related things, i.e.
Definition: fontcache.cpp:681
SetCurrentGrfLangID
void SetCurrentGrfLangID(byte language_id)
Equivalence Setter function between game and newgrf langID.
Definition: newgrf_text.cpp:672
abs
static T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition: math_func.hpp:21
stredup
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
Definition: string.cpp:137
LanguagePackHeader::num_genders
uint8 num_genders
the number of genders of this language
Definition: language.h:53
error
void CDECL error(const char *s,...)
Error handling for fatal non-user errors.
Definition: openttd.cpp:132
CompanyProperties::president_name_1
StringID president_name_1
Name of the president if the user did not change it.
Definition: company_base.h:60
LanguagePackDeleter
Definition: strings.cpp:178
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:2110
Debug
#define Debug(name, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
Depot
Definition: depot_base.h:19
Town
Town data structure.
Definition: town.h:50
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:378
YearMonthDay
Data structure to convert between Date and triplet (year, month, and day).
Definition: date_type.h:104
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:228
GameSettings::locale
LocaleSettings locale
settings related to used currency/unit system in the current game
Definition: settings_type.h:589
OverflowSafeInt< int64 >
CargoID
byte CargoID
Cargo slots to indicate a cargo type within a game.
Definition: cargo_type.h:20
MemSetT
static void MemSetT(T *ptr, byte value, size_t num=1)
Type-safe version of memset().
Definition: mem_func.hpp:49
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:210
Engine::IsEnabled
bool IsEnabled() const
Checks whether the engine is a valid (non-articulated part of an) engine.
Definition: engine.cpp:137
engine_base.h
LanguagePackHeader::ident
uint32 ident
32-bits identifier
Definition: language.h:27
strnatcmp
int strnatcmp(const char *s1, const char *s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
Definition: string.cpp:718
FontSize
FontSize
Available font sizes.
Definition: gfx_type.h:206
GetStringIndex
static uint GetStringIndex(StringID str)
Extract the StringIndex from a StringID.
Definition: strings_func.h:36
GetIndustrySpec
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
Definition: industry_cmd.cpp:121
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
Definition: settings_type.h:231
_config_language_file
std::string _config_language_file
The file (name) stored in the configuration.
Definition: strings.cpp:44
BaseVehicle::type
VehicleType type
Type of vehicle.
Definition: vehicle_type.h:52
StringParameters::HasTypeInformation
bool HasTypeInformation() const
Does this instance store information about the type of the parameters.
Definition: strings_func.h:145
FACIL_AIRPORT
@ FACIL_AIRPORT
Station with an airport.
Definition: station_type.h:55
Units::s
StringID s
String for the unit.
Definition: strings.cpp:692
strecpy
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: string.cpp:112
EngineInfo::string_id
StringID string_id
Default name of engine.
Definition: engine_type.h:146
free
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:460
ttd_opendir
static 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:113
CT_INVALID
@ CT_INVALID
Invalid cargo type.
Definition: cargo_type.h:69
strecat
char * strecat(char *dst, const char *src, const char *last)
Appends characters from one string to another.
Definition: string.cpp:84
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:51
GetLanguage
const LanguageMetadata * GetLanguage(byte newgrflangid)
Get the language with the given NewGRF language ID.
Definition: strings.cpp:1885
Company
Definition: company_base.h:115
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:76
WC_AIRCRAFT_LIST
@ WC_AIRCRAFT_LIST
Aircraft list; Window numbers:
Definition: window_type.h:318
Sprite
Data structure describing a sprite.
Definition: spritecache.h:17
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:394
WC_STATION_LIST
@ WC_STATION_LIST
Station list; Window numbers:
Definition: window_type.h:294
MAX_NUM_CASES
static const uint8 MAX_NUM_CASES
Maximum number of supported cases.
Definition: language.h:21
SetDParamMaxDigits
void SetDParamMaxDigits(uint n, uint count, FontSize size)
Set DParam n to some number that is suitable for string size computations.
Definition: strings.cpp:110
_current_text_dir
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:48
LocaleSettings::digit_group_separator
std::string digit_group_separator
thousand separator for non-currencies
Definition: settings_type.h:237
ConvertDisplaySpeedToSpeed
uint ConvertDisplaySpeedToSpeed(uint speed)
Convert the given display speed to the (internal) speed.
Definition: strings.cpp:764
signs_base.h
UnitConversion
Helper for unit conversion.
Definition: strings.cpp:661
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:693
CopyInDParam
void CopyInDParam(int offs, const uint64 *src, int num)
Copy num string parameters from array src into the global string parameter array.
Definition: strings.cpp:128
IsTextDirectionChar
static bool IsTextDirectionChar(WChar c)
Is the given character a text direction character.
Definition: string_func.h:225
SetDParamStr
void SetDParamStr(uint n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition: strings.cpp:296
GetLanguageFileHeader
static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
Reads the language file header and checks compatibility.
Definition: strings.cpp:1900
FreeTypeSettings
Settings for the freetype fonts.
Definition: fontcache.h:225
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
InitializeLanguagePacks
void InitializeLanguagePacks()
Make a list of the available language packs.
Definition: strings.cpp:1954
UsingNewGRFTextStack
bool UsingNewGRFTextStack()
Check whether the NewGRF text stack is in use.
Definition: newgrf_text.cpp:779
debug.h
DeterminePluralForm
static int DeterminePluralForm(int64 count, int plural_form)
Determine the "plural" index given a plural form and a number.
Definition: strings.cpp:523
LanguagePackHeader
Header of a language file.
Definition: language.h:24
SetFallbackFont
bool SetFallbackFont(struct FreeTypeSettings *settings, const char *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:70
CompanyProperties::name_1
StringID name_1
Name of the company if the user did not change it.
Definition: company_base.h:57
TextRefStack
Definition: newgrf_text.cpp:708