OpenTTD Source: src/strings.cpp Source File

OpenTTD Source 1.11.0-RC1

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