Fix ICU iterators on leading/trailing whitespace
[openttd/fttd.git] / src / strings.cpp
blobdcaa6ae292ef4e43011c44f565ee6982cf801705
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 "townname_func.h"
33 #include "string_func.h"
34 #include "company_base.h"
35 #include "smallmap_gui.h"
36 #include "window_func.h"
37 #include "debug.h"
38 #include "game/game_text.hpp"
39 #include <stack>
41 #include "table/strings.h"
42 #include "table/control_codes.h"
44 char _config_language_file[MAX_PATH]; ///< The file (name) stored in the configuration.
45 LanguageList _languages; ///< The actual list of language meta data.
46 const LanguageMetadata *_current_language = NULL; ///< The currently loaded language.
48 TextDirection _current_text_dir; ///< Text direction of the currently selected language.
50 #ifdef WITH_ICU
51 Collator *_current_collator = NULL; ///< Collator for the language currently in use.
52 #endif /* WITH_ICU */
54 static uint64 _global_string_params_data[20]; ///< Global array of string parameters. To access, use #SetDParam.
55 static WChar _global_string_params_type[20]; ///< Type of parameters stored in #_decode_parameters
56 StringParameters _global_string_params(_global_string_params_data, 20, _global_string_params_type);
58 /** Reset the type array. */
59 void StringParameters::ClearTypeInformation()
61 assert(this->type != NULL);
62 MemSetT(this->type, 0, this->num_param);
66 /**
67 * Read an int64 from the argument array. The offset is increased
68 * so the next time GetInt64 is called the next value is read.
70 int64 StringParameters::GetInt64(WChar type)
72 if (this->offset >= this->num_param) {
73 DEBUG(misc, 0, "Trying to read invalid string parameter");
74 return 0;
76 if (this->type != NULL) {
77 assert(this->type[this->offset] == 0 || this->type[this->offset] == type);
78 this->type[this->offset] = type;
80 return this->data[this->offset++];
83 /**
84 * Shift all data in the data array by the given amount to make
85 * room for some extra parameters.
87 void StringParameters::ShiftParameters(uint amount)
89 assert(amount <= this->num_param);
90 MemMoveT(this->data + amount, this->data, this->num_param - amount);
93 /**
94 * Set DParam n to some number that is suitable for string size computations.
95 * @param n Index of the string parameter.
96 * @param max_value The biggest value which shall be displayed.
97 * For the result only the number of digits of \a max_value matter.
98 * @param min_count Minimum number of digits independent of \a max.
99 * @param size Font of the number
101 void SetDParamMaxValue(uint n, uint64 max_value, uint min_count, FontSize size)
103 uint num_digits = 1;
104 while (max_value >= 10) {
105 num_digits++;
106 max_value /= 10;
108 SetDParamMaxDigits(n, max(min_count, num_digits), size);
112 * Set DParam n to some number that is suitable for string size computations.
113 * @param n Index of the string parameter.
114 * @param count Number of digits which shall be displayable.
115 * @param size Font of the number
117 void SetDParamMaxDigits(uint n, uint count, FontSize size)
119 uint front, next;
120 GetBroadestDigit(&front, &next, size);
121 uint64 val = count > 1 ? front : next;
122 for (; count > 1; count--) {
123 val = 10 * val + next;
125 SetDParam(n, val);
129 * Copy \a num string parameters from array \a src into the global string parameter array.
130 * @param offs Index in the global array to copy the first string parameter to.
131 * @param src Source array of string parameters.
132 * @param num Number of string parameters to copy.
134 void CopyInDParam(int offs, const uint64 *src, int num)
136 MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
140 * Copy \a num string parameters from the global string parameter array to the \a dst array.
141 * @param dst Destination array of string parameters.
142 * @param offs Index in the global array to copy the first string parameter from.
143 * @param num Number of string parameters to copy.
145 void CopyOutDParam(uint64 *dst, int offs, int num)
147 MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
151 * Copy \a num string parameters from the global string parameter array to the \a dst array.
152 * Furthermore clone raw string parameters into \a strings and amend the data in \a dst.
153 * @param dst Destination array of string parameters.
154 * @param strings Destination array for clone of the raw strings. Must be of same length as dst. Deallocation left to the caller.
155 * @param string The string used to determine where raw strings are and where there are no raw strings.
156 * @param num Number of string parameters to copy.
158 void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
160 char buf[DRAW_STRING_BUFFER];
161 GetString(buf, string, lastof(buf));
163 MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num);
164 for (int i = 0; i < num; i++) {
165 if (_global_string_params.HasTypeInformation() && _global_string_params.GetTypeAtOffset(i) == SCC_RAW_STRING_POINTER) {
166 strings[i] = strdup((const char *)(size_t)_global_string_params.GetParam(i));
167 dst[i] = (size_t)strings[i];
168 } else {
169 strings[i] = NULL;
174 static char *StationGetSpecialString(char *buff, int x, const char *last);
175 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
176 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
178 static char *FormatString(char *buff, const char *str, StringParameters *args, const char *last, uint case_index = 0, bool game_script = false, bool dry_run = false);
180 struct LanguagePack : public LanguagePackHeader {
181 char data[]; // list of strings
184 static char **_langpack_offs;
185 static LanguagePack *_langpack;
186 static uint _langtab_num[TAB_COUNT]; ///< Offset into langpack offs
187 static uint _langtab_start[TAB_COUNT]; ///< Offset into langpack offs
188 static bool _scan_for_gender_data = false; ///< Are we scanning for the gender of the current string? (instead of formatting it)
191 const char *GetStringPtr(StringID string)
193 switch (GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)) {
194 case GAME_TEXT_TAB: return GetGameStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
195 /* 0xD0xx and 0xD4xx IDs have been converted earlier. */
196 case 26: NOT_REACHED();
197 case 28: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
198 case 29: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x0800);
199 case 30: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x1000);
200 default: return _langpack_offs[_langtab_start[GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)] + GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS)];
205 * Get a parsed string with most special stringcodes replaced by the string parameters.
206 * @param buffr Pointer to a string buffer where the formatted string should be written to.
207 * @param string
208 * @param args Arguments for the string.
209 * @param last Pointer just past the end of \a buffr.
210 * @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case.
211 * @param game_script The string is coming directly from a game script.
212 * @return Pointer to the final zero byte of the formatted string.
214 char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
216 if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
218 uint index = GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS);
219 uint tab = GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS);
221 switch (tab) {
222 case 4:
223 if (index >= 0xC0 && !game_script) {
224 return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
226 break;
228 case 14:
229 if (index >= 0xE4 && !game_script) {
230 return GetSpecialNameString(buffr, index - 0xE4, args, last);
232 break;
234 case 15:
235 /* Old table for custom names. This is no longer used */
236 if (!game_script) {
237 error("Incorrect conversion of custom name string.");
239 break;
241 case GAME_TEXT_TAB:
242 return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
244 case 26:
245 NOT_REACHED();
247 case 28:
248 return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
250 case 29:
251 return FormatString(buffr, GetGRFStringPtr(index + 0x0800), args, last, case_index);
253 case 30:
254 return FormatString(buffr, GetGRFStringPtr(index + 0x1000), args, last, case_index);
257 if (index >= _langtab_num[tab]) {
258 if (game_script) {
259 return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
261 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
264 return FormatString(buffr, GetStringPtr(string), args, last, case_index);
267 char *GetString(char *buffr, StringID string, const char *last)
269 _global_string_params.ClearTypeInformation();
270 _global_string_params.offset = 0;
271 return GetStringWithArgs(buffr, string, &_global_string_params, last);
276 * This function is used to "bind" a C string to a OpenTTD dparam slot.
277 * @param n slot of the string
278 * @param str string to bind
280 void SetDParamStr(uint n, const char *str)
282 SetDParam(n, (uint64)(size_t)str);
286 * Shift the string parameters in the global string parameter array by \a amount positions, making room at the beginning.
287 * @param amount Number of positions to shift.
289 void InjectDParam(uint amount)
291 _global_string_params.ShiftParameters(amount);
295 * Format a number into a string.
296 * @param buff the buffer to write to
297 * @param number the number to write down
298 * @param last the last element in the buffer
299 * @param separator the thousands-separator to use
300 * @param zerofill minimum number of digits to print for the integer part. The number will be filled with zeros at the front if necessary.
301 * @param fractional_digits number of fractional digits to display after a decimal separator. The decimal separator is inserted
302 * in front of the \a fractional_digits last digit of \a number.
303 * @return till where we wrote
305 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
307 static const int max_digits = 20;
308 uint64 divisor = 10000000000000000000ULL;
309 zerofill += fractional_digits;
310 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
312 if (number < 0) {
313 buff += seprintf(buff, last, "-");
314 number = -number;
317 uint64 num = number;
318 uint64 tot = 0;
319 for (int i = 0; i < max_digits; i++) {
320 if (i == max_digits - fractional_digits) {
321 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
322 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
323 buff += seprintf(buff, last, "%s", decimal_separator);
326 uint64 quot = 0;
327 if (num >= divisor) {
328 quot = num / divisor;
329 num = num % divisor;
331 if ((tot |= quot) || i >= max_digits - zerofill) {
332 buff += seprintf(buff, last, "%i", (int)quot);
333 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
336 divisor /= 10;
339 *buff = '\0';
341 return buff;
344 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
346 const char *separator = _settings_game.locale.digit_group_separator;
347 if (separator == NULL) separator = _langpack->digit_group_separator;
348 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
351 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
353 return FormatNumber(buff, number, last, "");
356 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
358 return FormatNumber(buff, number, last, "", count);
361 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
363 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
367 * Format a given number as a number of bytes with the SI prefix.
368 * @param buff the buffer to write to
369 * @param number the number of bytes to write down
370 * @param last the last element in the buffer
371 * @return till where we wrote
373 static char *FormatBytes(char *buff, int64 number, const char *last)
375 assert(number >= 0);
377 /* 1 2^10 2^20 2^30 2^40 2^50 2^60 */
378 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
379 uint id = 1;
380 while (number >= 1024 * 1024) {
381 number /= 1024;
382 id++;
385 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
386 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
388 if (number < 1024) {
389 id = 0;
390 buff += seprintf(buff, last, "%i", (int)number);
391 } else if (number < 1024 * 10) {
392 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
393 } else if (number < 1024 * 100) {
394 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
395 } else {
396 assert(number < 1024 * 1024);
397 buff += seprintf(buff, last, "%i", (int)number / 1024);
400 assert(id < lengthof(iec_prefixes));
401 buff += seprintf(buff, last, " %sB", iec_prefixes[id]);
403 return buff;
406 static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
408 YearMonthDay ymd;
409 ConvertDateToYMD(date, &ymd);
411 int64 args[] = {ymd.day + STR_ORDINAL_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
412 StringParameters tmp_params(args);
413 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
416 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
418 YearMonthDay ymd;
419 ConvertDateToYMD(date, &ymd);
421 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
422 StringParameters tmp_params(args);
423 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
426 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
428 YearMonthDay ymd;
429 ConvertDateToYMD(date, &ymd);
431 char day[3];
432 char month[3];
433 /* We want to zero-pad the days and months */
434 snprintf(day, lengthof(day), "%02i", ymd.day);
435 snprintf(month, lengthof(month), "%02i", ymd.month + 1);
437 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
438 StringParameters tmp_params(args);
439 return FormatString(buff, GetStringPtr(str), &tmp_params, last);
442 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
444 /* We are going to make number absolute for printing, so
445 * keep this piece of data as we need it later on */
446 bool negative = number < 0;
447 const char *multiplier = "";
449 number *= spec->rate;
451 /* convert from negative */
452 if (number < 0) {
453 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
454 buff += Utf8Encode(buff, SCC_RED);
455 buff = strecpy(buff, "-", last);
456 number = -number;
459 /* Add prefix part, following symbol_pos specification.
460 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
461 * The only remaining value is 1 (suffix), so everything that is not 1 */
462 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
464 /* for huge numbers, compact the number into k or M */
465 if (compact) {
466 /* Take care of the 'k' rounding. Having 1 000 000 k
467 * and 1 000 M is inconsistent, so always use 1 000 M. */
468 if (number >= 1000000000 - 500) {
469 number = (number + 500000) / 1000000;
470 multiplier = "M";
471 } else if (number >= 1000000) {
472 number = (number + 500) / 1000;
473 multiplier = "k";
477 const char *separator = _settings_game.locale.digit_group_separator_currency;
478 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
479 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
480 buff = FormatNumber(buff, number, last, separator);
481 buff = strecpy(buff, multiplier, last);
483 /* Add suffix part, following symbol_pos specification.
484 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
485 * The only remaining value is 1 (prefix), so everything that is not 0 */
486 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
488 if (negative) {
489 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
490 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
491 *buff = '\0';
494 return buff;
498 * Determine the "plural" index given a plural form and a number.
499 * @param count The number to get the plural index of.
500 * @param plural_form The plural form we want an index for.
501 * @return The plural index for the given form.
503 static int DeterminePluralForm(int64 count, int plural_form)
505 /* The absolute value determines plurality */
506 uint64 n = abs(count);
508 switch (plural_form) {
509 default:
510 NOT_REACHED();
512 /* Two forms: singular used for one only.
513 * Used in:
514 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
515 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
516 case 0:
517 return n != 1 ? 1 : 0;
519 /* Only one form.
520 * Used in:
521 * Hungarian, Japanese, Korean, Turkish */
522 case 1:
523 return 0;
525 /* Two forms: singular used for 0 and 1.
526 * Used in:
527 * French, Brazilian Portuguese */
528 case 2:
529 return n > 1 ? 1 : 0;
531 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
532 * Note: Cases are out of order for hysterical reasons. '0' is last.
533 * Used in:
534 * Latvian */
535 case 3:
536 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
538 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
539 * Used in:
540 * Gaelige (Irish) */
541 case 4:
542 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
544 /* 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.
545 * Used in:
546 * Lithuanian */
547 case 5:
548 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
550 /* 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.
551 * Used in:
552 * Croatian, Russian, Ukrainian */
553 case 6:
554 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
556 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
557 * Used in:
558 * Polish */
559 case 7:
560 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
562 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
563 * Used in:
564 * Slovenian */
565 case 8:
566 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
568 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
569 * Used in:
570 * Icelandic */
571 case 9:
572 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
574 /* Three forms: special cases for 1, and 2 to 4
575 * Used in:
576 * Czech, Slovak */
577 case 10:
578 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
580 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
581 * Korean doesn't have the concept of plural, but depending on how a
582 * number is pronounced it needs another version of a particle.
583 * As such the plural system is misused to give this distinction.
585 case 11:
586 switch (n % 10) {
587 case 0: // yeong
588 case 1: // il
589 case 3: // sam
590 case 6: // yuk
591 case 7: // chil
592 case 8: // pal
593 return 0;
595 case 2: // i
596 case 4: // sa
597 case 5: // o
598 case 9: // gu
599 return 1;
601 default:
602 NOT_REACHED();
605 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
606 * Used in:
607 * Maltese */
608 case 12:
609 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
610 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
611 * Used in:
612 * Scottish Gaelic */
613 case 13:
614 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
618 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
620 /* <NUM> {Length of each string} {each string} */
621 uint n = (byte)*b++;
622 uint pos, i, mypos = 0;
624 for (i = pos = 0; i != n; i++) {
625 uint len = (byte)*b++;
626 if (i == form) mypos = pos;
627 pos += len;
630 *dst += seprintf(*dst, last, "%s", b + mypos);
631 return b + pos;
634 /** Helper for unit conversion. */
635 struct UnitConversion {
636 int multiplier; ///< Amount to multiply upon conversion.
637 int shift; ///< Amount to shift upon conversion.
640 * Convert value from OpenTTD's internal unit into the displayed value.
641 * @param input The input to convert.
642 * @param round Whether to round the value or not.
643 * @return The converted value.
645 int64 ToDisplay(int64 input, bool round = true) const
647 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
651 * Convert the displayed value back into a value of OpenTTD's internal unit.
652 * @param input The input to convert.
653 * @param round Whether to round the value up or not.
654 * @param divider Divide the return value by this.
655 * @return The converted value.
657 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
659 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
663 /** Information about a specific unit system. */
664 struct Units {
665 UnitConversion c; ///< Conversion
666 StringID s; ///< String for the unit
669 /** Information about a specific unit system with a long variant. */
670 struct UnitsLong {
671 UnitConversion c; ///< Conversion
672 StringID s; ///< String for the short variant of the unit
673 StringID l; ///< String for the long variant of the unit
676 /** Unit conversions for velocity. */
677 static const Units _units_velocity[] = {
678 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL },
679 { { 103, 6}, STR_UNITS_VELOCITY_METRIC },
680 { {1831, 12}, STR_UNITS_VELOCITY_SI },
683 /** Unit conversions for velocity. */
684 static const Units _units_power[] = {
685 { { 1, 0}, STR_UNITS_POWER_IMPERIAL },
686 { {4153, 12}, STR_UNITS_POWER_METRIC },
687 { {6109, 13}, STR_UNITS_POWER_SI },
690 /** Unit conversions for weight. */
691 static const UnitsLong _units_weight[] = {
692 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
693 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC },
694 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI },
697 /** Unit conversions for volume. */
698 static const UnitsLong _units_volume[] = {
699 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
700 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC },
701 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI },
704 /** Unit conversions for force. */
705 static const Units _units_force[] = {
706 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL },
707 { {3263, 5}, STR_UNITS_FORCE_METRIC },
708 { { 1, 0}, STR_UNITS_FORCE_SI },
711 /** Unit conversions for height. */
712 static const Units _units_height[] = {
713 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL }, // "Wrong" conversion factor for more nicer GUI values
714 { { 1, 0}, STR_UNITS_HEIGHT_METRIC },
715 { { 1, 0}, STR_UNITS_HEIGHT_SI },
719 * Convert the given (internal) speed to the display speed.
720 * @param speed the speed to convert
721 * @return the converted speed.
723 uint ConvertSpeedToDisplaySpeed(uint speed)
725 /* For historical reasons we don't want to mess with the
726 * conversion for speed. So, don't round it and keep the
727 * original conversion factors instead of the real ones. */
728 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed, false);
732 * Convert the given display speed to the (internal) speed.
733 * @param speed the speed to convert
734 * @return the converted speed.
736 uint ConvertDisplaySpeedToSpeed(uint speed)
738 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed);
742 * Convert the given km/h-ish speed to the display speed.
743 * @param speed the speed to convert
744 * @return the converted speed.
746 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
748 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed * 10, false) / 16;
752 * Convert the given display speed to the km/h-ish speed.
753 * @param speed the speed to convert
754 * @return the converted speed.
756 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
758 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed * 16, true, 10);
761 * Parse most format codes within a string and write the result to a buffer.
762 * @param buff The buffer to write the final string to.
763 * @param str The original string with format codes.
764 * @param args Pointer to extra arguments used by various string codes.
765 * @param case_index
766 * @param last Pointer to just past the end of the buff array.
767 * @param dry_run True when the argt array is not yet initialized.
769 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
771 uint orig_offset = args->offset;
773 /* When there is no array with types there is no need to do a dry run. */
774 if (args->HasTypeInformation() && !dry_run) {
775 if (UsingNewGRFTextStack()) {
776 /* Values from the NewGRF text stack are only copied to the normal
777 * argv array at the time they are encountered. That means that if
778 * another string command references a value later in the string it
779 * would fail. We solve that by running FormatString twice. The first
780 * pass makes sure the argv array is correctly filled and the second
781 * pass can reference later values without problems. */
782 struct TextRefStack *backup = CreateTextRefStackBackup();
783 FormatString(buff, str_arg, args, last, case_index, game_script, true);
784 RestoreTextRefStackBackup(backup);
785 } else {
786 FormatString(buff, str_arg, args, last, case_index, game_script, true);
788 /* We have to restore the original offset here to to read the correct values. */
789 args->offset = orig_offset;
791 WChar b = '\0';
792 uint next_substr_case_index = 0;
793 char *buf_start = buff;
794 std::stack<const char *> str_stack;
795 str_stack.push(str_arg);
797 for (;;) {
798 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
799 str_stack.pop();
801 if (str_stack.empty()) break;
802 const char *&str = str_stack.top();
804 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
805 /* We need to pass some stuff as it might be modified; oh boy. */
806 //todo: should argve be passed here too?
807 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), args->GetDataLeft(), dry_run);
808 if (b == 0) continue;
811 switch (b) {
812 case SCC_ENCODED: {
813 uint64 sub_args_data[20];
814 WChar sub_args_type[20];
815 bool sub_args_need_free[20];
816 StringParameters sub_args(sub_args_data, 20, sub_args_type);
818 sub_args.ClearTypeInformation();
819 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
821 uint16 stringid;
822 const char *s = str;
823 char *p;
824 stringid = strtol(str, &p, 16);
825 if (*p != ':' && *p != '\0') {
826 while (*p != '\0') p++;
827 str = p;
828 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
829 break;
831 if (stringid >= TAB_SIZE) {
832 while (*p != '\0') p++;
833 str = p;
834 buff = strecat(buff, "(invalid StringID)", last);
835 break;
838 int i = 0;
839 while (*p != '\0' && i < 20) {
840 uint64 param;
841 s = ++p;
843 /* Find the next value */
844 bool instring = false;
845 bool escape = false;
846 for (;; p++) {
847 if (*p == '\\') {
848 escape = true;
849 continue;
851 if (*p == '"' && escape) {
852 escape = false;
853 continue;
855 escape = false;
857 if (*p == '"') {
858 instring = !instring;
859 continue;
861 if (instring) {
862 continue;
865 if (*p == ':') break;
866 if (*p == '\0') break;
869 if (*s != '"') {
870 /* Check if we want to look up another string */
871 WChar l;
872 size_t len = Utf8Decode(&l, s);
873 bool lookup = (l == SCC_ENCODED);
874 if (lookup) s += len;
876 param = (int32)strtoul(s, &p, 16);
878 if (lookup) {
879 if (param >= TAB_SIZE) {
880 while (*p != '\0') p++;
881 str = p;
882 buff = strecat(buff, "(invalid sub-StringID)", last);
883 break;
885 param = (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + param;
888 sub_args.SetParam(i++, param);
889 } else {
890 char *g = strdup(s);
891 g[p - s] = '\0';
893 sub_args_need_free[i] = true;
894 sub_args.SetParam(i++, (uint64)(size_t)g);
897 /* If we didn't error out, we can actually print the string. */
898 if (*str != '\0') {
899 str = p;
900 buff = GetStringWithArgs(buff, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, last, true);
903 for (int i = 0; i < 20; i++) {
904 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
906 break;
909 case SCC_NEWGRF_STRINL: {
910 StringID substr = Utf8Consume(&str);
911 str_stack.push(GetStringPtr(substr));
912 break;
915 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
916 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
917 str_stack.push(GetStringPtr(substr));
918 case_index = next_substr_case_index;
919 next_substr_case_index = 0;
920 break;
924 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
925 /* First read the meta data from the language file. */
926 uint offset = orig_offset + (byte)*str++;
927 int gender = 0;
928 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
929 /* Now we need to figure out what text to resolve, i.e.
930 * what do we need to draw? So get the actual raw string
931 * first using the control code to get said string. */
932 char input[4 + 1];
933 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
934 *p = '\0';
936 /* Now do the string formatting. */
937 char buf[256];
938 bool old_sgd = _scan_for_gender_data;
939 _scan_for_gender_data = true;
940 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
941 p = FormatString(buf, input, &tmp_params, lastof(buf));
942 _scan_for_gender_data = old_sgd;
943 *p = '\0';
945 /* And determine the string. */
946 const char *s = buf;
947 WChar c = Utf8Consume(&s);
948 /* Does this string have a gender, if so, set it */
949 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
951 str = ParseStringChoice(str, gender, &buff, last);
952 break;
955 /* This sets up the gender for the string.
956 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
957 case SCC_GENDER_INDEX: // {GENDER 0}
958 if (_scan_for_gender_data) {
959 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
960 *buff++ = *str++;
961 } else {
962 str++;
964 break;
966 case SCC_PLURAL_LIST: { // {P}
967 int plural_form = *str++; // contains the plural form for this string
968 uint offset = orig_offset + (byte)*str++;
969 int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
970 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
971 break;
974 case SCC_ARG_INDEX: { // Move argument pointer
975 args->offset = orig_offset + (byte)*str++;
976 break;
979 case SCC_SET_CASE: { // {SET_CASE}
980 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
981 * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
982 next_substr_case_index = (byte)*str++;
983 break;
986 case SCC_SWITCH_CASE: { // {Used to implement case switching}
987 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
988 * Each LEN is printed using 2 bytes in big endian order. */
989 uint num = (byte)*str++;
990 while (num) {
991 if ((byte)str[0] == case_index) {
992 /* Found the case, adjust str pointer and continue */
993 str += 3;
994 break;
996 /* Otherwise skip to the next case */
997 str += 3 + (str[1] << 8) + str[2];
998 num--;
1000 break;
1003 case SCC_REVISION: // {REV}
1004 buff = strecpy(buff, _openttd_revision, last);
1005 break;
1007 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1008 if (game_script) break;
1009 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
1010 buff = FormatString(buff, str, args, last);
1011 break;
1014 case SCC_STRING: {// {STRING}
1015 StringID str = args->GetInt32(SCC_STRING);
1016 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
1017 /* WARNING. It's prohibited for the included string to consume any arguments.
1018 * For included strings that consume argument, you should use STRING1, STRING2 etc.
1019 * To debug stuff you can set argv to NULL and it will tell you */
1020 StringParameters tmp_params(args->GetDataPointer(), args->GetDataLeft(), NULL);
1021 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
1022 next_substr_case_index = 0;
1023 break;
1026 case SCC_STRING1:
1027 case SCC_STRING2:
1028 case SCC_STRING3:
1029 case SCC_STRING4:
1030 case SCC_STRING5:
1031 case SCC_STRING6:
1032 case SCC_STRING7: { // {STRING1..7}
1033 /* Strings that consume arguments */
1034 StringID str = args->GetInt32(b);
1035 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
1036 uint size = b - SCC_STRING1 + 1;
1037 if (game_script && size > args->GetDataLeft()) {
1038 buff = strecat(buff, "(too many parameters)", last);
1039 } else {
1040 StringParameters sub_args(*args, size);
1041 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
1043 next_substr_case_index = 0;
1044 break;
1047 case SCC_COMMA: // {COMMA}
1048 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
1049 break;
1051 case SCC_DECIMAL: {// {DECIMAL}
1052 int64 number = args->GetInt64(SCC_DECIMAL);
1053 int digits = args->GetInt32(SCC_DECIMAL);
1054 buff = FormatCommaNumber(buff, number, last, digits);
1055 break;
1058 case SCC_NUM: // {NUM}
1059 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
1060 break;
1062 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1063 int64 num = args->GetInt64();
1064 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
1065 break;
1068 case SCC_HEX: // {HEX}
1069 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
1070 break;
1072 case SCC_BYTES: // {BYTES}
1073 buff = FormatBytes(buff, args->GetInt64(), last);
1074 break;
1076 case SCC_CARGO_TINY: { // {CARGO_TINY}
1077 /* Tiny description of cargotypes. Layout:
1078 * param 1: cargo type
1079 * param 2: cargo count */
1080 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
1081 if (cargo >= CargoSpec::GetArraySize()) break;
1083 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1084 int64 amount = 0;
1085 switch (cargo_str) {
1086 case STR_TONS:
1087 amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64());
1088 break;
1090 case STR_LITERS:
1091 amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64());
1092 break;
1094 default: {
1095 amount = args->GetInt64();
1096 break;
1100 buff = FormatCommaNumber(buff, amount, last);
1101 break;
1104 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1105 /* Short description of cargotypes. Layout:
1106 * param 1: cargo type
1107 * param 2: cargo count */
1108 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
1109 if (cargo >= CargoSpec::GetArraySize()) break;
1111 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1112 switch (cargo_str) {
1113 case STR_TONS: {
1114 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1115 int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1116 StringParameters tmp_params(args_array);
1117 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1118 break;
1121 case STR_LITERS: {
1122 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1123 int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1124 StringParameters tmp_params(args_array);
1125 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1126 break;
1129 default: {
1130 StringParameters tmp_params(*args, 1);
1131 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
1132 break;
1135 break;
1138 case SCC_CARGO_LONG: { // {CARGO_LONG}
1139 /* First parameter is cargo type, second parameter is cargo count */
1140 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
1141 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
1143 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1144 StringParameters tmp_args(*args, 1);
1145 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
1146 break;
1149 case SCC_CARGO_LIST: { // {CARGO_LIST}
1150 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
1151 bool first = true;
1153 const CargoSpec *cs;
1154 FOR_ALL_SORTED_CARGOSPECS(cs) {
1155 if (!HasBit(cmask, cs->Index())) continue;
1157 if (buff >= last - 2) break; // ',' and ' '
1159 if (first) {
1160 first = false;
1161 } else {
1162 /* Add a comma if this is not the first item */
1163 *buff++ = ',';
1164 *buff++ = ' ';
1167 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
1170 /* If first is still true then no cargo is accepted */
1171 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
1173 *buff = '\0';
1174 next_substr_case_index = 0;
1176 /* Make sure we detect any buffer overflow */
1177 assert(buff < last);
1178 break;
1181 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1182 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
1183 break;
1185 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1186 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
1187 break;
1189 case SCC_DATE_TINY: // {DATE_TINY}
1190 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
1191 break;
1193 case SCC_DATE_SHORT: // {DATE_SHORT}
1194 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
1195 next_substr_case_index = 0;
1196 break;
1198 case SCC_DATE_LONG: // {DATE_LONG}
1199 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
1200 next_substr_case_index = 0;
1201 break;
1203 case SCC_DATE_ISO: // {DATE_ISO}
1204 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
1205 break;
1207 case SCC_FORCE: { // {FORCE}
1208 assert(_settings_game.locale.units_force < lengthof(_units_force));
1209 int64 args_array[1] = {_units_force[_settings_game.locale.units_force].c.ToDisplay(args->GetInt64())};
1210 StringParameters tmp_params(args_array);
1211 buff = FormatString(buff, GetStringPtr(_units_force[_settings_game.locale.units_force].s), &tmp_params, last);
1212 break;
1215 case SCC_HEIGHT: { // {HEIGHT}
1216 assert(_settings_game.locale.units_height < lengthof(_units_height));
1217 int64 args_array[] = {_units_height[_settings_game.locale.units_height].c.ToDisplay(args->GetInt64())};
1218 StringParameters tmp_params(args_array);
1219 buff = FormatString(buff, GetStringPtr(_units_height[_settings_game.locale.units_height].s), &tmp_params, last);
1220 break;
1223 case SCC_POWER: { // {POWER}
1224 assert(_settings_game.locale.units_power < lengthof(_units_power));
1225 int64 args_array[1] = {_units_power[_settings_game.locale.units_power].c.ToDisplay(args->GetInt64())};
1226 StringParameters tmp_params(args_array);
1227 buff = FormatString(buff, GetStringPtr(_units_power[_settings_game.locale.units_power].s), &tmp_params, last);
1228 break;
1231 case SCC_VELOCITY: { // {VELOCITY}
1232 assert(_settings_game.locale.units_velocity < lengthof(_units_velocity));
1233 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
1234 StringParameters tmp_params(args_array);
1235 buff = FormatString(buff, GetStringPtr(_units_velocity[_settings_game.locale.units_velocity].s), &tmp_params, last);
1236 break;
1239 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1240 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1241 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1242 StringParameters tmp_params(args_array);
1243 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].s), &tmp_params, last);
1244 break;
1247 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1248 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1249 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
1250 StringParameters tmp_params(args_array);
1251 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1252 break;
1255 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1256 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1257 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1258 StringParameters tmp_params(args_array);
1259 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].s), &tmp_params, last);
1260 break;
1263 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1264 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1265 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
1266 StringParameters tmp_params(args_array);
1267 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1268 break;
1271 case SCC_COMPANY_NAME: { // {COMPANY}
1272 const Company *c = Company::GetIfValid(args->GetInt32());
1273 if (c == NULL) break;
1275 if (c->name != NULL) {
1276 int64 args_array[] = {(uint64)(size_t)c->name};
1277 StringParameters tmp_params(args_array);
1278 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1279 } else {
1280 int64 args_array[] = {c->name_2};
1281 StringParameters tmp_params(args_array);
1282 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
1284 break;
1287 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1288 CompanyID company = (CompanyID)args->GetInt32();
1290 /* Nothing is added for AI or inactive companies */
1291 if (Company::IsValidHumanID(company)) {
1292 int64 args_array[] = {company + 1};
1293 StringParameters tmp_params(args_array);
1294 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
1296 break;
1299 case SCC_DEPOT_NAME: { // {DEPOT}
1300 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
1301 if (vt == VEH_AIRCRAFT) {
1302 uint64 args_array[] = {args->GetInt32()};
1303 WChar types_array[] = {SCC_STATION_NAME};
1304 StringParameters tmp_params(args_array, 1, types_array);
1305 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
1306 break;
1309 const Depot *d = Depot::Get(args->GetInt32());
1310 if (d->name != NULL) {
1311 int64 args_array[] = {(uint64)(size_t)d->name};
1312 StringParameters tmp_params(args_array);
1313 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1314 } else {
1315 int64 args_array[] = {d->town->index, d->town_cn + 1};
1316 StringParameters tmp_params(args_array);
1317 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
1319 break;
1322 case SCC_ENGINE_NAME: { // {ENGINE}
1323 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
1324 if (e == NULL) break;
1326 if (e->name != NULL && e->IsEnabled()) {
1327 int64 args_array[] = {(uint64)(size_t)e->name};
1328 StringParameters tmp_params(args_array);
1329 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1330 } else {
1331 StringParameters tmp_params(NULL, 0, NULL);
1332 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
1334 break;
1337 case SCC_GROUP_NAME: { // {GROUP}
1338 const Group *g = Group::GetIfValid(args->GetInt32());
1339 if (g == NULL) break;
1341 if (g->name != NULL) {
1342 int64 args_array[] = {(uint64)(size_t)g->name};
1343 StringParameters tmp_params(args_array);
1344 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1345 } else {
1346 int64 args_array[] = {g->index};
1347 StringParameters tmp_params(args_array);
1349 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
1351 break;
1354 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1355 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
1356 if (i == NULL) break;
1358 if (_scan_for_gender_data) {
1359 /* Gender is defined by the industry type.
1360 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1361 StringParameters tmp_params(NULL, 0, NULL);
1362 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
1363 } else {
1364 /* First print the town name and the industry type name. */
1365 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
1366 StringParameters tmp_params(args_array);
1368 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
1370 next_substr_case_index = 0;
1371 break;
1374 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1375 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
1376 if (c == NULL) break;
1378 if (c->president_name != NULL) {
1379 int64 args_array[] = {(uint64)(size_t)c->president_name};
1380 StringParameters tmp_params(args_array);
1381 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1382 } else {
1383 int64 args_array[] = {c->president_name_2};
1384 StringParameters tmp_params(args_array);
1385 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
1387 break;
1390 case SCC_STATION_NAME: { // {STATION}
1391 StationID sid = args->GetInt32(SCC_STATION_NAME);
1392 const Station *st = Station::GetIfValid(sid);
1394 if (st == NULL) {
1395 /* The station doesn't exist anymore. The only place where we might
1396 * be "drawing" an invalid station is in the case of cargo that is
1397 * in transit. */
1398 StringParameters tmp_params(NULL, 0, NULL);
1399 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
1400 break;
1403 if (st->name != NULL) {
1404 int64 args_array[] = {(uint64)(size_t)st->name};
1405 StringParameters tmp_params(args_array);
1406 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1407 } else {
1408 StringID str = st->string_id;
1409 if (st->indtype != IT_INVALID) {
1410 /* Special case where the industry provides the name for the station */
1411 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1413 /* Industry GRFs can change which might remove the station name and
1414 * thus cause very strange things. Here we check for that before we
1415 * actually set the station name. */
1416 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1417 str = indsp->station_name;
1421 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
1422 StringParameters tmp_params(args_array);
1423 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1425 break;
1428 case SCC_TOWN_NAME: { // {TOWN}
1429 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
1430 if (t == NULL) break;
1432 if (t->name != NULL) {
1433 int64 args_array[] = {(uint64)(size_t)t->name};
1434 StringParameters tmp_params(args_array);
1435 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1436 } else {
1437 buff = GetTownName(buff, t, last);
1439 break;
1442 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1443 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
1444 if (wp == NULL) break;
1446 if (wp->name != NULL) {
1447 int64 args_array[] = {(uint64)(size_t)wp->name};
1448 StringParameters tmp_params(args_array);
1449 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1450 } else {
1451 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
1452 StringParameters tmp_params(args_array);
1453 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1454 if (wp->town_cn != 0) str++;
1455 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1457 break;
1460 case SCC_VEHICLE_NAME: { // {VEHICLE}
1461 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
1462 if (v == NULL) break;
1464 if (v->name != NULL) {
1465 int64 args_array[] = {(uint64)(size_t)v->name};
1466 StringParameters tmp_params(args_array);
1467 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1468 } else {
1469 int64 args_array[] = {v->unitnumber};
1470 StringParameters tmp_params(args_array);
1472 StringID str;
1473 switch (v->type) {
1474 default: str = STR_INVALID_VEHICLE; break;
1475 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
1476 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
1477 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
1478 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
1481 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1483 break;
1486 case SCC_SIGN_NAME: { // {SIGN}
1487 const Sign *si = Sign::GetIfValid(args->GetInt32());
1488 if (si == NULL) break;
1490 if (si->name != NULL) {
1491 int64 args_array[] = {(uint64)(size_t)si->name};
1492 StringParameters tmp_params(args_array);
1493 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1494 } else {
1495 StringParameters tmp_params(NULL, 0, NULL);
1496 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
1498 break;
1501 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1502 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
1503 break;
1506 default:
1507 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
1508 break;
1511 *buff = '\0';
1512 return buff;
1516 static char *StationGetSpecialString(char *buff, int x, const char *last)
1518 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
1519 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
1520 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
1521 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
1522 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
1523 *buff = '\0';
1524 return buff;
1527 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
1529 return GenerateTownNameString(buff, last, ind, seed);
1532 static const char * const _silly_company_names[] = {
1533 "Bloggs Brothers",
1534 "Tiny Transport Ltd.",
1535 "Express Travel",
1536 "Comfy-Coach & Co.",
1537 "Crush & Bump Ltd.",
1538 "Broken & Late Ltd.",
1539 "Sam Speedy & Son",
1540 "Supersonic Travel",
1541 "Mike's Motors",
1542 "Lightning International",
1543 "Pannik & Loozit Ltd.",
1544 "Inter-City Transport",
1545 "Getout & Pushit Ltd."
1548 static const char * const _surname_list[] = {
1549 "Adams",
1550 "Allan",
1551 "Baker",
1552 "Bigwig",
1553 "Black",
1554 "Bloggs",
1555 "Brown",
1556 "Campbell",
1557 "Gordon",
1558 "Hamilton",
1559 "Hawthorn",
1560 "Higgins",
1561 "Green",
1562 "Gribble",
1563 "Jones",
1564 "McAlpine",
1565 "MacDonald",
1566 "McIntosh",
1567 "Muir",
1568 "Murphy",
1569 "Nelson",
1570 "O'Donnell",
1571 "Parker",
1572 "Phillips",
1573 "Pilkington",
1574 "Quigley",
1575 "Sharkey",
1576 "Thomson",
1577 "Watkins"
1580 static const char * const _silly_surname_list[] = {
1581 "Grumpy",
1582 "Dozy",
1583 "Speedy",
1584 "Nosey",
1585 "Dribble",
1586 "Mushroom",
1587 "Cabbage",
1588 "Sniffle",
1589 "Fishy",
1590 "Swindle",
1591 "Sneaky",
1592 "Nutkins"
1595 static const char _initial_name_letters[] = {
1596 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1597 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1600 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
1602 const char * const *base;
1603 uint num;
1605 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1606 base = _silly_surname_list;
1607 num = lengthof(_silly_surname_list);
1608 } else {
1609 base = _surname_list;
1610 num = lengthof(_surname_list);
1613 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
1614 buff = strecpy(buff, " & Co.", last);
1616 return buff;
1619 static char *GenPresidentName(char *buff, uint32 x, const char *last)
1621 char initial[] = "?. ";
1622 const char * const *base;
1623 uint num;
1624 uint i;
1626 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
1627 buff = strecpy(buff, initial, last);
1629 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
1630 if (i < sizeof(_initial_name_letters)) {
1631 initial[0] = _initial_name_letters[i];
1632 buff = strecpy(buff, initial, last);
1635 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1636 base = _silly_surname_list;
1637 num = lengthof(_silly_surname_list);
1638 } else {
1639 base = _surname_list;
1640 num = lengthof(_surname_list);
1643 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
1645 return buff;
1648 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
1650 switch (ind) {
1651 case 1: // not used
1652 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
1654 case 2: // used for Foobar & Co company names
1655 return GenAndCoName(buff, args->GetInt32(), last);
1657 case 3: // President name
1658 return GenPresidentName(buff, args->GetInt32(), last);
1661 /* town name? */
1662 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1663 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
1664 return strecpy(buff, " Transport", last);
1667 /* language name? */
1668 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
1669 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
1670 return strecpy(buff,
1671 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
1674 /* resolution size? */
1675 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
1676 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
1677 buff += seprintf(
1678 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
1680 return buff;
1683 /* screenshot format name? */
1684 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
1685 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
1686 return strecpy(buff, GetScreenshotFormatDesc(i), last);
1689 NOT_REACHED();
1692 #ifdef ENABLE_NETWORK
1693 extern void SortNetworkLanguages();
1694 #else /* ENABLE_NETWORK */
1695 static inline void SortNetworkLanguages() {}
1696 #endif /* ENABLE_NETWORK */
1699 * Check whether the header is a valid header for OpenTTD.
1700 * @return true iff the header is deemed valid.
1702 bool LanguagePackHeader::IsValid() const
1704 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1705 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1706 this->plural_form < LANGUAGE_MAX_PLURAL &&
1707 this->text_dir <= 1 &&
1708 this->newgrflangid < MAX_LANG &&
1709 this->num_genders < MAX_NUM_GENDERS &&
1710 this->num_cases < MAX_NUM_CASES &&
1711 StrValid(this->name, lastof(this->name)) &&
1712 StrValid(this->own_name, lastof(this->own_name)) &&
1713 StrValid(this->isocode, lastof(this->isocode)) &&
1714 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
1715 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
1716 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
1720 * Read a particular language.
1721 * @param lang The metadata about the language.
1722 * @return Whether the loading went okay or not.
1724 bool ReadLanguagePack(const LanguageMetadata *lang)
1726 /* Current language pack */
1727 size_t len;
1728 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
1729 if (lang_pack == NULL) return false;
1731 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1732 const char *end = (char *)lang_pack + len + 1;
1734 /* We need at least one byte of lang_pack->data */
1735 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1736 free(lang_pack);
1737 return false;
1740 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1741 for (uint i = 0; i < TAB_COUNT; i++) {
1742 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
1744 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
1746 uint count = 0;
1747 for (uint i = 0; i < TAB_COUNT; i++) {
1748 uint16 num = lang_pack->offsets[i];
1749 if (num > TAB_SIZE) {
1750 free(lang_pack);
1751 return false;
1754 _langtab_start[i] = count;
1755 _langtab_num[i] = num;
1756 count += num;
1759 /* Allocate offsets */
1760 char **langpack_offs = MallocT<char *>(count);
1762 /* Fill offsets */
1763 char *s = lang_pack->data;
1764 len = (byte)*s++;
1765 for (uint i = 0; i < count; i++) {
1766 if (s + len >= end) {
1767 free(lang_pack);
1768 free(langpack_offs);
1769 return false;
1771 if (len >= 0xC0) {
1772 len = ((len & 0x3F) << 8) + (byte)*s++;
1773 if (s + len >= end) {
1774 free(lang_pack);
1775 free(langpack_offs);
1776 return false;
1779 langpack_offs[i] = s;
1780 s += len;
1781 len = (byte)*s;
1782 *s++ = '\0'; // zero terminate the string
1785 free(_langpack);
1786 _langpack = lang_pack;
1788 free(_langpack_offs);
1789 _langpack_offs = langpack_offs;
1791 _current_language = lang;
1792 _current_text_dir = (TextDirection)_current_language->text_dir;
1793 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
1794 strecpy(_config_language_file, c_file, lastof(_config_language_file));
1795 SetCurrentGrfLangID(_current_language->newgrflangid);
1797 #ifdef WITH_ICU
1798 /* Delete previous collator. */
1799 if (_current_collator != NULL) {
1800 delete _current_collator;
1801 _current_collator = NULL;
1804 /* Create a collator instance for our current locale. */
1805 UErrorCode status = U_ZERO_ERROR;
1806 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
1807 /* Sort number substrings by their numerical value. */
1808 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
1809 /* Avoid using the collator if it is not correctly set. */
1810 if (U_FAILURE(status)) {
1811 delete _current_collator;
1812 _current_collator = NULL;
1814 #endif /* WITH_ICU */
1816 /* Some lists need to be sorted again after a language change. */
1817 ReconsiderGameScriptLanguage();
1818 InitializeSortedCargoSpecs();
1819 SortIndustryTypes();
1820 BuildIndustriesLegend();
1821 SortNetworkLanguages();
1822 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
1823 InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
1824 InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
1825 InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
1826 InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
1827 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
1828 InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
1830 return true;
1833 /* Win32 implementation in win32.cpp.
1834 * OS X implementation in os/macosx/macos.mm. */
1835 #if !(defined(WIN32) || defined(__APPLE__))
1837 * Determine the current charset based on the environment
1838 * First check some default values, after this one we passed ourselves
1839 * and if none exist return the value for $LANG
1840 * @param param environment variable to check conditionally if default ones are not
1841 * set. Pass NULL if you don't want additional checks.
1842 * @return return string containing current charset, or NULL if not-determinable
1844 const char *GetCurrentLocale(const char *param)
1846 const char *env;
1848 env = getenv("LANGUAGE");
1849 if (env != NULL) return env;
1851 env = getenv("LC_ALL");
1852 if (env != NULL) return env;
1854 if (param != NULL) {
1855 env = getenv(param);
1856 if (env != NULL) return env;
1859 return getenv("LANG");
1861 #else
1862 const char *GetCurrentLocale(const char *param);
1863 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
1865 int CDECL StringIDSorter(const StringID *a, const StringID *b)
1867 char stra[512];
1868 char strb[512];
1869 GetString(stra, *a, lastof(stra));
1870 GetString(strb, *b, lastof(strb));
1872 return strcmp(stra, strb);
1876 * Get the language with the given NewGRF language ID.
1877 * @param newgrflangid NewGRF languages ID to check.
1878 * @return The language's metadata, or NULL if it is not known.
1880 const LanguageMetadata *GetLanguage(byte newgrflangid)
1882 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
1883 if (newgrflangid == lang->newgrflangid) return lang;
1886 return NULL;
1890 * Reads the language file header and checks compatibility.
1891 * @param file the file to read
1892 * @param hdr the place to write the header information to
1893 * @return true if and only if the language file is of a compatible version
1895 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
1897 FILE *f = fopen(file, "rb");
1898 if (f == NULL) return false;
1900 size_t read = fread(hdr, sizeof(*hdr), 1, f);
1901 fclose(f);
1903 bool ret = read == 1 && hdr->IsValid();
1905 /* Convert endianness for the windows language ID */
1906 if (ret) {
1907 hdr->missing = FROM_LE16(hdr->missing);
1908 hdr->winlangid = FROM_LE16(hdr->winlangid);
1910 return ret;
1914 * Gets a list of languages from the given directory.
1915 * @param path the base directory to search in
1917 static void GetLanguageList(const char *path)
1919 DIR *dir = ttd_opendir(path);
1920 if (dir != NULL) {
1921 struct dirent *dirent;
1922 while ((dirent = readdir(dir)) != NULL) {
1923 const char *d_name = FS2OTTD(dirent->d_name);
1924 const char *extension = strrchr(d_name, '.');
1926 /* Not a language file */
1927 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
1929 LanguageMetadata lmd;
1930 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
1932 /* Check whether the file is of the correct version */
1933 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
1934 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
1935 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
1936 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
1937 } else {
1938 *_languages.Append() = lmd;
1941 closedir(dir);
1946 * Make a list of the available language packs. Put the data in
1947 * #_languages list.
1949 void InitializeLanguagePacks()
1951 Searchpath sp;
1953 FOR_ALL_SEARCHPATHS(sp) {
1954 char path[MAX_PATH];
1955 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
1956 GetLanguageList(path);
1958 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
1960 /* Acquire the locale of the current system */
1961 const char *lang = GetCurrentLocale("LC_MESSAGES");
1962 if (lang == NULL) lang = "en_GB";
1964 const LanguageMetadata *chosen_language = NULL; ///< Matching the language in the configuration file or the current locale
1965 const LanguageMetadata *language_fallback = NULL; ///< Using pt_PT for pt_BR locale when pt_BR is not available
1966 const LanguageMetadata *en_GB_fallback = _languages.Begin(); ///< Fallback when no locale-matching language has been found
1968 /* Find a proper language. */
1969 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
1970 /* We are trying to find a default language. The priority is by
1971 * configuration file, local environment and last, if nothing found,
1972 * English. */
1973 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
1974 if (strcmp(lang_file, _config_language_file) == 0) {
1975 chosen_language = lng;
1976 break;
1979 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
1980 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
1981 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
1984 /* We haven't found the language in the config nor the one in the locale.
1985 * Now we set it to one of the fallback languages */
1986 if (chosen_language == NULL) {
1987 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
1990 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
1994 * Get the ISO language code of the currently loaded language.
1995 * @return the ISO code.
1997 const char *GetCurrentLanguageIsoCode()
1999 return _langpack->isocode;
2003 * Check whether there are glyphs missing in the current language.
2004 * @param Pointer to an address for storing the text pointer.
2005 * @return If glyphs are missing, return \c true, else return \c false.
2006 * @post If \c true is returned and str is not NULL, *str points to a string that is found to contain at least one missing glyph.
2008 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
2010 InitFreeType(this->Monospace());
2011 const Sprite *question_mark[FS_END];
2013 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
2014 question_mark[size] = GetGlyph(size, '?');
2017 this->Reset();
2018 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
2019 FontSize size = this->DefaultSize();
2020 if (str != NULL) *str = text;
2021 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
2022 if (c == SCC_TINYFONT) {
2023 size = FS_SMALL;
2024 } else if (c == SCC_BIGFONT) {
2025 size = FS_LARGE;
2026 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
2027 /* The character is printable, but not in the normal font. This is the case we were testing for. */
2028 return true;
2032 return false;
2035 /** Helper for searching through the language pack. */
2036 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
2037 uint i; ///< Iterator for the primary language tables.
2038 uint j; ///< Iterator for the secondary language tables.
2040 /* virtual */ void Reset()
2042 this->i = 0;
2043 this->j = 0;
2046 /* virtual */ FontSize DefaultSize()
2048 return FS_NORMAL;
2051 /* virtual */ const char *NextString()
2053 if (this->i >= TAB_COUNT) return NULL;
2055 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
2057 this->j++;
2058 while (this->i < TAB_COUNT && this->j >= _langtab_num[this->i]) {
2059 this->i++;
2060 this->j = 0;
2063 return ret;
2066 /* virtual */ bool Monospace()
2068 return false;
2071 /* virtual */ void SetFontNames(FreeTypeSettings *settings, const char *font_name)
2073 #ifdef WITH_FREETYPE
2074 strecpy(settings->small.font, font_name, lastof(settings->small.font));
2075 strecpy(settings->medium.font, font_name, lastof(settings->medium.font));
2076 strecpy(settings->large.font, font_name, lastof(settings->large.font));
2077 #endif /* WITH_FREETYPE */
2082 * Check whether the currently loaded language pack
2083 * uses characters that the currently loaded font
2084 * does not support. If this is the case an error
2085 * message will be shown in English. The error
2086 * message will not be localized because that would
2087 * mean it might use characters that are not in the
2088 * font, which is the whole reason this check has
2089 * been added.
2090 * @param base_font Whether to look at the base font as well.
2091 * @param searcher The methods to use to search for strings to check.
2092 * If NULL the loaded language pack searcher is used.
2094 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2096 static LanguagePackGlyphSearcher pack_searcher;
2097 if (searcher == NULL) searcher = &pack_searcher;
2098 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
2099 #ifdef WITH_FREETYPE
2100 if (bad_font) {
2101 /* We found an unprintable character... lets try whether we can find
2102 * a fallback font that can print the characters in the current language. */
2103 FreeTypeSettings backup;
2104 memcpy(&backup, &_freetype, sizeof(backup));
2106 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
2108 memcpy(&_freetype, &backup, sizeof(backup));
2110 if (bad_font && base_font) {
2111 /* Our fallback font does miss characters too, so keep the
2112 * user chosen font as that is more likely to be any good than
2113 * the wild guess we made */
2114 InitFreeType(searcher->Monospace());
2117 #endif
2119 if (bad_font) {
2120 /* All attempts have failed. Display an error. As we do not want the string to be translated by
2121 * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
2122 * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
2123 * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
2124 * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
2125 static char *err_str = strdup("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.");
2126 Utf8Encode(err_str, SCC_YELLOW);
2127 SetDParamStr(0, err_str);
2128 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2130 /* Reset the font width */
2131 LoadStringWidthTable(searcher->Monospace());
2132 return;
2135 /* Update the font with cache */
2136 LoadStringWidthTable(searcher->Monospace());
2138 #if !defined(WITH_ICU)
2140 * For right-to-left languages we need the ICU library. If
2141 * we do not have support for that library we warn the user
2142 * about it with a message. As we do not want the string to
2143 * be translated by the translators, we 'force' it into the
2144 * binary and 'load' it via a BindCString. To do this
2145 * properly we have to set the colour of the string,
2146 * otherwise we end up with a lot of artifacts. The colour
2147 * 'character' might change in the future, so for safety
2148 * we just Utf8 Encode it into the string, which takes
2149 * exactly three characters, so it replaces the "XXX" with
2150 * the colour marker.
2152 if (_current_text_dir != TD_LTR) {
2153 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
2154 Utf8Encode(err_str, SCC_YELLOW);
2155 SetDParamStr(0, err_str);
2156 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
2158 #endif