OpenTTD Source: src/strings.cpp Source File

OpenTTD Source 13.1

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