OpenTTD: src/strings.cpp Source File

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 27102 2015-01-01 20:50:43Z rubidium $ */
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 #include <stack>
39
40 #include "table/strings.h"
41 #include "table/control_codes.h"
42
43 #include "safeguards.h"
44
45 char _config_language_file[MAX_PATH];
46 LanguageList _languages;
47 const LanguageMetadata *_current_language = NULL;
48
49 TextDirection _current_text_dir;
50
51 #ifdef WITH_ICU
52 Collator *_current_collator = NULL;
53 #endif /* WITH_ICU */
54
55 static uint64 _global_string_params_data[20];
56 static WChar _global_string_params_type[20];
57 StringParameters _global_string_params(_global_string_params_data, 20, _global_string_params_type);
58
60 void StringParameters::ClearTypeInformation()
61 {
62 assert(this->type != NULL);
63 MemSetT(this->type, 0, this->num_param);
64 }
65
66
71 int64 StringParameters::GetInt64(WChar type)
72 {
73 if (this->offset >= this->num_param) {
74 DEBUG(misc, 0, "Trying to read invalid string parameter");
75 return 0;
76 }
77 if (this->type != NULL) {
78 assert(this->type[this->offset] == 0 || this->type[this->offset] == type);
79 this->type[this->offset] = type;
80 }
81 return this->data[this->offset++];
82 }
83
88 void StringParameters::ShiftParameters(uint amount)
89 {
90 assert(amount <= this->num_param);
91 MemMoveT(this->data + amount, this->data, this->num_param - amount);
92 }
93
102 void SetDParamMaxValue(uint n, uint64 max_value, uint min_count, FontSize size)
103 {
104 uint num_digits = 1;
105 while (max_value >= 10) {
106 num_digits++;
107 max_value /= 10;
108 }
109 SetDParamMaxDigits(n, max(min_count, num_digits), size);
110 }
111
118 void SetDParamMaxDigits(uint n, uint count, FontSize size)
119 {
120 uint front, next;
121 GetBroadestDigit(&front, &next, size);
122 uint64 val = count > 1 ? front : next;
123 for (; count > 1; count--) {
124 val = 10 * val + next;
125 }
126 SetDParam(n, val);
127 }
128
135 void CopyInDParam(int offs, const uint64 *src, int num)
136 {
137 MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
138 }
139
146 void CopyOutDParam(uint64 *dst, int offs, int num)
147 {
148 MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
149 }
150
159 void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
160 {
161 char buf[DRAW_STRING_BUFFER];
162 GetString(buf, string, lastof(buf));
163
164 MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num);
165 for (int i = 0; i < num; i++) {
166 if (_global_string_params.HasTypeInformation() && _global_string_params.GetTypeAtOffset(i) == SCC_RAW_STRING_POINTER) {
167 strings[i] = stredup((const char *)(size_t)_global_string_params.GetParam(i));
168 dst[i] = (size_t)strings[i];
169 } else {
170 strings[i] = NULL;
171 }
172 }
173 }
174
175 static char *StationGetSpecialString(char *buff, int x, const char *last);
176 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
177 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
178
179 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);
180
181 struct LanguagePack : public LanguagePackHeader {
182 char data[]; // list of strings
183 };
184
185 static char **_langpack_offs;
186 static LanguagePack *_langpack;
187 static uint _langtab_num[TAB_COUNT];
188 static uint _langtab_start[TAB_COUNT];
189 static bool _scan_for_gender_data = false;
190
191
192 const char *GetStringPtr(StringID string)
193 {
194 switch (GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)) {
195 case GAME_TEXT_TAB: return GetGameStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
196 /* 0xD0xx and 0xD4xx IDs have been converted earlier. */
197 case 26: NOT_REACHED();
198 case 28: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
199 case 29: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x0800);
200 case 30: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x1000);
201 default: return _langpack_offs[_langtab_start[GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)] + GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS)];
202 }
203 }
204
215 char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
216 {
217 if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
218
219 uint index = GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS);
220 uint tab = GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS);
221
222 switch (tab) {
223 case 4:
224 if (index >= 0xC0 && !game_script) {
225 return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
226 }
227 break;
228
229 case 14:
230 if (index >= 0xE4 && !game_script) {
231 return GetSpecialNameString(buffr, index - 0xE4, args, last);
232 }
233 break;
234
235 case 15:
236 /* Old table for custom names. This is no longer used */
237 if (!game_script) {
238 error("Incorrect conversion of custom name string.");
239 }
240 break;
241
242 case GAME_TEXT_TAB:
243 return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
244
245 case 26:
246 NOT_REACHED();
247
248 case 28:
249 return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
250
251 case 29:
252 return FormatString(buffr, GetGRFStringPtr(index + 0x0800), args, last, case_index);
253
254 case 30:
255 return FormatString(buffr, GetGRFStringPtr(index + 0x1000), args, last, case_index);
256 }
257
258 if (index >= _langtab_num[tab]) {
259 if (game_script) {
260 return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
261 }
262 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
263 }
264
265 return FormatString(buffr, GetStringPtr(string), args, last, case_index);
266 }
267
268 char *GetString(char *buffr, StringID string, const char *last)
269 {
270 _global_string_params.ClearTypeInformation();
271 _global_string_params.offset = 0;
272 return GetStringWithArgs(buffr, string, &_global_string_params, last);
273 }
274
275
281 void SetDParamStr(uint n, const char *str)
282 {
283 SetDParam(n, (uint64)(size_t)str);
284 }
285
290 void InjectDParam(uint amount)
291 {
292 _global_string_params.ShiftParameters(amount);
293 }
294
306 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
307 {
308 static const int max_digits = 20;
309 uint64 divisor = 10000000000000000000ULL;
310 zerofill += fractional_digits;
311 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
312
313 if (number < 0) {
314 buff += seprintf(buff, last, "-");
315 number = -number;
316 }
317
318 uint64 num = number;
319 uint64 tot = 0;
320 for (int i = 0; i < max_digits; i++) {
321 if (i == max_digits - fractional_digits) {
322 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
323 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
324 buff += seprintf(buff, last, "%s", decimal_separator);
325 }
326
327 uint64 quot = 0;
328 if (num >= divisor) {
329 quot = num / divisor;
330 num = num % divisor;
331 }
332 if ((tot |= quot) || i >= max_digits - zerofill) {
333 buff += seprintf(buff, last, "%i", (int)quot);
334 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
335 }
336
337 divisor /= 10;
338 }
339
340 *buff = '\0';
341
342 return buff;
343 }
344
345 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
346 {
347 const char *separator = _settings_game.locale.digit_group_separator;
348 if (separator == NULL) separator = _langpack->digit_group_separator;
349 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
350 }
351
352 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
353 {
354 return FormatNumber(buff, number, last, "");
355 }
356
357 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
358 {
359 return FormatNumber(buff, number, last, "", count);
360 }
361
362 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
363 {
364 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
365 }
366
374 static char *FormatBytes(char *buff, int64 number, const char *last)
375 {
376 assert(number >= 0);
377
378 /* 1 2^10 2^20 2^30 2^40 2^50 2^60 */
379 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
380 uint id = 1;
381 while (number >= 1024 * 1024) {
382 number /= 1024;
383 id++;
384 }
385
386 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
387 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
388
389 if (number < 1024) {
390 id = 0;
391 buff += seprintf(buff, last, "%i", (int)number);
392 } else if (number < 1024 * 10) {
393 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
394 } else if (number < 1024 * 100) {
395 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
396 } else {
397 assert(number < 1024 * 1024);
398 buff += seprintf(buff, last, "%i", (int)number / 1024);
399 }
400
401 assert(id < lengthof(iec_prefixes));
402 buff += seprintf(buff, last, NBSP "%sB", iec_prefixes[id]);
403
404 return buff;
405 }
406
407 static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
408 {
409 YearMonthDay ymd;
410 ConvertDateToYMD(date, &ymd);
411
412 int64 args[] = {ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
413 StringParameters tmp_params(args);
414 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
415 }
416
417 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
418 {
419 YearMonthDay ymd;
420 ConvertDateToYMD(date, &ymd);
421
422 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
423 StringParameters tmp_params(args);
424 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
425 }
426
427 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
428 {
429 YearMonthDay ymd;
430 ConvertDateToYMD(date, &ymd);
431
432 char day[3];
433 char month[3];
434 /* We want to zero-pad the days and months */
435 seprintf(day, lastof(day), "%02i", ymd.day);
436 seprintf(month, lastof(month), "%02i", ymd.month + 1);
437
438 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
439 StringParameters tmp_params(args);
440 return FormatString(buff, GetStringPtr(str), &tmp_params, last);
441 }
442
443 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
444 {
445 /* We are going to make number absolute for printing, so
446 * keep this piece of data as we need it later on */
447 bool negative = number < 0;
448 const char *multiplier = "";
449
450 number *= spec->rate;
451
452 /* convert from negative */
453 if (number < 0) {
454 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
455 buff += Utf8Encode(buff, SCC_RED);
456 buff = strecpy(buff, "-", last);
457 number = -number;
458 }
459
460 /* Add prefix part, following symbol_pos specification.
461 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
462 * The only remaining value is 1 (suffix), so everything that is not 1 */
463 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
464
465 /* for huge numbers, compact the number into k or M */
466 if (compact) {
467 /* Take care of the 'k' rounding. Having 1 000 000 k
468 * and 1 000 M is inconsistent, so always use 1 000 M. */
469 if (number >= 1000000000 - 500) {
470 number = (number + 500000) / 1000000;
471 multiplier = NBSP "M";
472 } else if (number >= 1000000) {
473 number = (number + 500) / 1000;
474 multiplier = NBSP "k";
475 }
476 }
477
478 const char *separator = _settings_game.locale.digit_group_separator_currency;
479 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
480 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
481 buff = FormatNumber(buff, number, last, separator);
482 buff = strecpy(buff, multiplier, last);
483
484 /* Add suffix part, following symbol_pos specification.
485 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
486 * The only remaining value is 1 (prefix), so everything that is not 0 */
487 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
488
489 if (negative) {
490 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
491 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
492 *buff = '\0';
493 }
494
495 return buff;
496 }
497
504 static int DeterminePluralForm(int64 count, int plural_form)
505 {
506 /* The absolute value determines plurality */
507 uint64 n = abs(count);
508
509 switch (plural_form) {
510 default:
511 NOT_REACHED();
512
513 /* Two forms: singular used for one only.
514 * Used in:
515 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
516 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
517 case 0:
518 return n != 1 ? 1 : 0;
519
520 /* Only one form.
521 * Used in:
522 * Hungarian, Japanese, Korean, Turkish */
523 case 1:
524 return 0;
525
526 /* Two forms: singular used for 0 and 1.
527 * Used in:
528 * French, Brazilian Portuguese */
529 case 2:
530 return n > 1 ? 1 : 0;
531
532 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
533 * Note: Cases are out of order for hysterical reasons. '0' is last.
534 * Used in:
535 * Latvian */
536 case 3:
537 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
538
539 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
540 * Used in:
541 * Gaelige (Irish) */
542 case 4:
543 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
544
545 /* 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.
546 * Used in:
547 * Lithuanian */
548 case 5:
549 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
550
551 /* 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.
552 * Used in:
553 * Croatian, Russian, Ukrainian */
554 case 6:
555 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
556
557 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
558 * Used in:
559 * Polish */
560 case 7:
561 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
562
563 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
564 * Used in:
565 * Slovenian */
566 case 8:
567 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
568
569 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
570 * Used in:
571 * Icelandic */
572 case 9:
573 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
574
575 /* Three forms: special cases for 1, and 2 to 4
576 * Used in:
577 * Czech, Slovak */
578 case 10:
579 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
580
581 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
582 * Korean doesn't have the concept of plural, but depending on how a
583 * number is pronounced it needs another version of a particle.
584 * As such the plural system is misused to give this distinction.
585 */
586 case 11:
587 switch (n % 10) {
588 case 0: // yeong
589 case 1: // il
590 case 3: // sam
591 case 6: // yuk
592 case 7: // chil
593 case 8: // pal
594 return 0;
595
596 case 2: // i
597 case 4: // sa
598 case 5: // o
599 case 9: // gu
600 return 1;
601
602 default:
603 NOT_REACHED();
604 }
605
606 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
607 * Used in:
608 * Maltese */
609 case 12:
610 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
611 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
612 * Used in:
613 * Scottish Gaelic */
614 case 13:
615 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
616 }
617 }
618
619 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
620 {
621 /* <NUM> {Length of each string} {each string} */
622 uint n = (byte)*b++;
623 uint pos, i, mypos = 0;
624
625 for (i = pos = 0; i != n; i++) {
626 uint len = (byte)*b++;
627 if (i == form) mypos = pos;
628 pos += len;
629 }
630
631 *dst += seprintf(*dst, last, "%s", b + mypos);
632 return b + pos;
633 }
634
636 struct UnitConversion {
637 int multiplier;
638 int shift;
639
646 int64 ToDisplay(int64 input, bool round = true) const
647 {
648 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
649 }
650
658 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
659 {
660 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
661 }
662 };
663
665 struct Units {
666 UnitConversion c;
667 StringID s;
668 };
669
671 struct UnitsLong {
672 UnitConversion c;
673 StringID s;
674 StringID l;
675 };
676
678 static const Units _units_velocity[] = {
679 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL },
680 { { 103, 6}, STR_UNITS_VELOCITY_METRIC },
681 { {1831, 12}, STR_UNITS_VELOCITY_SI },
682 };
683
685 static const Units _units_power[] = {
686 { { 1, 0}, STR_UNITS_POWER_IMPERIAL },
687 { {4153, 12}, STR_UNITS_POWER_METRIC },
688 { {6109, 13}, STR_UNITS_POWER_SI },
689 };
690
692 static const UnitsLong _units_weight[] = {
693 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
694 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC },
695 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI },
696 };
697
699 static const UnitsLong _units_volume[] = {
700 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
701 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC },
702 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI },
703 };
704
706 static const Units _units_force[] = {
707 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL },
708 { {3263, 5}, STR_UNITS_FORCE_METRIC },
709 { { 1, 0}, STR_UNITS_FORCE_SI },
710 };
711
713 static const Units _units_height[] = {
714 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL }, // "Wrong" conversion factor for more nicer GUI values
715 { { 1, 0}, STR_UNITS_HEIGHT_METRIC },
716 { { 1, 0}, STR_UNITS_HEIGHT_SI },
717 };
718
724 uint ConvertSpeedToDisplaySpeed(uint speed)
725 {
726 /* For historical reasons we don't want to mess with the
727 * conversion for speed. So, don't round it and keep the
728 * original conversion factors instead of the real ones. */
729 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed, false);
730 }
731
737 uint ConvertDisplaySpeedToSpeed(uint speed)
738 {
739 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed);
740 }
741
747 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
748 {
749 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed * 10, false) / 16;
750 }
751
757 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
758 {
759 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed * 16, true, 10);
760 }
770 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
771 {
772 uint orig_offset = args->offset;
773
774 /* When there is no array with types there is no need to do a dry run. */
775 if (args->HasTypeInformation() && !dry_run) {
776 if (UsingNewGRFTextStack()) {
777 /* Values from the NewGRF text stack are only copied to the normal
778 * argv array at the time they are encountered. That means that if
779 * another string command references a value later in the string it
780 * would fail. We solve that by running FormatString twice. The first
781 * pass makes sure the argv array is correctly filled and the second
782 * pass can reference later values without problems. */
783 struct TextRefStack *backup = CreateTextRefStackBackup();
784 FormatString(buff, str_arg, args, last, case_index, game_script, true);
785 RestoreTextRefStackBackup(backup);
786 } else {
787 FormatString(buff, str_arg, args, last, case_index, game_script, true);
788 }
789 /* We have to restore the original offset here to to read the correct values. */
790 args->offset = orig_offset;
791 }
792 WChar b = '\0';
793 uint next_substr_case_index = 0;
794 char *buf_start = buff;
795 std::stack<const char *> str_stack;
796 str_stack.push(str_arg);
797
798 for (;;) {
799 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
800 str_stack.pop();
801 }
802 if (str_stack.empty()) break;
803 const char *&str = str_stack.top();
804
805 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
806 /* We need to pass some stuff as it might be modified; oh boy. */
807 //todo: should argve be passed here too?
808 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), args->GetDataLeft(), dry_run);
809 if (b == 0) continue;
810 }
811
812 switch (b) {
813 case SCC_ENCODED: {
814 uint64 sub_args_data[20];
815 WChar sub_args_type[20];
816 bool sub_args_need_free[20];
817 StringParameters sub_args(sub_args_data, 20, sub_args_type);
818
819 sub_args.ClearTypeInformation();
820 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
821
822 uint16 stringid;
823 const char *s = str;
824 char *p;
825 stringid = strtol(str, &p, 16);
826 if (*p != ':' && *p != '\0') {
827 while (*p != '\0') p++;
828 str = p;
829 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
830 break;
831 }
832 if (stringid >= TAB_SIZE) {
833 while (*p != '\0') p++;
834 str = p;
835 buff = strecat(buff, "(invalid StringID)", last);
836 break;
837 }
838
839 int i = 0;
840 while (*p != '\0' && i < 20) {
841 uint64 param;
842 s = ++p;
843
844 /* Find the next value */
845 bool instring = false;
846 bool escape = false;
847 for (;; p++) {
848 if (*p == '\\') {
849 escape = true;
850 continue;
851 }
852 if (*p == '"' && escape) {
853 escape = false;
854 continue;
855 }
856 escape = false;
857
858 if (*p == '"') {
859 instring = !instring;
860 continue;
861 }
862 if (instring) {
863 continue;
864 }
865
866 if (*p == ':') break;
867 if (*p == '\0') break;
868 }
869
870 if (*s != '"') {
871 /* Check if we want to look up another string */
872 WChar l;
873 size_t len = Utf8Decode(&l, s);
874 bool lookup = (l == SCC_ENCODED);
875 if (lookup) s += len;
876
877 param = strtoull(s, &p, 16);
878
879 if (lookup) {
880 if (param >= TAB_SIZE) {
881 while (*p != '\0') p++;
882 str = p;
883 buff = strecat(buff, "(invalid sub-StringID)", last);
884 break;
885 }
886 param = (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + param;
887 }
888
889 sub_args.SetParam(i++, param);
890 } else {
891 char *g = stredup(s);
892 g[p - s] = '\0';
893
894 sub_args_need_free[i] = true;
895 sub_args.SetParam(i++, (uint64)(size_t)g);
896 }
897 }
898 /* If we didn't error out, we can actually print the string. */
899 if (*str != '\0') {
900 str = p;
901 buff = GetStringWithArgs(buff, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, last, true);
902 }
903
904 for (int i = 0; i < 20; i++) {
905 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
906 }
907 break;
908 }
909
910 case SCC_NEWGRF_STRINL: {
911 StringID substr = Utf8Consume(&str);
912 str_stack.push(GetStringPtr(substr));
913 break;
914 }
915
916 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
917 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
918 str_stack.push(GetStringPtr(substr));
919 case_index = next_substr_case_index;
920 next_substr_case_index = 0;
921 break;
922 }
923
924
925 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
926 /* First read the meta data from the language file. */
927 uint offset = orig_offset + (byte)*str++;
928 int gender = 0;
929 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
930 /* Now we need to figure out what text to resolve, i.e.
931 * what do we need to draw? So get the actual raw string
932 * first using the control code to get said string. */
933 char input[4 + 1];
934 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
935 *p = '\0';
936
937 /* Now do the string formatting. */
938 char buf[256];
939 bool old_sgd = _scan_for_gender_data;
940 _scan_for_gender_data = true;
941 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
942 p = FormatString(buf, input, &tmp_params, lastof(buf));
943 _scan_for_gender_data = old_sgd;
944 *p = '\0';
945
946 /* And determine the string. */
947 const char *s = buf;
948 WChar c = Utf8Consume(&s);
949 /* Does this string have a gender, if so, set it */
950 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
951 }
952 str = ParseStringChoice(str, gender, &buff, last);
953 break;
954 }
955
956 /* This sets up the gender for the string.
957 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
958 case SCC_GENDER_INDEX: // {GENDER 0}
959 if (_scan_for_gender_data) {
960 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
961 *buff++ = *str++;
962 } else {
963 str++;
964 }
965 break;
966
967 case SCC_PLURAL_LIST: { // {P}
968 int plural_form = *str++; // contains the plural form for this string
969 uint offset = orig_offset + (byte)*str++;
970 int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
971 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
972 break;
973 }
974
975 case SCC_ARG_INDEX: { // Move argument pointer
976 args->offset = orig_offset + (byte)*str++;
977 break;
978 }
979
980 case SCC_SET_CASE: { // {SET_CASE}
981 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
982 * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
983 next_substr_case_index = (byte)*str++;
984 break;
985 }
986
987 case SCC_SWITCH_CASE: { // {Used to implement case switching}
988 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
989 * Each LEN is printed using 2 bytes in big endian order. */
990 uint num = (byte)*str++;
991 while (num) {
992 if ((byte)str[0] == case_index) {
993 /* Found the case, adjust str pointer and continue */
994 str += 3;
995 break;
996 }
997 /* Otherwise skip to the next case */
998 str += 3 + (str[1] << 8) + str[2];
999 num--;
1000 }
1001 break;
1002 }
1003
1004 case SCC_REVISION: // {REV}
1005 buff = strecpy(buff, _openttd_revision, last);
1006 break;
1007
1008 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1009 if (game_script) break;
1010 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
1011 buff = FormatString(buff, str, args, last);
1012 break;
1013 }
1014
1015 case SCC_STRING: {// {STRING}
1016 StringID str = args->GetInt32(SCC_STRING);
1017 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
1018 /* WARNING. It's prohibited for the included string to consume any arguments.
1019 * For included strings that consume argument, you should use STRING1, STRING2 etc.
1020 * To debug stuff you can set argv to NULL and it will tell you */
1021 StringParameters tmp_params(args->GetDataPointer(), args->GetDataLeft(), NULL);
1022 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
1023 next_substr_case_index = 0;
1024 break;
1025 }
1026
1027 case SCC_STRING1:
1028 case SCC_STRING2:
1029 case SCC_STRING3:
1030 case SCC_STRING4:
1031 case SCC_STRING5:
1032 case SCC_STRING6:
1033 case SCC_STRING7: { // {STRING1..7}
1034 /* Strings that consume arguments */
1035 StringID str = args->GetInt32(b);
1036 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
1037 uint size = b - SCC_STRING1 + 1;
1038 if (game_script && size > args->GetDataLeft()) {
1039 buff = strecat(buff, "(too many parameters)", last);
1040 } else {
1041 StringParameters sub_args(*args, size);
1042 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
1043 }
1044 next_substr_case_index = 0;
1045 break;
1046 }
1047
1048 case SCC_COMMA: // {COMMA}
1049 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
1050 break;
1051
1052 case SCC_DECIMAL: {// {DECIMAL}
1053 int64 number = args->GetInt64(SCC_DECIMAL);
1054 int digits = args->GetInt32(SCC_DECIMAL);
1055 buff = FormatCommaNumber(buff, number, last, digits);
1056 break;
1057 }
1058
1059 case SCC_NUM: // {NUM}
1060 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
1061 break;
1062
1063 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1064 int64 num = args->GetInt64();
1065 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
1066 break;
1067 }
1068
1069 case SCC_HEX: // {HEX}
1070 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
1071 break;
1072
1073 case SCC_BYTES: // {BYTES}
1074 buff = FormatBytes(buff, args->GetInt64(), last);
1075 break;
1076
1077 case SCC_CARGO_TINY: { // {CARGO_TINY}
1078 /* Tiny description of cargotypes. Layout:
1079 * param 1: cargo type
1080 * param 2: cargo count */
1081 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
1082 if (cargo >= CargoSpec::GetArraySize()) break;
1083
1084 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1085 int64 amount = 0;
1086 switch (cargo_str) {
1087 case STR_TONS:
1088 amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64());
1089 break;
1090
1091 case STR_LITERS:
1092 amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64());
1093 break;
1094
1095 default: {
1096 amount = args->GetInt64();
1097 break;
1098 }
1099 }
1100
1101 buff = FormatCommaNumber(buff, amount, last);
1102 break;
1103 }
1104
1105 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1106 /* Short description of cargotypes. Layout:
1107 * param 1: cargo type
1108 * param 2: cargo count */
1109 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
1110 if (cargo >= CargoSpec::GetArraySize()) break;
1111
1112 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1113 switch (cargo_str) {
1114 case STR_TONS: {
1115 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1116 int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1117 StringParameters tmp_params(args_array);
1118 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1119 break;
1120 }
1121
1122 case STR_LITERS: {
1123 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1124 int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1125 StringParameters tmp_params(args_array);
1126 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1127 break;
1128 }
1129
1130 default: {
1131 StringParameters tmp_params(*args, 1);
1132 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
1133 break;
1134 }
1135 }
1136 break;
1137 }
1138
1139 case SCC_CARGO_LONG: { // {CARGO_LONG}
1140 /* First parameter is cargo type, second parameter is cargo count */
1141 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
1142 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
1143
1144 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1145 StringParameters tmp_args(*args, 1);
1146 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
1147 break;
1148 }
1149
1150 case SCC_CARGO_LIST: { // {CARGO_LIST}
1151 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
1152 bool first = true;
1153
1154 const CargoSpec *cs;
1155 FOR_ALL_SORTED_CARGOSPECS(cs) {
1156 if (!HasBit(cmask, cs->Index())) continue;
1157
1158 if (buff >= last - 2) break; // ',' and ' '
1159
1160 if (first) {
1161 first = false;
1162 } else {
1163 /* Add a comma if this is not the first item */
1164 *buff++ = ',';
1165 *buff++ = ' ';
1166 }
1167
1168 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
1169 }
1170
1171 /* If first is still true then no cargo is accepted */
1172 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
1173
1174 *buff = '\0';
1175 next_substr_case_index = 0;
1176
1177 /* Make sure we detect any buffer overflow */
1178 assert(buff < last);
1179 break;
1180 }
1181
1182 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1183 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
1184 break;
1185
1186 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1187 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
1188 break;
1189
1190 case SCC_DATE_TINY: // {DATE_TINY}
1191 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
1192 break;
1193
1194 case SCC_DATE_SHORT: // {DATE_SHORT}
1195 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
1196 next_substr_case_index = 0;
1197 break;
1198
1199 case SCC_DATE_LONG: // {DATE_LONG}
1200 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
1201 next_substr_case_index = 0;
1202 break;
1203
1204 case SCC_DATE_ISO: // {DATE_ISO}
1205 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
1206 break;
1207
1208 case SCC_FORCE: { // {FORCE}
1209 assert(_settings_game.locale.units_force < lengthof(_units_force));
1210 int64 args_array[1] = {_units_force[_settings_game.locale.units_force].c.ToDisplay(args->GetInt64())};
1211 StringParameters tmp_params(args_array);
1212 buff = FormatString(buff, GetStringPtr(_units_force[_settings_game.locale.units_force].s), &tmp_params, last);
1213 break;
1214 }
1215
1216 case SCC_HEIGHT: { // {HEIGHT}
1217 assert(_settings_game.locale.units_height < lengthof(_units_height));
1218 int64 args_array[] = {_units_height[_settings_game.locale.units_height].c.ToDisplay(args->GetInt64())};
1219 StringParameters tmp_params(args_array);
1220 buff = FormatString(buff, GetStringPtr(_units_height[_settings_game.locale.units_height].s), &tmp_params, last);
1221 break;
1222 }
1223
1224 case SCC_POWER: { // {POWER}
1225 assert(_settings_game.locale.units_power < lengthof(_units_power));
1226 int64 args_array[1] = {_units_power[_settings_game.locale.units_power].c.ToDisplay(args->GetInt64())};
1227 StringParameters tmp_params(args_array);
1228 buff = FormatString(buff, GetStringPtr(_units_power[_settings_game.locale.units_power].s), &tmp_params, last);
1229 break;
1230 }
1231
1232 case SCC_VELOCITY: { // {VELOCITY}
1233 assert(_settings_game.locale.units_velocity < lengthof(_units_velocity));
1234 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
1235 StringParameters tmp_params(args_array);
1236 buff = FormatString(buff, GetStringPtr(_units_velocity[_settings_game.locale.units_velocity].s), &tmp_params, last);
1237 break;
1238 }
1239
1240 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1241 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1242 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1243 StringParameters tmp_params(args_array);
1244 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].s), &tmp_params, last);
1245 break;
1246 }
1247
1248 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1249 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1250 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
1251 StringParameters tmp_params(args_array);
1252 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1253 break;
1254 }
1255
1256 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1257 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1258 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1259 StringParameters tmp_params(args_array);
1260 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].s), &tmp_params, last);
1261 break;
1262 }
1263
1264 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1265 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1266 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
1267 StringParameters tmp_params(args_array);
1268 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1269 break;
1270 }
1271
1272 case SCC_COMPANY_NAME: { // {COMPANY}
1273 const Company *c = Company::GetIfValid(args->GetInt32());
1274 if (c == NULL) break;
1275
1276 if (c->name != NULL) {
1277 int64 args_array[] = {(uint64)(size_t)c->name};
1278 StringParameters tmp_params(args_array);
1279 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1280 } else {
1281 int64 args_array[] = {c->name_2};
1282 StringParameters tmp_params(args_array);
1283 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
1284 }
1285 break;
1286 }
1287
1288 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1289 CompanyID company = (CompanyID)args->GetInt32();
1290
1291 /* Nothing is added for AI or inactive companies */
1292 if (Company::IsValidHumanID(company)) {
1293 int64 args_array[] = {company + 1};
1294 StringParameters tmp_params(args_array);
1295 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
1296 }
1297 break;
1298 }
1299
1300 case SCC_DEPOT_NAME: { // {DEPOT}
1301 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
1302 if (vt == VEH_AIRCRAFT) {
1303 uint64 args_array[] = {args->GetInt32()};
1304 WChar types_array[] = {SCC_STATION_NAME};
1305 StringParameters tmp_params(args_array, 1, types_array);
1306 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
1307 break;
1308 }
1309
1310 const Depot *d = Depot::Get(args->GetInt32());
1311 if (d->name != NULL) {
1312 int64 args_array[] = {(uint64)(size_t)d->name};
1313 StringParameters tmp_params(args_array);
1314 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1315 } else {
1316 int64 args_array[] = {d->town->index, d->town_cn + 1};
1317 StringParameters tmp_params(args_array);
1318 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
1319 }
1320 break;
1321 }
1322
1323 case SCC_ENGINE_NAME: { // {ENGINE}
1324 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
1325 if (e == NULL) break;
1326
1327 if (e->name != NULL && e->IsEnabled()) {
1328 int64 args_array[] = {(uint64)(size_t)e->name};
1329 StringParameters tmp_params(args_array);
1330 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1331 } else {
1332 StringParameters tmp_params(NULL, 0, NULL);
1333 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
1334 }
1335 break;
1336 }
1337
1338 case SCC_GROUP_NAME: { // {GROUP}
1339 const Group *g = Group::GetIfValid(args->GetInt32());
1340 if (g == NULL) break;
1341
1342 if (g->name != NULL) {
1343 int64 args_array[] = {(uint64)(size_t)g->name};
1344 StringParameters tmp_params(args_array);
1345 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1346 } else {
1347 int64 args_array[] = {g->index};
1348 StringParameters tmp_params(args_array);
1349
1350 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
1351 }
1352 break;
1353 }
1354
1355 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1356 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
1357 if (i == NULL) break;
1358
1359 if (_scan_for_gender_data) {
1360 /* Gender is defined by the industry type.
1361 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1362 StringParameters tmp_params(NULL, 0, NULL);
1363 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
1364 } else {
1365 /* First print the town name and the industry type name. */
1366 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
1367 StringParameters tmp_params(args_array);
1368
1369 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
1370 }
1371 next_substr_case_index = 0;
1372 break;
1373 }
1374
1375 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1376 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
1377 if (c == NULL) break;
1378
1379 if (c->president_name != NULL) {
1380 int64 args_array[] = {(uint64)(size_t)c->president_name};
1381 StringParameters tmp_params(args_array);
1382 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1383 } else {
1384 int64 args_array[] = {c->president_name_2};
1385 StringParameters tmp_params(args_array);
1386 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
1387 }
1388 break;
1389 }
1390
1391 case SCC_STATION_NAME: { // {STATION}
1392 StationID sid = args->GetInt32(SCC_STATION_NAME);
1393 const Station *st = Station::GetIfValid(sid);
1394
1395 if (st == NULL) {
1396 /* The station doesn't exist anymore. The only place where we might
1397 * be "drawing" an invalid station is in the case of cargo that is
1398 * in transit. */
1399 StringParameters tmp_params(NULL, 0, NULL);
1400 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
1401 break;
1402 }
1403
1404 if (st->name != NULL) {
1405 int64 args_array[] = {(uint64)(size_t)st->name};
1406 StringParameters tmp_params(args_array);
1407 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1408 } else {
1409 StringID str = st->string_id;
1410 if (st->indtype != IT_INVALID) {
1411 /* Special case where the industry provides the name for the station */
1412 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1413
1414 /* Industry GRFs can change which might remove the station name and
1415 * thus cause very strange things. Here we check for that before we
1416 * actually set the station name. */
1417 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1418 str = indsp->station_name;
1419 }
1420 }
1421
1422 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
1423 StringParameters tmp_params(args_array);
1424 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1425 }
1426 break;
1427 }
1428
1429 case SCC_TOWN_NAME: { // {TOWN}
1430 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
1431 if (t == NULL) break;
1432
1433 if (t->name != NULL) {
1434 int64 args_array[] = {(uint64)(size_t)t->name};
1435 StringParameters tmp_params(args_array);
1436 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1437 } else {
1438 buff = GetTownName(buff, t, last);
1439 }
1440 break;
1441 }
1442
1443 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1444 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
1445 if (wp == NULL) break;
1446
1447 if (wp->name != NULL) {
1448 int64 args_array[] = {(uint64)(size_t)wp->name};
1449 StringParameters tmp_params(args_array);
1450 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1451 } else {
1452 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
1453 StringParameters tmp_params(args_array);
1454 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1455 if (wp->town_cn != 0) str++;
1456 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1457 }
1458 break;
1459 }
1460
1461 case SCC_VEHICLE_NAME: { // {VEHICLE}
1462 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
1463 if (v == NULL) break;
1464
1465 if (v->name != NULL) {
1466 int64 args_array[] = {(uint64)(size_t)v->name};
1467 StringParameters tmp_params(args_array);
1468 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1469 } else {
1470 int64 args_array[] = {v->unitnumber};
1471 StringParameters tmp_params(args_array);
1472
1473 StringID str;
1474 switch (v->type) {
1475 default: str = STR_INVALID_VEHICLE; break;
1476 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
1477 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
1478 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
1479 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
1480 }
1481
1482 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1483 }
1484 break;
1485 }
1486
1487 case SCC_SIGN_NAME: { // {SIGN}
1488 const Sign *si = Sign::GetIfValid(args->GetInt32());
1489 if (si == NULL) break;
1490
1491 if (si->name != NULL) {
1492 int64 args_array[] = {(uint64)(size_t)si->name};
1493 StringParameters tmp_params(args_array);
1494 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1495 } else {
1496 StringParameters tmp_params(NULL, 0, NULL);
1497 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
1498 }
1499 break;
1500 }
1501
1502 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1503 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
1504 break;
1505 }
1506
1507 default:
1508 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
1509 break;
1510 }
1511 }
1512 *buff = '\0';
1513 return buff;
1514 }
1515
1516
1517 static char *StationGetSpecialString(char *buff, int x, const char *last)
1518 {
1519 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
1520 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
1521 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
1522 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
1523 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
1524 *buff = '\0';
1525 return buff;
1526 }
1527
1528 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
1529 {
1530 return GenerateTownNameString(buff, last, ind, seed);
1531 }
1532
1533 static const char * const _silly_company_names[] = {
1534 "Bloggs Brothers",
1535 "Tiny Transport Ltd.",
1536 "Express Travel",
1537 "Comfy-Coach & Co.",
1538 "Crush & Bump Ltd.",
1539 "Broken & Late Ltd.",
1540 "Sam Speedy & Son",
1541 "Supersonic Travel",
1542 "Mike's Motors",
1543 "Lightning International",
1544 "Pannik & Loozit Ltd.",
1545 "Inter-City Transport",
1546 "Getout & Pushit Ltd."
1547 };
1548
1549 static const char * const _surname_list[] = {
1550 "Adams",
1551 "Allan",
1552 "Baker",
1553 "Bigwig",
1554 "Black",
1555 "Bloggs",
1556 "Brown",
1557 "Campbell",
1558 "Gordon",
1559 "Hamilton",
1560 "Hawthorn",
1561 "Higgins",
1562 "Green",
1563 "Gribble",
1564 "Jones",
1565 "McAlpine",
1566 "MacDonald",
1567 "McIntosh",
1568 "Muir",
1569 "Murphy",
1570 "Nelson",
1571 "O'Donnell",
1572 "Parker",
1573 "Phillips",
1574 "Pilkington",
1575 "Quigley",
1576 "Sharkey",
1577 "Thomson",
1578 "Watkins"
1579 };
1580
1581 static const char * const _silly_surname_list[] = {
1582 "Grumpy",
1583 "Dozy",
1584 "Speedy",
1585 "Nosey",
1586 "Dribble",
1587 "Mushroom",
1588 "Cabbage",
1589 "Sniffle",
1590 "Fishy",
1591 "Swindle",
1592 "Sneaky",
1593 "Nutkins"
1594 };
1595
1596 static const char _initial_name_letters[] = {
1597 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1598 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1599 };
1600
1601 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
1602 {
1603 const char * const *base;
1604 uint num;
1605
1606 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1607 base = _silly_surname_list;
1608 num = lengthof(_silly_surname_list);
1609 } else {
1610 base = _surname_list;
1611 num = lengthof(_surname_list);
1612 }
1613
1614 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
1615 buff = strecpy(buff, " & Co.", last);
1616
1617 return buff;
1618 }
1619
1620 static char *GenPresidentName(char *buff, uint32 x, const char *last)
1621 {
1622 char initial[] = "?. ";
1623 const char * const *base;
1624 uint num;
1625 uint i;
1626
1627 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
1628 buff = strecpy(buff, initial, last);
1629
1630 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
1631 if (i < sizeof(_initial_name_letters)) {
1632 initial[0] = _initial_name_letters[i];
1633 buff = strecpy(buff, initial, last);
1634 }
1635
1636 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1637 base = _silly_surname_list;
1638 num = lengthof(_silly_surname_list);
1639 } else {
1640 base = _surname_list;
1641 num = lengthof(_surname_list);
1642 }
1643
1644 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
1645
1646 return buff;
1647 }
1648
1649 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
1650 {
1651 switch (ind) {
1652 case 1: // not used
1653 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
1654
1655 case 2: // used for Foobar & Co company names
1656 return GenAndCoName(buff, args->GetInt32(), last);
1657
1658 case 3: // President name
1659 return GenPresidentName(buff, args->GetInt32(), last);
1660 }
1661
1662 /* town name? */
1663 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1664 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
1665 return strecpy(buff, " Transport", last);
1666 }
1667
1668 /* language name? */
1669 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
1670 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
1671 return strecpy(buff,
1672 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
1673 }
1674
1675 /* resolution size? */
1676 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
1677 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
1678 buff += seprintf(
1679 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
1680 );
1681 return buff;
1682 }
1683
1684 NOT_REACHED();
1685 }
1686
1687 #ifdef ENABLE_NETWORK
1688 extern void SortNetworkLanguages();
1689 #else /* ENABLE_NETWORK */
1690 static inline void SortNetworkLanguages() {}
1691 #endif /* ENABLE_NETWORK */
1692
1697 bool LanguagePackHeader::IsValid() const
1698 {
1699 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1700 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1701 this->plural_form < LANGUAGE_MAX_PLURAL &&
1702 this->text_dir <= 1 &&
1703 this->newgrflangid < MAX_LANG &&
1704 this->num_genders < MAX_NUM_GENDERS &&
1705 this->num_cases < MAX_NUM_CASES &&
1706 StrValid(this->name, lastof(this->name)) &&
1707 StrValid(this->own_name, lastof(this->own_name)) &&
1708 StrValid(this->isocode, lastof(this->isocode)) &&
1709 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
1710 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
1711 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
1712 }
1713
1719 bool ReadLanguagePack(const LanguageMetadata *lang)
1720 {
1721 /* Current language pack */
1722 size_t len;
1723 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
1724 if (lang_pack == NULL) return false;
1725
1726 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1727 const char *end = (char *)lang_pack + len + 1;
1728
1729 /* We need at least one byte of lang_pack->data */
1730 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1731 free(lang_pack);
1732 return false;
1733 }
1734
1735 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1736 for (uint i = 0; i < TAB_COUNT; i++) {
1737 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
1738 }
1739 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
1740
1741 uint count = 0;
1742 for (uint i = 0; i < TAB_COUNT; i++) {
1743 uint16 num = lang_pack->offsets[i];
1744 if (num > TAB_SIZE) {
1745 free(lang_pack);
1746 return false;
1747 }
1748
1749 _langtab_start[i] = count;
1750 _langtab_num[i] = num;
1751 count += num;
1752 }
1753
1754 /* Allocate offsets */
1755 char **langpack_offs = MallocT<char *>(count);
1756
1757 /* Fill offsets */
1758 char *s = lang_pack->data;
1759 len = (byte)*s++;
1760 for (uint i = 0; i < count; i++) {
1761 if (s + len >= end) {
1762 free(lang_pack);
1763 free(langpack_offs);
1764 return false;
1765 }
1766 if (len >= 0xC0) {
1767 len = ((len & 0x3F) << 8) + (byte)*s++;
1768 if (s + len >= end) {
1769 free(lang_pack);
1770 free(langpack_offs);
1771 return false;
1772 }
1773 }
1774 langpack_offs[i] = s;
1775 s += len;
1776 len = (byte)*s;
1777 *s++ = '\0'; // zero terminate the string
1778 }
1779
1780 free(_langpack);
1781 _langpack = lang_pack;
1782
1783 free(_langpack_offs);
1784 _langpack_offs = langpack_offs;
1785
1786 _current_language = lang;
1787 _current_text_dir = (TextDirection)_current_language->text_dir;
1788 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
1789 strecpy(_config_language_file, c_file, lastof(_config_language_file));
1790 SetCurrentGrfLangID(_current_language->newgrflangid);
1791
1792 #ifdef WITH_ICU
1793 /* Delete previous collator. */
1794 if (_current_collator != NULL) {
1795 delete _current_collator;
1796 _current_collator = NULL;
1797 }
1798
1799 /* Create a collator instance for our current locale. */
1800 UErrorCode status = U_ZERO_ERROR;
1801 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
1802 /* Sort number substrings by their numerical value. */
1803 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
1804 /* Avoid using the collator if it is not correctly set. */
1805 if (U_FAILURE(status)) {
1806 delete _current_collator;
1807 _current_collator = NULL;
1808 }
1809 #endif /* WITH_ICU */
1810
1811 /* Some lists need to be sorted again after a language change. */
1812 ReconsiderGameScriptLanguage();
1813 InitializeSortedCargoSpecs();
1814 SortIndustryTypes();
1815 BuildIndustriesLegend();
1816 SortNetworkLanguages();
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 >= TAB_COUNT) return NULL;
2049
2050 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
2051
2052 this->j++;
2053 while (this->i < TAB_COUNT && 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)
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
2154 }