Update readme and changelog for v1.27.0
[openttd-joker.git] / src / strings.cpp
blob62bff1d880efb2fd2209790059fcef51c3d9eecb
1 /* $Id: strings.cpp 26242 2014-01-12 18:00:55Z frosch $ */
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 <iostream>
14 #include <cctype>
15 #include "currency.h"
16 #include "station_base.h"
17 #include "town.h"
18 #include "waypoint_base.h"
19 #include "depot_base.h"
20 #include "industry.h"
21 #include "newgrf_text.h"
22 #include "fileio_func.h"
23 #include "signs_base.h"
24 #include "fontdetection.h"
25 #include "error.h"
26 #include "strings_func.h"
27 #include "rev.h"
28 #include "core/endian_func.hpp"
29 #include "date_func.h"
30 #include "vehicle_base.h"
31 #include "engine_base.h"
32 #include "language.h"
33 #include "townname_func.h"
34 #include "string_func.h"
35 #include "company_base.h"
36 #include "smallmap_gui.h"
37 #include "window_func.h"
38 #include "debug.h"
39 #include "tracerestrict.h"
40 #include "game/game_text.hpp"
41 #ifdef ENABLE_NETWORK
42 # include "network/network_content_gui.h"
43 #endif /* ENABLE_NETWORK */
44 #include <stack>
46 #include "table/strings.h"
47 #include "table/control_codes.h"
49 #include "safeguards.h"
51 char _config_language_file[MAX_PATH]; ///< The file (name) stored in the configuration.
52 LanguageList _languages; ///< The actual list of language meta data.
53 const LanguageMetadata *_current_language = nullptr; ///< The currently loaded language.
55 TextDirection _current_text_dir; ///< Text direction of the currently selected language.
57 #ifdef WITH_ICU_SORT
58 Collator *_current_collator = nullptr; ///< Collator for the language currently in use.
59 #endif /* WITH_ICU_SORT */
61 static uint64 _global_string_params_data[20]; ///< Global array of string parameters. To access, use #SetDParam.
62 static WChar _global_string_params_type[20]; ///< Type of parameters stored in #_decode_parameters
63 StringParameters _global_string_params(_global_string_params_data, 20, _global_string_params_type);
65 /** Reset the type array. */
66 void StringParameters::ClearTypeInformation()
68 assert(this->type != nullptr);
69 MemSetT(this->type, 0, this->num_param);
73 /**
74 * Read an int64 from the argument array. The offset is increased
75 * so the next time GetInt64 is called the next value is read.
77 int64 StringParameters::GetInt64(WChar type)
79 if (this->offset >= this->num_param) {
80 DEBUG(misc, 0, "Trying to read invalid string parameter");
81 return 0;
83 if (this->type != nullptr) {
84 assert(this->type[this->offset] == 0 || this->type[this->offset] == type);
85 this->type[this->offset] = type;
87 return this->data[this->offset++];
90 /**
91 * Shift all data in the data array by the given amount to make
92 * room for some extra parameters.
94 void StringParameters::ShiftParameters(uint amount)
96 assert(amount <= this->num_param);
97 MemMoveT(this->data + amount, this->data, this->num_param - amount);
101 * Set DParam n to some number that is suitable for string size computations.
102 * @param n Index of the string parameter.
103 * @param max_value The biggest value which shall be displayed.
104 * For the result only the number of digits of \a max_value matter.
105 * @param min_count Minimum number of digits independent of \a max.
106 * @param size Font of the number
108 void SetDParamMaxValue(uint n, uint64 max_value, uint min_count, FontSize size)
110 uint num_digits = 1;
111 while (max_value >= 10) {
112 num_digits++;
113 max_value /= 10;
115 SetDParamMaxDigits(n, max(min_count, num_digits), size);
119 * Set DParam n to some number that is suitable for string size computations.
120 * @param n Index of the string parameter.
121 * @param count Number of digits which shall be displayable.
122 * @param size Font of the number
124 void SetDParamMaxDigits(uint n, uint count, FontSize size)
126 uint front, next;
127 GetBroadestDigit(&front, &next, size);
128 uint64 val = count > 1 ? front : next;
129 for (; count > 1; count--) {
130 val = 10 * val + next;
132 SetDParam(n, val);
136 * Copy \a num string parameters from array \a src into the global string parameter array.
137 * @param offs Index in the global array to copy the first string parameter to.
138 * @param src Source array of string parameters.
139 * @param num Number of string parameters to copy.
141 void CopyInDParam(int offs, const uint64 *src, int num)
143 MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
147 * Copy \a num string parameters from the global string parameter array to the \a dst array.
148 * @param dst Destination array of string parameters.
149 * @param offs Index in the global array to copy the first string parameter from.
150 * @param num Number of string parameters to copy.
152 void CopyOutDParam(uint64 *dst, int offs, int num)
154 MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
158 * Copy \a num string parameters from the global string parameter array to the \a dst array.
159 * Furthermore clone raw string parameters into \a strings and amend the data in \a dst.
160 * @param dst Destination array of string parameters.
161 * @param strings Destination array for clone of the raw strings. Must be of same length as dst. Deallocation left to the caller.
162 * @param string The string used to determine where raw strings are and where there are no raw strings.
163 * @param num Number of string parameters to copy.
165 void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
167 char buf[DRAW_STRING_BUFFER];
168 GetString(buf, string, lastof(buf));
170 MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num);
171 for (int i = 0; i < num; i++) {
172 if (_global_string_params.HasTypeInformation() && _global_string_params.GetTypeAtOffset(i) == SCC_RAW_STRING_POINTER) {
173 strings[i] = stredup((const char *)(size_t)_global_string_params.GetParam(i));
174 dst[i] = (size_t)strings[i];
175 } else {
176 strings[i] = nullptr;
181 static char *StationGetSpecialString(char *buff, int x, const char *last);
182 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
183 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
185 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);
187 struct LanguagePack : public LanguagePackHeader {
188 char data[]; // list of strings
191 static char **_langpack_offs;
192 static LanguagePack *_langpack;
193 static uint _langtab_num[TEXT_TAB_END]; ///< Offset into langpack offs
194 static uint _langtab_start[TEXT_TAB_END]; ///< Offset into langpack offs
195 static bool _scan_for_gender_data = false; ///< Are we scanning for the gender of the current string? (instead of formatting it)
198 const char *GetStringPtr(StringID string)
200 switch (GetStringTab(string)) {
201 case TEXT_TAB_GAMESCRIPT_START: return GetGameStringPtr(GetStringIndex(string));
202 /* 0xD0xx and 0xD4xx IDs have been converted earlier. */
203 case TEXT_TAB_OLD_NEWGRF: NOT_REACHED();
204 case TEXT_TAB_NEWGRF_START: return GetGRFStringPtr(GetStringIndex(string));
205 default: return _langpack_offs[_langtab_start[GetStringTab(string)] + GetStringIndex(string)];
210 * Get a parsed string with most special stringcodes replaced by the string parameters.
211 * @param buffr Pointer to a string buffer where the formatted string should be written to.
212 * @param string
213 * @param args Arguments for the string.
214 * @param last Pointer just past the end of \a buffr.
215 * @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case.
216 * @param game_script The string is coming directly from a game script.
217 * @return Pointer to the final zero byte of the formatted string.
219 char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
221 if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
223 uint index = GetStringIndex(string);
224 StringTab tab = GetStringTab(string);
226 switch (tab) {
227 case TEXT_TAB_TOWN:
228 if (index >= 0xC0 && !game_script) {
229 return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
231 break;
233 case TEXT_TAB_SPECIAL:
234 if (index >= 0xE4 && !game_script) {
235 return GetSpecialNameString(buffr, index - 0xE4, args, last);
237 break;
239 case TEXT_TAB_OLD_CUSTOM:
240 /* Old table for custom names. This is no longer used */
241 if (!game_script) {
242 error("Incorrect conversion of custom name string.");
244 break;
246 case TEXT_TAB_GAMESCRIPT_START:
247 return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
249 case TEXT_TAB_OLD_NEWGRF:
250 NOT_REACHED();
252 case TEXT_TAB_NEWGRF_START:
253 return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
255 default:
256 break;
259 if (index >= _langtab_num[tab]) {
260 if (game_script) {
261 return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
263 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
266 return FormatString(buffr, GetStringPtr(string), args, last, case_index);
269 char *GetString(char *buffr, StringID string, const char *last)
271 _global_string_params.ClearTypeInformation();
272 _global_string_params.offset = 0;
273 return GetStringWithArgs(buffr, string, &_global_string_params, last);
278 * This function is used to "bind" a C string to a OpenTTD dparam slot.
279 * @param n slot of the string
280 * @param str string to bind
282 void SetDParamStr(uint n, const char *str)
284 SetDParam(n, (uint64)(size_t)str);
288 * Shift the string parameters in the global string parameter array by \a amount positions, making room at the beginning.
289 * @param amount Number of positions to shift.
291 void InjectDParam(uint amount)
293 _global_string_params.ShiftParameters(amount);
297 * Format a number into a string.
298 * @param buff the buffer to write to
299 * @param number the number to write down
300 * @param last the last element in the buffer
301 * @param separator the thousands-separator to use
302 * @param zerofill minimum number of digits to print for the integer part. The number will be filled with zeros at the front if necessary.
303 * @param fractional_digits number of fractional digits to display after a decimal separator. The decimal separator is inserted
304 * in front of the \a fractional_digits last digit of \a number.
305 * @return till where we wrote
307 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
309 static const int max_digits = 20;
310 uint64 divisor = 10000000000000000000ULL;
311 zerofill += fractional_digits;
312 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
314 if (number < 0) {
315 buff += seprintf(buff, last, "-");
316 number = -number;
319 uint64 num = number;
320 uint64 tot = 0;
321 for (int i = 0; i < max_digits; i++) {
322 if (i == max_digits - fractional_digits) {
323 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
324 if (decimal_separator == nullptr) decimal_separator = _langpack->digit_decimal_separator;
325 buff += seprintf(buff, last, "%s", decimal_separator);
328 uint64 quot = 0;
329 if (num >= divisor) {
330 quot = num / divisor;
331 num = num % divisor;
333 if ((tot |= quot) || i >= max_digits - zerofill) {
334 buff += seprintf(buff, last, "%i", (int)quot);
335 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
338 divisor /= 10;
341 *buff = '\0';
343 return buff;
346 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
348 const char *separator = _settings_game.locale.digit_group_separator;
349 if (separator == nullptr) separator = _langpack->digit_group_separator;
350 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
353 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
355 return FormatNumber(buff, number, last, "");
358 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
360 return FormatNumber(buff, number, last, "", count);
363 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
365 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
369 * Format a given number as a number of bytes with the SI prefix.
370 * @param buff the buffer to write to
371 * @param number the number of bytes to write down
372 * @param last the last element in the buffer
373 * @return till where we wrote
375 static char *FormatBytes(char *buff, int64 number, const char *last)
377 assert(number >= 0);
379 /* 1 2^10 2^20 2^30 2^40 2^50 2^60 */
380 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
381 uint id = 1;
382 while (number >= 1024 * 1024) {
383 number /= 1024;
384 id++;
387 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
388 if (decimal_separator == nullptr) decimal_separator = _langpack->digit_decimal_separator;
390 if (number < 1024) {
391 id = 0;
392 buff += seprintf(buff, last, "%i", (int)number);
393 } else if (number < 1024 * 10) {
394 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
395 } else if (number < 1024 * 100) {
396 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
397 } else {
398 assert(number < 1024 * 1024);
399 buff += seprintf(buff, last, "%i", (int)number / 1024);
402 assert(id < lengthof(iec_prefixes));
403 buff += seprintf(buff, last, NBSP "%sB", iec_prefixes[id]);
405 return buff;
408 static char *FormatTimeString(char *buff, Ticks ticks, const char *last, uint case_index)
410 auto minutes = ticks / _settings_client.gui.ticks_per_minute;
411 char hour[3], minute[3];
412 seprintf(hour, lastof(hour), "%02i", ((minutes / 60) % 24));
413 seprintf(minute, lastof(minute), "%02i", (minutes % 60));
415 int64 args[2] = { (int64)hour, (int64)minute };
416 StringParameters tmp_params(args);
418 return FormatString(buff, GetStringPtr(STR_FORMAT_TIME), &tmp_params, last, case_index);
421 static char *FormatTimeDurationString(char *buff, bool color_red, Ticks ticks, const char *last, uint case_index)
423 auto total_minutes = ticks / _settings_client.gui.ticks_per_minute;
425 auto minutes = total_minutes % 60;
426 auto hours = total_minutes / 60;
428 if (hours == 0) {
429 int64 args[1] = { (int64)minutes };
430 StringParameters tmp_params(args);
432 return FormatString(buff, GetStringPtr(color_red ? STR_FORMAT_TIME_MINUTES_RED : STR_FORMAT_TIME_MINUTES), &tmp_params, last, case_index);
434 else {
435 int64 args[2] = { (int64)hours, (int64)minutes };
436 StringParameters tmp_params(args);
438 return FormatString(buff, GetStringPtr(color_red ? STR_FORMAT_TIME_HOURS_MINUTES_RED : STR_FORMAT_TIME_HOURS_MINUTES), &tmp_params, last, case_index);
442 static char *FormatTicksString(char *buff, bool color_red, Ticks ticks, const char *last, uint case_index)
444 int64 args[1] = { (int64)ticks };
445 StringParameters tmp_params(args);
447 return FormatString(buff, GetStringPtr(color_red ? STR_FORMAT_TIME_TICKS_RED : STR_FORMAT_TIME_TICKS), &tmp_params, last, case_index);
450 static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
452 YearMonthDay ymd;
453 ConvertDateToYMD(date, &ymd);
455 int64 args[] = {ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
456 StringParameters tmp_params(args);
457 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
460 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
462 YearMonthDay ymd;
463 ConvertDateToYMD(date, &ymd);
465 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
466 StringParameters tmp_params(args);
467 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
470 static char *FormatNormalOrISODate(char *buff, Date date, StringID str, const char *last)
472 YearMonthDay ymd;
473 ConvertDateToYMD(date, &ymd);
475 char day[3];
476 char month[3];
477 /* We want to zero-pad the days and months */
478 seprintf(day, lastof(day), "%02i", ymd.day);
479 seprintf(month, lastof(month), "%02i", ymd.month + 1);
481 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
482 StringParameters tmp_params(args);
483 return FormatString(buff, GetStringPtr(str), &tmp_params, last);
486 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
488 /* We are going to make number absolute for printing, so
489 * keep this piece of data as we need it later on */
490 bool negative = number < 0;
491 const char *multiplier = "";
493 number *= spec->rate;
495 /* convert from negative */
496 if (number < 0) {
497 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
498 buff += Utf8Encode(buff, SCC_RED);
499 buff = strecpy(buff, "-", last);
500 number = -number;
503 /* Add prefix part, following symbol_pos specification.
504 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
505 * The only remaining value is 1 (suffix), so everything that is not 1 */
506 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
508 /* for huge numbers, compact the number into k or M */
509 if (compact) {
510 /* Take care of the 'k' rounding. Having 1 000 000 k
511 * and 1 000 M is inconsistent, so always use 1 000 M. */
512 if (number >= 1000000000 - 500) {
513 number = (number + 500000) / 1000000;
514 multiplier = NBSP "M";
515 } else if (number >= 1000000) {
516 number = (number + 500) / 1000;
517 multiplier = NBSP "k";
521 const char *separator = _settings_game.locale.digit_group_separator_currency;
522 if (separator == nullptr && !StrEmpty(_currency->separator)) separator = _currency->separator;
523 if (separator == nullptr) separator = _langpack->digit_group_separator_currency;
524 buff = FormatNumber(buff, number, last, separator);
525 buff = strecpy(buff, multiplier, last);
527 /* Add suffix part, following symbol_pos specification.
528 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
529 * The only remaining value is 1 (prefix), so everything that is not 0 */
530 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
532 if (negative) {
533 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
534 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
535 *buff = '\0';
538 return buff;
541 void MakeCamelCase(char line[]) {
542 bool active = true;
544 for (int i = 0; line[i] != '\0'; i++) {
545 if (std::isalpha(line[i])) {
546 if (active) {
547 line[i] = std::toupper(line[i]);
548 active = false;
550 else {
551 line[i] = std::tolower(line[i]);
554 else if (line[i] == ' ') {
555 active = true;
561 * Determine the "plural" index given a plural form and a number.
562 * @param count The number to get the plural index of.
563 * @param plural_form The plural form we want an index for.
564 * @return The plural index for the given form.
566 static int DeterminePluralForm(int64 count, int plural_form)
568 /* The absolute value determines plurality */
569 uint64 n = abs(count);
571 switch (plural_form) {
572 default:
573 NOT_REACHED();
575 /* Two forms: singular used for one only.
576 * Used in:
577 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
578 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
579 case 0:
580 return n != 1 ? 1 : 0;
582 /* Only one form.
583 * Used in:
584 * Hungarian, Japanese, Korean, Turkish */
585 case 1:
586 return 0;
588 /* Two forms: singular used for 0 and 1.
589 * Used in:
590 * French, Brazilian Portuguese */
591 case 2:
592 return n > 1 ? 1 : 0;
594 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
595 * Note: Cases are out of order for hysterical reasons. '0' is last.
596 * Used in:
597 * Latvian */
598 case 3:
599 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
601 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
602 * Used in:
603 * Gaelige (Irish) */
604 case 4:
605 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
607 /* 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.
608 * Used in:
609 * Lithuanian */
610 case 5:
611 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
613 /* 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.
614 * Used in:
615 * Croatian, Russian, Ukrainian */
616 case 6:
617 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
619 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
620 * Used in:
621 * Polish */
622 case 7:
623 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
625 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
626 * Used in:
627 * Slovenian */
628 case 8:
629 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
631 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
632 * Used in:
633 * Icelandic */
634 case 9:
635 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
637 /* Three forms: special cases for 1, and 2 to 4
638 * Used in:
639 * Czech, Slovak */
640 case 10:
641 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
643 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
644 * Korean doesn't have the concept of plural, but depending on how a
645 * number is pronounced it needs another version of a particle.
646 * As such the plural system is misused to give this distinction.
648 case 11:
649 switch (n % 10) {
650 case 0: // yeong
651 case 1: // il
652 case 3: // sam
653 case 6: // yuk
654 case 7: // chil
655 case 8: // pal
656 return 0;
658 case 2: // i
659 case 4: // sa
660 case 5: // o
661 case 9: // gu
662 return 1;
664 default:
665 NOT_REACHED();
668 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
669 * Used in:
670 * Maltese */
671 case 12:
672 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
673 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
674 * Used in:
675 * Scottish Gaelic */
676 case 13:
677 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
681 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
683 /* <NUM> {Length of each string} {each string} */
684 uint n = (byte)*b++;
685 uint pos, i, mypos = 0;
687 for (i = pos = 0; i != n; i++) {
688 uint len = (byte)*b++;
689 if (i == form) mypos = pos;
690 pos += len;
693 *dst += seprintf(*dst, last, "%s", b + mypos);
694 return b + pos;
697 /** Helper for unit conversion. */
698 struct UnitConversion {
699 int multiplier; ///< Amount to multiply upon conversion.
700 int shift; ///< Amount to shift upon conversion.
703 * Convert value from OpenTTD's internal unit into the displayed value.
704 * @param input The input to convert.
705 * @param round Whether to round the value or not.
706 * @return The converted value.
708 int64 ToDisplay(int64 input, bool round = true) const
710 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
714 * Convert the displayed value back into a value of OpenTTD's internal unit.
715 * @param input The input to convert.
716 * @param round Whether to round the value up or not.
717 * @param divider Divide the return value by this.
718 * @return The converted value.
720 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
722 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
726 /** Information about a specific unit system. */
727 struct Units {
728 UnitConversion c; ///< Conversion
729 StringID s; ///< String for the unit
732 /** Information about a specific unit system with a long variant. */
733 struct UnitsLong {
734 UnitConversion c; ///< Conversion
735 StringID s; ///< String for the short variant of the unit
736 StringID l; ///< String for the long variant of the unit
739 /** Unit conversions for velocity. */
740 static const Units _units_velocity[] = {
741 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL },
742 { { 103, 6}, STR_UNITS_VELOCITY_METRIC },
743 { {1831, 12}, STR_UNITS_VELOCITY_SI },
746 /** Unit conversions for velocity. */
747 static const Units _units_power[] = {
748 { { 1, 0}, STR_UNITS_POWER_IMPERIAL },
749 { {4153, 12}, STR_UNITS_POWER_METRIC },
750 { {6109, 13}, STR_UNITS_POWER_SI },
753 /** Unit conversions for weight. */
754 static const UnitsLong _units_weight[] = {
755 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
756 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC },
757 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI },
760 /** Unit conversions for volume. */
761 static const UnitsLong _units_volume[] = {
762 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
763 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC },
764 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI },
767 /** Unit conversions for force. */
768 static const Units _units_force[] = {
769 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL },
770 { {3263, 5}, STR_UNITS_FORCE_METRIC },
771 { { 1, 0}, STR_UNITS_FORCE_SI },
774 /** Unit conversions for height. */
775 static const Units _units_height[] = {
776 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL }, // "Wrong" conversion factor for more nicer GUI values
777 { { 1, 0}, STR_UNITS_HEIGHT_METRIC },
778 { { 1, 0}, STR_UNITS_HEIGHT_SI },
782 * Convert the given (internal) speed to the display speed.
783 * @param speed the speed to convert
784 * @return the converted speed.
786 uint ConvertSpeedToDisplaySpeed(uint speed)
788 /* For historical reasons we don't want to mess with the
789 * conversion for speed. So, don't round it and keep the
790 * original conversion factors instead of the real ones. */
791 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed, false);
795 * Convert the given display speed to the (internal) speed.
796 * @param speed the speed to convert
797 * @return the converted speed.
799 uint ConvertDisplaySpeedToSpeed(uint speed)
801 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed);
805 * Convert the given km/h-ish speed to the display speed.
806 * @param speed the speed to convert
807 * @return the converted speed.
809 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
811 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed * 10, false) / 16;
815 * Convert the given display speed to the km/h-ish speed.
816 * @param speed the speed to convert
817 * @return the converted speed.
819 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
821 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed * 16, true, 10);
824 * Parse most format codes within a string and write the result to a buffer.
825 * @param buff The buffer to write the final string to.
826 * @param str The original string with format codes.
827 * @param args Pointer to extra arguments used by various string codes.
828 * @param case_index
829 * @param last Pointer to just past the end of the buff array.
830 * @param dry_run True when the argt array is not yet initialized.
832 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
834 uint orig_offset = args->offset;
836 /* When there is no array with types there is no need to do a dry run. */
837 if (args->HasTypeInformation() && !dry_run) {
838 if (UsingNewGRFTextStack()) {
839 /* Values from the NewGRF text stack are only copied to the normal
840 * argv array at the time they are encountered. That means that if
841 * another string command references a value later in the string it
842 * would fail. We solve that by running FormatString twice. The first
843 * pass makes sure the argv array is correctly filled and the second
844 * pass can reference later values without problems. */
845 struct TextRefStack *backup = CreateTextRefStackBackup();
846 FormatString(buff, str_arg, args, last, case_index, game_script, true);
847 RestoreTextRefStackBackup(backup);
848 } else {
849 FormatString(buff, str_arg, args, last, case_index, game_script, true);
851 /* We have to restore the original offset here to to read the correct values. */
852 args->offset = orig_offset;
854 WChar b = '\0';
855 uint next_substr_case_index = 0;
856 char *buf_start = buff;
857 std::stack<const char *> str_stack;
858 str_stack.push(str_arg);
860 for (;;) {
861 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
862 str_stack.pop();
864 if (str_stack.empty()) break;
865 const char *&str = str_stack.top();
867 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
868 /* We need to pass some stuff as it might be modified; oh boy. */
869 //todo: should argve be passed here too?
870 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), args->GetDataLeft(), dry_run);
871 if (b == 0) continue;
874 switch (b) {
875 case SCC_ENCODED: {
876 uint64 sub_args_data[20];
877 WChar sub_args_type[20];
878 bool sub_args_need_free[20];
879 StringParameters sub_args(sub_args_data, 20, sub_args_type);
881 sub_args.ClearTypeInformation();
882 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
884 const char *s = str;
885 char *p;
886 uint32 stringid = strtoul(str, &p, 16);
887 if (*p != ':' && *p != '\0') {
888 while (*p != '\0') p++;
889 str = p;
890 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
891 break;
893 if (stringid >= TAB_SIZE_GAMESCRIPT) {
894 while (*p != '\0') p++;
895 str = p;
896 buff = strecat(buff, "(invalid StringID)", last);
897 break;
900 int i = 0;
901 while (*p != '\0' && i < 20) {
902 uint64 param;
903 s = ++p;
905 /* Find the next value */
906 bool instring = false;
907 bool escape = false;
908 for (;; p++) {
909 if (*p == '\\') {
910 escape = true;
911 continue;
913 if (*p == '"' && escape) {
914 escape = false;
915 continue;
917 escape = false;
919 if (*p == '"') {
920 instring = !instring;
921 continue;
923 if (instring) {
924 continue;
927 if (*p == ':') break;
928 if (*p == '\0') break;
931 if (*s != '"') {
932 /* Check if we want to look up another string */
933 WChar l;
934 size_t len = Utf8Decode(&l, s);
935 bool lookup = (l == SCC_ENCODED);
936 if (lookup) s += len;
938 param = strtoull(s, &p, 16);
940 if (lookup) {
941 if (param >= TAB_SIZE_GAMESCRIPT) {
942 while (*p != '\0') p++;
943 str = p;
944 buff = strecat(buff, "(invalid sub-StringID)", last);
945 break;
947 param = MakeStringID(TEXT_TAB_GAMESCRIPT_START, param);
950 sub_args.SetParam(i++, param);
951 } else {
952 char *g = stredup(s);
953 g[p - s] = '\0';
955 sub_args_need_free[i] = true;
956 sub_args.SetParam(i++, (uint64)(size_t)g);
959 /* If we didn't error out, we can actually print the string. */
960 if (*str != '\0') {
961 str = p;
962 buff = GetStringWithArgs(buff, MakeStringID(TEXT_TAB_GAMESCRIPT_START, stringid), &sub_args, last, true);
965 for (int i = 0; i < 20; i++) {
966 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
968 break;
971 case SCC_NEWGRF_STRINL: {
972 StringID substr = Utf8Consume(&str);
973 str_stack.push(GetStringPtr(substr));
974 break;
977 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
978 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
979 str_stack.push(GetStringPtr(substr));
980 case_index = next_substr_case_index;
981 next_substr_case_index = 0;
982 break;
986 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
987 /* First read the meta data from the language file. */
988 uint offset = orig_offset + (byte)*str++;
989 int gender = 0;
990 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
991 /* Now we need to figure out what text to resolve, i.e.
992 * what do we need to draw? So get the actual raw string
993 * first using the control code to get said string. */
994 char input[4 + 1];
995 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
996 *p = '\0';
998 /* Now do the string formatting. */
999 char buf[256];
1000 bool old_sgd = _scan_for_gender_data;
1001 _scan_for_gender_data = true;
1002 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, nullptr);
1003 p = FormatString(buf, input, &tmp_params, lastof(buf));
1004 _scan_for_gender_data = old_sgd;
1005 *p = '\0';
1007 /* And determine the string. */
1008 const char *s = buf;
1009 WChar c = Utf8Consume(&s);
1010 /* Does this string have a gender, if so, set it */
1011 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
1013 str = ParseStringChoice(str, gender, &buff, last);
1014 break;
1017 /* This sets up the gender for the string.
1018 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
1019 case SCC_GENDER_INDEX: // {GENDER 0}
1020 if (_scan_for_gender_data) {
1021 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
1022 *buff++ = *str++;
1023 } else {
1024 str++;
1026 break;
1028 case SCC_PLURAL_LIST: { // {P}
1029 int plural_form = *str++; // contains the plural form for this string
1030 uint offset = orig_offset + (byte)*str++;
1031 int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
1032 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
1033 break;
1036 case SCC_ARG_INDEX: { // Move argument pointer
1037 args->offset = orig_offset + (byte)*str++;
1038 break;
1041 case SCC_SET_CASE: { // {SET_CASE}
1042 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
1043 * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
1044 next_substr_case_index = (byte)*str++;
1045 break;
1048 case SCC_SWITCH_CASE: { // {Used to implement case switching}
1049 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
1050 * Each LEN is printed using 2 bytes in big endian order. */
1051 uint num = (byte)*str++;
1052 while (num) {
1053 if ((byte)str[0] == case_index) {
1054 /* Found the case, adjust str pointer and continue */
1055 str += 3;
1056 break;
1058 /* Otherwise skip to the next case */
1059 str += 3 + (str[1] << 8) + str[2];
1060 num--;
1062 break;
1065 case SCC_REVISION: // {REV}
1066 buff = strecpy(buff, _openttd_revision, last);
1067 break;
1069 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1070 if (game_script) break;
1071 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
1072 buff = FormatString(buff, str, args, last);
1073 break;
1076 case SCC_STRING: {// {STRING}
1077 StringID str = args->GetInt32(SCC_STRING);
1078 if (game_script && GetStringTab(str) != TEXT_TAB_GAMESCRIPT_START) break;
1079 /* WARNING. It's prohibited for the included string to consume any arguments.
1080 * For included strings that consume argument, you should use STRING1, STRING2 etc.
1081 * To debug stuff you can set argv to nullptr and it will tell you */
1082 StringParameters tmp_params(args->GetDataPointer(), args->GetDataLeft(), nullptr);
1083 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
1084 next_substr_case_index = 0;
1085 break;
1088 case SCC_STRING1:
1089 case SCC_STRING2:
1090 case SCC_STRING3:
1091 case SCC_STRING4:
1092 case SCC_STRING5:
1093 case SCC_STRING6:
1094 case SCC_STRING7: { // {STRING1..7}
1095 /* Strings that consume arguments */
1096 StringID str = args->GetInt32(b);
1097 if (game_script && GetStringTab(str) != TEXT_TAB_GAMESCRIPT_START) break;
1098 uint size = b - SCC_STRING1 + 1;
1099 if (game_script && size > args->GetDataLeft()) {
1100 buff = strecat(buff, "(too many parameters)", last);
1101 } else {
1102 StringParameters sub_args(*args, size);
1103 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
1105 next_substr_case_index = 0;
1106 break;
1109 case SCC_COMMA: // {COMMA}
1110 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
1111 break;
1113 case SCC_DECIMAL: {// {DECIMAL}
1114 int64 number = args->GetInt64(SCC_DECIMAL);
1115 int digits = args->GetInt32(SCC_DECIMAL);
1116 buff = FormatCommaNumber(buff, number, last, digits);
1117 break;
1120 case SCC_NUM: // {NUM}
1121 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
1122 break;
1124 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1125 int64 num = args->GetInt64();
1126 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
1127 break;
1130 case SCC_HEX: // {HEX}
1131 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
1132 break;
1134 case SCC_BYTES: // {BYTES}
1135 buff = FormatBytes(buff, args->GetInt64(), last);
1136 break;
1138 case SCC_CARGO_TINY: { // {CARGO_TINY}
1139 /* Tiny description of cargotypes. Layout:
1140 * param 1: cargo type
1141 * param 2: cargo count */
1142 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
1143 if (cargo >= CargoSpec::GetArraySize()) break;
1145 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1146 int64 amount = 0;
1147 switch (cargo_str) {
1148 case STR_TONS:
1149 amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64());
1150 break;
1152 case STR_LITERS:
1153 amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64());
1154 break;
1156 default: {
1157 amount = args->GetInt64();
1158 break;
1162 buff = FormatCommaNumber(buff, amount, last);
1163 break;
1166 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1167 /* Short description of cargotypes. Layout:
1168 * param 1: cargo type
1169 * param 2: cargo count */
1170 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
1171 if (cargo >= CargoSpec::GetArraySize()) break;
1173 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1174 switch (cargo_str) {
1175 case STR_TONS: {
1176 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1177 int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1178 StringParameters tmp_params(args_array);
1179 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1180 break;
1183 case STR_LITERS: {
1184 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1185 int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1186 StringParameters tmp_params(args_array);
1187 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1188 break;
1191 default: {
1192 StringParameters tmp_params(*args, 1);
1193 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
1194 break;
1197 break;
1200 case SCC_CARGO_LONG: { // {CARGO_LONG}
1201 /* First parameter is cargo type, second parameter is cargo count */
1202 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
1203 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
1205 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1206 StringParameters tmp_args(*args, 1);
1207 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
1208 break;
1211 case SCC_CARGO_LIST: { // {CARGO_LIST}
1212 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
1213 bool first = true;
1215 const CargoSpec *cs;
1216 FOR_ALL_SORTED_CARGOSPECS(cs) {
1217 if (!HasBit(cmask, cs->Index())) continue;
1219 if (buff >= last - 2) break; // ',' and ' '
1221 if (first) {
1222 first = false;
1223 } else {
1224 /* Add a comma if this is not the first item */
1225 *buff++ = ',';
1226 *buff++ = ' ';
1229 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
1232 /* If first is still true then no cargo is accepted */
1233 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
1235 *buff = '\0';
1236 next_substr_case_index = 0;
1238 /* Make sure we detect any buffer overflow */
1239 assert(buff < last);
1240 break;
1243 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1244 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
1245 break;
1247 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1248 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
1249 break;
1251 case SCC_DATE: // {DATE}
1252 buff = FormatNormalOrISODate(buff, args->GetInt32(SCC_DATE), STR_FORMAT_DATE, last);
1253 break;
1255 case SCC_DATE_SHORT: // {DATE_SHORT}
1256 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
1257 next_substr_case_index = 0;
1258 break;
1260 case SCC_DATE_LONG: // {DATE_LONG}
1261 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
1262 next_substr_case_index = 0;
1263 break;
1265 case SCC_DATE_ISO: // {DATE_ISO}
1266 buff = FormatNormalOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
1267 break;
1269 case SCC_TIME: // {TIME}
1270 buff = FormatTimeString(buff, args->GetInt32(), last, next_substr_case_index);
1271 break;
1273 case SCC_TIME_DURATION: // {TIME_DURATION}
1274 case SCC_TIME_DURATION_RED: // {TIME_DURATION_RED}
1275 buff = FormatTimeDurationString(buff, b == SCC_TIME_DURATION_RED, args->GetInt32(), last, next_substr_case_index);
1276 break;
1278 case SCC_TICKS: // {TICKS}
1279 case SCC_TICKS_RED: // {TICKS_RED}
1280 buff = FormatTicksString(buff, b == SCC_TICKS_RED, args->GetInt32(), last, next_substr_case_index);
1281 break;
1283 case SCC_FORCE: { // {FORCE}
1284 assert(_settings_game.locale.units_force < lengthof(_units_force));
1285 int64 args_array[1] = {_units_force[_settings_game.locale.units_force].c.ToDisplay(args->GetInt64())};
1286 StringParameters tmp_params(args_array);
1287 buff = FormatString(buff, GetStringPtr(_units_force[_settings_game.locale.units_force].s), &tmp_params, last);
1288 break;
1291 case SCC_HEIGHT: { // {HEIGHT}
1292 assert(_settings_game.locale.units_height < lengthof(_units_height));
1293 int64 args_array[] = {_units_height[_settings_game.locale.units_height].c.ToDisplay(args->GetInt64())};
1294 StringParameters tmp_params(args_array);
1295 buff = FormatString(buff, GetStringPtr(_units_height[_settings_game.locale.units_height].s), &tmp_params, last);
1296 break;
1299 case SCC_POWER: { // {POWER}
1300 assert(_settings_game.locale.units_power < lengthof(_units_power));
1301 int64 args_array[1] = {_units_power[_settings_game.locale.units_power].c.ToDisplay(args->GetInt64())};
1302 StringParameters tmp_params(args_array);
1303 buff = FormatString(buff, GetStringPtr(_units_power[_settings_game.locale.units_power].s), &tmp_params, last);
1304 break;
1307 case SCC_VELOCITY: { // {VELOCITY}
1308 assert(_settings_game.locale.units_velocity < lengthof(_units_velocity));
1309 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
1310 StringParameters tmp_params(args_array);
1311 buff = FormatString(buff, GetStringPtr(_units_velocity[_settings_game.locale.units_velocity].s), &tmp_params, last);
1312 break;
1315 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1316 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1317 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1318 StringParameters tmp_params(args_array);
1319 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].s), &tmp_params, last);
1320 break;
1323 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1324 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1325 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
1326 StringParameters tmp_params(args_array);
1327 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1328 break;
1331 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1332 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1333 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1334 StringParameters tmp_params(args_array);
1335 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].s), &tmp_params, last);
1336 break;
1339 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1340 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1341 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
1342 StringParameters tmp_params(args_array);
1343 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1344 break;
1347 case SCC_COMPANY_NAME: { // {COMPANY}
1348 const Company *c = Company::GetIfValid(args->GetInt32());
1349 if (c == nullptr) break;
1351 if (c->name != nullptr) {
1352 int64 args_array[] = {(int64)(size_t)c->name};
1353 StringParameters tmp_params(args_array);
1354 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1355 } else {
1356 int64 args_array[] = {c->name_2};
1357 StringParameters tmp_params(args_array);
1358 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
1360 break;
1363 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1364 CompanyID company = (CompanyID)args->GetInt32();
1366 /* Nothing is added for AI or inactive companies */
1367 if (Company::IsValidHumanID(company)) {
1368 int64 args_array[] = {company + 1};
1369 StringParameters tmp_params(args_array);
1370 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
1372 break;
1375 case SCC_DEPOT_NAME: { // {DEPOT}
1376 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
1377 if (vt == VEH_AIRCRAFT) {
1378 uint64 args_array[] = {(uint64)args->GetInt32()};
1379 WChar types_array[] = {SCC_STATION_NAME};
1380 StringParameters tmp_params(args_array, 1, types_array);
1381 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
1382 break;
1385 const Depot *d = Depot::Get(args->GetInt32());
1386 if (d->name != nullptr) {
1387 int64 args_array[] = {(int64)(size_t)d->name};
1388 StringParameters tmp_params(args_array);
1389 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1390 } else {
1391 int64 args_array[] = {d->town->index, d->town_cn + 1};
1392 StringParameters tmp_params(args_array);
1393 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
1395 break;
1398 case SCC_ENGINE_NAME: { // {ENGINE}
1399 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
1400 if (e == nullptr) break;
1402 if (e->name != nullptr && e->IsEnabled()) {
1403 int64 args_array[] = {(int64)(size_t)e->name};
1404 StringParameters tmp_params(args_array);
1405 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1406 } else {
1407 StringParameters tmp_params(nullptr, 0, nullptr);
1408 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
1410 break;
1413 case SCC_GROUP_NAME: { // {GROUP}
1414 const Group *g = Group::GetIfValid(args->GetInt32());
1415 if (g == nullptr) break;
1417 if (g->name != nullptr) {
1418 int64 args_array[] = {(int64)(size_t)g->name};
1419 StringParameters tmp_params(args_array);
1420 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1421 } else {
1422 int64 args_array[] = {g->index};
1423 StringParameters tmp_params(args_array);
1425 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
1427 break;
1430 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1431 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
1432 if (i == nullptr) break;
1434 if (_scan_for_gender_data) {
1435 /* Gender is defined by the industry type.
1436 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1437 StringParameters tmp_params(nullptr, 0, nullptr);
1438 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
1439 } else {
1440 /* First print the town name and the industry type name. */
1441 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
1442 StringParameters tmp_params(args_array);
1444 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
1446 next_substr_case_index = 0;
1447 break;
1450 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1451 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
1452 if (c == nullptr) break;
1454 if (c->president_name != nullptr) {
1455 int64 args_array[] = {(int64)(size_t)c->president_name};
1456 StringParameters tmp_params(args_array);
1457 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1458 } else {
1459 int64 args_array[] = {c->president_name_2};
1460 StringParameters tmp_params(args_array);
1461 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
1463 break;
1466 case SCC_STATION_NAME: { // {STATION}
1467 StationID sid = args->GetInt32(SCC_STATION_NAME);
1468 const Station *st = Station::GetIfValid(sid);
1470 if (st == nullptr) {
1471 /* The station doesn't exist anymore. The only place where we might
1472 * be "drawing" an invalid station is in the case of cargo that is
1473 * in transit. */
1474 StringParameters tmp_params(nullptr, 0, nullptr);
1475 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
1476 break;
1479 if (st->name != nullptr) {
1480 int64 args_array[] = {(int64)(size_t)st->name};
1481 StringParameters tmp_params(args_array);
1482 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1483 } else {
1484 StringID str = st->string_id;
1485 if (st->indtype != IT_INVALID) {
1486 /* Special case where the industry provides the name for the station */
1487 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1489 /* Industry GRFs can change which might remove the station name and
1490 * thus cause very strange things. Here we check for that before we
1491 * actually set the station name. */
1492 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1493 str = indsp->station_name;
1497 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
1498 StringParameters tmp_params(args_array);
1499 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1501 break;
1504 case SCC_TOWN_NAME: { // {TOWN}
1505 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
1506 if (t == nullptr) break;
1508 if (t->name != nullptr) {
1509 int64 args_array[] = {(int64)(size_t)t->name};
1510 StringParameters tmp_params(args_array);
1511 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1512 } else {
1513 buff = GetTownName(buff, t, last);
1515 break;
1518 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1519 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
1520 if (wp == nullptr) break;
1522 if (wp->name != nullptr) {
1523 int64 args_array[] = {(int64)(size_t)wp->name};
1524 StringParameters tmp_params(args_array);
1525 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1526 } else {
1527 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
1528 StringParameters tmp_params(args_array);
1529 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1530 if (wp->town_cn != 0) str++;
1531 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1533 break;
1536 case SCC_VEHICLE_NAME: { // {VEHICLE}
1537 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
1538 if (v == nullptr) break;
1540 if (v->name != nullptr) {
1541 int64 args_array[] = {(int64)(size_t)v->name};
1542 StringParameters tmp_params(args_array);
1543 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1544 } else if (v->group_id != DEFAULT_GROUP) {
1545 /* The vehicle has no name, but is member of a group, so print group name */
1546 int64 args_array[] = {(uint64)(size_t)v->group_id, v->unitnumber};
1547 StringParameters tmp_params(args_array);
1548 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_VEHICLE, &tmp_params, last);
1549 } else {
1550 int64 args_array[] = {v->unitnumber};
1551 StringParameters tmp_params(args_array);
1553 StringID str;
1554 switch (v->type) {
1555 default: str = STR_INVALID_VEHICLE; break;
1556 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
1557 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
1558 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
1559 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
1562 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1564 break;
1567 case SCC_SIGN_NAME: { // {SIGN}
1568 const Sign *si = Sign::GetIfValid(args->GetInt32());
1569 if (si == nullptr) break;
1571 if (si->name != nullptr) {
1572 int64 args_array[] = {(int64)(size_t)si->name};
1573 StringParameters tmp_params(args_array);
1574 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1575 } else {
1576 StringParameters tmp_params(nullptr, 0, nullptr);
1577 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
1579 break;
1582 case SCC_TR_SLOT_NAME: { // {TRSLOT}
1583 const TraceRestrictSlot *slot = TraceRestrictSlot::GetIfValid(args->GetInt32(SCC_TR_SLOT_NAME));
1584 if (slot == nullptr) break;
1585 int64 args_array[] = {(int64)(size_t)slot->name.c_str()};
1586 StringParameters tmp_params(args_array);
1587 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1588 break;
1591 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1592 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
1593 break;
1596 case SCC_CONSUME_ARG:
1597 // do nothing
1598 break;
1600 default:
1601 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
1602 break;
1605 *buff = '\0';
1606 return buff;
1610 static char *StationGetSpecialString(char *buff, int x, const char *last)
1612 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
1613 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
1614 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
1615 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
1616 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
1617 *buff = '\0';
1618 return buff;
1621 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
1623 return GenerateTownNameString(buff, last, ind, seed);
1626 static const char * const _silly_company_names[] = {
1627 "Bloggs Brothers",
1628 "Tiny Transport Ltd.",
1629 "Express Travel",
1630 "Comfy-Coach & Co.",
1631 "Crush & Bump Ltd.",
1632 "Broken & Late Ltd.",
1633 "Sam Speedy & Son",
1634 "Supersonic Travel",
1635 "Mike's Motors",
1636 "Lightning International",
1637 "Pannik & Loozit Ltd.",
1638 "Inter-City Transport",
1639 "Getout & Pushit Ltd."
1642 static const char * const _surname_list[] = {
1643 "Adams",
1644 "Allan",
1645 "Baker",
1646 "Bigwig",
1647 "Black",
1648 "Bloggs",
1649 "Brown",
1650 "Campbell",
1651 "Gordon",
1652 "Hamilton",
1653 "Hawthorn",
1654 "Higgins",
1655 "Green",
1656 "Gribble",
1657 "Jones",
1658 "McAlpine",
1659 "MacDonald",
1660 "McIntosh",
1661 "Muir",
1662 "Murphy",
1663 "Nelson",
1664 "O'Donnell",
1665 "Parker",
1666 "Phillips",
1667 "Pilkington",
1668 "Quigley",
1669 "Sharkey",
1670 "Thomson",
1671 "Watkins"
1674 static const char * const _silly_surname_list[] = {
1675 "Grumpy",
1676 "Dozy",
1677 "Speedy",
1678 "Nosey",
1679 "Dribble",
1680 "Mushroom",
1681 "Cabbage",
1682 "Sniffle",
1683 "Fishy",
1684 "Swindle",
1685 "Sneaky",
1686 "Nutkins"
1689 static const char _initial_name_letters[] = {
1690 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1691 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1694 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
1696 const char * const *base;
1697 uint num;
1699 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1700 base = _silly_surname_list;
1701 num = lengthof(_silly_surname_list);
1702 } else {
1703 base = _surname_list;
1704 num = lengthof(_surname_list);
1707 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
1708 buff = strecpy(buff, " & Co.", last);
1710 return buff;
1713 static char *GenPresidentName(char *buff, uint32 x, const char *last)
1715 char initial[] = "?. ";
1716 const char * const *base;
1717 uint num;
1718 uint i;
1720 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
1721 buff = strecpy(buff, initial, last);
1723 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
1724 if (i < sizeof(_initial_name_letters)) {
1725 initial[0] = _initial_name_letters[i];
1726 buff = strecpy(buff, initial, last);
1729 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1730 base = _silly_surname_list;
1731 num = lengthof(_silly_surname_list);
1732 } else {
1733 base = _surname_list;
1734 num = lengthof(_surname_list);
1737 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
1739 return buff;
1742 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
1744 switch (ind) {
1745 case 1: // not used
1746 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
1748 case 2: // used for Foobar & Co company names
1749 return GenAndCoName(buff, args->GetInt32(), last);
1751 case 3: // President name
1752 return GenPresidentName(buff, args->GetInt32(), last);
1755 /* town name? */
1756 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1757 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
1758 return strecpy(buff, " Transport", last);
1761 /* language name? */
1762 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
1763 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
1764 return strecpy(buff,
1765 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
1768 /* resolution size? */
1769 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
1770 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
1771 buff += seprintf(
1772 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
1774 return buff;
1777 NOT_REACHED();
1780 #ifdef ENABLE_NETWORK
1781 extern void SortNetworkLanguages();
1782 #else /* ENABLE_NETWORK */
1783 static inline void SortNetworkLanguages() {}
1784 #endif /* ENABLE_NETWORK */
1787 * Check whether the header is a valid header for OpenTTD.
1788 * @return true iff the header is deemed valid.
1790 bool LanguagePackHeader::IsValid() const
1792 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1793 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1794 this->plural_form < LANGUAGE_MAX_PLURAL &&
1795 this->text_dir <= 1 &&
1796 this->newgrflangid < MAX_LANG &&
1797 this->num_genders < MAX_NUM_GENDERS &&
1798 this->num_cases < MAX_NUM_CASES &&
1799 StrValid(this->name, lastof(this->name)) &&
1800 StrValid(this->own_name, lastof(this->own_name)) &&
1801 StrValid(this->isocode, lastof(this->isocode)) &&
1802 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
1803 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
1804 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
1808 * Read a particular language.
1809 * @param lang The metadata about the language.
1810 * @return Whether the loading went okay or not.
1812 bool ReadLanguagePack(const LanguageMetadata *lang)
1814 /* Current language pack */
1815 size_t len;
1816 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
1817 if (lang_pack == nullptr) return false;
1819 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1820 const char *end = (char *)lang_pack + len + 1;
1822 /* We need at least one byte of lang_pack->data */
1823 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1824 free(lang_pack);
1825 return false;
1828 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1829 for (uint i = 0; i < TEXT_TAB_END; i++) {
1830 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
1832 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
1834 uint count = 0;
1835 for (uint i = 0; i < TEXT_TAB_END; i++) {
1836 uint16 num = lang_pack->offsets[i];
1837 if (num > TAB_SIZE) {
1838 free(lang_pack);
1839 return false;
1842 _langtab_start[i] = count;
1843 _langtab_num[i] = num;
1844 count += num;
1847 /* Allocate offsets */
1848 char **langpack_offs = MallocT<char *>(count);
1850 /* Fill offsets */
1851 char *s = lang_pack->data;
1852 len = (byte)*s++;
1853 for (uint i = 0; i < count; i++) {
1854 if (s + len >= end) {
1855 free(lang_pack);
1856 free(langpack_offs);
1857 return false;
1859 if (len >= 0xC0) {
1860 len = ((len & 0x3F) << 8) + (byte)*s++;
1861 if (s + len >= end) {
1862 free(lang_pack);
1863 free(langpack_offs);
1864 return false;
1867 langpack_offs[i] = s;
1868 s += len;
1869 len = (byte)*s;
1870 *s++ = '\0'; // zero terminate the string
1873 free(_langpack);
1874 _langpack = lang_pack;
1876 free(_langpack_offs);
1877 _langpack_offs = langpack_offs;
1879 _current_language = lang;
1880 _current_text_dir = (TextDirection)_current_language->text_dir;
1881 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
1882 strecpy(_config_language_file, c_file, lastof(_config_language_file));
1883 SetCurrentGrfLangID(_current_language->newgrflangid);
1885 #ifdef WITH_ICU_SORT
1886 /* Delete previous collator. */
1887 if (_current_collator != nullptr) {
1888 delete _current_collator;
1889 _current_collator = nullptr;
1892 /* Create a collator instance for our current locale. */
1893 UErrorCode status = U_ZERO_ERROR;
1894 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
1895 /* Sort number substrings by their numerical value. */
1896 if (_current_collator != nullptr) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
1897 /* Avoid using the collator if it is not correctly set. */
1898 if (U_FAILURE(status)) {
1899 delete _current_collator;
1900 _current_collator = nullptr;
1902 #endif /* WITH_ICU_SORT */
1904 /* Some lists need to be sorted again after a language change. */
1905 ReconsiderGameScriptLanguage();
1906 InitializeSortedCargoSpecs();
1907 SortIndustryTypes();
1908 BuildIndustriesLegend();
1909 SortNetworkLanguages();
1910 #ifdef ENABLE_NETWORK
1911 BuildContentTypeStringList();
1912 #endif /* ENABLE_NETWORK */
1913 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
1914 InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
1915 InvalidateWindowClassesData(WC_TRACE_RESTRICT_SLOTS);
1916 InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
1917 InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
1918 InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
1919 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
1920 InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
1922 return true;
1925 /* Win32 implementation in win32.cpp.
1926 * OS X implementation in os/macosx/macos.mm. */
1927 #if !(defined(WIN32) || defined(__APPLE__))
1929 * Determine the current charset based on the environment
1930 * First check some default values, after this one we passed ourselves
1931 * and if none exist return the value for $LANG
1932 * @param param environment variable to check conditionally if default ones are not
1933 * set. Pass nullptr if you don't want additional checks.
1934 * @return return string containing current charset, or nullptr if not-determinable
1936 const char *GetCurrentLocale(const char *param)
1938 const char *env;
1940 env = getenv("LANGUAGE");
1941 if (env != nullptr) return env;
1943 env = getenv("LC_ALL");
1944 if (env != nullptr) return env;
1946 if (param != nullptr) {
1947 env = getenv(param);
1948 if (env != nullptr) return env;
1951 return getenv("LANG");
1953 #else
1954 const char *GetCurrentLocale(const char *param);
1955 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
1957 int CDECL StringIDSorter(const StringID *a, const StringID *b)
1959 char stra[512];
1960 char strb[512];
1961 GetString(stra, *a, lastof(stra));
1962 GetString(strb, *b, lastof(strb));
1964 return strnatcmp(stra, strb);
1968 * Get the language with the given NewGRF language ID.
1969 * @param newgrflangid NewGRF languages ID to check.
1970 * @return The language's metadata, or nullptr if it is not known.
1972 const LanguageMetadata *GetLanguage(byte newgrflangid)
1974 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
1975 if (newgrflangid == lang->newgrflangid) return lang;
1978 return nullptr;
1982 * Reads the language file header and checks compatibility.
1983 * @param file the file to read
1984 * @param hdr the place to write the header information to
1985 * @return true if and only if the language file is of a compatible version
1987 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
1989 FILE *f = fopen(file, "rb");
1990 if (f == nullptr) return false;
1992 size_t read = fread(hdr, sizeof(*hdr), 1, f);
1993 fclose(f);
1995 bool ret = read == 1 && hdr->IsValid();
1997 /* Convert endianness for the windows language ID */
1998 if (ret) {
1999 hdr->missing = FROM_LE16(hdr->missing);
2000 hdr->winlangid = FROM_LE16(hdr->winlangid);
2002 return ret;
2006 * Gets a list of languages from the given directory.
2007 * @param path the base directory to search in
2009 static void GetLanguageList(const char *path)
2011 DIR *dir = ttd_opendir(path);
2012 if (dir != nullptr) {
2013 struct dirent *dirent;
2014 while ((dirent = readdir(dir)) != nullptr) {
2015 const char *d_name = FS2OTTD(dirent->d_name);
2016 const char *extension = strrchr(d_name, '.');
2018 /* Not a language file */
2019 if (extension == nullptr || strcmp(extension, ".lng") != 0) continue;
2021 LanguageMetadata lmd;
2022 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
2024 /* Check whether the file is of the correct version */
2025 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
2026 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
2027 } else if (GetLanguage(lmd.newgrflangid) != nullptr) {
2028 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
2029 } else {
2030 *_languages.Append() = lmd;
2033 closedir(dir);
2038 * Make a list of the available language packs. Put the data in
2039 * #_languages list.
2041 void InitializeLanguagePacks()
2043 Searchpath sp;
2045 FOR_ALL_SEARCHPATHS(sp) {
2046 char path[MAX_PATH];
2047 FioAppendDirectory(path, lastof(path), sp, LANG_DIR);
2048 GetLanguageList(path);
2050 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
2052 /* Acquire the locale of the current system */
2053 const char *lang = GetCurrentLocale("LC_MESSAGES");
2054 if (lang == nullptr) lang = "en_GB";
2056 const LanguageMetadata *chosen_language = nullptr; ///< Matching the language in the configuration file or the current locale
2057 const LanguageMetadata *language_fallback = nullptr; ///< Using pt_PT for pt_BR locale when pt_BR is not available
2058 const LanguageMetadata *en_GB_fallback = _languages.Begin(); ///< Fallback when no locale-matching language has been found
2060 /* Find a proper language. */
2061 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
2062 /* We are trying to find a default language. The priority is by
2063 * configuration file, local environment and last, if nothing found,
2064 * English. */
2065 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
2066 if (strcmp(lang_file, _config_language_file) == 0) {
2067 chosen_language = lng;
2068 break;
2071 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
2072 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
2073 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
2076 /* We haven't found the language in the config nor the one in the locale.
2077 * Now we set it to one of the fallback languages */
2078 if (chosen_language == nullptr) {
2079 chosen_language = (language_fallback != nullptr) ? language_fallback : en_GB_fallback;
2082 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
2086 * Get the ISO language code of the currently loaded language.
2087 * @return the ISO code.
2089 const char *GetCurrentLanguageIsoCode()
2091 return _langpack->isocode;
2095 * Check whether there are glyphs missing in the current language.
2096 * @param Pointer to an address for storing the text pointer.
2097 * @return If glyphs are missing, return \c true, else return \c false.
2098 * @post If \c true is returned and str is not nullptr, *str points to a string that is found to contain at least one missing glyph.
2100 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
2102 InitFreeType(this->Monospace());
2103 const Sprite *question_mark[FS_END];
2105 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
2106 question_mark[size] = GetGlyph(size, '?');
2109 this->Reset();
2110 for (const char *text = this->NextString(); text != nullptr; text = this->NextString()) {
2111 FontSize size = this->DefaultSize();
2112 if (str != nullptr) *str = text;
2113 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
2114 if (c == SCC_TINYFONT) {
2115 size = FS_SMALL;
2116 } else if (c == SCC_BIGFONT) {
2117 size = FS_LARGE;
2118 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
2119 /* The character is printable, but not in the normal font. This is the case we were testing for. */
2120 return true;
2124 return false;
2127 /** Helper for searching through the language pack. */
2128 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
2129 uint i; ///< Iterator for the primary language tables.
2130 uint j; ///< Iterator for the secondary language tables.
2132 /* virtual */ void Reset()
2134 this->i = 0;
2135 this->j = 0;
2138 /* virtual */ FontSize DefaultSize()
2140 return FS_NORMAL;
2143 /* virtual */ const char *NextString()
2145 if (this->i >= TEXT_TAB_END) return nullptr;
2147 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
2149 this->j++;
2150 while (this->i < TEXT_TAB_END && this->j >= _langtab_num[this->i]) {
2151 this->i++;
2152 this->j = 0;
2155 return ret;
2158 /* virtual */ bool Monospace()
2160 return false;
2163 /* virtual */ void SetFontNames(FreeTypeSettings *settings, const char *font_name)
2165 #ifdef WITH_FREETYPE
2166 strecpy(settings->small.font, font_name, lastof(settings->small.font));
2167 strecpy(settings->medium.font, font_name, lastof(settings->medium.font));
2168 strecpy(settings->large.font, font_name, lastof(settings->large.font));
2169 #endif /* WITH_FREETYPE */
2174 * Check whether the currently loaded language pack
2175 * uses characters that the currently loaded font
2176 * does not support. If this is the case an error
2177 * message will be shown in English. The error
2178 * message will not be localized because that would
2179 * mean it might use characters that are not in the
2180 * font, which is the whole reason this check has
2181 * been added.
2182 * @param base_font Whether to look at the base font as well.
2183 * @param searcher The methods to use to search for strings to check.
2184 * If nullptr the loaded language pack searcher is used.
2186 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2188 static LanguagePackGlyphSearcher pack_searcher;
2189 if (searcher == nullptr) searcher = &pack_searcher;
2190 bool bad_font = !base_font || searcher->FindMissingGlyphs(nullptr);
2191 #ifdef WITH_FREETYPE
2192 if (bad_font) {
2193 /* We found an unprintable character... lets try whether we can find
2194 * a fallback font that can print the characters in the current language. */
2195 FreeTypeSettings backup;
2196 memcpy(&backup, &_freetype, sizeof(backup));
2198 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
2200 memcpy(&_freetype, &backup, sizeof(backup));
2202 if (bad_font && base_font) {
2203 /* Our fallback font does miss characters too, so keep the
2204 * user chosen font as that is more likely to be any good than
2205 * the wild guess we made */
2206 InitFreeType(searcher->Monospace());
2209 #endif
2211 if (bad_font) {
2212 /* All attempts have failed. Display an error. As we do not want the string to be translated by
2213 * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
2214 * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
2215 * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
2216 * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
2217 static char *err_str = stredup("XXXThe current font is missing some of the characters used in the texts for this language. Read the readme to see how to solve this.");
2218 Utf8Encode(err_str, SCC_YELLOW);
2219 SetDParamStr(0, err_str);
2220 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2222 /* Reset the font width */
2223 LoadStringWidthTable(searcher->Monospace());
2224 return;
2227 /* Update the font with cache */
2228 LoadStringWidthTable(searcher->Monospace());
2230 #if !defined(WITH_ICU_LAYOUT)
2232 * For right-to-left languages we need the ICU library. If
2233 * we do not have support for that library we warn the user
2234 * about it with a message. As we do not want the string to
2235 * be translated by the translators, we 'force' it into the
2236 * binary and 'load' it via a BindCString. To do this
2237 * properly we have to set the colour of the string,
2238 * otherwise we end up with a lot of artifacts. The colour
2239 * 'character' might change in the future, so for safety
2240 * we just Utf8 Encode it into the string, which takes
2241 * exactly three characters, so it replaces the "XXX" with
2242 * the colour marker.
2244 if (_current_text_dir != TD_LTR) {
2245 static char *err_str = stredup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
2246 Utf8Encode(err_str, SCC_YELLOW);
2247 SetDParamStr(0, err_str);
2248 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
2250 #endif /* !WITH_ICU_LAYOUT */