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