Fix incorrect tile and trackdir in reserve through program execution
[openttd-joker.git] / src / strings.cpp
blob8902f65e15360cb12d0380caf497d069b43b7624
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 = NULL; ///< 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 = NULL; ///< 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 != NULL);
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 != NULL) {
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] = NULL;
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 == NULL) 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 == NULL) 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 == NULL) 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 / 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 *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
423 YearMonthDay ymd;
424 ConvertDateToYMD(date, &ymd);
426 int64 args[] = {ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
427 StringParameters tmp_params(args);
428 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
431 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
433 YearMonthDay ymd;
434 ConvertDateToYMD(date, &ymd);
436 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
437 StringParameters tmp_params(args);
438 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
441 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
443 YearMonthDay ymd;
444 ConvertDateToYMD(date, &ymd);
446 char day[3];
447 char month[3];
448 /* We want to zero-pad the days and months */
449 seprintf(day, lastof(day), "%02i", ymd.day);
450 seprintf(month, lastof(month), "%02i", ymd.month + 1);
452 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
453 StringParameters tmp_params(args);
454 return FormatString(buff, GetStringPtr(str), &tmp_params, last);
457 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
459 /* We are going to make number absolute for printing, so
460 * keep this piece of data as we need it later on */
461 bool negative = number < 0;
462 const char *multiplier = "";
464 number *= spec->rate;
466 /* convert from negative */
467 if (number < 0) {
468 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
469 buff += Utf8Encode(buff, SCC_RED);
470 buff = strecpy(buff, "-", last);
471 number = -number;
474 /* Add prefix part, following symbol_pos specification.
475 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
476 * The only remaining value is 1 (suffix), so everything that is not 1 */
477 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
479 /* for huge numbers, compact the number into k or M */
480 if (compact) {
481 /* Take care of the 'k' rounding. Having 1 000 000 k
482 * and 1 000 M is inconsistent, so always use 1 000 M. */
483 if (number >= 1000000000 - 500) {
484 number = (number + 500000) / 1000000;
485 multiplier = NBSP "M";
486 } else if (number >= 1000000) {
487 number = (number + 500) / 1000;
488 multiplier = NBSP "k";
492 const char *separator = _settings_game.locale.digit_group_separator_currency;
493 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
494 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
495 buff = FormatNumber(buff, number, last, separator);
496 buff = strecpy(buff, multiplier, last);
498 /* Add suffix part, following symbol_pos specification.
499 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
500 * The only remaining value is 1 (prefix), so everything that is not 0 */
501 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
503 if (negative) {
504 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
505 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
506 *buff = '\0';
509 return buff;
512 void MakeCamelCase(char line[]) {
513 bool active = true;
515 for (int i = 0; line[i] != '\0'; i++) {
516 if (std::isalpha(line[i])) {
517 if (active) {
518 line[i] = std::toupper(line[i]);
519 active = false;
521 else {
522 line[i] = std::tolower(line[i]);
525 else if (line[i] == ' ') {
526 active = true;
532 * Determine the "plural" index given a plural form and a number.
533 * @param count The number to get the plural index of.
534 * @param plural_form The plural form we want an index for.
535 * @return The plural index for the given form.
537 static int DeterminePluralForm(int64 count, int plural_form)
539 /* The absolute value determines plurality */
540 uint64 n = abs(count);
542 switch (plural_form) {
543 default:
544 NOT_REACHED();
546 /* Two forms: singular used for one only.
547 * Used in:
548 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
549 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
550 case 0:
551 return n != 1 ? 1 : 0;
553 /* Only one form.
554 * Used in:
555 * Hungarian, Japanese, Korean, Turkish */
556 case 1:
557 return 0;
559 /* Two forms: singular used for 0 and 1.
560 * Used in:
561 * French, Brazilian Portuguese */
562 case 2:
563 return n > 1 ? 1 : 0;
565 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
566 * Note: Cases are out of order for hysterical reasons. '0' is last.
567 * Used in:
568 * Latvian */
569 case 3:
570 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
572 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
573 * Used in:
574 * Gaelige (Irish) */
575 case 4:
576 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
578 /* 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.
579 * Used in:
580 * Lithuanian */
581 case 5:
582 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
584 /* 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.
585 * Used in:
586 * Croatian, Russian, Ukrainian */
587 case 6:
588 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
590 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
591 * Used in:
592 * Polish */
593 case 7:
594 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
596 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
597 * Used in:
598 * Slovenian */
599 case 8:
600 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
602 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
603 * Used in:
604 * Icelandic */
605 case 9:
606 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
608 /* Three forms: special cases for 1, and 2 to 4
609 * Used in:
610 * Czech, Slovak */
611 case 10:
612 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
614 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
615 * Korean doesn't have the concept of plural, but depending on how a
616 * number is pronounced it needs another version of a particle.
617 * As such the plural system is misused to give this distinction.
619 case 11:
620 switch (n % 10) {
621 case 0: // yeong
622 case 1: // il
623 case 3: // sam
624 case 6: // yuk
625 case 7: // chil
626 case 8: // pal
627 return 0;
629 case 2: // i
630 case 4: // sa
631 case 5: // o
632 case 9: // gu
633 return 1;
635 default:
636 NOT_REACHED();
639 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
640 * Used in:
641 * Maltese */
642 case 12:
643 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
644 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
645 * Used in:
646 * Scottish Gaelic */
647 case 13:
648 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
652 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
654 /* <NUM> {Length of each string} {each string} */
655 uint n = (byte)*b++;
656 uint pos, i, mypos = 0;
658 for (i = pos = 0; i != n; i++) {
659 uint len = (byte)*b++;
660 if (i == form) mypos = pos;
661 pos += len;
664 *dst += seprintf(*dst, last, "%s", b + mypos);
665 return b + pos;
668 /** Helper for unit conversion. */
669 struct UnitConversion {
670 int multiplier; ///< Amount to multiply upon conversion.
671 int shift; ///< Amount to shift upon conversion.
674 * Convert value from OpenTTD's internal unit into the displayed value.
675 * @param input The input to convert.
676 * @param round Whether to round the value or not.
677 * @return The converted value.
679 int64 ToDisplay(int64 input, bool round = true) const
681 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
685 * Convert the displayed value back into a value of OpenTTD's internal unit.
686 * @param input The input to convert.
687 * @param round Whether to round the value up or not.
688 * @param divider Divide the return value by this.
689 * @return The converted value.
691 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
693 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
697 /** Information about a specific unit system. */
698 struct Units {
699 UnitConversion c; ///< Conversion
700 StringID s; ///< String for the unit
703 /** Information about a specific unit system with a long variant. */
704 struct UnitsLong {
705 UnitConversion c; ///< Conversion
706 StringID s; ///< String for the short variant of the unit
707 StringID l; ///< String for the long variant of the unit
710 /** Unit conversions for velocity. */
711 static const Units _units_velocity[] = {
712 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL },
713 { { 103, 6}, STR_UNITS_VELOCITY_METRIC },
714 { {1831, 12}, STR_UNITS_VELOCITY_SI },
717 /** Unit conversions for velocity. */
718 static const Units _units_power[] = {
719 { { 1, 0}, STR_UNITS_POWER_IMPERIAL },
720 { {4153, 12}, STR_UNITS_POWER_METRIC },
721 { {6109, 13}, STR_UNITS_POWER_SI },
724 /** Unit conversions for weight. */
725 static const UnitsLong _units_weight[] = {
726 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
727 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC },
728 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI },
731 /** Unit conversions for volume. */
732 static const UnitsLong _units_volume[] = {
733 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
734 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC },
735 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI },
738 /** Unit conversions for force. */
739 static const Units _units_force[] = {
740 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL },
741 { {3263, 5}, STR_UNITS_FORCE_METRIC },
742 { { 1, 0}, STR_UNITS_FORCE_SI },
745 /** Unit conversions for height. */
746 static const Units _units_height[] = {
747 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL }, // "Wrong" conversion factor for more nicer GUI values
748 { { 1, 0}, STR_UNITS_HEIGHT_METRIC },
749 { { 1, 0}, STR_UNITS_HEIGHT_SI },
753 * Convert the given (internal) speed to the display speed.
754 * @param speed the speed to convert
755 * @return the converted speed.
757 uint ConvertSpeedToDisplaySpeed(uint speed)
759 /* For historical reasons we don't want to mess with the
760 * conversion for speed. So, don't round it and keep the
761 * original conversion factors instead of the real ones. */
762 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed, false);
766 * Convert the given display speed to the (internal) speed.
767 * @param speed the speed to convert
768 * @return the converted speed.
770 uint ConvertDisplaySpeedToSpeed(uint speed)
772 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed);
776 * Convert the given km/h-ish speed to the display speed.
777 * @param speed the speed to convert
778 * @return the converted speed.
780 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
782 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed * 10, false) / 16;
786 * Convert the given display speed to the km/h-ish speed.
787 * @param speed the speed to convert
788 * @return the converted speed.
790 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
792 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed * 16, true, 10);
795 * Parse most format codes within a string and write the result to a buffer.
796 * @param buff The buffer to write the final string to.
797 * @param str The original string with format codes.
798 * @param args Pointer to extra arguments used by various string codes.
799 * @param case_index
800 * @param last Pointer to just past the end of the buff array.
801 * @param dry_run True when the argt array is not yet initialized.
803 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
805 uint orig_offset = args->offset;
807 /* When there is no array with types there is no need to do a dry run. */
808 if (args->HasTypeInformation() && !dry_run) {
809 if (UsingNewGRFTextStack()) {
810 /* Values from the NewGRF text stack are only copied to the normal
811 * argv array at the time they are encountered. That means that if
812 * another string command references a value later in the string it
813 * would fail. We solve that by running FormatString twice. The first
814 * pass makes sure the argv array is correctly filled and the second
815 * pass can reference later values without problems. */
816 struct TextRefStack *backup = CreateTextRefStackBackup();
817 FormatString(buff, str_arg, args, last, case_index, game_script, true);
818 RestoreTextRefStackBackup(backup);
819 } else {
820 FormatString(buff, str_arg, args, last, case_index, game_script, true);
822 /* We have to restore the original offset here to to read the correct values. */
823 args->offset = orig_offset;
825 WChar b = '\0';
826 uint next_substr_case_index = 0;
827 char *buf_start = buff;
828 std::stack<const char *> str_stack;
829 str_stack.push(str_arg);
831 for (;;) {
832 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
833 str_stack.pop();
835 if (str_stack.empty()) break;
836 const char *&str = str_stack.top();
838 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
839 /* We need to pass some stuff as it might be modified; oh boy. */
840 //todo: should argve be passed here too?
841 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), args->GetDataLeft(), dry_run);
842 if (b == 0) continue;
845 switch (b) {
846 case SCC_ENCODED: {
847 uint64 sub_args_data[20];
848 WChar sub_args_type[20];
849 bool sub_args_need_free[20];
850 StringParameters sub_args(sub_args_data, 20, sub_args_type);
852 sub_args.ClearTypeInformation();
853 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
855 const char *s = str;
856 char *p;
857 uint32 stringid = strtoul(str, &p, 16);
858 if (*p != ':' && *p != '\0') {
859 while (*p != '\0') p++;
860 str = p;
861 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
862 break;
864 if (stringid >= TAB_SIZE_GAMESCRIPT) {
865 while (*p != '\0') p++;
866 str = p;
867 buff = strecat(buff, "(invalid StringID)", last);
868 break;
871 int i = 0;
872 while (*p != '\0' && i < 20) {
873 uint64 param;
874 s = ++p;
876 /* Find the next value */
877 bool instring = false;
878 bool escape = false;
879 for (;; p++) {
880 if (*p == '\\') {
881 escape = true;
882 continue;
884 if (*p == '"' && escape) {
885 escape = false;
886 continue;
888 escape = false;
890 if (*p == '"') {
891 instring = !instring;
892 continue;
894 if (instring) {
895 continue;
898 if (*p == ':') break;
899 if (*p == '\0') break;
902 if (*s != '"') {
903 /* Check if we want to look up another string */
904 WChar l;
905 size_t len = Utf8Decode(&l, s);
906 bool lookup = (l == SCC_ENCODED);
907 if (lookup) s += len;
909 param = strtoull(s, &p, 16);
911 if (lookup) {
912 if (param >= TAB_SIZE_GAMESCRIPT) {
913 while (*p != '\0') p++;
914 str = p;
915 buff = strecat(buff, "(invalid sub-StringID)", last);
916 break;
918 param = MakeStringID(TEXT_TAB_GAMESCRIPT_START, param);
921 sub_args.SetParam(i++, param);
922 } else {
923 char *g = stredup(s);
924 g[p - s] = '\0';
926 sub_args_need_free[i] = true;
927 sub_args.SetParam(i++, (uint64)(size_t)g);
930 /* If we didn't error out, we can actually print the string. */
931 if (*str != '\0') {
932 str = p;
933 buff = GetStringWithArgs(buff, MakeStringID(TEXT_TAB_GAMESCRIPT_START, stringid), &sub_args, last, true);
936 for (int i = 0; i < 20; i++) {
937 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
939 break;
942 case SCC_NEWGRF_STRINL: {
943 StringID substr = Utf8Consume(&str);
944 str_stack.push(GetStringPtr(substr));
945 break;
948 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
949 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
950 str_stack.push(GetStringPtr(substr));
951 case_index = next_substr_case_index;
952 next_substr_case_index = 0;
953 break;
957 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
958 /* First read the meta data from the language file. */
959 uint offset = orig_offset + (byte)*str++;
960 int gender = 0;
961 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
962 /* Now we need to figure out what text to resolve, i.e.
963 * what do we need to draw? So get the actual raw string
964 * first using the control code to get said string. */
965 char input[4 + 1];
966 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
967 *p = '\0';
969 /* Now do the string formatting. */
970 char buf[256];
971 bool old_sgd = _scan_for_gender_data;
972 _scan_for_gender_data = true;
973 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
974 p = FormatString(buf, input, &tmp_params, lastof(buf));
975 _scan_for_gender_data = old_sgd;
976 *p = '\0';
978 /* And determine the string. */
979 const char *s = buf;
980 WChar c = Utf8Consume(&s);
981 /* Does this string have a gender, if so, set it */
982 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
984 str = ParseStringChoice(str, gender, &buff, last);
985 break;
988 /* This sets up the gender for the string.
989 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
990 case SCC_GENDER_INDEX: // {GENDER 0}
991 if (_scan_for_gender_data) {
992 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
993 *buff++ = *str++;
994 } else {
995 str++;
997 break;
999 case SCC_PLURAL_LIST: { // {P}
1000 int plural_form = *str++; // contains the plural form for this string
1001 uint offset = orig_offset + (byte)*str++;
1002 int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
1003 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
1004 break;
1007 case SCC_ARG_INDEX: { // Move argument pointer
1008 args->offset = orig_offset + (byte)*str++;
1009 break;
1012 case SCC_SET_CASE: { // {SET_CASE}
1013 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
1014 * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
1015 next_substr_case_index = (byte)*str++;
1016 break;
1019 case SCC_SWITCH_CASE: { // {Used to implement case switching}
1020 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
1021 * Each LEN is printed using 2 bytes in big endian order. */
1022 uint num = (byte)*str++;
1023 while (num) {
1024 if ((byte)str[0] == case_index) {
1025 /* Found the case, adjust str pointer and continue */
1026 str += 3;
1027 break;
1029 /* Otherwise skip to the next case */
1030 str += 3 + (str[1] << 8) + str[2];
1031 num--;
1033 break;
1036 case SCC_REVISION: // {REV}
1037 buff = strecpy(buff, _openttd_revision, last);
1038 break;
1040 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1041 if (game_script) break;
1042 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
1043 buff = FormatString(buff, str, args, last);
1044 break;
1047 case SCC_STRING: {// {STRING}
1048 StringID str = args->GetInt32(SCC_STRING);
1049 if (game_script && GetStringTab(str) != TEXT_TAB_GAMESCRIPT_START) break;
1050 /* WARNING. It's prohibited for the included string to consume any arguments.
1051 * For included strings that consume argument, you should use STRING1, STRING2 etc.
1052 * To debug stuff you can set argv to NULL and it will tell you */
1053 StringParameters tmp_params(args->GetDataPointer(), args->GetDataLeft(), NULL);
1054 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
1055 next_substr_case_index = 0;
1056 break;
1059 case SCC_STRING1:
1060 case SCC_STRING2:
1061 case SCC_STRING3:
1062 case SCC_STRING4:
1063 case SCC_STRING5:
1064 case SCC_STRING6:
1065 case SCC_STRING7: { // {STRING1..7}
1066 /* Strings that consume arguments */
1067 StringID str = args->GetInt32(b);
1068 if (game_script && GetStringTab(str) != TEXT_TAB_GAMESCRIPT_START) break;
1069 uint size = b - SCC_STRING1 + 1;
1070 if (game_script && size > args->GetDataLeft()) {
1071 buff = strecat(buff, "(too many parameters)", last);
1072 } else {
1073 StringParameters sub_args(*args, size);
1074 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
1076 next_substr_case_index = 0;
1077 break;
1080 case SCC_COMMA: // {COMMA}
1081 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
1082 break;
1084 case SCC_DECIMAL: {// {DECIMAL}
1085 int64 number = args->GetInt64(SCC_DECIMAL);
1086 int digits = args->GetInt32(SCC_DECIMAL);
1087 buff = FormatCommaNumber(buff, number, last, digits);
1088 break;
1091 case SCC_NUM: // {NUM}
1092 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
1093 break;
1095 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1096 int64 num = args->GetInt64();
1097 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
1098 break;
1101 case SCC_HEX: // {HEX}
1102 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
1103 break;
1105 case SCC_BYTES: // {BYTES}
1106 buff = FormatBytes(buff, args->GetInt64(), last);
1107 break;
1109 case SCC_CARGO_TINY: { // {CARGO_TINY}
1110 /* Tiny description of cargotypes. Layout:
1111 * param 1: cargo type
1112 * param 2: cargo count */
1113 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
1114 if (cargo >= CargoSpec::GetArraySize()) break;
1116 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1117 int64 amount = 0;
1118 switch (cargo_str) {
1119 case STR_TONS:
1120 amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64());
1121 break;
1123 case STR_LITERS:
1124 amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64());
1125 break;
1127 default: {
1128 amount = args->GetInt64();
1129 break;
1133 buff = FormatCommaNumber(buff, amount, last);
1134 break;
1137 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1138 /* Short description of cargotypes. Layout:
1139 * param 1: cargo type
1140 * param 2: cargo count */
1141 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
1142 if (cargo >= CargoSpec::GetArraySize()) break;
1144 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1145 switch (cargo_str) {
1146 case STR_TONS: {
1147 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1148 int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1149 StringParameters tmp_params(args_array);
1150 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1151 break;
1154 case STR_LITERS: {
1155 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1156 int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1157 StringParameters tmp_params(args_array);
1158 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1159 break;
1162 default: {
1163 StringParameters tmp_params(*args, 1);
1164 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
1165 break;
1168 break;
1171 case SCC_CARGO_LONG: { // {CARGO_LONG}
1172 /* First parameter is cargo type, second parameter is cargo count */
1173 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
1174 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
1176 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1177 StringParameters tmp_args(*args, 1);
1178 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
1179 break;
1182 case SCC_CARGO_LIST: { // {CARGO_LIST}
1183 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
1184 bool first = true;
1186 const CargoSpec *cs;
1187 FOR_ALL_SORTED_CARGOSPECS(cs) {
1188 if (!HasBit(cmask, cs->Index())) continue;
1190 if (buff >= last - 2) break; // ',' and ' '
1192 if (first) {
1193 first = false;
1194 } else {
1195 /* Add a comma if this is not the first item */
1196 *buff++ = ',';
1197 *buff++ = ' ';
1200 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
1203 /* If first is still true then no cargo is accepted */
1204 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
1206 *buff = '\0';
1207 next_substr_case_index = 0;
1209 /* Make sure we detect any buffer overflow */
1210 assert(buff < last);
1211 break;
1214 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1215 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
1216 break;
1218 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1219 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
1220 break;
1222 case SCC_DATE_TINY: // {DATE_TINY}
1223 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
1224 break;
1226 case SCC_DATE_SHORT: // {DATE_SHORT}
1227 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
1228 next_substr_case_index = 0;
1229 break;
1231 case SCC_DATE_LONG: // {DATE_LONG}
1232 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
1233 next_substr_case_index = 0;
1234 break;
1236 case SCC_DATE_ISO: // {DATE_ISO}
1237 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
1238 break;
1240 case SCC_TIME: // {TIME}
1241 buff = FormatTimeString(buff, args->GetInt64(SCC_TIME), last, next_substr_case_index);
1242 break;
1244 case SCC_TIME_TINY: // {TIME_TINY}
1245 buff = FormatTimeString(buff, args->GetInt64(SCC_TIME_TINY), last, next_substr_case_index);
1246 break;
1248 case SCC_FORCE: { // {FORCE}
1249 assert(_settings_game.locale.units_force < lengthof(_units_force));
1250 int64 args_array[1] = {_units_force[_settings_game.locale.units_force].c.ToDisplay(args->GetInt64())};
1251 StringParameters tmp_params(args_array);
1252 buff = FormatString(buff, GetStringPtr(_units_force[_settings_game.locale.units_force].s), &tmp_params, last);
1253 break;
1256 case SCC_HEIGHT: { // {HEIGHT}
1257 assert(_settings_game.locale.units_height < lengthof(_units_height));
1258 int64 args_array[] = {_units_height[_settings_game.locale.units_height].c.ToDisplay(args->GetInt64())};
1259 StringParameters tmp_params(args_array);
1260 buff = FormatString(buff, GetStringPtr(_units_height[_settings_game.locale.units_height].s), &tmp_params, last);
1261 break;
1264 case SCC_POWER: { // {POWER}
1265 assert(_settings_game.locale.units_power < lengthof(_units_power));
1266 int64 args_array[1] = {_units_power[_settings_game.locale.units_power].c.ToDisplay(args->GetInt64())};
1267 StringParameters tmp_params(args_array);
1268 buff = FormatString(buff, GetStringPtr(_units_power[_settings_game.locale.units_power].s), &tmp_params, last);
1269 break;
1272 case SCC_VELOCITY: { // {VELOCITY}
1273 assert(_settings_game.locale.units_velocity < lengthof(_units_velocity));
1274 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
1275 StringParameters tmp_params(args_array);
1276 buff = FormatString(buff, GetStringPtr(_units_velocity[_settings_game.locale.units_velocity].s), &tmp_params, last);
1277 break;
1280 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1281 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1282 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1283 StringParameters tmp_params(args_array);
1284 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].s), &tmp_params, last);
1285 break;
1288 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1289 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1290 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
1291 StringParameters tmp_params(args_array);
1292 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1293 break;
1296 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1297 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1298 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1299 StringParameters tmp_params(args_array);
1300 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].s), &tmp_params, last);
1301 break;
1304 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1305 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1306 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
1307 StringParameters tmp_params(args_array);
1308 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1309 break;
1312 case SCC_COMPANY_NAME: { // {COMPANY}
1313 const Company *c = Company::GetIfValid(args->GetInt32());
1314 if (c == NULL) break;
1316 if (c->name != NULL) {
1317 int64 args_array[] = {(int64)(size_t)c->name};
1318 StringParameters tmp_params(args_array);
1319 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1320 } else {
1321 int64 args_array[] = {c->name_2};
1322 StringParameters tmp_params(args_array);
1323 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
1325 break;
1328 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1329 CompanyID company = (CompanyID)args->GetInt32();
1331 /* Nothing is added for AI or inactive companies */
1332 if (Company::IsValidHumanID(company)) {
1333 int64 args_array[] = {company + 1};
1334 StringParameters tmp_params(args_array);
1335 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
1337 break;
1340 case SCC_DEPOT_NAME: { // {DEPOT}
1341 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
1342 if (vt == VEH_AIRCRAFT) {
1343 uint64 args_array[] = {(uint64)args->GetInt32()};
1344 WChar types_array[] = {SCC_STATION_NAME};
1345 StringParameters tmp_params(args_array, 1, types_array);
1346 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
1347 break;
1350 const Depot *d = Depot::Get(args->GetInt32());
1351 if (d->name != NULL) {
1352 int64 args_array[] = {(int64)(size_t)d->name};
1353 StringParameters tmp_params(args_array);
1354 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1355 } else {
1356 int64 args_array[] = {d->town->index, d->town_cn + 1};
1357 StringParameters tmp_params(args_array);
1358 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
1360 break;
1363 case SCC_ENGINE_NAME: { // {ENGINE}
1364 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
1365 if (e == NULL) break;
1367 if (e->name != NULL && e->IsEnabled()) {
1368 int64 args_array[] = {(int64)(size_t)e->name};
1369 StringParameters tmp_params(args_array);
1370 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1371 } else {
1372 StringParameters tmp_params(NULL, 0, NULL);
1373 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
1375 break;
1378 case SCC_GROUP_NAME: { // {GROUP}
1379 const Group *g = Group::GetIfValid(args->GetInt32());
1380 if (g == NULL) break;
1382 if (g->name != NULL) {
1383 int64 args_array[] = {(int64)(size_t)g->name};
1384 StringParameters tmp_params(args_array);
1385 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1386 } else {
1387 int64 args_array[] = {g->index};
1388 StringParameters tmp_params(args_array);
1390 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
1392 break;
1395 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1396 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
1397 if (i == NULL) break;
1399 if (_scan_for_gender_data) {
1400 /* Gender is defined by the industry type.
1401 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1402 StringParameters tmp_params(NULL, 0, NULL);
1403 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
1404 } else {
1405 /* First print the town name and the industry type name. */
1406 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
1407 StringParameters tmp_params(args_array);
1409 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
1411 next_substr_case_index = 0;
1412 break;
1415 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1416 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
1417 if (c == NULL) break;
1419 if (c->president_name != NULL) {
1420 int64 args_array[] = {(int64)(size_t)c->president_name};
1421 StringParameters tmp_params(args_array);
1422 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1423 } else {
1424 int64 args_array[] = {c->president_name_2};
1425 StringParameters tmp_params(args_array);
1426 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
1428 break;
1431 case SCC_STATION_NAME: { // {STATION}
1432 StationID sid = args->GetInt32(SCC_STATION_NAME);
1433 const Station *st = Station::GetIfValid(sid);
1435 if (st == NULL) {
1436 /* The station doesn't exist anymore. The only place where we might
1437 * be "drawing" an invalid station is in the case of cargo that is
1438 * in transit. */
1439 StringParameters tmp_params(NULL, 0, NULL);
1440 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
1441 break;
1444 if (st->name != NULL) {
1445 int64 args_array[] = {(int64)(size_t)st->name};
1446 StringParameters tmp_params(args_array);
1447 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1448 } else {
1449 StringID str = st->string_id;
1450 if (st->indtype != IT_INVALID) {
1451 /* Special case where the industry provides the name for the station */
1452 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1454 /* Industry GRFs can change which might remove the station name and
1455 * thus cause very strange things. Here we check for that before we
1456 * actually set the station name. */
1457 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1458 str = indsp->station_name;
1462 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
1463 StringParameters tmp_params(args_array);
1464 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1466 break;
1469 case SCC_TOWN_NAME: { // {TOWN}
1470 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
1471 if (t == NULL) break;
1473 if (t->name != NULL) {
1474 int64 args_array[] = {(int64)(size_t)t->name};
1475 StringParameters tmp_params(args_array);
1476 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1477 } else {
1478 buff = GetTownName(buff, t, last);
1480 break;
1483 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1484 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
1485 if (wp == NULL) break;
1487 if (wp->name != NULL) {
1488 int64 args_array[] = {(int64)(size_t)wp->name};
1489 StringParameters tmp_params(args_array);
1490 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1491 } else {
1492 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
1493 StringParameters tmp_params(args_array);
1494 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1495 if (wp->town_cn != 0) str++;
1496 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1498 break;
1501 case SCC_VEHICLE_NAME: { // {VEHICLE}
1502 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
1503 if (v == NULL) break;
1505 if (v->name != NULL) {
1506 int64 args_array[] = {(int64)(size_t)v->name};
1507 StringParameters tmp_params(args_array);
1508 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1509 } else if (v->group_id != DEFAULT_GROUP) {
1510 /* The vehicle has no name, but is member of a group, so print group name */
1511 int64 args_array[] = {(uint64)(size_t)v->group_id, v->unitnumber};
1512 StringParameters tmp_params(args_array);
1513 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_VEHICLE, &tmp_params, last);
1514 } else {
1515 int64 args_array[] = {v->unitnumber};
1516 StringParameters tmp_params(args_array);
1518 StringID str;
1519 switch (v->type) {
1520 default: str = STR_INVALID_VEHICLE; break;
1521 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
1522 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
1523 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
1524 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
1527 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1529 break;
1532 case SCC_SIGN_NAME: { // {SIGN}
1533 const Sign *si = Sign::GetIfValid(args->GetInt32());
1534 if (si == NULL) break;
1536 if (si->name != NULL) {
1537 int64 args_array[] = {(int64)(size_t)si->name};
1538 StringParameters tmp_params(args_array);
1539 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1540 } else {
1541 StringParameters tmp_params(NULL, 0, NULL);
1542 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
1544 break;
1547 case SCC_TR_SLOT_NAME: { // {TRSLOT}
1548 const TraceRestrictSlot *slot = TraceRestrictSlot::GetIfValid(args->GetInt32(SCC_TR_SLOT_NAME));
1549 if (slot == NULL) break;
1550 int64 args_array[] = {(int64)(size_t)slot->name.c_str()};
1551 StringParameters tmp_params(args_array);
1552 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1553 break;
1556 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1557 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
1558 break;
1561 case SCC_CONSUME_ARG:
1562 // do nothing
1563 break;
1565 default:
1566 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
1567 break;
1570 *buff = '\0';
1571 return buff;
1575 static char *StationGetSpecialString(char *buff, int x, const char *last)
1577 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
1578 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
1579 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
1580 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
1581 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
1582 *buff = '\0';
1583 return buff;
1586 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
1588 return GenerateTownNameString(buff, last, ind, seed);
1591 static const char * const _silly_company_names[] = {
1592 "Bloggs Brothers",
1593 "Tiny Transport Ltd.",
1594 "Express Travel",
1595 "Comfy-Coach & Co.",
1596 "Crush & Bump Ltd.",
1597 "Broken & Late Ltd.",
1598 "Sam Speedy & Son",
1599 "Supersonic Travel",
1600 "Mike's Motors",
1601 "Lightning International",
1602 "Pannik & Loozit Ltd.",
1603 "Inter-City Transport",
1604 "Getout & Pushit Ltd."
1607 static const char * const _surname_list[] = {
1608 "Adams",
1609 "Allan",
1610 "Baker",
1611 "Bigwig",
1612 "Black",
1613 "Bloggs",
1614 "Brown",
1615 "Campbell",
1616 "Gordon",
1617 "Hamilton",
1618 "Hawthorn",
1619 "Higgins",
1620 "Green",
1621 "Gribble",
1622 "Jones",
1623 "McAlpine",
1624 "MacDonald",
1625 "McIntosh",
1626 "Muir",
1627 "Murphy",
1628 "Nelson",
1629 "O'Donnell",
1630 "Parker",
1631 "Phillips",
1632 "Pilkington",
1633 "Quigley",
1634 "Sharkey",
1635 "Thomson",
1636 "Watkins"
1639 static const char * const _silly_surname_list[] = {
1640 "Grumpy",
1641 "Dozy",
1642 "Speedy",
1643 "Nosey",
1644 "Dribble",
1645 "Mushroom",
1646 "Cabbage",
1647 "Sniffle",
1648 "Fishy",
1649 "Swindle",
1650 "Sneaky",
1651 "Nutkins"
1654 static const char _initial_name_letters[] = {
1655 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1656 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1659 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
1661 const char * const *base;
1662 uint num;
1664 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1665 base = _silly_surname_list;
1666 num = lengthof(_silly_surname_list);
1667 } else {
1668 base = _surname_list;
1669 num = lengthof(_surname_list);
1672 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
1673 buff = strecpy(buff, " & Co.", last);
1675 return buff;
1678 static char *GenPresidentName(char *buff, uint32 x, const char *last)
1680 char initial[] = "?. ";
1681 const char * const *base;
1682 uint num;
1683 uint i;
1685 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
1686 buff = strecpy(buff, initial, last);
1688 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
1689 if (i < sizeof(_initial_name_letters)) {
1690 initial[0] = _initial_name_letters[i];
1691 buff = strecpy(buff, initial, last);
1694 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1695 base = _silly_surname_list;
1696 num = lengthof(_silly_surname_list);
1697 } else {
1698 base = _surname_list;
1699 num = lengthof(_surname_list);
1702 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
1704 return buff;
1707 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
1709 switch (ind) {
1710 case 1: // not used
1711 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
1713 case 2: // used for Foobar & Co company names
1714 return GenAndCoName(buff, args->GetInt32(), last);
1716 case 3: // President name
1717 return GenPresidentName(buff, args->GetInt32(), last);
1720 /* town name? */
1721 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1722 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
1723 return strecpy(buff, " Transport", last);
1726 /* language name? */
1727 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
1728 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
1729 return strecpy(buff,
1730 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
1733 /* resolution size? */
1734 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
1735 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
1736 buff += seprintf(
1737 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
1739 return buff;
1742 NOT_REACHED();
1745 #ifdef ENABLE_NETWORK
1746 extern void SortNetworkLanguages();
1747 #else /* ENABLE_NETWORK */
1748 static inline void SortNetworkLanguages() {}
1749 #endif /* ENABLE_NETWORK */
1752 * Check whether the header is a valid header for OpenTTD.
1753 * @return true iff the header is deemed valid.
1755 bool LanguagePackHeader::IsValid() const
1757 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1758 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1759 this->plural_form < LANGUAGE_MAX_PLURAL &&
1760 this->text_dir <= 1 &&
1761 this->newgrflangid < MAX_LANG &&
1762 this->num_genders < MAX_NUM_GENDERS &&
1763 this->num_cases < MAX_NUM_CASES &&
1764 StrValid(this->name, lastof(this->name)) &&
1765 StrValid(this->own_name, lastof(this->own_name)) &&
1766 StrValid(this->isocode, lastof(this->isocode)) &&
1767 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
1768 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
1769 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
1773 * Read a particular language.
1774 * @param lang The metadata about the language.
1775 * @return Whether the loading went okay or not.
1777 bool ReadLanguagePack(const LanguageMetadata *lang)
1779 /* Current language pack */
1780 size_t len;
1781 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
1782 if (lang_pack == NULL) return false;
1784 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1785 const char *end = (char *)lang_pack + len + 1;
1787 /* We need at least one byte of lang_pack->data */
1788 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1789 free(lang_pack);
1790 return false;
1793 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1794 for (uint i = 0; i < TEXT_TAB_END; i++) {
1795 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
1797 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
1799 uint count = 0;
1800 for (uint i = 0; i < TEXT_TAB_END; i++) {
1801 uint16 num = lang_pack->offsets[i];
1802 if (num > TAB_SIZE) {
1803 free(lang_pack);
1804 return false;
1807 _langtab_start[i] = count;
1808 _langtab_num[i] = num;
1809 count += num;
1812 /* Allocate offsets */
1813 char **langpack_offs = MallocT<char *>(count);
1815 /* Fill offsets */
1816 char *s = lang_pack->data;
1817 len = (byte)*s++;
1818 for (uint i = 0; i < count; i++) {
1819 if (s + len >= end) {
1820 free(lang_pack);
1821 free(langpack_offs);
1822 return false;
1824 if (len >= 0xC0) {
1825 len = ((len & 0x3F) << 8) + (byte)*s++;
1826 if (s + len >= end) {
1827 free(lang_pack);
1828 free(langpack_offs);
1829 return false;
1832 langpack_offs[i] = s;
1833 s += len;
1834 len = (byte)*s;
1835 *s++ = '\0'; // zero terminate the string
1838 free(_langpack);
1839 _langpack = lang_pack;
1841 free(_langpack_offs);
1842 _langpack_offs = langpack_offs;
1844 _current_language = lang;
1845 _current_text_dir = (TextDirection)_current_language->text_dir;
1846 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
1847 strecpy(_config_language_file, c_file, lastof(_config_language_file));
1848 SetCurrentGrfLangID(_current_language->newgrflangid);
1850 #ifdef WITH_ICU_SORT
1851 /* Delete previous collator. */
1852 if (_current_collator != NULL) {
1853 delete _current_collator;
1854 _current_collator = NULL;
1857 /* Create a collator instance for our current locale. */
1858 UErrorCode status = U_ZERO_ERROR;
1859 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
1860 /* Sort number substrings by their numerical value. */
1861 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
1862 /* Avoid using the collator if it is not correctly set. */
1863 if (U_FAILURE(status)) {
1864 delete _current_collator;
1865 _current_collator = NULL;
1867 #endif /* WITH_ICU_SORT */
1869 /* Some lists need to be sorted again after a language change. */
1870 ReconsiderGameScriptLanguage();
1871 InitializeSortedCargoSpecs();
1872 SortIndustryTypes();
1873 BuildIndustriesLegend();
1874 SortNetworkLanguages();
1875 #ifdef ENABLE_NETWORK
1876 BuildContentTypeStringList();
1877 #endif /* ENABLE_NETWORK */
1878 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
1879 InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
1880 InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
1881 InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
1882 InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
1883 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
1884 InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
1886 return true;
1889 /* Win32 implementation in win32.cpp.
1890 * OS X implementation in os/macosx/macos.mm. */
1891 #if !(defined(WIN32) || defined(__APPLE__))
1893 * Determine the current charset based on the environment
1894 * First check some default values, after this one we passed ourselves
1895 * and if none exist return the value for $LANG
1896 * @param param environment variable to check conditionally if default ones are not
1897 * set. Pass NULL if you don't want additional checks.
1898 * @return return string containing current charset, or NULL if not-determinable
1900 const char *GetCurrentLocale(const char *param)
1902 const char *env;
1904 env = getenv("LANGUAGE");
1905 if (env != NULL) return env;
1907 env = getenv("LC_ALL");
1908 if (env != NULL) return env;
1910 if (param != NULL) {
1911 env = getenv(param);
1912 if (env != NULL) return env;
1915 return getenv("LANG");
1917 #else
1918 const char *GetCurrentLocale(const char *param);
1919 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
1921 int CDECL StringIDSorter(const StringID *a, const StringID *b)
1923 char stra[512];
1924 char strb[512];
1925 GetString(stra, *a, lastof(stra));
1926 GetString(strb, *b, lastof(strb));
1928 return strnatcmp(stra, strb);
1932 * Get the language with the given NewGRF language ID.
1933 * @param newgrflangid NewGRF languages ID to check.
1934 * @return The language's metadata, or NULL if it is not known.
1936 const LanguageMetadata *GetLanguage(byte newgrflangid)
1938 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
1939 if (newgrflangid == lang->newgrflangid) return lang;
1942 return NULL;
1946 * Reads the language file header and checks compatibility.
1947 * @param file the file to read
1948 * @param hdr the place to write the header information to
1949 * @return true if and only if the language file is of a compatible version
1951 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
1953 FILE *f = fopen(file, "rb");
1954 if (f == NULL) return false;
1956 size_t read = fread(hdr, sizeof(*hdr), 1, f);
1957 fclose(f);
1959 bool ret = read == 1 && hdr->IsValid();
1961 /* Convert endianness for the windows language ID */
1962 if (ret) {
1963 hdr->missing = FROM_LE16(hdr->missing);
1964 hdr->winlangid = FROM_LE16(hdr->winlangid);
1966 return ret;
1970 * Gets a list of languages from the given directory.
1971 * @param path the base directory to search in
1973 static void GetLanguageList(const char *path)
1975 DIR *dir = ttd_opendir(path);
1976 if (dir != NULL) {
1977 struct dirent *dirent;
1978 while ((dirent = readdir(dir)) != NULL) {
1979 const char *d_name = FS2OTTD(dirent->d_name);
1980 const char *extension = strrchr(d_name, '.');
1982 /* Not a language file */
1983 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
1985 LanguageMetadata lmd;
1986 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
1988 /* Check whether the file is of the correct version */
1989 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
1990 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
1991 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
1992 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
1993 } else {
1994 *_languages.Append() = lmd;
1997 closedir(dir);
2002 * Make a list of the available language packs. Put the data in
2003 * #_languages list.
2005 void InitializeLanguagePacks()
2007 Searchpath sp;
2009 FOR_ALL_SEARCHPATHS(sp) {
2010 char path[MAX_PATH];
2011 FioAppendDirectory(path, lastof(path), sp, LANG_DIR);
2012 GetLanguageList(path);
2014 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
2016 /* Acquire the locale of the current system */
2017 const char *lang = GetCurrentLocale("LC_MESSAGES");
2018 if (lang == NULL) lang = "en_GB";
2020 const LanguageMetadata *chosen_language = NULL; ///< Matching the language in the configuration file or the current locale
2021 const LanguageMetadata *language_fallback = NULL; ///< Using pt_PT for pt_BR locale when pt_BR is not available
2022 const LanguageMetadata *en_GB_fallback = _languages.Begin(); ///< Fallback when no locale-matching language has been found
2024 /* Find a proper language. */
2025 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
2026 /* We are trying to find a default language. The priority is by
2027 * configuration file, local environment and last, if nothing found,
2028 * English. */
2029 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
2030 if (strcmp(lang_file, _config_language_file) == 0) {
2031 chosen_language = lng;
2032 break;
2035 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
2036 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
2037 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
2040 /* We haven't found the language in the config nor the one in the locale.
2041 * Now we set it to one of the fallback languages */
2042 if (chosen_language == NULL) {
2043 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
2046 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
2050 * Get the ISO language code of the currently loaded language.
2051 * @return the ISO code.
2053 const char *GetCurrentLanguageIsoCode()
2055 return _langpack->isocode;
2059 * Check whether there are glyphs missing in the current language.
2060 * @param Pointer to an address for storing the text pointer.
2061 * @return If glyphs are missing, return \c true, else return \c false.
2062 * @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.
2064 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
2066 InitFreeType(this->Monospace());
2067 const Sprite *question_mark[FS_END];
2069 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
2070 question_mark[size] = GetGlyph(size, '?');
2073 this->Reset();
2074 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
2075 FontSize size = this->DefaultSize();
2076 if (str != NULL) *str = text;
2077 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
2078 if (c == SCC_TINYFONT) {
2079 size = FS_SMALL;
2080 } else if (c == SCC_BIGFONT) {
2081 size = FS_LARGE;
2082 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
2083 /* The character is printable, but not in the normal font. This is the case we were testing for. */
2084 return true;
2088 return false;
2091 /** Helper for searching through the language pack. */
2092 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
2093 uint i; ///< Iterator for the primary language tables.
2094 uint j; ///< Iterator for the secondary language tables.
2096 /* virtual */ void Reset()
2098 this->i = 0;
2099 this->j = 0;
2102 /* virtual */ FontSize DefaultSize()
2104 return FS_NORMAL;
2107 /* virtual */ const char *NextString()
2109 if (this->i >= TEXT_TAB_END) return NULL;
2111 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
2113 this->j++;
2114 while (this->i < TEXT_TAB_END && this->j >= _langtab_num[this->i]) {
2115 this->i++;
2116 this->j = 0;
2119 return ret;
2122 /* virtual */ bool Monospace()
2124 return false;
2127 /* virtual */ void SetFontNames(FreeTypeSettings *settings, const char *font_name)
2129 #ifdef WITH_FREETYPE
2130 strecpy(settings->small.font, font_name, lastof(settings->small.font));
2131 strecpy(settings->medium.font, font_name, lastof(settings->medium.font));
2132 strecpy(settings->large.font, font_name, lastof(settings->large.font));
2133 #endif /* WITH_FREETYPE */
2138 * Check whether the currently loaded language pack
2139 * uses characters that the currently loaded font
2140 * does not support. If this is the case an error
2141 * message will be shown in English. The error
2142 * message will not be localized because that would
2143 * mean it might use characters that are not in the
2144 * font, which is the whole reason this check has
2145 * been added.
2146 * @param base_font Whether to look at the base font as well.
2147 * @param searcher The methods to use to search for strings to check.
2148 * If NULL the loaded language pack searcher is used.
2150 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2152 static LanguagePackGlyphSearcher pack_searcher;
2153 if (searcher == NULL) searcher = &pack_searcher;
2154 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
2155 #ifdef WITH_FREETYPE
2156 if (bad_font) {
2157 /* We found an unprintable character... lets try whether we can find
2158 * a fallback font that can print the characters in the current language. */
2159 FreeTypeSettings backup;
2160 memcpy(&backup, &_freetype, sizeof(backup));
2162 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
2164 memcpy(&_freetype, &backup, sizeof(backup));
2166 if (bad_font && base_font) {
2167 /* Our fallback font does miss characters too, so keep the
2168 * user chosen font as that is more likely to be any good than
2169 * the wild guess we made */
2170 InitFreeType(searcher->Monospace());
2173 #endif
2175 if (bad_font) {
2176 /* All attempts have failed. Display an error. As we do not want the string to be translated by
2177 * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
2178 * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
2179 * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
2180 * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
2181 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.");
2182 Utf8Encode(err_str, SCC_YELLOW);
2183 SetDParamStr(0, err_str);
2184 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2186 /* Reset the font width */
2187 LoadStringWidthTable(searcher->Monospace());
2188 return;
2191 /* Update the font with cache */
2192 LoadStringWidthTable(searcher->Monospace());
2194 #if !defined(WITH_ICU_LAYOUT)
2196 * For right-to-left languages we need the ICU library. If
2197 * we do not have support for that library we warn the user
2198 * about it with a message. As we do not want the string to
2199 * be translated by the translators, we 'force' it into the
2200 * binary and 'load' it via a BindCString. To do this
2201 * properly we have to set the colour of the string,
2202 * otherwise we end up with a lot of artifacts. The colour
2203 * 'character' might change in the future, so for safety
2204 * we just Utf8 Encode it into the string, which takes
2205 * exactly three characters, so it replaces the "XXX" with
2206 * the colour marker.
2208 if (_current_text_dir != TD_LTR) {
2209 static char *err_str = stredup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
2210 Utf8Encode(err_str, SCC_YELLOW);
2211 SetDParamStr(0, err_str);
2212 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
2214 #endif /* !WITH_ICU_LAYOUT */