Translations update
[openttd/fttd.git] / src / strings.cpp
blobdc67e6cc30725ae5e1df9adadadfa34683cc659c
1 /* $Id$ */
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 */
10 /** @file strings.cpp Handling of translated strings. */
12 #include "stdafx.h"
13 #include "currency.h"
14 #include "station_base.h"
15 #include "town.h"
16 #include "screenshot.h"
17 #include "waypoint_base.h"
18 #include "depot_base.h"
19 #include "industry.h"
20 #include "newgrf_text.h"
21 #include "fileio_func.h"
22 #include "signs_base.h"
23 #include "fontdetection.h"
24 #include "error.h"
25 #include "strings_func.h"
26 #include "rev.h"
27 #include "core/endian_func.hpp"
28 #include "date_func.h"
29 #include "vehicle_base.h"
30 #include "engine_base.h"
31 #include "language.h"
32 #include "townnamegen.h"
33 #include "townname_func.h"
34 #include "string.h"
35 #include "company_base.h"
36 #include "smallmap_gui.h"
37 #include "window_func.h"
38 #include "debug.h"
39 #include "game/game_text.hpp"
40 #include "network/network_content_gui.h"
41 #include <stack>
43 #include "table/strings.h"
44 #include "table/control_codes.h"
46 char _config_language_file[MAX_PATH]; ///< The file (name) stored in the configuration.
47 LanguageList _languages; ///< The actual list of language meta data.
48 const LanguageMetadata *_current_language = NULL; ///< The currently loaded language.
50 TextDirection _current_text_dir; ///< Text direction of the currently selected language.
52 #ifdef WITH_ICU_SORT
53 Collator *_current_collator = NULL; ///< Collator for the language currently in use.
54 #endif /* WITH_ICU_SORT */
56 static uint64 _global_string_params_data[20]; ///< Global array of string parameters. To access, use #SetDParam.
57 static WChar _global_string_params_type[20]; ///< Type of parameters stored in #_decode_parameters
58 StringParameters _global_string_params(_global_string_params_data, 20, _global_string_params_type);
60 /** Reset the type array. */
61 void StringParameters::ClearTypeInformation()
63 assert(this->type != NULL);
64 MemSetT(this->type, 0, this->num_param);
68 /**
69 * Read an int64 from the argument array. The offset is increased
70 * so the next time GetInt64 is called the next value is read.
72 int64 StringParameters::GetInt64(WChar type)
74 if (this->offset >= this->num_param) {
75 DEBUG(misc, 0, "Trying to read invalid string parameter");
76 return 0;
78 if (this->type != NULL) {
79 assert(this->type[this->offset] == 0 || this->type[this->offset] == type);
80 this->type[this->offset] = type;
82 return this->data[this->offset++];
85 /**
86 * Set DParam n to some number that is suitable for string size computations.
87 * @param n Index of the string parameter.
88 * @param max_value The biggest value which shall be displayed.
89 * For the result only the number of digits of \a max_value matter.
90 * @param min_count Minimum number of digits independent of \a max.
91 * @param size Font of the number
93 void SetDParamMaxValue(uint n, uint64 max_value, uint min_count, FontSize size)
95 uint num_digits = 1;
96 while (max_value >= 10) {
97 num_digits++;
98 max_value /= 10;
100 SetDParamMaxDigits(n, max(min_count, num_digits), size);
104 * Set DParam n to some number that is suitable for string size computations.
105 * @param n Index of the string parameter.
106 * @param count Number of digits which shall be displayable.
107 * @param size Font of the number
109 void SetDParamMaxDigits(uint n, uint count, FontSize size)
111 SetDParam (n, FontCache::Get(size)->GetBroadestValue (count));
115 * Copy \a num string parameters from array \a src into the global string parameter array.
116 * @param offs Index in the global array to copy the first string parameter to.
117 * @param src Source array of string parameters.
118 * @param num Number of string parameters to copy.
120 void CopyInDParam(int offs, const uint64 *src, int num)
122 MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
126 * Copy \a num string parameters from the global string parameter array to the \a dst array.
127 * @param dst Destination array of string parameters.
128 * @param offs Index in the global array to copy the first string parameter from.
129 * @param num Number of string parameters to copy.
131 void CopyOutDParam(uint64 *dst, int offs, int num)
133 MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
137 * Copy \a num string parameters from the global string parameter array to the \a dst array.
138 * Furthermore clone raw string parameters into \a strings and amend the data in \a dst.
139 * @param dst Destination array of string parameters.
140 * @param strings Destination array for clone of the raw strings. Must be of same length as dst. Deallocation left to the caller.
141 * @param string The string used to determine where raw strings are and where there are no raw strings.
142 * @param num Number of string parameters to copy.
144 void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
146 char buf[DRAW_STRING_BUFFER];
147 GetString (buf, string);
149 MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num);
150 for (int i = 0; i < num; i++) {
151 if (_global_string_params.HasTypeInformation() && _global_string_params.GetTypeAtOffset(i) == SCC_RAW_STRING_POINTER) {
152 strings[i] = xstrdup((const char *)(size_t)_global_string_params.GetParam(i));
153 dst[i] = (size_t)strings[i];
154 } else {
155 strings[i] = NULL;
160 static void AppendStationSpecialString (stringb *buf, int x);
161 static void AppendSpecialNameString (stringb *buf, int ind, StringParameters *args);
163 static void FormatString (stringb *buf, const char *str, StringParameters *args, uint case_index = 0, bool game_script = false, bool dry_run = false);
165 struct LanguagePack : public LanguagePackHeader {
166 char data[]; // list of strings
169 static char **_langpack_offs;
170 static LanguagePack *_langpack;
171 static uint _langtab_num[TAB_COUNT]; ///< Offset into langpack offs
172 static uint _langtab_start[TAB_COUNT]; ///< Offset into langpack offs
173 static bool _scan_for_gender_data = false; ///< Are we scanning for the gender of the current string? (instead of formatting it)
176 const char *GetStringPtr(StringID string)
178 switch (GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)) {
179 case GAME_TEXT_TAB: return GetGameStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
180 /* 0xD0xx and 0xD4xx IDs have been converted earlier. */
181 case 26: NOT_REACHED();
182 case 28: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
183 case 29: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x0800);
184 case 30: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x1000);
185 default: return _langpack_offs[_langtab_start[GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)] + GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS)];
190 * Get a parsed string with most special stringcodes replaced by the string parameters.
191 * @param buf Buffer where the formatted string should be written to.
192 * @param string
193 * @param args Arguments for the string.
194 * @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case.
195 * @param game_script The string is coming directly from a game script.
197 void AppendStringWithArgs (stringb *buf, StringID string, StringParameters *args, uint case_index, bool game_script)
199 if (string == 0) {
200 AppendStringWithArgs (buf, STR_UNDEFINED, args);
201 return;
204 uint index = GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS);
205 uint tab = GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS);
207 switch (tab) {
208 case 4:
209 if (index >= 0xC0 && !game_script) {
210 GenerateTownNameString (buf, index - 0xC0, args->GetInt32());
211 return;
213 break;
215 case 14:
216 if (index >= 0xE4 && !game_script) {
217 AppendSpecialNameString (buf, index - 0xE4, args);
218 return;
220 break;
222 case 15:
223 /* Old table for custom names. This is no longer used */
224 if (!game_script) {
225 error("Incorrect conversion of custom name string.");
227 break;
229 case GAME_TEXT_TAB:
230 FormatString (buf, GetGameStringPtr(index), args, case_index, true);
231 return;
233 case 26:
234 NOT_REACHED();
236 case 28:
237 FormatString (buf, GetGRFStringPtr(index), args, case_index);
238 return;
240 case 29:
241 FormatString (buf, GetGRFStringPtr(index + 0x0800), args, case_index);
242 return;
244 case 30:
245 FormatString (buf, GetGRFStringPtr(index + 0x1000), args, case_index);
246 return;
249 if (index >= _langtab_num[tab]) {
250 if (game_script) {
251 AppendStringWithArgs (buf, STR_UNDEFINED, args);
252 return;
254 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
257 FormatString (buf, GetStringPtr(string), args, case_index);
260 void AppendString (stringb *buf, StringID string)
262 _global_string_params.ClearTypeInformation();
263 _global_string_params.offset = 0;
264 AppendStringWithArgs (buf, string, &_global_string_params);
269 * This function is used to "bind" a C string to a OpenTTD dparam slot.
270 * @param n slot of the string
271 * @param str string to bind
273 void SetDParamStr(uint n, const char *str)
275 SetDParam(n, (uint64)(size_t)str);
279 * Format a number into a string.
280 * @param buf the buffer to write to
281 * @param number the number to write down
282 * @param separator the thousands-separator to use
283 * @param zerofill minimum number of digits to print for the integer part. The number will be filled with zeros at the front if necessary.
284 * @param fractional_digits number of fractional digits to display after a decimal separator. The decimal separator is inserted
285 * in front of the \a fractional_digits last digit of \a number.
287 static void FormatNumber (stringb *buf, int64 number, const char *separator, int zerofill = 1, int fractional_digits = 0)
289 static const int max_digits = 20;
290 uint64 divisor = 10000000000000000000ULL;
291 zerofill += fractional_digits;
292 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
294 if (number < 0) {
295 buf->append ('-');
296 number = -number;
299 uint64 num = number;
300 uint64 tot = 0;
301 for (int i = 0; i < max_digits; i++) {
302 if (i == max_digits - fractional_digits) {
303 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
304 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
305 buf->append (decimal_separator);
308 uint64 quot = 0;
309 if (num >= divisor) {
310 quot = num / divisor;
311 num = num % divisor;
313 if ((tot |= quot) || i >= max_digits - zerofill) {
314 buf->append_fmt ("%i", (int)quot);
315 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buf->append (separator);
318 divisor /= 10;
322 static void FormatCommaNumber (stringb *buf, int64 number, int fractional_digits = 0)
324 const char *separator = _settings_game.locale.digit_group_separator;
325 if (separator == NULL) separator = _langpack->digit_group_separator;
326 FormatNumber (buf, number, separator, 1, fractional_digits);
329 static void FormatNoCommaNumber (stringb *buf, int64 number)
331 FormatNumber (buf, number, "");
334 static void FormatZerofillNumber (stringb *buf, int64 number, int64 count)
336 FormatNumber (buf, number, "", count);
339 static void FormatHexNumber (stringb *buf, uint64 number)
341 buf->append_fmt ("0x" OTTD_PRINTFHEX64, number);
345 * Format a given number as a number of bytes with the SI prefix.
346 * @param buf the buffer to write to
347 * @param number the number of bytes to write down
349 static void FormatBytes (stringb *buf, int64 number)
351 assert(number >= 0);
353 if (number < 1024) {
354 buf->append_fmt ("%i" NBSP "B", (int)number);
355 return;
358 /* 2^10 2^20 2^30 2^40 2^50 2^60 */
359 static const char iec_prefixes[] = {'K', 'M', 'G', 'T', 'P', 'E'};
360 uint id = 0;
361 while (number >= 1024 * 1024) {
362 number /= 1024;
363 id++;
366 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
367 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
369 if (number < 1024 * 10) {
370 buf->append_fmt ("%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
371 } else if (number < 1024 * 100) {
372 buf->append_fmt ("%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
373 } else {
374 assert(number < 1024 * 1024);
375 buf->append_fmt ("%i", (int)number / 1024);
378 assert(id < lengthof(iec_prefixes));
379 buf->append_fmt (NBSP "%ciB", iec_prefixes[id]);
382 static void FormatYmdString (stringb *buf, Date date, uint case_index)
384 YearMonthDay ymd;
385 ConvertDateToYMD(date, &ymd);
387 int64 args[] = {ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
388 StringParameters tmp_params(args);
389 FormatString (buf, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, case_index);
392 static void FormatMonthAndYear (stringb *buf, Date date, uint case_index)
394 YearMonthDay ymd;
395 ConvertDateToYMD(date, &ymd);
397 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
398 StringParameters tmp_params(args);
399 FormatString (buf, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, case_index);
402 static void FormatTinyOrISODate (stringb *buf, Date date, StringID str)
404 YearMonthDay ymd;
405 ConvertDateToYMD(date, &ymd);
407 char day[3];
408 char month[3];
409 /* We want to zero-pad the days and months */
410 snprintf(day, lengthof(day), "%02i", ymd.day);
411 snprintf(month, lengthof(month), "%02i", ymd.month + 1);
413 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
414 StringParameters tmp_params(args);
415 FormatString (buf, GetStringPtr(str), &tmp_params);
418 static void FormatGenericCurrency (stringb *buf, const CurrencySpec *spec, Money number, bool compact)
420 /* We are going to make number absolute for printing, so
421 * keep this piece of data as we need it later on */
422 bool negative = number < 0;
423 const char *multiplier = "";
425 number *= spec->rate;
427 /* convert from negative */
428 if (number < 0) {
429 if (!buf->append_utf8(SCC_RED)) return;
430 buf->append ('-');
431 number = -number;
434 /* Add prefix part, following symbol_pos specification.
435 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
436 * The only remaining value is 1 (suffix), so everything that is not 1 */
437 if (spec->symbol_pos != 1) buf->append (spec->prefix);
439 /* for huge numbers, compact the number into k or M */
440 if (compact) {
441 /* Take care of the 'k' rounding. Having 1 000 000 k
442 * and 1 000 M is inconsistent, so always use 1 000 M. */
443 if (number >= 1000000000 - 500) {
444 number = (number + 500000) / 1000000;
445 multiplier = NBSP "M";
446 } else if (number >= 1000000) {
447 number = (number + 500) / 1000;
448 multiplier = NBSP "k";
452 const char *separator = _settings_game.locale.digit_group_separator_currency;
453 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
454 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
455 FormatNumber (buf, number, separator);
456 buf->append (multiplier);
458 /* Add suffix part, following symbol_pos specification.
459 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
460 * The only remaining value is 1 (prefix), so everything that is not 0 */
461 if (spec->symbol_pos != 0) buf->append (spec->suffix);
463 if (negative) buf->append_utf8(SCC_PREVIOUS_COLOUR);
467 * Determine the "plural" index given a plural form and a number.
468 * @param count The number to get the plural index of.
469 * @param plural_form The plural form we want an index for.
470 * @return The plural index for the given form.
472 static int DeterminePluralForm(int64 count, int plural_form)
474 /* The absolute value determines plurality */
475 uint64 n = abs(count);
477 switch (plural_form) {
478 default:
479 NOT_REACHED();
481 /* Two forms: singular used for one only.
482 * Used in:
483 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
484 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
485 case 0:
486 return n != 1 ? 1 : 0;
488 /* Only one form.
489 * Used in:
490 * Hungarian, Japanese, Korean, Turkish */
491 case 1:
492 return 0;
494 /* Two forms: singular used for 0 and 1.
495 * Used in:
496 * French, Brazilian Portuguese */
497 case 2:
498 return n > 1 ? 1 : 0;
500 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
501 * Note: Cases are out of order for hysterical reasons. '0' is last.
502 * Used in:
503 * Latvian */
504 case 3:
505 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
507 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
508 * Used in:
509 * Gaelige (Irish) */
510 case 4:
511 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
513 /* 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.
514 * Used in:
515 * Lithuanian */
516 case 5:
517 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
519 /* 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.
520 * Used in:
521 * Croatian, Russian, Ukrainian */
522 case 6:
523 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
525 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
526 * Used in:
527 * Polish */
528 case 7:
529 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
531 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
532 * Used in:
533 * Slovenian */
534 case 8:
535 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
537 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
538 * Used in:
539 * Icelandic */
540 case 9:
541 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
543 /* Three forms: special cases for 1, and 2 to 4
544 * Used in:
545 * Czech, Slovak */
546 case 10:
547 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
549 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
550 * Korean doesn't have the concept of plural, but depending on how a
551 * number is pronounced it needs another version of a particle.
552 * As such the plural system is misused to give this distinction.
554 case 11:
555 switch (n % 10) {
556 case 0: // yeong
557 case 1: // il
558 case 3: // sam
559 case 6: // yuk
560 case 7: // chil
561 case 8: // pal
562 return 0;
564 case 2: // i
565 case 4: // sa
566 case 5: // o
567 case 9: // gu
568 return 1;
570 default:
571 NOT_REACHED();
574 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
575 * Used in:
576 * Maltese */
577 case 12:
578 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
579 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
580 * Used in:
581 * Scottish Gaelic */
582 case 13:
583 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
587 static const char *ParseStringChoice (const char *b, uint form, stringb *dst)
589 /* <NUM> {Length of each string} {each string} */
590 uint n = (byte)*b++;
591 uint pos, i, mypos = 0;
593 for (i = pos = 0; i != n; i++) {
594 uint len = (byte)*b++;
595 if (i == form) mypos = pos;
596 pos += len;
599 dst->append (b + mypos);
600 return b + pos;
603 /** Helper for unit conversion. */
604 struct UnitConversion {
605 int multiplier; ///< Amount to multiply upon conversion.
606 int shift; ///< Amount to shift upon conversion.
609 * Convert value from OpenTTD's internal unit into the displayed value.
610 * @param input The input to convert.
611 * @param round Whether to round the value or not.
612 * @return The converted value.
614 int64 ToDisplay(int64 input, bool round = true) const
616 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
620 * Convert the displayed value back into a value of OpenTTD's internal unit.
621 * @param input The input to convert.
622 * @param round Whether to round the value up or not.
623 * @param divider Divide the return value by this.
624 * @return The converted value.
626 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
628 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
632 /** Information about a specific unit system. */
633 struct Units {
634 UnitConversion c; ///< Conversion
635 StringID s; ///< String for the unit
638 /** Information about a specific unit system with a long variant. */
639 struct UnitsLong {
640 UnitConversion c; ///< Conversion
641 StringID s; ///< String for the short variant of the unit
642 StringID l; ///< String for the long variant of the unit
645 /** Unit conversions for velocity. */
646 static const Units _units_velocity[] = {
647 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL },
648 { { 103, 6}, STR_UNITS_VELOCITY_METRIC },
649 { {1831, 12}, STR_UNITS_VELOCITY_SI },
652 /** Unit conversions for velocity. */
653 static const Units _units_power[] = {
654 { { 1, 0}, STR_UNITS_POWER_IMPERIAL },
655 { {4153, 12}, STR_UNITS_POWER_METRIC },
656 { {6109, 13}, STR_UNITS_POWER_SI },
659 /** Unit conversions for weight. */
660 static const UnitsLong _units_weight[] = {
661 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
662 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC },
663 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI },
666 /** Unit conversions for volume. */
667 static const UnitsLong _units_volume[] = {
668 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
669 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC },
670 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI },
673 /** Unit conversions for force. */
674 static const Units _units_force[] = {
675 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL },
676 { {3263, 5}, STR_UNITS_FORCE_METRIC },
677 { { 1, 0}, STR_UNITS_FORCE_SI },
680 /** Unit conversions for height. */
681 static const Units _units_height[] = {
682 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL }, // "Wrong" conversion factor for more nicer GUI values
683 { { 1, 0}, STR_UNITS_HEIGHT_METRIC },
684 { { 1, 0}, STR_UNITS_HEIGHT_SI },
688 * Convert the given (internal) speed to the display speed.
689 * @param speed the speed to convert
690 * @return the converted speed.
692 uint ConvertSpeedToDisplaySpeed(uint speed)
694 /* For historical reasons we don't want to mess with the
695 * conversion for speed. So, don't round it and keep the
696 * original conversion factors instead of the real ones. */
697 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed, false);
701 * Convert the given display speed to the (internal) speed.
702 * @param speed the speed to convert
703 * @return the converted speed.
705 uint ConvertDisplaySpeedToSpeed(uint speed)
707 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed);
711 * Convert the given km/h-ish speed to the display speed.
712 * @param speed the speed to convert
713 * @return the converted speed.
715 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
717 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed * 10, false) / 16;
721 * Convert the given display speed to the km/h-ish speed.
722 * @param speed the speed to convert
723 * @return the converted speed.
725 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
727 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed * 16, true, 10);
730 * Parse most format codes within a string and write the result to a buffer.
731 * @param buf The buffer to write the final string to.
732 * @param str The original string with format codes.
733 * @param args Pointer to extra arguments used by various string codes.
734 * @param case_index
735 * @param dry_run True when the argt array is not yet initialized.
737 static void FormatString (stringb *buf, const char *str_arg, StringParameters *args, uint case_index, bool game_script, bool dry_run)
739 uint orig_offset = args->offset;
741 /* When there is no array with types there is no need to do a dry run. */
742 if (args->HasTypeInformation() && !dry_run) {
743 size_t length = buf->length();
744 if (UsingNewGRFTextStack()) {
745 /* Values from the NewGRF text stack are only copied to the normal
746 * argv array at the time they are encountered. That means that if
747 * another string command references a value later in the string it
748 * would fail. We solve that by running FormatString twice. The first
749 * pass makes sure the argv array is correctly filled and the second
750 * pass can reference later values without problems. */
751 struct TextRefStack *backup = CreateTextRefStackBackup();
752 FormatString (buf, str_arg, args, case_index, game_script, true);
753 RestoreTextRefStackBackup(backup);
754 } else {
755 FormatString (buf, str_arg, args, case_index, game_script, true);
757 buf->truncate (length);
758 /* We have to restore the original offset here to to read the correct values. */
759 args->offset = orig_offset;
761 WChar b = '\0';
762 uint next_substr_case_index = 0;
763 std::stack<const char *> str_stack;
764 str_stack.push(str_arg);
766 for (;;) {
767 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
768 str_stack.pop();
770 if (str_stack.empty()) break;
771 const char *&str = str_stack.top();
773 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
774 /* We need to pass some stuff as it might be modified; oh boy. */
775 //todo: should argve be passed here too?
776 b = RemapNewGRFStringControlCode (b, buf, &str, (int64 *)args->GetDataPointer(), args->GetDataLeft(), dry_run);
777 if (b == 0) continue;
780 switch (b) {
781 case SCC_ENCODED: {
782 uint64 sub_args_data[20];
783 WChar sub_args_type[20];
784 bool sub_args_need_free[20];
785 StringParameters sub_args(sub_args_data, 20, sub_args_type);
787 sub_args.ClearTypeInformation();
788 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
790 uint16 stringid;
791 const char *s = str;
792 char *p;
793 stringid = strtol(str, &p, 16);
794 if (*p != ':' && *p != '\0') {
795 while (*p != '\0') p++;
796 str = p;
797 buf->append ("(invalid SCC_ENCODED)");
798 break;
800 if (stringid >= TAB_SIZE) {
801 while (*p != '\0') p++;
802 str = p;
803 buf->append ("(invalid StringID)");
804 break;
807 int i = 0;
808 while (*p != '\0' && i < 20) {
809 uint64 param;
810 s = ++p;
812 /* Find the next value */
813 bool instring = false;
814 bool escape = false;
815 for (;; p++) {
816 if (*p == '\\') {
817 escape = true;
818 continue;
820 if (*p == '"' && escape) {
821 escape = false;
822 continue;
824 escape = false;
826 if (*p == '"') {
827 instring = !instring;
828 continue;
830 if (instring) {
831 continue;
834 if (*p == ':') break;
835 if (*p == '\0') break;
838 if (*s != '"') {
839 /* Check if we want to look up another string */
840 WChar l;
841 size_t len = Utf8Decode(&l, s);
842 bool lookup = (l == SCC_ENCODED);
843 if (lookup) s += len;
845 param = strtoull(s, &p, 16);
847 if (lookup) {
848 if (param >= TAB_SIZE) {
849 while (*p != '\0') p++;
850 str = p;
851 buf->append ("(invalid sub-StringID)");
852 break;
854 param = (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + param;
857 sub_args.SetParam(i++, param);
858 } else {
859 char *g = xstrmemdup (s, p - s);
861 sub_args_need_free[i] = true;
862 sub_args.SetParam(i++, (uint64)(size_t)g);
865 /* If we didn't error out, we can actually print the string. */
866 if (*str != '\0') {
867 str = p;
868 AppendStringWithArgs (buf, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, true);
871 for (int i = 0; i < 20; i++) {
872 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
874 break;
877 case SCC_NEWGRF_STRINL: {
878 StringID substr = Utf8Consume(&str);
879 str_stack.push(GetStringPtr(substr));
880 break;
883 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
884 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
885 str_stack.push(GetStringPtr(substr));
886 case_index = next_substr_case_index;
887 next_substr_case_index = 0;
888 break;
892 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
893 /* First read the meta data from the language file. */
894 uint offset = orig_offset + (byte)*str++;
895 int gender = 0;
896 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
897 /* Now we need to figure out what text to resolve, i.e.
898 * what do we need to draw? So get the actual raw string
899 * first using the control code to get said string. */
900 char input[4 + 1];
901 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
902 *p = '\0';
904 /* Now do the string formatting. */
905 sstring<256> tmp;
906 bool old_sgd = _scan_for_gender_data;
907 _scan_for_gender_data = true;
908 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
909 FormatString (&tmp, input, &tmp_params);
910 _scan_for_gender_data = old_sgd;
912 /* And determine the string. */
913 const char *s = tmp.c_str();
914 WChar c = Utf8Consume(&s);
915 /* Does this string have a gender, if so, set it */
916 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
918 str = ParseStringChoice(str, gender, buf);
919 break;
922 /* This sets up the gender for the string.
923 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
924 case SCC_GENDER_INDEX: // {GENDER 0}
925 if (_scan_for_gender_data) {
926 buf->append_utf8 (SCC_GENDER_INDEX);
927 buf->append (*str++);
928 } else {
929 str++;
931 break;
933 case SCC_PLURAL_LIST: { // {P}
934 int plural_form = *str++; // contains the plural form for this string
935 uint offset = orig_offset + (byte)*str++;
936 int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
937 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), buf);
938 break;
941 case SCC_ARG_INDEX: { // Move argument pointer
942 args->offset = orig_offset + (byte)*str++;
943 break;
946 case SCC_SET_CASE: { // {SET_CASE}
947 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
948 * The modifier is added to all subsequent AppendStringWithArgs that accept the modifier. */
949 next_substr_case_index = (byte)*str++;
950 break;
953 case SCC_SWITCH_CASE: { // {Used to implement case switching}
954 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
955 * Each LEN is printed using 2 bytes in big endian order. */
956 uint num = (byte)*str++;
957 while (num) {
958 if ((byte)str[0] == case_index) {
959 /* Found the case, adjust str pointer and continue */
960 str += 3;
961 break;
963 /* Otherwise skip to the next case */
964 str += 3 + (str[1] << 8) + str[2];
965 num--;
967 break;
970 case SCC_REVISION: // {REV}
971 buf->append (_openttd_revision);
972 break;
974 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
975 if (game_script) break;
976 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
977 FormatString (buf, str, args);
978 break;
981 case SCC_STRING: {// {STRING}
982 StringID str = args->GetInt32(SCC_STRING);
983 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
984 /* WARNING. It's prohibited for the included string to consume any arguments.
985 * For included strings that consume argument, you should use STRING1, STRING2 etc.
986 * To debug stuff you can set argv to NULL and it will tell you */
987 StringParameters tmp_params(args->GetDataPointer(), args->GetDataLeft(), NULL);
988 AppendStringWithArgs (buf, str, &tmp_params, next_substr_case_index, game_script);
989 next_substr_case_index = 0;
990 break;
993 case SCC_STRING1:
994 case SCC_STRING2:
995 case SCC_STRING3:
996 case SCC_STRING4:
997 case SCC_STRING5:
998 case SCC_STRING6:
999 case SCC_STRING7: { // {STRING1..7}
1000 /* Strings that consume arguments */
1001 StringID str = args->GetInt32(b);
1002 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
1003 uint size = b - SCC_STRING1 + 1;
1004 if (game_script && size > args->GetDataLeft()) {
1005 buf->append ("(too many parameters)");
1006 } else {
1007 StringParameters sub_args(*args, size);
1008 AppendStringWithArgs (buf, str, &sub_args, next_substr_case_index, game_script);
1010 next_substr_case_index = 0;
1011 break;
1014 case SCC_COMMA: // {COMMA}
1015 FormatCommaNumber (buf, args->GetInt64(SCC_COMMA));
1016 break;
1018 case SCC_DECIMAL: {// {DECIMAL}
1019 int64 number = args->GetInt64(SCC_DECIMAL);
1020 int digits = args->GetInt32(SCC_DECIMAL);
1021 FormatCommaNumber (buf, number, digits);
1022 break;
1025 case SCC_NUM: // {NUM}
1026 FormatNoCommaNumber (buf, args->GetInt64(SCC_NUM));
1027 break;
1029 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1030 int64 num = args->GetInt64();
1031 FormatZerofillNumber (buf, num, args->GetInt64());
1032 break;
1035 case SCC_HEX: // {HEX}
1036 FormatHexNumber (buf, (uint64)args->GetInt64(SCC_HEX));
1037 break;
1039 case SCC_BYTES: // {BYTES}
1040 FormatBytes (buf, args->GetInt64());
1041 break;
1043 case SCC_CARGO_TINY: { // {CARGO_TINY}
1044 /* Tiny description of cargotypes. Layout:
1045 * param 1: cargo type
1046 * param 2: cargo count */
1047 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
1048 if (cargo >= CargoSpec::GetArraySize()) break;
1050 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1051 int64 amount = 0;
1052 switch (cargo_str) {
1053 case STR_TONS:
1054 amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64());
1055 break;
1057 case STR_LITERS:
1058 amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64());
1059 break;
1061 default: {
1062 amount = args->GetInt64();
1063 break;
1067 FormatCommaNumber (buf, amount);
1068 break;
1071 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1072 /* Short description of cargotypes. Layout:
1073 * param 1: cargo type
1074 * param 2: cargo count */
1075 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
1076 if (cargo >= CargoSpec::GetArraySize()) break;
1078 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1079 switch (cargo_str) {
1080 case STR_TONS: {
1081 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1082 int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1083 StringParameters tmp_params(args_array);
1084 FormatString (buf, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params);
1085 break;
1088 case STR_LITERS: {
1089 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1090 int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1091 StringParameters tmp_params(args_array);
1092 FormatString (buf, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params);
1093 break;
1096 default: {
1097 StringParameters tmp_params(*args, 1);
1098 AppendStringWithArgs (buf, cargo_str, &tmp_params);
1099 break;
1102 break;
1105 case SCC_CARGO_LONG: { // {CARGO_LONG}
1106 /* First parameter is cargo type, second parameter is cargo count */
1107 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
1108 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
1110 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1111 StringParameters tmp_args(*args, 1);
1112 AppendStringWithArgs (buf, cargo_str, &tmp_args);
1113 break;
1116 case SCC_CARGO_LIST: { // {CARGO_LIST}
1117 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
1118 bool first = true;
1120 const CargoSpec *cs;
1121 FOR_ALL_SORTED_CARGOSPECS(cs) {
1122 if (!HasBit(cmask, cs->Index())) continue;
1124 if (buf->length() >= buf->capacity - 3) break; // ',' and ' '
1126 if (first) {
1127 first = false;
1128 } else {
1129 /* Add a comma if this is not the first item */
1130 buf->append (", ");
1133 AppendStringWithArgs (buf, cs->name, args, next_substr_case_index, game_script);
1136 /* If first is still true then no cargo is accepted */
1137 if (first) AppendStringWithArgs (buf, STR_JUST_NOTHING, args, next_substr_case_index, game_script);
1139 next_substr_case_index = 0;
1140 break;
1143 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1144 FormatGenericCurrency (buf, _currency, args->GetInt64(), true);
1145 break;
1147 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1148 FormatGenericCurrency (buf, _currency, args->GetInt64(SCC_CURRENCY_LONG), false);
1149 break;
1151 case SCC_DATE_TINY: // {DATE_TINY}
1152 FormatTinyOrISODate (buf, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY);
1153 break;
1155 case SCC_DATE_SHORT: // {DATE_SHORT}
1156 FormatMonthAndYear (buf, args->GetInt32(SCC_DATE_SHORT), next_substr_case_index);
1157 next_substr_case_index = 0;
1158 break;
1160 case SCC_DATE_LONG: // {DATE_LONG}
1161 FormatYmdString (buf, args->GetInt32(SCC_DATE_LONG), next_substr_case_index);
1162 next_substr_case_index = 0;
1163 break;
1165 case SCC_DATE_ISO: // {DATE_ISO}
1166 FormatTinyOrISODate (buf, args->GetInt32(), STR_FORMAT_DATE_ISO);
1167 break;
1169 case SCC_FORCE: { // {FORCE}
1170 assert(_settings_game.locale.units_force < lengthof(_units_force));
1171 int64 args_array[1] = {_units_force[_settings_game.locale.units_force].c.ToDisplay(args->GetInt64())};
1172 StringParameters tmp_params(args_array);
1173 FormatString (buf, GetStringPtr(_units_force[_settings_game.locale.units_force].s), &tmp_params);
1174 break;
1177 case SCC_HEIGHT: { // {HEIGHT}
1178 assert(_settings_game.locale.units_height < lengthof(_units_height));
1179 int64 args_array[] = {_units_height[_settings_game.locale.units_height].c.ToDisplay(args->GetInt64())};
1180 StringParameters tmp_params(args_array);
1181 FormatString (buf, GetStringPtr(_units_height[_settings_game.locale.units_height].s), &tmp_params);
1182 break;
1185 case SCC_POWER: { // {POWER}
1186 assert(_settings_game.locale.units_power < lengthof(_units_power));
1187 int64 args_array[1] = {_units_power[_settings_game.locale.units_power].c.ToDisplay(args->GetInt64())};
1188 StringParameters tmp_params(args_array);
1189 FormatString (buf, GetStringPtr(_units_power[_settings_game.locale.units_power].s), &tmp_params);
1190 break;
1193 case SCC_VELOCITY: { // {VELOCITY}
1194 assert(_settings_game.locale.units_velocity < lengthof(_units_velocity));
1195 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
1196 StringParameters tmp_params(args_array);
1197 FormatString (buf, GetStringPtr(_units_velocity[_settings_game.locale.units_velocity].s), &tmp_params);
1198 break;
1201 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1202 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1203 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1204 StringParameters tmp_params(args_array);
1205 FormatString (buf, GetStringPtr(_units_volume[_settings_game.locale.units_volume].s), &tmp_params);
1206 break;
1209 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1210 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1211 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
1212 StringParameters tmp_params(args_array);
1213 FormatString (buf, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params);
1214 break;
1217 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1218 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1219 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1220 StringParameters tmp_params(args_array);
1221 FormatString (buf, GetStringPtr(_units_weight[_settings_game.locale.units_weight].s), &tmp_params);
1222 break;
1225 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1226 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1227 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
1228 StringParameters tmp_params(args_array);
1229 FormatString (buf, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params);
1230 break;
1233 case SCC_COMPANY_NAME: { // {COMPANY}
1234 const Company *c = Company::GetIfValid(args->GetInt32());
1235 if (c == NULL) break;
1237 if (c->name != NULL) {
1238 int64 args_array[] = {(int64)(size_t)c->name};
1239 StringParameters tmp_params(args_array);
1240 AppendStringWithArgs (buf, STR_JUST_RAW_STRING, &tmp_params);
1241 } else {
1242 int64 args_array[] = {c->name_2};
1243 StringParameters tmp_params(args_array);
1244 AppendStringWithArgs (buf, c->name_1, &tmp_params);
1246 break;
1249 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1250 CompanyID company = (CompanyID)args->GetInt32();
1252 /* Nothing is added for AI or inactive companies */
1253 if (Company::IsValidHumanID(company)) {
1254 int64 args_array[] = {company + 1};
1255 StringParameters tmp_params(args_array);
1256 AppendStringWithArgs (buf, STR_FORMAT_COMPANY_NUM, &tmp_params);
1258 break;
1261 case SCC_DEPOT_NAME: { // {DEPOT}
1262 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
1263 if (vt == VEH_AIRCRAFT) {
1264 uint64 args_array[] = {(uint64)args->GetInt32()};
1265 WChar types_array[] = {SCC_STATION_NAME};
1266 StringParameters tmp_params(args_array, 1, types_array);
1267 AppendStringWithArgs (buf, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params);
1268 break;
1271 const Depot *d = Depot::Get(args->GetInt32());
1272 if (d->name != NULL) {
1273 int64 args_array[] = {(int64)(size_t)d->name};
1274 StringParameters tmp_params(args_array);
1275 AppendStringWithArgs (buf, STR_JUST_RAW_STRING, &tmp_params);
1276 } else {
1277 int64 args_array[] = {d->town->index, d->town_cn + 1};
1278 StringParameters tmp_params(args_array);
1279 AppendStringWithArgs (buf, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params);
1281 break;
1284 case SCC_ENGINE_NAME: { // {ENGINE}
1285 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
1286 if (e == NULL) break;
1288 if (e->name != NULL && e->IsEnabled()) {
1289 int64 args_array[] = {(int64)(size_t)e->name};
1290 StringParameters tmp_params(args_array);
1291 AppendStringWithArgs (buf, STR_JUST_RAW_STRING, &tmp_params);
1292 } else {
1293 StringParameters tmp_params(NULL, 0, NULL);
1294 AppendStringWithArgs (buf, e->info.string_id, &tmp_params);
1296 break;
1299 case SCC_GROUP_NAME: { // {GROUP}
1300 const Group *g = Group::GetIfValid(args->GetInt32());
1301 if (g == NULL) break;
1303 if (g->name != NULL) {
1304 int64 args_array[] = {(int64)(size_t)g->name};
1305 StringParameters tmp_params(args_array);
1306 AppendStringWithArgs (buf, STR_JUST_RAW_STRING, &tmp_params);
1307 } else {
1308 int64 args_array[] = {g->index};
1309 StringParameters tmp_params(args_array);
1311 AppendStringWithArgs (buf, STR_FORMAT_GROUP_NAME, &tmp_params);
1313 break;
1316 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1317 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
1318 if (i == NULL) break;
1320 if (_scan_for_gender_data) {
1321 /* Gender is defined by the industry type.
1322 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1323 StringParameters tmp_params(NULL, 0, NULL);
1324 FormatString (buf, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, next_substr_case_index);
1325 } else {
1326 /* First print the town name and the industry type name. */
1327 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
1328 StringParameters tmp_params(args_array);
1330 FormatString (buf, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, next_substr_case_index);
1332 next_substr_case_index = 0;
1333 break;
1336 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1337 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
1338 if (c == NULL) break;
1340 if (c->president_name != NULL) {
1341 int64 args_array[] = {(int64)(size_t)c->president_name};
1342 StringParameters tmp_params(args_array);
1343 AppendStringWithArgs (buf, STR_JUST_RAW_STRING, &tmp_params);
1344 } else {
1345 int64 args_array[] = {c->president_name_2};
1346 StringParameters tmp_params(args_array);
1347 AppendStringWithArgs (buf, c->president_name_1, &tmp_params);
1349 break;
1352 case SCC_STATION_NAME: { // {STATION}
1353 StationID sid = args->GetInt32(SCC_STATION_NAME);
1354 const Station *st = Station::GetIfValid(sid);
1356 if (st == NULL) {
1357 /* The station doesn't exist anymore. The only place where we might
1358 * be "drawing" an invalid station is in the case of cargo that is
1359 * in transit. */
1360 StringParameters tmp_params(NULL, 0, NULL);
1361 AppendStringWithArgs (buf, STR_UNKNOWN_STATION, &tmp_params);
1362 break;
1365 if (st->name != NULL) {
1366 int64 args_array[] = {(int64)(size_t)st->name};
1367 StringParameters tmp_params(args_array);
1368 AppendStringWithArgs (buf, STR_JUST_RAW_STRING, &tmp_params);
1369 } else {
1370 StringID str = st->string_id;
1371 if (st->indtype != IT_INVALID) {
1372 /* Special case where the industry provides the name for the station */
1373 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1375 /* Industry GRFs can change which might remove the station name and
1376 * thus cause very strange things. Here we check for that before we
1377 * actually set the station name. */
1378 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1379 str = indsp->station_name;
1383 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
1384 StringParameters tmp_params(args_array);
1385 AppendStringWithArgs (buf, str, &tmp_params);
1387 break;
1390 case SCC_TOWN_NAME: { // {TOWN}
1391 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
1392 if (t == NULL) break;
1394 if (t->name != NULL) {
1395 int64 args_array[] = {(int64)(size_t)t->name};
1396 StringParameters tmp_params(args_array);
1397 AppendStringWithArgs (buf, STR_JUST_RAW_STRING, &tmp_params);
1398 } else {
1399 AppendTownName (buf, t);
1401 break;
1404 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1405 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
1406 if (wp == NULL) break;
1408 if (wp->name != NULL) {
1409 int64 args_array[] = {(int64)(size_t)wp->name};
1410 StringParameters tmp_params(args_array);
1411 AppendStringWithArgs (buf, STR_JUST_RAW_STRING, &tmp_params);
1412 } else {
1413 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
1414 StringParameters tmp_params(args_array);
1415 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1416 if (wp->town_cn != 0) str++;
1417 AppendStringWithArgs (buf, str, &tmp_params);
1419 break;
1422 case SCC_VEHICLE_NAME: { // {VEHICLE}
1423 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
1424 if (v == NULL) break;
1426 if (v->name != NULL) {
1427 int64 args_array[] = {(int64)(size_t)v->name};
1428 StringParameters tmp_params(args_array);
1429 AppendStringWithArgs (buf, STR_JUST_RAW_STRING, &tmp_params);
1430 } else {
1431 int64 args_array[] = {v->unitnumber};
1432 StringParameters tmp_params(args_array);
1434 StringID str;
1435 switch (v->type) {
1436 default: str = STR_INVALID_VEHICLE; break;
1437 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
1438 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
1439 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
1440 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
1443 AppendStringWithArgs (buf, str, &tmp_params);
1445 break;
1448 case SCC_SIGN_NAME: { // {SIGN}
1449 const Sign *si = Sign::GetIfValid(args->GetInt32());
1450 if (si == NULL) break;
1452 if (si->name != NULL) {
1453 int64 args_array[] = {(int64)(size_t)si->name};
1454 StringParameters tmp_params(args_array);
1455 AppendStringWithArgs (buf, STR_JUST_RAW_STRING, &tmp_params);
1456 } else {
1457 StringParameters tmp_params(NULL, 0, NULL);
1458 AppendStringWithArgs (buf, STR_DEFAULT_SIGN_NAME, &tmp_params);
1460 break;
1463 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1464 AppendStationSpecialString (buf, args->GetInt32(SCC_STATION_FEATURES));
1465 break;
1468 default:
1469 buf->append_utf8 (b);
1470 break;
1476 static void AppendStationSpecialString (stringb *buf, int x)
1478 if (x & FACIL_TRAIN ) buf->append_utf8 (SCC_TRAIN);
1479 if (x & FACIL_TRUCK_STOP) buf->append_utf8 (SCC_LORRY);
1480 if (x & FACIL_BUS_STOP ) buf->append_utf8 (SCC_BUS);
1481 if (x & FACIL_DOCK ) buf->append_utf8 (SCC_SHIP);
1482 if (x & FACIL_AIRPORT ) buf->append_utf8 (SCC_PLANE);
1486 static const char *GenSurname (uint32 arg)
1488 static const char * const surname_list[] = {
1489 "Adams", "Allan", "Baker", "Bigwig", "Black",
1490 "Bloggs", "Brown", "Campbell", "Gordon", "Hamilton",
1491 "Hawthorn", "Higgins", "Green", "Gribble", "Jones",
1492 "McAlpine", "MacDonald", "McIntosh", "Muir", "Murphy",
1493 "Nelson", "O'Donnell", "Parker", "Phillips", "Pilkington",
1494 "Quigley", "Sharkey", "Thomson", "Watkins",
1497 static const char * const silly_list[] = {
1498 "Grumpy", "Dozy", "Speedy", "Nosey", "Dribble", "Mushroom",
1499 "Cabbage", "Sniffle", "Fishy", "Swindle", "Sneaky", "Nutkins",
1502 const char * const *base;
1503 uint num;
1505 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1506 base = silly_list;
1507 num = lengthof(silly_list);
1508 } else {
1509 base = surname_list;
1510 num = lengthof(surname_list);
1513 return base[num * GB(arg, 16, 8) >> 8];
1516 static void GenAndCoName (stringb *buf, uint32 arg)
1518 buf->append (GenSurname (arg));
1519 buf->append (" & Co.");
1522 static void GenPresidentName (stringb *buf, uint32 x)
1524 static const char initials[] = {
1525 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1526 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1529 char initial[] = "?. ";
1530 uint i;
1532 initial[0] = initials[sizeof(initials) * GB(x, 0, 8) >> 8];
1533 buf->append (initial);
1535 i = (sizeof(initials) + 35) * GB(x, 8, 8) >> 8;
1536 if (i < sizeof(initials)) {
1537 initial[0] = initials[i];
1538 buf->append (initial);
1541 buf->append (GenSurname (x));
1544 static void AppendSpecialNameString (stringb *buf, int ind, StringParameters *args)
1546 static const char * const silly_company_names[] = {
1547 "Bloggs Brothers",
1548 "Tiny Transport Ltd.",
1549 "Express Travel",
1550 "Comfy-Coach & Co.",
1551 "Crush & Bump Ltd.",
1552 "Broken & Late Ltd.",
1553 "Sam Speedy & Son",
1554 "Supersonic Travel",
1555 "Mike's Motors",
1556 "Lightning International",
1557 "Pannik & Loozit Ltd.",
1558 "Inter-City Transport",
1559 "Getout & Pushit Ltd.",
1562 switch (ind) {
1563 case 1: // not used
1564 buf->append (silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(silly_company_names) - 1)]);
1565 return;
1567 case 2: // used for Foobar & Co company names
1568 GenAndCoName (buf, args->GetInt32());
1569 return;
1571 case 3: // President name
1572 GenPresidentName (buf, args->GetInt32());
1573 return;
1576 /* town name? */
1577 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1578 GenerateTownNameString (buf, ind - 6, args->GetInt32());
1579 buf->append (" Transport");
1580 return;
1583 /* language name? */
1584 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
1585 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
1586 buf->append (&_languages[i] == _current_language ? _current_language->own_name : _languages[i].name);
1587 return;
1590 /* resolution size? */
1591 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
1592 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
1593 buf->append_fmt ("%ux%u", _resolutions[i].width, _resolutions[i].height);
1594 return;
1597 /* screenshot format name? */
1598 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
1599 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
1600 buf->append (GetScreenshotFormatDesc(i));
1601 return;
1604 NOT_REACHED();
1607 #ifdef ENABLE_NETWORK
1608 extern void SortNetworkLanguages();
1609 #else /* ENABLE_NETWORK */
1610 static inline void SortNetworkLanguages() {}
1611 #endif /* ENABLE_NETWORK */
1614 * Check whether the header is a valid header for OpenTTD.
1615 * @return true iff the header is deemed valid.
1617 bool LanguagePackHeader::IsValid() const
1619 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1620 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1621 this->plural_form < LANGUAGE_MAX_PLURAL &&
1622 this->text_dir <= 1 &&
1623 this->newgrflangid < MAX_LANG &&
1624 this->num_genders < MAX_NUM_GENDERS &&
1625 this->num_cases < MAX_NUM_CASES &&
1626 StrValid(this->name, lastof(this->name)) &&
1627 StrValid(this->own_name, lastof(this->own_name)) &&
1628 StrValid(this->isocode, lastof(this->isocode)) &&
1629 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
1630 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
1631 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
1635 * Read a particular language.
1636 * @param lang The metadata about the language.
1637 * @return Whether the loading went okay or not.
1639 bool ReadLanguagePack(const LanguageMetadata *lang)
1641 /* Current language pack */
1642 size_t len;
1643 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
1644 if (lang_pack == NULL) return false;
1646 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1647 const char *end = (char *)lang_pack + len + 1;
1649 /* We need at least one byte of lang_pack->data */
1650 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1651 free(lang_pack);
1652 return false;
1655 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1656 for (uint i = 0; i < TAB_COUNT; i++) {
1657 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
1659 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
1661 uint count = 0;
1662 for (uint i = 0; i < TAB_COUNT; i++) {
1663 uint16 num = lang_pack->offsets[i];
1664 if (num > TAB_SIZE) {
1665 free(lang_pack);
1666 return false;
1669 _langtab_start[i] = count;
1670 _langtab_num[i] = num;
1671 count += num;
1674 /* Allocate offsets */
1675 char **langpack_offs = xmalloct<char *>(count);
1677 /* Fill offsets */
1678 char *s = lang_pack->data;
1679 len = (byte)*s++;
1680 for (uint i = 0; i < count; i++) {
1681 if (s + len >= end) {
1682 free(lang_pack);
1683 free(langpack_offs);
1684 return false;
1686 if (len >= 0xC0) {
1687 len = ((len & 0x3F) << 8) + (byte)*s++;
1688 if (s + len >= end) {
1689 free(lang_pack);
1690 free(langpack_offs);
1691 return false;
1694 langpack_offs[i] = s;
1695 s += len;
1696 len = (byte)*s;
1697 *s++ = '\0'; // zero terminate the string
1700 free(_langpack);
1701 _langpack = lang_pack;
1703 free(_langpack_offs);
1704 _langpack_offs = langpack_offs;
1706 _current_language = lang;
1707 _current_text_dir = (TextDirection)_current_language->text_dir;
1708 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
1709 bstrcpy (_config_language_file, c_file);
1710 SetCurrentGrfLangID(_current_language->newgrflangid);
1712 #ifdef WITH_ICU_SORT
1713 /* Delete previous collator. */
1714 if (_current_collator != NULL) {
1715 delete _current_collator;
1716 _current_collator = NULL;
1719 /* Create a collator instance for our current locale. */
1720 UErrorCode status = U_ZERO_ERROR;
1721 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
1722 /* Sort number substrings by their numerical value. */
1723 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
1724 /* Avoid using the collator if it is not correctly set. */
1725 if (U_FAILURE(status)) {
1726 delete _current_collator;
1727 _current_collator = NULL;
1729 #endif /* WITH_ICU_SORT */
1731 /* Some lists need to be sorted again after a language change. */
1732 ReconsiderGameScriptLanguage();
1733 InitializeSortedCargoSpecs();
1734 SortIndustryTypes();
1735 BuildIndustriesLegend();
1736 SortNetworkLanguages();
1737 BuildContentTypeStringList();
1738 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
1739 InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
1740 InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
1741 InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
1742 InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
1743 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
1744 InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
1746 return true;
1749 /* Win32 implementation in win32.cpp.
1750 * OS X implementation in os/macosx/macos.mm. */
1751 #if !(defined(WIN32) || defined(__APPLE__))
1753 * Determine the current charset based on the environment
1754 * First check some default values, after this one we passed ourselves
1755 * and if none exist return the value for $LANG
1756 * @param param environment variable to check conditionally if default ones are not
1757 * set. Pass NULL if you don't want additional checks.
1758 * @return return string containing current charset, or NULL if not-determinable
1760 const char *GetCurrentLocale(const char *param)
1762 const char *env;
1764 env = getenv("LANGUAGE");
1765 if (env != NULL) return env;
1767 env = getenv("LC_ALL");
1768 if (env != NULL) return env;
1770 if (param != NULL) {
1771 env = getenv(param);
1772 if (env != NULL) return env;
1775 return getenv("LANG");
1777 #else
1778 const char *GetCurrentLocale(const char *param);
1779 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
1781 int CDECL StringIDSorter(const StringID *a, const StringID *b)
1783 char stra[512];
1784 char strb[512];
1785 GetString (stra, *a);
1786 GetString (strb, *b);
1788 return strnatcmp(stra, strb);
1792 * Get the language with the given NewGRF language ID.
1793 * @param newgrflangid NewGRF languages ID to check.
1794 * @return The language's metadata, or NULL if it is not known.
1796 const LanguageMetadata *GetLanguage(byte newgrflangid)
1798 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
1799 if (newgrflangid == lang->newgrflangid) return lang;
1802 return NULL;
1806 * Reads the language file header and checks compatibility.
1807 * @param file the file to read
1808 * @param hdr the place to write the header information to
1809 * @return true if and only if the language file is of a compatible version
1811 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
1813 FILE *f = fopen(file, "rb");
1814 if (f == NULL) return false;
1816 size_t read = fread(hdr, sizeof(*hdr), 1, f);
1817 fclose(f);
1819 bool ret = read == 1 && hdr->IsValid();
1821 /* Convert endianness for the windows language ID */
1822 if (ret) {
1823 hdr->missing = FROM_LE16(hdr->missing);
1824 hdr->winlangid = FROM_LE16(hdr->winlangid);
1826 return ret;
1830 * Gets a list of languages from the given directory.
1831 * @param path the base directory to search in
1833 static void GetLanguageList(const char *path)
1835 DIR *dir = ttd_opendir(path);
1836 if (dir != NULL) {
1837 struct dirent *dirent;
1838 while ((dirent = readdir(dir)) != NULL) {
1839 const char *d_name = FS2OTTD(dirent->d_name);
1840 const char *extension = strrchr(d_name, '.');
1842 /* Not a language file */
1843 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
1845 LanguageMetadata lmd;
1846 bstrfmt (lmd.file, "%s%s", path, d_name);
1848 /* Check whether the file is of the correct version */
1849 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
1850 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
1851 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
1852 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
1853 } else {
1854 *_languages.Append() = lmd;
1857 closedir(dir);
1862 * Make a list of the available language packs. Put the data in
1863 * #_languages list.
1865 void InitializeLanguagePacks()
1867 Searchpath sp;
1869 FOR_ALL_SEARCHPATHS(sp) {
1870 char path[MAX_PATH];
1871 FioGetFullPath (path, lengthof(path), sp, LANG_DIR);
1872 GetLanguageList(path);
1874 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
1876 /* Acquire the locale of the current system */
1877 const char *lang = GetCurrentLocale("LC_MESSAGES");
1878 if (lang == NULL) lang = "en_GB";
1880 const LanguageMetadata *chosen_language = NULL; ///< Matching the language in the configuration file or the current locale
1881 const LanguageMetadata *language_fallback = NULL; ///< Using pt_PT for pt_BR locale when pt_BR is not available
1882 const LanguageMetadata *en_GB_fallback = _languages.Begin(); ///< Fallback when no locale-matching language has been found
1884 /* Find a proper language. */
1885 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
1886 /* We are trying to find a default language. The priority is by
1887 * configuration file, local environment and last, if nothing found,
1888 * English. */
1889 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
1890 if (strcmp(lang_file, _config_language_file) == 0) {
1891 chosen_language = lng;
1892 break;
1895 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
1896 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
1897 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
1900 /* We haven't found the language in the config nor the one in the locale.
1901 * Now we set it to one of the fallback languages */
1902 if (chosen_language == NULL) {
1903 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
1906 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
1910 * Get the ISO language code of the currently loaded language.
1911 * @return the ISO code.
1913 const char *GetCurrentLanguageIsoCode()
1915 return _langpack->isocode;
1919 * Set the right font names.
1920 * @param settings The settings to modify.
1921 * @param font_name The new font name.
1923 void MissingGlyphSearcher::SetFontNames (struct FreeTypeSettings *settings, const char *font_name)
1925 #ifdef WITH_FREETYPE
1926 if (this->Monospace()) {
1927 bstrcpy (settings->mono.font, font_name);
1928 } else {
1929 bstrcpy (settings->small.font, font_name);
1930 bstrcpy (settings->medium.font, font_name);
1931 bstrcpy (settings->large.font, font_name);
1933 #endif /* WITH_FREETYPE */
1937 * Check whether there are glyphs missing in the current language.
1938 * @return If glyphs are missing, return \c true, else return \c false.
1940 bool MissingGlyphSearcher::FindMissingGlyphs (void)
1942 InitFreeType(this->Monospace());
1943 const Sprite *question_mark[FS_END];
1945 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
1946 question_mark[size] = GetGlyph(size, '?');
1949 this->Reset();
1950 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
1951 FontSize size = this->DefaultSize();
1952 for (;;) {
1953 WChar c = Utf8Consume(&text);
1954 if (c == '\0') {
1955 break;
1956 } else if (c == SCC_TINYFONT) {
1957 size = FS_SMALL;
1958 } else if (c == SCC_BIGFONT) {
1959 size = FS_LARGE;
1960 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
1961 /* The character is printable, but not in the normal font. This is the case we were testing for. */
1962 return true;
1966 return false;
1969 /** Helper for searching through the language pack. */
1970 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
1971 uint i; ///< Iterator for the primary language tables.
1972 uint j; ///< Iterator for the secondary language tables.
1974 public:
1975 CONSTEXPR LanguagePackGlyphSearcher()
1976 : MissingGlyphSearcher (FS_NORMAL, false), i(0), j(0)
1980 private:
1981 /* virtual */ void Reset()
1983 this->i = 0;
1984 this->j = 0;
1987 /* virtual */ const char *NextString()
1989 if (this->i >= TAB_COUNT) return NULL;
1991 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
1993 this->j++;
1994 while (this->i < TAB_COUNT && this->j >= _langtab_num[this->i]) {
1995 this->i++;
1996 this->j = 0;
1999 return ret;
2004 * Check whether the currently loaded language pack
2005 * uses characters that the currently loaded font
2006 * does not support. If this is the case an error
2007 * message will be shown in English. The error
2008 * message will not be localized because that would
2009 * mean it might use characters that are not in the
2010 * font, which is the whole reason this check has
2011 * been added.
2012 * @param base_font Whether to look at the base font as well.
2013 * @param searcher The methods to use to search for strings to check.
2014 * If NULL the loaded language pack searcher is used.
2016 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2018 static LanguagePackGlyphSearcher pack_searcher;
2019 if (searcher == NULL) searcher = &pack_searcher;
2020 bool bad_font = !base_font || searcher->FindMissingGlyphs();
2021 #ifdef WITH_FREETYPE
2022 if (bad_font) {
2023 /* We found an unprintable character... lets try whether we can find
2024 * a fallback font that can print the characters in the current language. */
2025 FreeTypeSettings backup;
2026 memcpy(&backup, &_freetype, sizeof(backup));
2028 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
2030 memcpy(&_freetype, &backup, sizeof(backup));
2032 if (bad_font && base_font) {
2033 /* Our fallback font does miss characters too, so keep the
2034 * user chosen font as that is more likely to be any good than
2035 * the wild guess we made */
2036 InitFreeType(searcher->Monospace());
2039 #endif
2041 if (bad_font) {
2042 /* All attempts have failed. Display an error. */
2043 SetDParamStr (0,
2044 SCCSTR_YELLOW "The current font is missing some of "
2045 "the characters used in the texts for this language. "
2046 "Read the readme to see how to solve this.");
2047 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2049 /* Reset the font width */
2050 LoadStringWidthTable(searcher->Monospace());
2051 return;
2054 /* Update the font with cache */
2055 LoadStringWidthTable(searcher->Monospace());
2057 #if !defined(WITH_ICU_LAYOUT)
2059 * For right-to-left languages we need the ICU library. If
2060 * we do not have support for that library we warn the user
2061 * about it with a message.
2063 if (_current_text_dir != TD_LTR) {
2064 SetDParamStr (0,
2065 SCCSTR_YELLOW "This version of OpenTTD does not "
2066 "support right-to-left languages. "
2067 "Recompile with icu enabled.");
2068 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
2070 #endif /* !WITH_ICU_LAYOUT */