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