OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

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