Add non-animated SSSE3 blitter
[openttd/fttd.git] / src / strings.cpp
blob3f35c5c8f0f8b748109c2e164a446f1c7e3b682b
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /** @file strings.cpp Handling of translated strings. */
12 #include "stdafx.h"
13 #include "currency.h"
14 #include "station_base.h"
15 #include "town.h"
16 #include "screenshot.h"
17 #include "waypoint_base.h"
18 #include "depot_base.h"
19 #include "industry.h"
20 #include "newgrf_text.h"
21 #include "fileio_func.h"
22 #include "signs_base.h"
23 #include "fontdetection.h"
24 #include "error.h"
25 #include "strings_func.h"
26 #include "rev.h"
27 #include "core/endian_func.hpp"
28 #include "date_func.h"
29 #include "vehicle_base.h"
30 #include "engine_base.h"
31 #include "language.h"
32 #include "townname_func.h"
33 #include "string_func.h"
34 #include "company_base.h"
35 #include "smallmap_gui.h"
36 #include "window_func.h"
37 #include "debug.h"
38 #include "game/game_text.hpp"
39 #include <stack>
41 #include "table/strings.h"
42 #include "table/control_codes.h"
44 char _config_language_file[MAX_PATH]; ///< The file (name) stored in the configuration.
45 LanguageList _languages; ///< The actual list of language meta data.
46 const LanguageMetadata *_current_language = NULL; ///< The currently loaded language.
48 TextDirection _current_text_dir; ///< Text direction of the currently selected language.
50 #ifdef WITH_ICU
51 Collator *_current_collator = NULL; ///< Collator for the language currently in use.
52 #endif /* WITH_ICU */
54 static uint64 _global_string_params_data[20]; ///< Global array of string parameters. To access, use #SetDParam.
55 static WChar _global_string_params_type[20]; ///< Type of parameters stored in #_decode_parameters
56 StringParameters _global_string_params(_global_string_params_data, 20, _global_string_params_type);
58 /** Reset the type array. */
59 void StringParameters::ClearTypeInformation()
61 assert(this->type != NULL);
62 MemSetT(this->type, 0, this->num_param);
66 /**
67 * Read an int64 from the argument array. The offset is increased
68 * so the next time GetInt64 is called the next value is read.
70 int64 StringParameters::GetInt64(WChar type)
72 if (this->offset >= this->num_param) {
73 DEBUG(misc, 0, "Trying to read invalid string parameter");
74 return 0;
76 if (this->type != NULL) {
77 assert(this->type[this->offset] == 0 || this->type[this->offset] == type);
78 this->type[this->offset] = type;
80 return this->data[this->offset++];
83 /**
84 * Shift all data in the data array by the given amount to make
85 * room for some extra parameters.
87 void StringParameters::ShiftParameters(uint amount)
89 assert(amount <= this->num_param);
90 MemMoveT(this->data + amount, this->data, this->num_param - amount);
93 /**
94 * Set DParam n to some number that is suitable for string size computations.
95 * @param n Index of the string parameter.
96 * @param max_value The biggest value which shall be displayed.
97 * For the result only the number of digits of \a max_value matter.
98 * @param min_count Minimum number of digits independent of \a max.
99 * @param size Font of the number
101 void SetDParamMaxValue(uint n, uint64 max_value, uint min_count, FontSize size)
103 uint num_digits = 1;
104 while (max_value >= 10) {
105 num_digits++;
106 max_value /= 10;
108 SetDParamMaxDigits(n, max(min_count, num_digits), size);
112 * Set DParam n to some number that is suitable for string size computations.
113 * @param n Index of the string parameter.
114 * @param count Number of digits which shall be displayable.
115 * @param size Font of the number
117 void SetDParamMaxDigits(uint n, uint count, FontSize size)
119 uint front, next;
120 GetBroadestDigit(&front, &next, size);
121 uint64 val = count > 1 ? front : next;
122 for (; count > 1; count--) {
123 val = 10 * val + next;
125 SetDParam(n, val);
129 * Copy \a num string parameters from array \a src into the global string parameter array.
130 * @param offs Index in the global array to copy the first string parameter to.
131 * @param src Source array of string parameters.
132 * @param num Number of string parameters to copy.
134 void CopyInDParam(int offs, const uint64 *src, int num)
136 MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
140 * Copy \a num string parameters from the global string parameter array to the \a dst array.
141 * @param dst Destination array of string parameters.
142 * @param offs Index in the global array to copy the first string parameter from.
143 * @param num Number of string parameters to copy.
145 void CopyOutDParam(uint64 *dst, int offs, int num)
147 MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
151 * Copy \a num string parameters from the global string parameter array to the \a dst array.
152 * Furthermore clone raw string parameters into \a strings and amend the data in \a dst.
153 * @param dst Destination array of string parameters.
154 * @param strings Destination array for clone of the raw strings. Must be of same length as dst. Deallocation left to the caller.
155 * @param string The string used to determine where raw strings are and where there are no raw strings.
156 * @param num Number of string parameters to copy.
158 void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
160 char buf[DRAW_STRING_BUFFER];
161 GetString(buf, string, lastof(buf));
163 MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num);
164 for (int i = 0; i < num; i++) {
165 if (_global_string_params.HasTypeInformation() && _global_string_params.GetTypeAtOffset(i) == SCC_RAW_STRING_POINTER) {
166 strings[i] = strdup((const char *)(size_t)_global_string_params.GetParam(i));
167 dst[i] = (size_t)strings[i];
168 } else {
169 strings[i] = NULL;
174 static char *StationGetSpecialString(char *buff, int x, const char *last);
175 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
176 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
178 static char *FormatString(char *buff, const char *str, StringParameters *args, const char *last, uint case_index = 0, bool game_script = false, bool dry_run = false);
180 struct LanguagePack : public LanguagePackHeader {
181 char data[]; // list of strings
184 static char **_langpack_offs;
185 static LanguagePack *_langpack;
186 static uint _langtab_num[TAB_COUNT]; ///< Offset into langpack offs
187 static uint _langtab_start[TAB_COUNT]; ///< Offset into langpack offs
188 static bool _scan_for_gender_data = false; ///< Are we scanning for the gender of the current string? (instead of formatting it)
191 const char *GetStringPtr(StringID string)
193 switch (GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)) {
194 case GAME_TEXT_TAB: return GetGameStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
195 /* GetGRFStringPtr doesn't handle 0xD4xx ids, we need to convert those to 0xD0xx. */
196 case 26: return GetStringPtr(GetGRFStringID(0, 0xD000 + GB(string, TAB_SIZE_OFFSET, 10)));
197 case 28: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
198 case 29: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x0800);
199 case 30: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x1000);
200 default: return _langpack_offs[_langtab_start[GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)] + GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS)];
205 * Get a parsed string with most special stringcodes replaced by the string parameters.
206 * @param buffr Pointer to a string buffer where the formatted string should be written to.
207 * @param string
208 * @param args Arguments for the string.
209 * @param last Pointer just past the end of \a buffr.
210 * @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case.
211 * @param game_script The string is coming directly from a game script.
212 * @return Pointer to the final zero byte of the formatted string.
214 char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
216 if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
218 uint index = GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS);
219 uint tab = GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS);
221 switch (tab) {
222 case 4:
223 if (index >= 0xC0 && !game_script) {
224 return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
226 break;
228 case 14:
229 if (index >= 0xE4 && !game_script) {
230 return GetSpecialNameString(buffr, index - 0xE4, args, last);
232 break;
234 case 15:
235 /* Old table for custom names. This is no longer used */
236 if (!game_script) {
237 error("Incorrect conversion of custom name string.");
239 break;
241 case GAME_TEXT_TAB:
242 return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
244 case 26:
245 /* Include string within newgrf text (format code 81) */
246 if (HasBit(index, 10)) {
247 StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
248 return GetStringWithArgs(buffr, string, args, last, case_index);
250 break;
252 case 28:
253 return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
255 case 29:
256 return FormatString(buffr, GetGRFStringPtr(index + 0x0800), args, last, case_index);
258 case 30:
259 return FormatString(buffr, GetGRFStringPtr(index + 0x1000), args, last, case_index);
262 if (index >= _langtab_num[tab]) {
263 if (game_script) {
264 return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
266 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
269 return FormatString(buffr, GetStringPtr(string), args, last, case_index);
272 char *GetString(char *buffr, StringID string, const char *last)
274 _global_string_params.ClearTypeInformation();
275 _global_string_params.offset = 0;
276 return GetStringWithArgs(buffr, string, &_global_string_params, last);
280 char *InlineString(char *buf, StringID string)
282 buf += Utf8Encode(buf, SCC_STRING_ID);
283 buf += Utf8Encode(buf, string);
284 return buf;
289 * This function is used to "bind" a C string to a OpenTTD dparam slot.
290 * @param n slot of the string
291 * @param str string to bind
293 void SetDParamStr(uint n, const char *str)
295 SetDParam(n, (uint64)(size_t)str);
299 * Shift the string parameters in the global string parameter array by \a amount positions, making room at the beginning.
300 * @param amount Number of positions to shift.
302 void InjectDParam(uint amount)
304 _global_string_params.ShiftParameters(amount);
308 * Format a number into a string.
309 * @param buff the buffer to write to
310 * @param number the number to write down
311 * @param last the last element in the buffer
312 * @param separator the thousands-separator to use
313 * @param zerofill minimum number of digits to print for the integer part. The number will be filled with zeros at the front if necessary.
314 * @param fractional_digits number of fractional digits to display after a decimal separator. The decimal separator is inserted
315 * in front of the \a fractional_digits last digit of \a number.
316 * @return till where we wrote
318 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
320 static const int max_digits = 20;
321 uint64 divisor = 10000000000000000000ULL;
322 zerofill += fractional_digits;
323 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
325 if (number < 0) {
326 buff += seprintf(buff, last, "-");
327 number = -number;
330 uint64 num = number;
331 uint64 tot = 0;
332 for (int i = 0; i < max_digits; i++) {
333 if (i == max_digits - fractional_digits) {
334 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
335 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
336 buff += seprintf(buff, last, "%s", decimal_separator);
339 uint64 quot = 0;
340 if (num >= divisor) {
341 quot = num / divisor;
342 num = num % divisor;
344 if ((tot |= quot) || i >= max_digits - zerofill) {
345 buff += seprintf(buff, last, "%i", (int)quot);
346 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
349 divisor /= 10;
352 *buff = '\0';
354 return buff;
357 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
359 const char *separator = _settings_game.locale.digit_group_separator;
360 if (separator == NULL) separator = _langpack->digit_group_separator;
361 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
364 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
366 return FormatNumber(buff, number, last, "");
369 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
371 return FormatNumber(buff, number, last, "", count);
374 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
376 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
380 * Format a given number as a number of bytes with the SI prefix.
381 * @param buff the buffer to write to
382 * @param number the number of bytes to write down
383 * @param last the last element in the buffer
384 * @return till where we wrote
386 static char *FormatBytes(char *buff, int64 number, const char *last)
388 assert(number >= 0);
390 /* 1 2^10 2^20 2^30 2^40 2^50 2^60 */
391 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
392 uint id = 1;
393 while (number >= 1024 * 1024) {
394 number /= 1024;
395 id++;
398 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
399 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
401 if (number < 1024) {
402 id = 0;
403 buff += seprintf(buff, last, "%i", (int)number);
404 } else if (number < 1024 * 10) {
405 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
406 } else if (number < 1024 * 100) {
407 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
408 } else {
409 assert(number < 1024 * 1024);
410 buff += seprintf(buff, last, "%i", (int)number / 1024);
413 assert(id < lengthof(iec_prefixes));
414 buff += seprintf(buff, last, " %sB", iec_prefixes[id]);
416 return buff;
419 static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
421 YearMonthDay ymd;
422 ConvertDateToYMD(date, &ymd);
424 int64 args[] = {ymd.day + STR_ORDINAL_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
425 StringParameters tmp_params(args);
426 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
429 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
431 YearMonthDay ymd;
432 ConvertDateToYMD(date, &ymd);
434 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
435 StringParameters tmp_params(args);
436 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
439 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
441 YearMonthDay ymd;
442 ConvertDateToYMD(date, &ymd);
444 char day[3];
445 char month[3];
446 /* We want to zero-pad the days and months */
447 snprintf(day, lengthof(day), "%02i", ymd.day);
448 snprintf(month, lengthof(month), "%02i", ymd.month + 1);
450 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
451 StringParameters tmp_params(args);
452 return FormatString(buff, GetStringPtr(str), &tmp_params, last);
455 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
457 /* We are going to make number absolute for printing, so
458 * keep this piece of data as we need it later on */
459 bool negative = number < 0;
460 const char *multiplier = "";
462 number *= spec->rate;
464 /* convert from negative */
465 if (number < 0) {
466 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
467 buff += Utf8Encode(buff, SCC_RED);
468 buff = strecpy(buff, "-", last);
469 number = -number;
472 /* Add prefix part, following symbol_pos specification.
473 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
474 * The only remaining value is 1 (suffix), so everything that is not 1 */
475 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
477 /* for huge numbers, compact the number into k or M */
478 if (compact) {
479 /* Take care of the 'k' rounding. Having 1 000 000 k
480 * and 1 000 M is inconsistent, so always use 1 000 M. */
481 if (number >= 1000000000 - 500) {
482 number = (number + 500000) / 1000000;
483 multiplier = "M";
484 } else if (number >= 1000000) {
485 number = (number + 500) / 1000;
486 multiplier = "k";
490 const char *separator = _settings_game.locale.digit_group_separator_currency;
491 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
492 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
493 buff = FormatNumber(buff, number, last, separator);
494 buff = strecpy(buff, multiplier, last);
496 /* Add suffix part, following symbol_pos specification.
497 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
498 * The only remaining value is 1 (prefix), so everything that is not 0 */
499 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
501 if (negative) {
502 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
503 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
504 *buff = '\0';
507 return buff;
511 * Determine the "plural" index given a plural form and a number.
512 * @param count The number to get the plural index of.
513 * @param plural_form The plural form we want an index for.
514 * @return The plural index for the given form.
516 static int DeterminePluralForm(int64 count, int plural_form)
518 /* The absolute value determines plurality */
519 uint64 n = abs(count);
521 switch (plural_form) {
522 default:
523 NOT_REACHED();
525 /* Two forms: singular used for one only.
526 * Used in:
527 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
528 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
529 case 0:
530 return n != 1 ? 1 : 0;
532 /* Only one form.
533 * Used in:
534 * Hungarian, Japanese, Korean, Turkish */
535 case 1:
536 return 0;
538 /* Two forms: singular used for 0 and 1.
539 * Used in:
540 * French, Brazilian Portuguese */
541 case 2:
542 return n > 1 ? 1 : 0;
544 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
545 * Note: Cases are out of order for hysterical reasons. '0' is last.
546 * Used in:
547 * Latvian */
548 case 3:
549 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
551 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
552 * Used in:
553 * Gaelige (Irish) */
554 case 4:
555 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
557 /* 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.
558 * Used in:
559 * Lithuanian */
560 case 5:
561 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
563 /* 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.
564 * Used in:
565 * Croatian, Russian, Ukrainian */
566 case 6:
567 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
569 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
570 * Used in:
571 * Polish */
572 case 7:
573 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
575 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
576 * Used in:
577 * Slovenian */
578 case 8:
579 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
581 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
582 * Used in:
583 * Icelandic */
584 case 9:
585 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
587 /* Three forms: special cases for 1, and 2 to 4
588 * Used in:
589 * Czech, Slovak */
590 case 10:
591 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
593 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
594 * Korean doesn't have the concept of plural, but depending on how a
595 * number is pronounced it needs another version of a particle.
596 * As such the plural system is misused to give this distinction.
598 case 11:
599 switch (n % 10) {
600 case 0: // yeong
601 case 1: // il
602 case 3: // sam
603 case 6: // yuk
604 case 7: // chil
605 case 8: // pal
606 return 0;
608 case 2: // i
609 case 4: // sa
610 case 5: // o
611 case 9: // gu
612 return 1;
614 default:
615 NOT_REACHED();
618 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
619 * Used in:
620 * Maltese */
621 case 12:
622 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
623 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
624 * Used in:
625 * Scottish Gaelic */
626 case 13:
627 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
631 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
633 /* <NUM> {Length of each string} {each string} */
634 uint n = (byte)*b++;
635 uint pos, i, mypos = 0;
637 for (i = pos = 0; i != n; i++) {
638 uint len = (byte)*b++;
639 if (i == form) mypos = pos;
640 pos += len;
643 *dst += seprintf(*dst, last, "%s", b + mypos);
644 return b + pos;
647 /** Helper for unit conversion. */
648 struct UnitConversion {
649 int multiplier; ///< Amount to multiply upon conversion.
650 int shift; ///< Amount to shift upon conversion.
653 * Convert value from OpenTTD's internal unit into the displayed value.
654 * @param input The input to convert.
655 * @param round Whether to round the value or not.
656 * @return The converted value.
658 int64 ToDisplay(int64 input, bool round = true) const
660 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
664 * Convert the displayed value back into a value of OpenTTD's internal unit.
665 * @param input The input to convert.
666 * @param round Whether to round the value up or not.
667 * @param divider Divide the return value by this.
668 * @return The converted value.
670 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
672 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
676 /** Information about a specific unit system. */
677 struct Units {
678 UnitConversion c; ///< Conversion
679 StringID s; ///< String for the unit
682 /** Information about a specific unit system with a long variant. */
683 struct UnitsLong {
684 UnitConversion c; ///< Conversion
685 StringID s; ///< String for the short variant of the unit
686 StringID l; ///< String for the long variant of the unit
689 /** Unit conversions for velocity. */
690 static const Units _units_velocity[] = {
691 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL },
692 { { 103, 6}, STR_UNITS_VELOCITY_METRIC },
693 { {1831, 12}, STR_UNITS_VELOCITY_SI },
696 /** Unit conversions for velocity. */
697 static const Units _units_power[] = {
698 { { 1, 0}, STR_UNITS_POWER_IMPERIAL },
699 { {4153, 12}, STR_UNITS_POWER_METRIC },
700 { {6109, 13}, STR_UNITS_POWER_SI },
703 /** Unit conversions for weight. */
704 static const UnitsLong _units_weight[] = {
705 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
706 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC },
707 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI },
710 /** Unit conversions for volume. */
711 static const UnitsLong _units_volume[] = {
712 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
713 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC },
714 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI },
717 /** Unit conversions for force. */
718 static const Units _units_force[] = {
719 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL },
720 { {3263, 5}, STR_UNITS_FORCE_METRIC },
721 { { 1, 0}, STR_UNITS_FORCE_SI },
724 /** Unit conversions for height. */
725 static const Units _units_height[] = {
726 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL }, // "Wrong" conversion factor for more nicer GUI values
727 { { 1, 0}, STR_UNITS_HEIGHT_METRIC },
728 { { 1, 0}, STR_UNITS_HEIGHT_SI },
732 * Convert the given (internal) speed to the display speed.
733 * @param speed the speed to convert
734 * @return the converted speed.
736 uint ConvertSpeedToDisplaySpeed(uint speed)
738 /* For historical reasons we don't want to mess with the
739 * conversion for speed. So, don't round it and keep the
740 * original conversion factors instead of the real ones. */
741 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed, false);
745 * Convert the given display speed to the (internal) speed.
746 * @param speed the speed to convert
747 * @return the converted speed.
749 uint ConvertDisplaySpeedToSpeed(uint speed)
751 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed);
755 * Convert the given km/h-ish speed to the display speed.
756 * @param speed the speed to convert
757 * @return the converted speed.
759 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
761 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed * 10, false) / 16;
765 * Convert the given display speed to the km/h-ish speed.
766 * @param speed the speed to convert
767 * @return the converted speed.
769 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
771 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed * 16, true, 10);
774 * Parse most format codes within a string and write the result to a buffer.
775 * @param buff The buffer to write the final string to.
776 * @param str The original string with format codes.
777 * @param args Pointer to extra arguments used by various string codes.
778 * @param case_index
779 * @param last Pointer to just past the end of the buff array.
780 * @param dry_run True when the argt array is not yet initialized.
782 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
784 uint orig_offset = args->offset;
786 /* When there is no array with types there is no need to do a dry run. */
787 if (args->HasTypeInformation() && !dry_run) {
788 if (UsingNewGRFTextStack()) {
789 /* Values from the NewGRF text stack are only copied to the normal
790 * argv array at the time they are encountered. That means that if
791 * another string command references a value later in the string it
792 * would fail. We solve that by running FormatString twice. The first
793 * pass makes sure the argv array is correctly filled and the second
794 * pass can reference later values without problems. */
795 struct TextRefStack *backup = CreateTextRefStackBackup();
796 FormatString(buff, str_arg, args, last, case_index, game_script, true);
797 RestoreTextRefStackBackup(backup);
798 } else {
799 FormatString(buff, str_arg, args, last, case_index, game_script, true);
801 /* We have to restore the original offset here to to read the correct values. */
802 args->offset = orig_offset;
804 WChar b = '\0';
805 uint next_substr_case_index = 0;
806 char *buf_start = buff;
807 std::stack<const char *> str_stack;
808 str_stack.push(str_arg);
810 for (;;) {
811 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
812 str_stack.pop();
814 if (str_stack.empty()) break;
815 const char *&str = str_stack.top();
817 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
818 /* We need to pass some stuff as it might be modified; oh boy. */
819 //todo: should argve be passed here too?
820 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), dry_run);
821 if (b == 0) continue;
824 switch (b) {
825 case SCC_ENCODED: {
826 uint64 sub_args_data[20];
827 WChar sub_args_type[20];
828 bool sub_args_need_free[20];
829 StringParameters sub_args(sub_args_data, 20, sub_args_type);
831 sub_args.ClearTypeInformation();
832 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
834 uint16 stringid;
835 const char *s = str;
836 char *p;
837 stringid = strtol(str, &p, 16);
838 if (*p != ':' && *p != '\0') {
839 while (*p != '\0') p++;
840 str = p;
841 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
842 break;
844 if (stringid >= TAB_SIZE) {
845 while (*p != '\0') p++;
846 str = p;
847 buff = strecat(buff, "(invalid StringID)", last);
848 break;
851 int i = 0;
852 while (*p != '\0' && i < 20) {
853 uint64 param;
854 s = ++p;
856 /* Find the next value */
857 bool instring = false;
858 bool escape = false;
859 for (;; p++) {
860 if (*p == '\\') {
861 escape = true;
862 continue;
864 if (*p == '"' && escape) {
865 escape = false;
866 continue;
868 escape = false;
870 if (*p == '"') {
871 instring = !instring;
872 continue;
874 if (instring) {
875 continue;
878 if (*p == ':') break;
879 if (*p == '\0') break;
882 if (*s != '"') {
883 /* Check if we want to look up another string */
884 WChar l;
885 size_t len = Utf8Decode(&l, s);
886 bool lookup = (l == SCC_ENCODED);
887 if (lookup) s += len;
889 param = (int32)strtoul(s, &p, 16);
891 if (lookup) {
892 if (param >= TAB_SIZE) {
893 while (*p != '\0') p++;
894 str = p;
895 buff = strecat(buff, "(invalid sub-StringID)", last);
896 break;
898 param = (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + param;
901 sub_args.SetParam(i++, param);
902 } else {
903 char *g = strdup(s);
904 g[p - s] = '\0';
906 sub_args_need_free[i] = true;
907 sub_args.SetParam(i++, (uint64)(size_t)g);
910 /* If we didn't error out, we can actually print the string. */
911 if (*str != '\0') {
912 str = p;
913 buff = GetStringWithArgs(buff, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, last, true);
916 for (int i = 0; i < 20; i++) {
917 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
919 break;
922 case SCC_NEWGRF_STRINL: {
923 StringID substr = Utf8Consume(&str);
924 str_stack.push(GetStringPtr(substr));
925 break;
928 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
929 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
930 str_stack.push(GetStringPtr(substr));
931 case_index = next_substr_case_index;
932 next_substr_case_index = 0;
933 break;
937 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
938 /* First read the meta data from the language file. */
939 uint offset = orig_offset + (byte)*str++;
940 int gender = 0;
941 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
942 /* Now we need to figure out what text to resolve, i.e.
943 * what do we need to draw? So get the actual raw string
944 * first using the control code to get said string. */
945 char input[4 + 1];
946 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
947 *p = '\0';
949 /* Now do the string formatting. */
950 char buf[256];
951 bool old_sgd = _scan_for_gender_data;
952 _scan_for_gender_data = true;
953 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
954 p = FormatString(buf, input, &tmp_params, lastof(buf));
955 _scan_for_gender_data = old_sgd;
956 *p = '\0';
958 /* And determine the string. */
959 const char *s = buf;
960 WChar c = Utf8Consume(&s);
961 /* Does this string have a gender, if so, set it */
962 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
964 str = ParseStringChoice(str, gender, &buff, last);
965 break;
968 /* This sets up the gender for the string.
969 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
970 case SCC_GENDER_INDEX: // {GENDER 0}
971 if (_scan_for_gender_data) {
972 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
973 *buff++ = *str++;
974 } else {
975 str++;
977 break;
979 case SCC_PLURAL_LIST: { // {P}
980 int plural_form = *str++; // contains the plural form for this string
981 uint offset = orig_offset + (byte)*str++;
982 int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
983 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
984 break;
987 case SCC_ARG_INDEX: { // Move argument pointer
988 args->offset = orig_offset + (byte)*str++;
989 break;
992 case SCC_SET_CASE: { // {SET_CASE}
993 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
994 * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
995 next_substr_case_index = (byte)*str++;
996 break;
999 case SCC_SWITCH_CASE: { // {Used to implement case switching}
1000 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
1001 * Each LEN is printed using 2 bytes in big endian order. */
1002 uint num = (byte)*str++;
1003 while (num) {
1004 if ((byte)str[0] == case_index) {
1005 /* Found the case, adjust str pointer and continue */
1006 str += 3;
1007 break;
1009 /* Otherwise skip to the next case */
1010 str += 3 + (str[1] << 8) + str[2];
1011 num--;
1013 break;
1016 case SCC_REVISION: // {REV}
1017 buff = strecpy(buff, _openttd_revision, last);
1018 break;
1020 case SCC_STRING_ID: // {STRINL}
1021 if (game_script) break;
1022 buff = GetStringWithArgs(buff, Utf8Consume(&str), args, last);
1023 break;
1025 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1026 if (game_script) break;
1027 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
1028 buff = FormatString(buff, str, args, last);
1029 break;
1032 case SCC_STRING: {// {STRING}
1033 StringID str = args->GetInt32(SCC_STRING);
1034 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
1035 /* WARNING. It's prohibited for the included string to consume any arguments.
1036 * For included strings that consume argument, you should use STRING1, STRING2 etc.
1037 * To debug stuff you can set argv to NULL and it will tell you */
1038 StringParameters tmp_params(args->GetDataPointer(), args->num_param - args->offset, NULL);
1039 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
1040 next_substr_case_index = 0;
1041 break;
1044 case SCC_STRING1:
1045 case SCC_STRING2:
1046 case SCC_STRING3:
1047 case SCC_STRING4:
1048 case SCC_STRING5:
1049 case SCC_STRING6:
1050 case SCC_STRING7: { // {STRING1..7}
1051 /* Strings that consume arguments */
1052 StringID str = args->GetInt32(b);
1053 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
1054 uint size = b - SCC_STRING1 + 1;
1055 if (game_script && size > args->num_param - args->offset) {
1056 buff = strecat(buff, "(too many parameters)", last);
1057 } else {
1058 StringParameters sub_args(*args, size);
1059 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
1061 next_substr_case_index = 0;
1062 break;
1065 case SCC_COMMA: // {COMMA}
1066 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
1067 break;
1069 case SCC_DECIMAL: {// {DECIMAL}
1070 int64 number = args->GetInt64(SCC_DECIMAL);
1071 int digits = args->GetInt32(SCC_DECIMAL);
1072 buff = FormatCommaNumber(buff, number, last, digits);
1073 break;
1076 case SCC_NUM: // {NUM}
1077 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
1078 break;
1080 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1081 int64 num = args->GetInt64();
1082 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
1083 break;
1086 case SCC_HEX: // {HEX}
1087 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
1088 break;
1090 case SCC_BYTES: // {BYTES}
1091 buff = FormatBytes(buff, args->GetInt64(), last);
1092 break;
1094 case SCC_CARGO_TINY: { // {CARGO_TINY}
1095 /* Tiny description of cargotypes. Layout:
1096 * param 1: cargo type
1097 * param 2: cargo count */
1098 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
1099 if (cargo >= CargoSpec::GetArraySize()) break;
1101 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1102 int64 amount = 0;
1103 switch (cargo_str) {
1104 case STR_TONS:
1105 amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64());
1106 break;
1108 case STR_LITERS:
1109 amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64());
1110 break;
1112 default: {
1113 amount = args->GetInt64();
1114 break;
1118 buff = FormatCommaNumber(buff, amount, last);
1119 break;
1122 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1123 /* Short description of cargotypes. Layout:
1124 * param 1: cargo type
1125 * param 2: cargo count */
1126 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
1127 if (cargo >= CargoSpec::GetArraySize()) break;
1129 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1130 switch (cargo_str) {
1131 case STR_TONS: {
1132 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1133 int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1134 StringParameters tmp_params(args_array);
1135 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1136 break;
1139 case STR_LITERS: {
1140 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1141 int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1142 StringParameters tmp_params(args_array);
1143 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1144 break;
1147 default: {
1148 StringParameters tmp_params(*args, 1);
1149 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
1150 break;
1153 break;
1156 case SCC_CARGO_LONG: { // {CARGO_LONG}
1157 /* First parameter is cargo type, second parameter is cargo count */
1158 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
1159 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
1161 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1162 StringParameters tmp_args(*args, 1);
1163 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
1164 break;
1167 case SCC_CARGO_LIST: { // {CARGO_LIST}
1168 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
1169 bool first = true;
1171 const CargoSpec *cs;
1172 FOR_ALL_SORTED_CARGOSPECS(cs) {
1173 if (!HasBit(cmask, cs->Index())) continue;
1175 if (buff >= last - 2) break; // ',' and ' '
1177 if (first) {
1178 first = false;
1179 } else {
1180 /* Add a comma if this is not the first item */
1181 *buff++ = ',';
1182 *buff++ = ' ';
1185 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
1188 /* If first is still true then no cargo is accepted */
1189 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
1191 *buff = '\0';
1192 next_substr_case_index = 0;
1194 /* Make sure we detect any buffer overflow */
1195 assert(buff < last);
1196 break;
1199 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1200 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
1201 break;
1203 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1204 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
1205 break;
1207 case SCC_DATE_TINY: // {DATE_TINY}
1208 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
1209 break;
1211 case SCC_DATE_SHORT: // {DATE_SHORT}
1212 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
1213 next_substr_case_index = 0;
1214 break;
1216 case SCC_DATE_LONG: // {DATE_LONG}
1217 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
1218 next_substr_case_index = 0;
1219 break;
1221 case SCC_DATE_ISO: // {DATE_ISO}
1222 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
1223 break;
1225 case SCC_FORCE: { // {FORCE}
1226 assert(_settings_game.locale.units_force < lengthof(_units_force));
1227 int64 args_array[1] = {_units_force[_settings_game.locale.units_force].c.ToDisplay(args->GetInt64())};
1228 StringParameters tmp_params(args_array);
1229 buff = FormatString(buff, GetStringPtr(_units_force[_settings_game.locale.units_force].s), &tmp_params, last);
1230 break;
1233 case SCC_HEIGHT: { // {HEIGHT}
1234 assert(_settings_game.locale.units_height < lengthof(_units_height));
1235 int64 args_array[] = {_units_height[_settings_game.locale.units_height].c.ToDisplay(args->GetInt64())};
1236 StringParameters tmp_params(args_array);
1237 buff = FormatString(buff, GetStringPtr(_units_height[_settings_game.locale.units_height].s), &tmp_params, last);
1238 break;
1241 case SCC_POWER: { // {POWER}
1242 assert(_settings_game.locale.units_power < lengthof(_units_power));
1243 int64 args_array[1] = {_units_power[_settings_game.locale.units_power].c.ToDisplay(args->GetInt64())};
1244 StringParameters tmp_params(args_array);
1245 buff = FormatString(buff, GetStringPtr(_units_power[_settings_game.locale.units_power].s), &tmp_params, last);
1246 break;
1249 case SCC_VELOCITY: { // {VELOCITY}
1250 assert(_settings_game.locale.units_velocity < lengthof(_units_velocity));
1251 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
1252 StringParameters tmp_params(args_array);
1253 buff = FormatString(buff, GetStringPtr(_units_velocity[_settings_game.locale.units_velocity].s), &tmp_params, last);
1254 break;
1257 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1258 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1259 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1260 StringParameters tmp_params(args_array);
1261 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].s), &tmp_params, last);
1262 break;
1265 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1266 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1267 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
1268 StringParameters tmp_params(args_array);
1269 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1270 break;
1273 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1274 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1275 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1276 StringParameters tmp_params(args_array);
1277 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].s), &tmp_params, last);
1278 break;
1281 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1282 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1283 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
1284 StringParameters tmp_params(args_array);
1285 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1286 break;
1289 case SCC_COMPANY_NAME: { // {COMPANY}
1290 const Company *c = Company::GetIfValid(args->GetInt32());
1291 if (c == NULL) break;
1293 if (c->name != NULL) {
1294 int64 args_array[] = {(uint64)(size_t)c->name};
1295 StringParameters tmp_params(args_array);
1296 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1297 } else {
1298 int64 args_array[] = {c->name_2};
1299 StringParameters tmp_params(args_array);
1300 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
1302 break;
1305 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1306 CompanyID company = (CompanyID)args->GetInt32();
1308 /* Nothing is added for AI or inactive companies */
1309 if (Company::IsValidHumanID(company)) {
1310 int64 args_array[] = {company + 1};
1311 StringParameters tmp_params(args_array);
1312 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
1314 break;
1317 case SCC_DEPOT_NAME: { // {DEPOT}
1318 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
1319 if (vt == VEH_AIRCRAFT) {
1320 uint64 args_array[] = {args->GetInt32()};
1321 WChar types_array[] = {SCC_STATION_NAME};
1322 StringParameters tmp_params(args_array, 1, types_array);
1323 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
1324 break;
1327 const Depot *d = Depot::Get(args->GetInt32());
1328 if (d->name != NULL) {
1329 int64 args_array[] = {(uint64)(size_t)d->name};
1330 StringParameters tmp_params(args_array);
1331 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1332 } else {
1333 int64 args_array[] = {d->town->index, d->town_cn + 1};
1334 StringParameters tmp_params(args_array);
1335 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
1337 break;
1340 case SCC_ENGINE_NAME: { // {ENGINE}
1341 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
1342 if (e == NULL) break;
1344 if (e->name != NULL && e->IsEnabled()) {
1345 int64 args_array[] = {(uint64)(size_t)e->name};
1346 StringParameters tmp_params(args_array);
1347 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1348 } else {
1349 StringParameters tmp_params(NULL, 0, NULL);
1350 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
1352 break;
1355 case SCC_GROUP_NAME: { // {GROUP}
1356 const Group *g = Group::GetIfValid(args->GetInt32());
1357 if (g == NULL) break;
1359 if (g->name != NULL) {
1360 int64 args_array[] = {(uint64)(size_t)g->name};
1361 StringParameters tmp_params(args_array);
1362 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1363 } else {
1364 int64 args_array[] = {g->index};
1365 StringParameters tmp_params(args_array);
1367 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
1369 break;
1372 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1373 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
1374 if (i == NULL) break;
1376 if (_scan_for_gender_data) {
1377 /* Gender is defined by the industry type.
1378 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1379 StringParameters tmp_params(NULL, 0, NULL);
1380 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
1381 } else {
1382 /* First print the town name and the industry type name. */
1383 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
1384 StringParameters tmp_params(args_array);
1386 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
1388 next_substr_case_index = 0;
1389 break;
1392 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1393 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
1394 if (c == NULL) break;
1396 if (c->president_name != NULL) {
1397 int64 args_array[] = {(uint64)(size_t)c->president_name};
1398 StringParameters tmp_params(args_array);
1399 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1400 } else {
1401 int64 args_array[] = {c->president_name_2};
1402 StringParameters tmp_params(args_array);
1403 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
1405 break;
1408 case SCC_STATION_NAME: { // {STATION}
1409 StationID sid = args->GetInt32(SCC_STATION_NAME);
1410 const Station *st = Station::GetIfValid(sid);
1412 if (st == NULL) {
1413 /* The station doesn't exist anymore. The only place where we might
1414 * be "drawing" an invalid station is in the case of cargo that is
1415 * in transit. */
1416 StringParameters tmp_params(NULL, 0, NULL);
1417 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
1418 break;
1421 if (st->name != NULL) {
1422 int64 args_array[] = {(uint64)(size_t)st->name};
1423 StringParameters tmp_params(args_array);
1424 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1425 } else {
1426 StringID str = st->string_id;
1427 if (st->indtype != IT_INVALID) {
1428 /* Special case where the industry provides the name for the station */
1429 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1431 /* Industry GRFs can change which might remove the station name and
1432 * thus cause very strange things. Here we check for that before we
1433 * actually set the station name. */
1434 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1435 str = indsp->station_name;
1439 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
1440 StringParameters tmp_params(args_array);
1441 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1443 break;
1446 case SCC_TOWN_NAME: { // {TOWN}
1447 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
1448 if (t == NULL) break;
1450 if (t->name != NULL) {
1451 int64 args_array[] = {(uint64)(size_t)t->name};
1452 StringParameters tmp_params(args_array);
1453 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1454 } else {
1455 buff = GetTownName(buff, t, last);
1457 break;
1460 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1461 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
1462 if (wp == NULL) break;
1464 if (wp->name != NULL) {
1465 int64 args_array[] = {(uint64)(size_t)wp->name};
1466 StringParameters tmp_params(args_array);
1467 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1468 } else {
1469 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
1470 StringParameters tmp_params(args_array);
1471 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1472 if (wp->town_cn != 0) str++;
1473 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1475 break;
1478 case SCC_VEHICLE_NAME: { // {VEHICLE}
1479 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
1480 if (v == NULL) break;
1482 if (v->name != NULL) {
1483 int64 args_array[] = {(uint64)(size_t)v->name};
1484 StringParameters tmp_params(args_array);
1485 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1486 } else {
1487 int64 args_array[] = {v->unitnumber};
1488 StringParameters tmp_params(args_array);
1490 StringID str;
1491 switch (v->type) {
1492 default: str = STR_INVALID_VEHICLE; break;
1493 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
1494 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
1495 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
1496 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
1499 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1501 break;
1504 case SCC_SIGN_NAME: { // {SIGN}
1505 const Sign *si = Sign::GetIfValid(args->GetInt32());
1506 if (si == NULL) break;
1508 if (si->name != NULL) {
1509 int64 args_array[] = {(uint64)(size_t)si->name};
1510 StringParameters tmp_params(args_array);
1511 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1512 } else {
1513 StringParameters tmp_params(NULL, 0, NULL);
1514 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
1516 break;
1519 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1520 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
1521 break;
1524 default:
1525 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
1526 break;
1529 *buff = '\0';
1530 return buff;
1534 static char *StationGetSpecialString(char *buff, int x, const char *last)
1536 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
1537 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
1538 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
1539 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
1540 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
1541 *buff = '\0';
1542 return buff;
1545 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
1547 return GenerateTownNameString(buff, last, ind, seed);
1550 static const char * const _silly_company_names[] = {
1551 "Bloggs Brothers",
1552 "Tiny Transport Ltd.",
1553 "Express Travel",
1554 "Comfy-Coach & Co.",
1555 "Crush & Bump Ltd.",
1556 "Broken & Late Ltd.",
1557 "Sam Speedy & Son",
1558 "Supersonic Travel",
1559 "Mike's Motors",
1560 "Lightning International",
1561 "Pannik & Loozit Ltd.",
1562 "Inter-City Transport",
1563 "Getout & Pushit Ltd."
1566 static const char * const _surname_list[] = {
1567 "Adams",
1568 "Allan",
1569 "Baker",
1570 "Bigwig",
1571 "Black",
1572 "Bloggs",
1573 "Brown",
1574 "Campbell",
1575 "Gordon",
1576 "Hamilton",
1577 "Hawthorn",
1578 "Higgins",
1579 "Green",
1580 "Gribble",
1581 "Jones",
1582 "McAlpine",
1583 "MacDonald",
1584 "McIntosh",
1585 "Muir",
1586 "Murphy",
1587 "Nelson",
1588 "O'Donnell",
1589 "Parker",
1590 "Phillips",
1591 "Pilkington",
1592 "Quigley",
1593 "Sharkey",
1594 "Thomson",
1595 "Watkins"
1598 static const char * const _silly_surname_list[] = {
1599 "Grumpy",
1600 "Dozy",
1601 "Speedy",
1602 "Nosey",
1603 "Dribble",
1604 "Mushroom",
1605 "Cabbage",
1606 "Sniffle",
1607 "Fishy",
1608 "Swindle",
1609 "Sneaky",
1610 "Nutkins"
1613 static const char _initial_name_letters[] = {
1614 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1615 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1618 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
1620 const char * const *base;
1621 uint num;
1623 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1624 base = _silly_surname_list;
1625 num = lengthof(_silly_surname_list);
1626 } else {
1627 base = _surname_list;
1628 num = lengthof(_surname_list);
1631 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
1632 buff = strecpy(buff, " & Co.", last);
1634 return buff;
1637 static char *GenPresidentName(char *buff, uint32 x, const char *last)
1639 char initial[] = "?. ";
1640 const char * const *base;
1641 uint num;
1642 uint i;
1644 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
1645 buff = strecpy(buff, initial, last);
1647 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
1648 if (i < sizeof(_initial_name_letters)) {
1649 initial[0] = _initial_name_letters[i];
1650 buff = strecpy(buff, initial, last);
1653 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1654 base = _silly_surname_list;
1655 num = lengthof(_silly_surname_list);
1656 } else {
1657 base = _surname_list;
1658 num = lengthof(_surname_list);
1661 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
1663 return buff;
1666 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
1668 switch (ind) {
1669 case 1: // not used
1670 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
1672 case 2: // used for Foobar & Co company names
1673 return GenAndCoName(buff, args->GetInt32(), last);
1675 case 3: // President name
1676 return GenPresidentName(buff, args->GetInt32(), last);
1679 /* town name? */
1680 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1681 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
1682 return strecpy(buff, " Transport", last);
1685 /* language name? */
1686 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
1687 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
1688 return strecpy(buff,
1689 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
1692 /* resolution size? */
1693 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
1694 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
1695 buff += seprintf(
1696 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
1698 return buff;
1701 /* screenshot format name? */
1702 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
1703 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
1704 return strecpy(buff, GetScreenshotFormatDesc(i), last);
1707 NOT_REACHED();
1710 #ifdef ENABLE_NETWORK
1711 extern void SortNetworkLanguages();
1712 #else /* ENABLE_NETWORK */
1713 static inline void SortNetworkLanguages() {}
1714 #endif /* ENABLE_NETWORK */
1717 * Check whether the header is a valid header for OpenTTD.
1718 * @return true iff the header is deemed valid.
1720 bool LanguagePackHeader::IsValid() const
1722 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1723 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1724 this->plural_form < LANGUAGE_MAX_PLURAL &&
1725 this->text_dir <= 1 &&
1726 this->newgrflangid < MAX_LANG &&
1727 this->num_genders < MAX_NUM_GENDERS &&
1728 this->num_cases < MAX_NUM_CASES &&
1729 StrValid(this->name, lastof(this->name)) &&
1730 StrValid(this->own_name, lastof(this->own_name)) &&
1731 StrValid(this->isocode, lastof(this->isocode)) &&
1732 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
1733 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
1734 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
1738 * Read a particular language.
1739 * @param lang The metadata about the language.
1740 * @return Whether the loading went okay or not.
1742 bool ReadLanguagePack(const LanguageMetadata *lang)
1744 /* Current language pack */
1745 size_t len;
1746 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
1747 if (lang_pack == NULL) return false;
1749 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1750 const char *end = (char *)lang_pack + len + 1;
1752 /* We need at least one byte of lang_pack->data */
1753 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1754 free(lang_pack);
1755 return false;
1758 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1759 for (uint i = 0; i < TAB_COUNT; i++) {
1760 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
1762 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
1764 uint count = 0;
1765 for (uint i = 0; i < TAB_COUNT; i++) {
1766 uint16 num = lang_pack->offsets[i];
1767 if (num > TAB_SIZE) {
1768 free(lang_pack);
1769 return false;
1772 _langtab_start[i] = count;
1773 _langtab_num[i] = num;
1774 count += num;
1777 /* Allocate offsets */
1778 char **langpack_offs = MallocT<char *>(count);
1780 /* Fill offsets */
1781 char *s = lang_pack->data;
1782 len = (byte)*s++;
1783 for (uint i = 0; i < count; i++) {
1784 if (s + len >= end) {
1785 free(lang_pack);
1786 free(langpack_offs);
1787 return false;
1789 if (len >= 0xC0) {
1790 len = ((len & 0x3F) << 8) + (byte)*s++;
1791 if (s + len >= end) {
1792 free(lang_pack);
1793 free(langpack_offs);
1794 return false;
1797 langpack_offs[i] = s;
1798 s += len;
1799 len = (byte)*s;
1800 *s++ = '\0'; // zero terminate the string
1803 free(_langpack);
1804 _langpack = lang_pack;
1806 free(_langpack_offs);
1807 _langpack_offs = langpack_offs;
1809 _current_language = lang;
1810 _current_text_dir = (TextDirection)_current_language->text_dir;
1811 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
1812 strecpy(_config_language_file, c_file, lastof(_config_language_file));
1813 SetCurrentGrfLangID(_current_language->newgrflangid);
1815 #ifdef WITH_ICU
1816 /* Delete previous collator. */
1817 if (_current_collator != NULL) {
1818 delete _current_collator;
1819 _current_collator = NULL;
1822 /* Create a collator instance for our current locale. */
1823 UErrorCode status = U_ZERO_ERROR;
1824 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
1825 /* Sort number substrings by their numerical value. */
1826 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
1827 /* Avoid using the collator if it is not correctly set. */
1828 if (U_FAILURE(status)) {
1829 delete _current_collator;
1830 _current_collator = NULL;
1832 #endif /* WITH_ICU */
1834 /* Some lists need to be sorted again after a language change. */
1835 ReconsiderGameScriptLanguage();
1836 InitializeSortedCargoSpecs();
1837 SortIndustryTypes();
1838 BuildIndustriesLegend();
1839 SortNetworkLanguages();
1840 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
1841 InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
1842 InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
1843 InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
1844 InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
1845 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
1846 InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
1848 return true;
1851 /* Win32 implementation in win32.cpp.
1852 * OS X implementation in os/macosx/macos.mm. */
1853 #if !(defined(WIN32) || defined(__APPLE__))
1855 * Determine the current charset based on the environment
1856 * First check some default values, after this one we passed ourselves
1857 * and if none exist return the value for $LANG
1858 * @param param environment variable to check conditionally if default ones are not
1859 * set. Pass NULL if you don't want additional checks.
1860 * @return return string containing current charset, or NULL if not-determinable
1862 const char *GetCurrentLocale(const char *param)
1864 const char *env;
1866 env = getenv("LANGUAGE");
1867 if (env != NULL) return env;
1869 env = getenv("LC_ALL");
1870 if (env != NULL) return env;
1872 if (param != NULL) {
1873 env = getenv(param);
1874 if (env != NULL) return env;
1877 return getenv("LANG");
1879 #else
1880 const char *GetCurrentLocale(const char *param);
1881 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
1883 int CDECL StringIDSorter(const StringID *a, const StringID *b)
1885 char stra[512];
1886 char strb[512];
1887 GetString(stra, *a, lastof(stra));
1888 GetString(strb, *b, lastof(strb));
1890 return strcmp(stra, strb);
1894 * Get the language with the given NewGRF language ID.
1895 * @param newgrflangid NewGRF languages ID to check.
1896 * @return The language's metadata, or NULL if it is not known.
1898 const LanguageMetadata *GetLanguage(byte newgrflangid)
1900 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
1901 if (newgrflangid == lang->newgrflangid) return lang;
1904 return NULL;
1908 * Reads the language file header and checks compatibility.
1909 * @param file the file to read
1910 * @param hdr the place to write the header information to
1911 * @return true if and only if the language file is of a compatible version
1913 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
1915 FILE *f = fopen(file, "rb");
1916 if (f == NULL) return false;
1918 size_t read = fread(hdr, sizeof(*hdr), 1, f);
1919 fclose(f);
1921 bool ret = read == 1 && hdr->IsValid();
1923 /* Convert endianness for the windows language ID */
1924 if (ret) {
1925 hdr->missing = FROM_LE16(hdr->missing);
1926 hdr->winlangid = FROM_LE16(hdr->winlangid);
1928 return ret;
1932 * Gets a list of languages from the given directory.
1933 * @param path the base directory to search in
1935 static void GetLanguageList(const char *path)
1937 DIR *dir = ttd_opendir(path);
1938 if (dir != NULL) {
1939 struct dirent *dirent;
1940 while ((dirent = readdir(dir)) != NULL) {
1941 const char *d_name = FS2OTTD(dirent->d_name);
1942 const char *extension = strrchr(d_name, '.');
1944 /* Not a language file */
1945 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
1947 LanguageMetadata lmd;
1948 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
1950 /* Check whether the file is of the correct version */
1951 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
1952 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
1953 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
1954 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
1955 } else {
1956 *_languages.Append() = lmd;
1959 closedir(dir);
1964 * Make a list of the available language packs. Put the data in
1965 * #_languages list.
1967 void InitializeLanguagePacks()
1969 Searchpath sp;
1971 FOR_ALL_SEARCHPATHS(sp) {
1972 char path[MAX_PATH];
1973 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
1974 GetLanguageList(path);
1976 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
1978 /* Acquire the locale of the current system */
1979 const char *lang = GetCurrentLocale("LC_MESSAGES");
1980 if (lang == NULL) lang = "en_GB";
1982 const LanguageMetadata *chosen_language = NULL; ///< Matching the language in the configuration file or the current locale
1983 const LanguageMetadata *language_fallback = NULL; ///< Using pt_PT for pt_BR locale when pt_BR is not available
1984 const LanguageMetadata *en_GB_fallback = _languages.Begin(); ///< Fallback when no locale-matching language has been found
1986 /* Find a proper language. */
1987 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
1988 /* We are trying to find a default language. The priority is by
1989 * configuration file, local environment and last, if nothing found,
1990 * English. */
1991 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
1992 if (strcmp(lang_file, _config_language_file) == 0) {
1993 chosen_language = lng;
1994 break;
1997 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
1998 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
1999 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
2002 /* We haven't found the language in the config nor the one in the locale.
2003 * Now we set it to one of the fallback languages */
2004 if (chosen_language == NULL) {
2005 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
2008 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
2012 * Get the ISO language code of the currently loaded language.
2013 * @return the ISO code.
2015 const char *GetCurrentLanguageIsoCode()
2017 return _langpack->isocode;
2021 * Check whether there are glyphs missing in the current language.
2022 * @param Pointer to an address for storing the text pointer.
2023 * @return If glyphs are missing, return \c true, else return \c false.
2024 * @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.
2026 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
2028 InitFreeType(this->Monospace());
2029 const Sprite *question_mark[FS_END];
2031 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
2032 question_mark[size] = GetGlyph(size, '?');
2035 this->Reset();
2036 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
2037 FontSize size = this->DefaultSize();
2038 if (str != NULL) *str = text;
2039 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
2040 if (c == SCC_TINYFONT) {
2041 size = FS_SMALL;
2042 } else if (c == SCC_BIGFONT) {
2043 size = FS_LARGE;
2044 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
2045 /* The character is printable, but not in the normal font. This is the case we were testing for. */
2046 return true;
2050 return false;
2053 /** Helper for searching through the language pack. */
2054 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
2055 uint i; ///< Iterator for the primary language tables.
2056 uint j; ///< Iterator for the secondary language tables.
2058 /* virtual */ void Reset()
2060 this->i = 0;
2061 this->j = 0;
2064 /* virtual */ FontSize DefaultSize()
2066 return FS_NORMAL;
2069 /* virtual */ const char *NextString()
2071 if (this->i >= TAB_COUNT) return NULL;
2073 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
2075 this->j++;
2076 while (this->i < TAB_COUNT && this->j >= _langtab_num[this->i]) {
2077 this->i++;
2078 this->j = 0;
2081 return ret;
2084 /* virtual */ bool Monospace()
2086 return false;
2089 /* virtual */ void SetFontNames(FreeTypeSettings *settings, const char *font_name)
2091 #ifdef WITH_FREETYPE
2092 strecpy(settings->small.font, font_name, lastof(settings->small.font));
2093 strecpy(settings->medium.font, font_name, lastof(settings->medium.font));
2094 strecpy(settings->large.font, font_name, lastof(settings->large.font));
2095 #endif /* WITH_FREETYPE */
2100 * Check whether the currently loaded language pack
2101 * uses characters that the currently loaded font
2102 * does not support. If this is the case an error
2103 * message will be shown in English. The error
2104 * message will not be localized because that would
2105 * mean it might use characters that are not in the
2106 * font, which is the whole reason this check has
2107 * been added.
2108 * @param base_font Whether to look at the base font as well.
2109 * @param searcher The methods to use to search for strings to check.
2110 * If NULL the loaded language pack searcher is used.
2112 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2114 static LanguagePackGlyphSearcher pack_searcher;
2115 if (searcher == NULL) searcher = &pack_searcher;
2116 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
2117 #ifdef WITH_FREETYPE
2118 if (bad_font) {
2119 /* We found an unprintable character... lets try whether we can find
2120 * a fallback font that can print the characters in the current language. */
2121 FreeTypeSettings backup;
2122 memcpy(&backup, &_freetype, sizeof(backup));
2124 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
2126 memcpy(&_freetype, &backup, sizeof(backup));
2128 if (bad_font && base_font) {
2129 /* Our fallback font does miss characters too, so keep the
2130 * user chosen font as that is more likely to be any good than
2131 * the wild guess we made */
2132 InitFreeType(searcher->Monospace());
2135 #endif
2137 if (bad_font) {
2138 /* All attempts have failed. Display an error. As we do not want the string to be translated by
2139 * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
2140 * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
2141 * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
2142 * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
2143 static char *err_str = strdup("XXXThe current font is missing some of the characters used in the texts for this language. Read the readme to see how to solve this.");
2144 Utf8Encode(err_str, SCC_YELLOW);
2145 SetDParamStr(0, err_str);
2146 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2148 /* Reset the font width */
2149 LoadStringWidthTable(searcher->Monospace());
2150 return;
2153 /* Update the font with cache */
2154 LoadStringWidthTable(searcher->Monospace());
2156 #if !defined(WITH_ICU)
2158 * For right-to-left languages we need the ICU library. If
2159 * we do not have support for that library we warn the user
2160 * about it with a message. As we do not want the string to
2161 * be translated by the translators, we 'force' it into the
2162 * binary and 'load' it via a BindCString. To do this
2163 * properly we have to set the colour of the string,
2164 * otherwise we end up with a lot of artifacts. The colour
2165 * 'character' might change in the future, so for safety
2166 * we just Utf8 Encode it into the string, which takes
2167 * exactly three characters, so it replaces the "XXX" with
2168 * the colour marker.
2170 if (_current_text_dir != TD_LTR) {
2171 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
2172 Utf8Encode(err_str, SCC_YELLOW);
2173 SetDParamStr(0, err_str);
2174 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
2176 #endif