(svn r27950) -Merge: Documentation updates from 1.7 branch
[openttd.git] / src / strings.cpp
blob1c539d934325d9f34c788691f6b369d39fe0ccc0
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 "waypoint_base.h"
17 #include "depot_base.h"
18 #include "industry.h"
19 #include "newgrf_text.h"
20 #include "fileio_func.h"
21 #include "signs_base.h"
22 #include "fontdetection.h"
23 #include "error.h"
24 #include "strings_func.h"
25 #include "rev.h"
26 #include "core/endian_func.hpp"
27 #include "date_func.h"
28 #include "vehicle_base.h"
29 #include "engine_base.h"
30 #include "language.h"
31 #include "townname_func.h"
32 #include "string_func.h"
33 #include "company_base.h"
34 #include "smallmap_gui.h"
35 #include "window_func.h"
36 #include "debug.h"
37 #include "game/game_text.hpp"
38 #ifdef ENABLE_NETWORK
39 # include "network/network_content_gui.h"
40 #endif /* ENABLE_NETWORK */
41 #include <stack>
43 #include "table/strings.h"
44 #include "table/control_codes.h"
46 #include "safeguards.h"
48 char _config_language_file[MAX_PATH]; ///< The file (name) stored in the configuration.
49 LanguageList _languages; ///< The actual list of language meta data.
50 const LanguageMetadata *_current_language = NULL; ///< The currently loaded language.
52 TextDirection _current_text_dir; ///< Text direction of the currently selected language.
54 #ifdef WITH_ICU_SORT
55 Collator *_current_collator = NULL; ///< Collator for the language currently in use.
56 #endif /* WITH_ICU_SORT */
58 static uint64 _global_string_params_data[20]; ///< Global array of string parameters. To access, use #SetDParam.
59 static WChar _global_string_params_type[20]; ///< Type of parameters stored in #_decode_parameters
60 StringParameters _global_string_params(_global_string_params_data, 20, _global_string_params_type);
62 /** Reset the type array. */
63 void StringParameters::ClearTypeInformation()
65 assert(this->type != NULL);
66 MemSetT(this->type, 0, this->num_param);
70 /**
71 * Read an int64 from the argument array. The offset is increased
72 * so the next time GetInt64 is called the next value is read.
74 int64 StringParameters::GetInt64(WChar type)
76 if (this->offset >= this->num_param) {
77 DEBUG(misc, 0, "Trying to read invalid string parameter");
78 return 0;
80 if (this->type != NULL) {
81 assert(this->type[this->offset] == 0 || this->type[this->offset] == type);
82 this->type[this->offset] = type;
84 return this->data[this->offset++];
87 /**
88 * Shift all data in the data array by the given amount to make
89 * room for some extra parameters.
91 void StringParameters::ShiftParameters(uint amount)
93 assert(amount <= this->num_param);
94 MemMoveT(this->data + amount, this->data, this->num_param - amount);
97 /**
98 * Set DParam n to some number that is suitable for string size computations.
99 * @param n Index of the string parameter.
100 * @param max_value The biggest value which shall be displayed.
101 * For the result only the number of digits of \a max_value matter.
102 * @param min_count Minimum number of digits independent of \a max.
103 * @param size Font of the number
105 void SetDParamMaxValue(uint n, uint64 max_value, uint min_count, FontSize size)
107 uint num_digits = 1;
108 while (max_value >= 10) {
109 num_digits++;
110 max_value /= 10;
112 SetDParamMaxDigits(n, max(min_count, num_digits), size);
116 * Set DParam n to some number that is suitable for string size computations.
117 * @param n Index of the string parameter.
118 * @param count Number of digits which shall be displayable.
119 * @param size Font of the number
121 void SetDParamMaxDigits(uint n, uint count, FontSize size)
123 uint front, next;
124 GetBroadestDigit(&front, &next, size);
125 uint64 val = count > 1 ? front : next;
126 for (; count > 1; count--) {
127 val = 10 * val + next;
129 SetDParam(n, val);
133 * Copy \a num string parameters from array \a src into the global string parameter array.
134 * @param offs Index in the global array to copy the first string parameter to.
135 * @param src Source array of string parameters.
136 * @param num Number of string parameters to copy.
138 void CopyInDParam(int offs, const uint64 *src, int num)
140 MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
144 * Copy \a num string parameters from the global string parameter array to the \a dst array.
145 * @param dst Destination array of string parameters.
146 * @param offs Index in the global array to copy the first string parameter from.
147 * @param num Number of string parameters to copy.
149 void CopyOutDParam(uint64 *dst, int offs, int num)
151 MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
155 * Copy \a num string parameters from the global string parameter array to the \a dst array.
156 * Furthermore clone raw string parameters into \a strings and amend the data in \a dst.
157 * @param dst Destination array of string parameters.
158 * @param strings Destination array for clone of the raw strings. Must be of same length as dst. Deallocation left to the caller.
159 * @param string The string used to determine where raw strings are and where there are no raw strings.
160 * @param num Number of string parameters to copy.
162 void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
164 char buf[DRAW_STRING_BUFFER];
165 GetString(buf, string, lastof(buf));
167 MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num);
168 for (int i = 0; i < num; i++) {
169 if (_global_string_params.HasTypeInformation() && _global_string_params.GetTypeAtOffset(i) == SCC_RAW_STRING_POINTER) {
170 strings[i] = stredup((const char *)(size_t)_global_string_params.GetParam(i));
171 dst[i] = (size_t)strings[i];
172 } else {
173 strings[i] = NULL;
178 static char *StationGetSpecialString(char *buff, int x, const char *last);
179 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
180 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
182 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);
184 struct LanguagePack : public LanguagePackHeader {
185 char data[]; // list of strings
188 static char **_langpack_offs;
189 static LanguagePack *_langpack;
190 static uint _langtab_num[TEXT_TAB_END]; ///< Offset into langpack offs
191 static uint _langtab_start[TEXT_TAB_END]; ///< Offset into langpack offs
192 static bool _scan_for_gender_data = false; ///< Are we scanning for the gender of the current string? (instead of formatting it)
195 const char *GetStringPtr(StringID string)
197 switch (GetStringTab(string)) {
198 case TEXT_TAB_GAMESCRIPT_START: return GetGameStringPtr(GetStringIndex(string));
199 /* 0xD0xx and 0xD4xx IDs have been converted earlier. */
200 case TEXT_TAB_OLD_NEWGRF: NOT_REACHED();
201 case TEXT_TAB_NEWGRF_START: return GetGRFStringPtr(GetStringIndex(string));
202 default: return _langpack_offs[_langtab_start[GetStringTab(string)] + GetStringIndex(string)];
207 * Get a parsed string with most special stringcodes replaced by the string parameters.
208 * @param buffr Pointer to a string buffer where the formatted string should be written to.
209 * @param string
210 * @param args Arguments for the string.
211 * @param last Pointer just past the end of \a buffr.
212 * @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case.
213 * @param game_script The string is coming directly from a game script.
214 * @return Pointer to the final zero byte of the formatted string.
216 char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
218 if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
220 uint index = GetStringIndex(string);
221 StringTab tab = GetStringTab(string);
223 switch (tab) {
224 case TEXT_TAB_TOWN:
225 if (index >= 0xC0 && !game_script) {
226 return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
228 break;
230 case TEXT_TAB_SPECIAL:
231 if (index >= 0xE4 && !game_script) {
232 return GetSpecialNameString(buffr, index - 0xE4, args, last);
234 break;
236 case TEXT_TAB_OLD_CUSTOM:
237 /* Old table for custom names. This is no longer used */
238 if (!game_script) {
239 error("Incorrect conversion of custom name string.");
241 break;
243 case TEXT_TAB_GAMESCRIPT_START:
244 return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
246 case TEXT_TAB_OLD_NEWGRF:
247 NOT_REACHED();
249 case TEXT_TAB_NEWGRF_START:
250 return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
252 default:
253 break;
256 if (index >= _langtab_num[tab]) {
257 if (game_script) {
258 return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
260 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
263 return FormatString(buffr, GetStringPtr(string), args, last, case_index);
266 char *GetString(char *buffr, StringID string, const char *last)
268 _global_string_params.ClearTypeInformation();
269 _global_string_params.offset = 0;
270 return GetStringWithArgs(buffr, string, &_global_string_params, last);
275 * This function is used to "bind" a C string to a OpenTTD dparam slot.
276 * @param n slot of the string
277 * @param str string to bind
279 void SetDParamStr(uint n, const char *str)
281 SetDParam(n, (uint64)(size_t)str);
285 * Shift the string parameters in the global string parameter array by \a amount positions, making room at the beginning.
286 * @param amount Number of positions to shift.
288 void InjectDParam(uint amount)
290 _global_string_params.ShiftParameters(amount);
294 * Format a number into a string.
295 * @param buff the buffer to write to
296 * @param number the number to write down
297 * @param last the last element in the buffer
298 * @param separator the thousands-separator to use
299 * @param zerofill minimum number of digits to print for the integer part. The number will be filled with zeros at the front if necessary.
300 * @param fractional_digits number of fractional digits to display after a decimal separator. The decimal separator is inserted
301 * in front of the \a fractional_digits last digit of \a number.
302 * @return till where we wrote
304 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
306 static const int max_digits = 20;
307 uint64 divisor = 10000000000000000000ULL;
308 zerofill += fractional_digits;
309 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
311 if (number < 0) {
312 buff += seprintf(buff, last, "-");
313 number = -number;
316 uint64 num = number;
317 uint64 tot = 0;
318 for (int i = 0; i < max_digits; i++) {
319 if (i == max_digits - fractional_digits) {
320 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
321 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
322 buff += seprintf(buff, last, "%s", decimal_separator);
325 uint64 quot = 0;
326 if (num >= divisor) {
327 quot = num / divisor;
328 num = num % divisor;
330 if ((tot |= quot) || i >= max_digits - zerofill) {
331 buff += seprintf(buff, last, "%i", (int)quot);
332 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
335 divisor /= 10;
338 *buff = '\0';
340 return buff;
343 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
345 const char *separator = _settings_game.locale.digit_group_separator;
346 if (separator == NULL) separator = _langpack->digit_group_separator;
347 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
350 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
352 return FormatNumber(buff, number, last, "");
355 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
357 return FormatNumber(buff, number, last, "", count);
360 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
362 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
366 * Format a given number as a number of bytes with the SI prefix.
367 * @param buff the buffer to write to
368 * @param number the number of bytes to write down
369 * @param last the last element in the buffer
370 * @return till where we wrote
372 static char *FormatBytes(char *buff, int64 number, const char *last)
374 assert(number >= 0);
376 /* 1 2^10 2^20 2^30 2^40 2^50 2^60 */
377 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
378 uint id = 1;
379 while (number >= 1024 * 1024) {
380 number /= 1024;
381 id++;
384 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
385 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
387 if (number < 1024) {
388 id = 0;
389 buff += seprintf(buff, last, "%i", (int)number);
390 } else if (number < 1024 * 10) {
391 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
392 } else if (number < 1024 * 100) {
393 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
394 } else {
395 assert(number < 1024 * 1024);
396 buff += seprintf(buff, last, "%i", (int)number / 1024);
399 assert(id < lengthof(iec_prefixes));
400 buff += seprintf(buff, last, NBSP "%sB", iec_prefixes[id]);
402 return buff;
405 static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
407 YearMonthDay ymd;
408 ConvertDateToYMD(date, &ymd);
410 int64 args[] = {ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
411 StringParameters tmp_params(args);
412 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
415 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
417 YearMonthDay ymd;
418 ConvertDateToYMD(date, &ymd);
420 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
421 StringParameters tmp_params(args);
422 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
425 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
427 YearMonthDay ymd;
428 ConvertDateToYMD(date, &ymd);
430 char day[3];
431 char month[3];
432 /* We want to zero-pad the days and months */
433 seprintf(day, lastof(day), "%02i", ymd.day);
434 seprintf(month, lastof(month), "%02i", ymd.month + 1);
436 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
437 StringParameters tmp_params(args);
438 return FormatString(buff, GetStringPtr(str), &tmp_params, last);
441 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
443 /* We are going to make number absolute for printing, so
444 * keep this piece of data as we need it later on */
445 bool negative = number < 0;
446 const char *multiplier = "";
448 number *= spec->rate;
450 /* convert from negative */
451 if (number < 0) {
452 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
453 buff += Utf8Encode(buff, SCC_RED);
454 buff = strecpy(buff, "-", last);
455 number = -number;
458 /* Add prefix part, following symbol_pos specification.
459 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
460 * The only remaining value is 1 (suffix), so everything that is not 1 */
461 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
463 /* for huge numbers, compact the number into k or M */
464 if (compact) {
465 /* Take care of the 'k' rounding. Having 1 000 000 k
466 * and 1 000 M is inconsistent, so always use 1 000 M. */
467 if (number >= 1000000000 - 500) {
468 number = (number + 500000) / 1000000;
469 multiplier = NBSP "M";
470 } else if (number >= 1000000) {
471 number = (number + 500) / 1000;
472 multiplier = NBSP "k";
476 const char *separator = _settings_game.locale.digit_group_separator_currency;
477 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
478 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
479 buff = FormatNumber(buff, number, last, separator);
480 buff = strecpy(buff, multiplier, last);
482 /* Add suffix part, following symbol_pos specification.
483 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
484 * The only remaining value is 1 (prefix), so everything that is not 0 */
485 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
487 if (negative) {
488 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
489 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
490 *buff = '\0';
493 return buff;
497 * Determine the "plural" index given a plural form and a number.
498 * @param count The number to get the plural index of.
499 * @param plural_form The plural form we want an index for.
500 * @return The plural index for the given form.
502 static int DeterminePluralForm(int64 count, int plural_form)
504 /* The absolute value determines plurality */
505 uint64 n = abs(count);
507 switch (plural_form) {
508 default:
509 NOT_REACHED();
511 /* Two forms: singular used for one only.
512 * Used in:
513 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
514 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
515 case 0:
516 return n != 1 ? 1 : 0;
518 /* Only one form.
519 * Used in:
520 * Hungarian, Japanese, Korean, Turkish */
521 case 1:
522 return 0;
524 /* Two forms: singular used for 0 and 1.
525 * Used in:
526 * French, Brazilian Portuguese */
527 case 2:
528 return n > 1 ? 1 : 0;
530 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
531 * Note: Cases are out of order for hysterical reasons. '0' is last.
532 * Used in:
533 * Latvian */
534 case 3:
535 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
537 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
538 * Used in:
539 * Gaelige (Irish) */
540 case 4:
541 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
543 /* 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.
544 * Used in:
545 * Lithuanian */
546 case 5:
547 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
549 /* 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.
550 * Used in:
551 * Croatian, Russian, Ukrainian */
552 case 6:
553 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
555 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
556 * Used in:
557 * Polish */
558 case 7:
559 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
561 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
562 * Used in:
563 * Slovenian */
564 case 8:
565 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
567 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
568 * Used in:
569 * Icelandic */
570 case 9:
571 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
573 /* Three forms: special cases for 1, and 2 to 4
574 * Used in:
575 * Czech, Slovak */
576 case 10:
577 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
579 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
580 * Korean doesn't have the concept of plural, but depending on how a
581 * number is pronounced it needs another version of a particle.
582 * As such the plural system is misused to give this distinction.
584 case 11:
585 switch (n % 10) {
586 case 0: // yeong
587 case 1: // il
588 case 3: // sam
589 case 6: // yuk
590 case 7: // chil
591 case 8: // pal
592 return 0;
594 case 2: // i
595 case 4: // sa
596 case 5: // o
597 case 9: // gu
598 return 1;
600 default:
601 NOT_REACHED();
604 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
605 * Used in:
606 * Maltese */
607 case 12:
608 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
609 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
610 * Used in:
611 * Scottish Gaelic */
612 case 13:
613 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
617 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
619 /* <NUM> {Length of each string} {each string} */
620 uint n = (byte)*b++;
621 uint pos, i, mypos = 0;
623 for (i = pos = 0; i != n; i++) {
624 uint len = (byte)*b++;
625 if (i == form) mypos = pos;
626 pos += len;
629 *dst += seprintf(*dst, last, "%s", b + mypos);
630 return b + pos;
633 /** Helper for unit conversion. */
634 struct UnitConversion {
635 int multiplier; ///< Amount to multiply upon conversion.
636 int shift; ///< Amount to shift upon conversion.
639 * Convert value from OpenTTD's internal unit into the displayed value.
640 * @param input The input to convert.
641 * @param round Whether to round the value or not.
642 * @return The converted value.
644 int64 ToDisplay(int64 input, bool round = true) const
646 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
650 * Convert the displayed value back into a value of OpenTTD's internal unit.
651 * @param input The input to convert.
652 * @param round Whether to round the value up or not.
653 * @param divider Divide the return value by this.
654 * @return The converted value.
656 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
658 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
662 /** Information about a specific unit system. */
663 struct Units {
664 UnitConversion c; ///< Conversion
665 StringID s; ///< String for the unit
668 /** Information about a specific unit system with a long variant. */
669 struct UnitsLong {
670 UnitConversion c; ///< Conversion
671 StringID s; ///< String for the short variant of the unit
672 StringID l; ///< String for the long variant of the unit
675 /** Unit conversions for velocity. */
676 static const Units _units_velocity[] = {
677 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL },
678 { { 103, 6}, STR_UNITS_VELOCITY_METRIC },
679 { {1831, 12}, STR_UNITS_VELOCITY_SI },
682 /** Unit conversions for velocity. */
683 static const Units _units_power[] = {
684 { { 1, 0}, STR_UNITS_POWER_IMPERIAL },
685 { {4153, 12}, STR_UNITS_POWER_METRIC },
686 { {6109, 13}, STR_UNITS_POWER_SI },
689 /** Unit conversions for weight. */
690 static const UnitsLong _units_weight[] = {
691 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
692 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC },
693 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI },
696 /** Unit conversions for volume. */
697 static const UnitsLong _units_volume[] = {
698 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
699 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC },
700 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI },
703 /** Unit conversions for force. */
704 static const Units _units_force[] = {
705 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL },
706 { {3263, 5}, STR_UNITS_FORCE_METRIC },
707 { { 1, 0}, STR_UNITS_FORCE_SI },
710 /** Unit conversions for height. */
711 static const Units _units_height[] = {
712 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL }, // "Wrong" conversion factor for more nicer GUI values
713 { { 1, 0}, STR_UNITS_HEIGHT_METRIC },
714 { { 1, 0}, STR_UNITS_HEIGHT_SI },
718 * Convert the given (internal) speed to the display speed.
719 * @param speed the speed to convert
720 * @return the converted speed.
722 uint ConvertSpeedToDisplaySpeed(uint speed)
724 /* For historical reasons we don't want to mess with the
725 * conversion for speed. So, don't round it and keep the
726 * original conversion factors instead of the real ones. */
727 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed, false);
731 * Convert the given display speed to the (internal) speed.
732 * @param speed the speed to convert
733 * @return the converted speed.
735 uint ConvertDisplaySpeedToSpeed(uint speed)
737 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed);
741 * Convert the given km/h-ish speed to the display speed.
742 * @param speed the speed to convert
743 * @return the converted speed.
745 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
747 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed * 10, false) / 16;
751 * Convert the given display speed to the km/h-ish speed.
752 * @param speed the speed to convert
753 * @return the converted speed.
755 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
757 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed * 16, true, 10);
760 * Parse most format codes within a string and write the result to a buffer.
761 * @param buff The buffer to write the final string to.
762 * @param str The original string with format codes.
763 * @param args Pointer to extra arguments used by various string codes.
764 * @param case_index
765 * @param last Pointer to just past the end of the buff array.
766 * @param dry_run True when the argt array is not yet initialized.
768 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
770 uint orig_offset = args->offset;
772 /* When there is no array with types there is no need to do a dry run. */
773 if (args->HasTypeInformation() && !dry_run) {
774 if (UsingNewGRFTextStack()) {
775 /* Values from the NewGRF text stack are only copied to the normal
776 * argv array at the time they are encountered. That means that if
777 * another string command references a value later in the string it
778 * would fail. We solve that by running FormatString twice. The first
779 * pass makes sure the argv array is correctly filled and the second
780 * pass can reference later values without problems. */
781 struct TextRefStack *backup = CreateTextRefStackBackup();
782 FormatString(buff, str_arg, args, last, case_index, game_script, true);
783 RestoreTextRefStackBackup(backup);
784 } else {
785 FormatString(buff, str_arg, args, last, case_index, game_script, true);
787 /* We have to restore the original offset here to to read the correct values. */
788 args->offset = orig_offset;
790 WChar b = '\0';
791 uint next_substr_case_index = 0;
792 char *buf_start = buff;
793 std::stack<const char *> str_stack;
794 str_stack.push(str_arg);
796 for (;;) {
797 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
798 str_stack.pop();
800 if (str_stack.empty()) break;
801 const char *&str = str_stack.top();
803 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
804 /* We need to pass some stuff as it might be modified; oh boy. */
805 //todo: should argve be passed here too?
806 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), args->GetDataLeft(), dry_run);
807 if (b == 0) continue;
810 switch (b) {
811 case SCC_ENCODED: {
812 uint64 sub_args_data[20];
813 WChar sub_args_type[20];
814 bool sub_args_need_free[20];
815 StringParameters sub_args(sub_args_data, 20, sub_args_type);
817 sub_args.ClearTypeInformation();
818 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
820 const char *s = str;
821 char *p;
822 uint32 stringid = strtoul(str, &p, 16);
823 if (*p != ':' && *p != '\0') {
824 while (*p != '\0') p++;
825 str = p;
826 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
827 break;
829 if (stringid >= TAB_SIZE_GAMESCRIPT) {
830 while (*p != '\0') p++;
831 str = p;
832 buff = strecat(buff, "(invalid StringID)", last);
833 break;
836 int i = 0;
837 while (*p != '\0' && i < 20) {
838 uint64 param;
839 s = ++p;
841 /* Find the next value */
842 bool instring = false;
843 bool escape = false;
844 for (;; p++) {
845 if (*p == '\\') {
846 escape = true;
847 continue;
849 if (*p == '"' && escape) {
850 escape = false;
851 continue;
853 escape = false;
855 if (*p == '"') {
856 instring = !instring;
857 continue;
859 if (instring) {
860 continue;
863 if (*p == ':') break;
864 if (*p == '\0') break;
867 if (*s != '"') {
868 /* Check if we want to look up another string */
869 WChar l;
870 size_t len = Utf8Decode(&l, s);
871 bool lookup = (l == SCC_ENCODED);
872 if (lookup) s += len;
874 param = strtoull(s, &p, 16);
876 if (lookup) {
877 if (param >= TAB_SIZE_GAMESCRIPT) {
878 while (*p != '\0') p++;
879 str = p;
880 buff = strecat(buff, "(invalid sub-StringID)", last);
881 break;
883 param = MakeStringID(TEXT_TAB_GAMESCRIPT_START, param);
886 sub_args.SetParam(i++, param);
887 } else {
888 char *g = stredup(s);
889 g[p - s] = '\0';
891 sub_args_need_free[i] = true;
892 sub_args.SetParam(i++, (uint64)(size_t)g);
895 /* If we didn't error out, we can actually print the string. */
896 if (*str != '\0') {
897 str = p;
898 buff = GetStringWithArgs(buff, MakeStringID(TEXT_TAB_GAMESCRIPT_START, stringid), &sub_args, last, true);
901 for (int i = 0; i < 20; i++) {
902 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
904 break;
907 case SCC_NEWGRF_STRINL: {
908 StringID substr = Utf8Consume(&str);
909 str_stack.push(GetStringPtr(substr));
910 break;
913 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
914 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
915 str_stack.push(GetStringPtr(substr));
916 case_index = next_substr_case_index;
917 next_substr_case_index = 0;
918 break;
922 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
923 /* First read the meta data from the language file. */
924 uint offset = orig_offset + (byte)*str++;
925 int gender = 0;
926 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
927 /* Now we need to figure out what text to resolve, i.e.
928 * what do we need to draw? So get the actual raw string
929 * first using the control code to get said string. */
930 char input[4 + 1];
931 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
932 *p = '\0';
934 /* Now do the string formatting. */
935 char buf[256];
936 bool old_sgd = _scan_for_gender_data;
937 _scan_for_gender_data = true;
938 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
939 p = FormatString(buf, input, &tmp_params, lastof(buf));
940 _scan_for_gender_data = old_sgd;
941 *p = '\0';
943 /* And determine the string. */
944 const char *s = buf;
945 WChar c = Utf8Consume(&s);
946 /* Does this string have a gender, if so, set it */
947 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
949 str = ParseStringChoice(str, gender, &buff, last);
950 break;
953 /* This sets up the gender for the string.
954 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
955 case SCC_GENDER_INDEX: // {GENDER 0}
956 if (_scan_for_gender_data) {
957 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
958 *buff++ = *str++;
959 } else {
960 str++;
962 break;
964 case SCC_PLURAL_LIST: { // {P}
965 int plural_form = *str++; // contains the plural form for this string
966 uint offset = orig_offset + (byte)*str++;
967 int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
968 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
969 break;
972 case SCC_ARG_INDEX: { // Move argument pointer
973 args->offset = orig_offset + (byte)*str++;
974 break;
977 case SCC_SET_CASE: { // {SET_CASE}
978 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
979 * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
980 next_substr_case_index = (byte)*str++;
981 break;
984 case SCC_SWITCH_CASE: { // {Used to implement case switching}
985 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
986 * Each LEN is printed using 2 bytes in big endian order. */
987 uint num = (byte)*str++;
988 while (num) {
989 if ((byte)str[0] == case_index) {
990 /* Found the case, adjust str pointer and continue */
991 str += 3;
992 break;
994 /* Otherwise skip to the next case */
995 str += 3 + (str[1] << 8) + str[2];
996 num--;
998 break;
1001 case SCC_REVISION: // {REV}
1002 buff = strecpy(buff, _openttd_revision, last);
1003 break;
1005 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1006 if (game_script) break;
1007 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
1008 buff = FormatString(buff, str, args, last);
1009 break;
1012 case SCC_STRING: {// {STRING}
1013 StringID str = args->GetInt32(SCC_STRING);
1014 if (game_script && GetStringTab(str) != TEXT_TAB_GAMESCRIPT_START) break;
1015 /* WARNING. It's prohibited for the included string to consume any arguments.
1016 * For included strings that consume argument, you should use STRING1, STRING2 etc.
1017 * To debug stuff you can set argv to NULL and it will tell you */
1018 StringParameters tmp_params(args->GetDataPointer(), args->GetDataLeft(), NULL);
1019 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
1020 next_substr_case_index = 0;
1021 break;
1024 case SCC_STRING1:
1025 case SCC_STRING2:
1026 case SCC_STRING3:
1027 case SCC_STRING4:
1028 case SCC_STRING5:
1029 case SCC_STRING6:
1030 case SCC_STRING7: { // {STRING1..7}
1031 /* Strings that consume arguments */
1032 StringID str = args->GetInt32(b);
1033 if (game_script && GetStringTab(str) != TEXT_TAB_GAMESCRIPT_START) break;
1034 uint size = b - SCC_STRING1 + 1;
1035 if (game_script && size > args->GetDataLeft()) {
1036 buff = strecat(buff, "(too many parameters)", last);
1037 } else {
1038 StringParameters sub_args(*args, size);
1039 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
1041 next_substr_case_index = 0;
1042 break;
1045 case SCC_COMMA: // {COMMA}
1046 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
1047 break;
1049 case SCC_DECIMAL: {// {DECIMAL}
1050 int64 number = args->GetInt64(SCC_DECIMAL);
1051 int digits = args->GetInt32(SCC_DECIMAL);
1052 buff = FormatCommaNumber(buff, number, last, digits);
1053 break;
1056 case SCC_NUM: // {NUM}
1057 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
1058 break;
1060 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1061 int64 num = args->GetInt64();
1062 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
1063 break;
1066 case SCC_HEX: // {HEX}
1067 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
1068 break;
1070 case SCC_BYTES: // {BYTES}
1071 buff = FormatBytes(buff, args->GetInt64(), last);
1072 break;
1074 case SCC_CARGO_TINY: { // {CARGO_TINY}
1075 /* Tiny description of cargotypes. Layout:
1076 * param 1: cargo type
1077 * param 2: cargo count */
1078 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
1079 if (cargo >= CargoSpec::GetArraySize()) break;
1081 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1082 int64 amount = 0;
1083 switch (cargo_str) {
1084 case STR_TONS:
1085 amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64());
1086 break;
1088 case STR_LITERS:
1089 amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64());
1090 break;
1092 default: {
1093 amount = args->GetInt64();
1094 break;
1098 buff = FormatCommaNumber(buff, amount, last);
1099 break;
1102 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1103 /* Short description of cargotypes. Layout:
1104 * param 1: cargo type
1105 * param 2: cargo count */
1106 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
1107 if (cargo >= CargoSpec::GetArraySize()) break;
1109 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1110 switch (cargo_str) {
1111 case STR_TONS: {
1112 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1113 int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1114 StringParameters tmp_params(args_array);
1115 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1116 break;
1119 case STR_LITERS: {
1120 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1121 int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1122 StringParameters tmp_params(args_array);
1123 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1124 break;
1127 default: {
1128 StringParameters tmp_params(*args, 1);
1129 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
1130 break;
1133 break;
1136 case SCC_CARGO_LONG: { // {CARGO_LONG}
1137 /* First parameter is cargo type, second parameter is cargo count */
1138 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
1139 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
1141 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1142 StringParameters tmp_args(*args, 1);
1143 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
1144 break;
1147 case SCC_CARGO_LIST: { // {CARGO_LIST}
1148 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
1149 bool first = true;
1151 const CargoSpec *cs;
1152 FOR_ALL_SORTED_CARGOSPECS(cs) {
1153 if (!HasBit(cmask, cs->Index())) continue;
1155 if (buff >= last - 2) break; // ',' and ' '
1157 if (first) {
1158 first = false;
1159 } else {
1160 /* Add a comma if this is not the first item */
1161 *buff++ = ',';
1162 *buff++ = ' ';
1165 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
1168 /* If first is still true then no cargo is accepted */
1169 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
1171 *buff = '\0';
1172 next_substr_case_index = 0;
1174 /* Make sure we detect any buffer overflow */
1175 assert(buff < last);
1176 break;
1179 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1180 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
1181 break;
1183 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1184 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
1185 break;
1187 case SCC_DATE_TINY: // {DATE_TINY}
1188 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
1189 break;
1191 case SCC_DATE_SHORT: // {DATE_SHORT}
1192 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
1193 next_substr_case_index = 0;
1194 break;
1196 case SCC_DATE_LONG: // {DATE_LONG}
1197 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
1198 next_substr_case_index = 0;
1199 break;
1201 case SCC_DATE_ISO: // {DATE_ISO}
1202 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
1203 break;
1205 case SCC_FORCE: { // {FORCE}
1206 assert(_settings_game.locale.units_force < lengthof(_units_force));
1207 int64 args_array[1] = {_units_force[_settings_game.locale.units_force].c.ToDisplay(args->GetInt64())};
1208 StringParameters tmp_params(args_array);
1209 buff = FormatString(buff, GetStringPtr(_units_force[_settings_game.locale.units_force].s), &tmp_params, last);
1210 break;
1213 case SCC_HEIGHT: { // {HEIGHT}
1214 assert(_settings_game.locale.units_height < lengthof(_units_height));
1215 int64 args_array[] = {_units_height[_settings_game.locale.units_height].c.ToDisplay(args->GetInt64())};
1216 StringParameters tmp_params(args_array);
1217 buff = FormatString(buff, GetStringPtr(_units_height[_settings_game.locale.units_height].s), &tmp_params, last);
1218 break;
1221 case SCC_POWER: { // {POWER}
1222 assert(_settings_game.locale.units_power < lengthof(_units_power));
1223 int64 args_array[1] = {_units_power[_settings_game.locale.units_power].c.ToDisplay(args->GetInt64())};
1224 StringParameters tmp_params(args_array);
1225 buff = FormatString(buff, GetStringPtr(_units_power[_settings_game.locale.units_power].s), &tmp_params, last);
1226 break;
1229 case SCC_VELOCITY: { // {VELOCITY}
1230 assert(_settings_game.locale.units_velocity < lengthof(_units_velocity));
1231 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
1232 StringParameters tmp_params(args_array);
1233 buff = FormatString(buff, GetStringPtr(_units_velocity[_settings_game.locale.units_velocity].s), &tmp_params, last);
1234 break;
1237 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1238 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1239 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1240 StringParameters tmp_params(args_array);
1241 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].s), &tmp_params, last);
1242 break;
1245 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1246 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1247 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
1248 StringParameters tmp_params(args_array);
1249 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1250 break;
1253 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1254 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1255 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1256 StringParameters tmp_params(args_array);
1257 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].s), &tmp_params, last);
1258 break;
1261 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1262 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1263 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
1264 StringParameters tmp_params(args_array);
1265 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1266 break;
1269 case SCC_COMPANY_NAME: { // {COMPANY}
1270 const Company *c = Company::GetIfValid(args->GetInt32());
1271 if (c == NULL) break;
1273 if (c->name != NULL) {
1274 int64 args_array[] = {(int64)(size_t)c->name};
1275 StringParameters tmp_params(args_array);
1276 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1277 } else {
1278 int64 args_array[] = {c->name_2};
1279 StringParameters tmp_params(args_array);
1280 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
1282 break;
1285 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1286 CompanyID company = (CompanyID)args->GetInt32();
1288 /* Nothing is added for AI or inactive companies */
1289 if (Company::IsValidHumanID(company)) {
1290 int64 args_array[] = {company + 1};
1291 StringParameters tmp_params(args_array);
1292 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
1294 break;
1297 case SCC_DEPOT_NAME: { // {DEPOT}
1298 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
1299 if (vt == VEH_AIRCRAFT) {
1300 uint64 args_array[] = {(uint64)args->GetInt32()};
1301 WChar types_array[] = {SCC_STATION_NAME};
1302 StringParameters tmp_params(args_array, 1, types_array);
1303 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
1304 break;
1307 const Depot *d = Depot::Get(args->GetInt32());
1308 if (d->name != NULL) {
1309 int64 args_array[] = {(int64)(size_t)d->name};
1310 StringParameters tmp_params(args_array);
1311 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1312 } else {
1313 int64 args_array[] = {d->town->index, d->town_cn + 1};
1314 StringParameters tmp_params(args_array);
1315 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
1317 break;
1320 case SCC_ENGINE_NAME: { // {ENGINE}
1321 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
1322 if (e == NULL) break;
1324 if (e->name != NULL && e->IsEnabled()) {
1325 int64 args_array[] = {(int64)(size_t)e->name};
1326 StringParameters tmp_params(args_array);
1327 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1328 } else {
1329 StringParameters tmp_params(NULL, 0, NULL);
1330 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
1332 break;
1335 case SCC_GROUP_NAME: { // {GROUP}
1336 const Group *g = Group::GetIfValid(args->GetInt32());
1337 if (g == NULL) break;
1339 if (g->name != NULL) {
1340 int64 args_array[] = {(int64)(size_t)g->name};
1341 StringParameters tmp_params(args_array);
1342 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1343 } else {
1344 int64 args_array[] = {g->index};
1345 StringParameters tmp_params(args_array);
1347 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
1349 break;
1352 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1353 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
1354 if (i == NULL) break;
1356 if (_scan_for_gender_data) {
1357 /* Gender is defined by the industry type.
1358 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1359 StringParameters tmp_params(NULL, 0, NULL);
1360 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
1361 } else {
1362 /* First print the town name and the industry type name. */
1363 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
1364 StringParameters tmp_params(args_array);
1366 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
1368 next_substr_case_index = 0;
1369 break;
1372 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1373 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
1374 if (c == NULL) break;
1376 if (c->president_name != NULL) {
1377 int64 args_array[] = {(int64)(size_t)c->president_name};
1378 StringParameters tmp_params(args_array);
1379 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1380 } else {
1381 int64 args_array[] = {c->president_name_2};
1382 StringParameters tmp_params(args_array);
1383 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
1385 break;
1388 case SCC_STATION_NAME: { // {STATION}
1389 StationID sid = args->GetInt32(SCC_STATION_NAME);
1390 const Station *st = Station::GetIfValid(sid);
1392 if (st == NULL) {
1393 /* The station doesn't exist anymore. The only place where we might
1394 * be "drawing" an invalid station is in the case of cargo that is
1395 * in transit. */
1396 StringParameters tmp_params(NULL, 0, NULL);
1397 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
1398 break;
1401 if (st->name != NULL) {
1402 int64 args_array[] = {(int64)(size_t)st->name};
1403 StringParameters tmp_params(args_array);
1404 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1405 } else {
1406 StringID str = st->string_id;
1407 if (st->indtype != IT_INVALID) {
1408 /* Special case where the industry provides the name for the station */
1409 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1411 /* Industry GRFs can change which might remove the station name and
1412 * thus cause very strange things. Here we check for that before we
1413 * actually set the station name. */
1414 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1415 str = indsp->station_name;
1419 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
1420 StringParameters tmp_params(args_array);
1421 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1423 break;
1426 case SCC_TOWN_NAME: { // {TOWN}
1427 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
1428 if (t == NULL) break;
1430 if (t->name != NULL) {
1431 int64 args_array[] = {(int64)(size_t)t->name};
1432 StringParameters tmp_params(args_array);
1433 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1434 } else {
1435 buff = GetTownName(buff, t, last);
1437 break;
1440 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1441 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
1442 if (wp == NULL) break;
1444 if (wp->name != NULL) {
1445 int64 args_array[] = {(int64)(size_t)wp->name};
1446 StringParameters tmp_params(args_array);
1447 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1448 } else {
1449 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
1450 StringParameters tmp_params(args_array);
1451 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1452 if (wp->town_cn != 0) str++;
1453 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1455 break;
1458 case SCC_VEHICLE_NAME: { // {VEHICLE}
1459 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
1460 if (v == NULL) break;
1462 if (v->name != NULL) {
1463 int64 args_array[] = {(int64)(size_t)v->name};
1464 StringParameters tmp_params(args_array);
1465 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1466 } else {
1467 int64 args_array[] = {v->unitnumber};
1468 StringParameters tmp_params(args_array);
1470 StringID str;
1471 switch (v->type) {
1472 default: str = STR_INVALID_VEHICLE; break;
1473 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
1474 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
1475 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
1476 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
1479 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1481 break;
1484 case SCC_SIGN_NAME: { // {SIGN}
1485 const Sign *si = Sign::GetIfValid(args->GetInt32());
1486 if (si == NULL) break;
1488 if (si->name != NULL) {
1489 int64 args_array[] = {(int64)(size_t)si->name};
1490 StringParameters tmp_params(args_array);
1491 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1492 } else {
1493 StringParameters tmp_params(NULL, 0, NULL);
1494 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
1496 break;
1499 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1500 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
1501 break;
1504 default:
1505 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
1506 break;
1509 *buff = '\0';
1510 return buff;
1514 static char *StationGetSpecialString(char *buff, int x, const char *last)
1516 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
1517 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
1518 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
1519 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
1520 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
1521 *buff = '\0';
1522 return buff;
1525 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
1527 return GenerateTownNameString(buff, last, ind, seed);
1530 static const char * const _silly_company_names[] = {
1531 "Bloggs Brothers",
1532 "Tiny Transport Ltd.",
1533 "Express Travel",
1534 "Comfy-Coach & Co.",
1535 "Crush & Bump Ltd.",
1536 "Broken & Late Ltd.",
1537 "Sam Speedy & Son",
1538 "Supersonic Travel",
1539 "Mike's Motors",
1540 "Lightning International",
1541 "Pannik & Loozit Ltd.",
1542 "Inter-City Transport",
1543 "Getout & Pushit Ltd."
1546 static const char * const _surname_list[] = {
1547 "Adams",
1548 "Allan",
1549 "Baker",
1550 "Bigwig",
1551 "Black",
1552 "Bloggs",
1553 "Brown",
1554 "Campbell",
1555 "Gordon",
1556 "Hamilton",
1557 "Hawthorn",
1558 "Higgins",
1559 "Green",
1560 "Gribble",
1561 "Jones",
1562 "McAlpine",
1563 "MacDonald",
1564 "McIntosh",
1565 "Muir",
1566 "Murphy",
1567 "Nelson",
1568 "O'Donnell",
1569 "Parker",
1570 "Phillips",
1571 "Pilkington",
1572 "Quigley",
1573 "Sharkey",
1574 "Thomson",
1575 "Watkins"
1578 static const char * const _silly_surname_list[] = {
1579 "Grumpy",
1580 "Dozy",
1581 "Speedy",
1582 "Nosey",
1583 "Dribble",
1584 "Mushroom",
1585 "Cabbage",
1586 "Sniffle",
1587 "Fishy",
1588 "Swindle",
1589 "Sneaky",
1590 "Nutkins"
1593 static const char _initial_name_letters[] = {
1594 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1595 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1598 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
1600 const char * const *base;
1601 uint num;
1603 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1604 base = _silly_surname_list;
1605 num = lengthof(_silly_surname_list);
1606 } else {
1607 base = _surname_list;
1608 num = lengthof(_surname_list);
1611 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
1612 buff = strecpy(buff, " & Co.", last);
1614 return buff;
1617 static char *GenPresidentName(char *buff, uint32 x, const char *last)
1619 char initial[] = "?. ";
1620 const char * const *base;
1621 uint num;
1622 uint i;
1624 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
1625 buff = strecpy(buff, initial, last);
1627 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
1628 if (i < sizeof(_initial_name_letters)) {
1629 initial[0] = _initial_name_letters[i];
1630 buff = strecpy(buff, initial, last);
1633 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1634 base = _silly_surname_list;
1635 num = lengthof(_silly_surname_list);
1636 } else {
1637 base = _surname_list;
1638 num = lengthof(_surname_list);
1641 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
1643 return buff;
1646 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
1648 switch (ind) {
1649 case 1: // not used
1650 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
1652 case 2: // used for Foobar & Co company names
1653 return GenAndCoName(buff, args->GetInt32(), last);
1655 case 3: // President name
1656 return GenPresidentName(buff, args->GetInt32(), last);
1659 /* town name? */
1660 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1661 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
1662 return strecpy(buff, " Transport", last);
1665 /* language name? */
1666 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
1667 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
1668 return strecpy(buff,
1669 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
1672 /* resolution size? */
1673 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
1674 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
1675 buff += seprintf(
1676 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
1678 return buff;
1681 NOT_REACHED();
1684 #ifdef ENABLE_NETWORK
1685 extern void SortNetworkLanguages();
1686 #else /* ENABLE_NETWORK */
1687 static inline void SortNetworkLanguages() {}
1688 #endif /* ENABLE_NETWORK */
1691 * Check whether the header is a valid header for OpenTTD.
1692 * @return true iff the header is deemed valid.
1694 bool LanguagePackHeader::IsValid() const
1696 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1697 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1698 this->plural_form < LANGUAGE_MAX_PLURAL &&
1699 this->text_dir <= 1 &&
1700 this->newgrflangid < MAX_LANG &&
1701 this->num_genders < MAX_NUM_GENDERS &&
1702 this->num_cases < MAX_NUM_CASES &&
1703 StrValid(this->name, lastof(this->name)) &&
1704 StrValid(this->own_name, lastof(this->own_name)) &&
1705 StrValid(this->isocode, lastof(this->isocode)) &&
1706 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
1707 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
1708 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
1712 * Read a particular language.
1713 * @param lang The metadata about the language.
1714 * @return Whether the loading went okay or not.
1716 bool ReadLanguagePack(const LanguageMetadata *lang)
1718 /* Current language pack */
1719 size_t len;
1720 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
1721 if (lang_pack == NULL) return false;
1723 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1724 const char *end = (char *)lang_pack + len + 1;
1726 /* We need at least one byte of lang_pack->data */
1727 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1728 free(lang_pack);
1729 return false;
1732 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1733 for (uint i = 0; i < TEXT_TAB_END; i++) {
1734 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
1736 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
1738 uint count = 0;
1739 for (uint i = 0; i < TEXT_TAB_END; i++) {
1740 uint16 num = lang_pack->offsets[i];
1741 if (num > TAB_SIZE) {
1742 free(lang_pack);
1743 return false;
1746 _langtab_start[i] = count;
1747 _langtab_num[i] = num;
1748 count += num;
1751 /* Allocate offsets */
1752 char **langpack_offs = MallocT<char *>(count);
1754 /* Fill offsets */
1755 char *s = lang_pack->data;
1756 len = (byte)*s++;
1757 for (uint i = 0; i < count; i++) {
1758 if (s + len >= end) {
1759 free(lang_pack);
1760 free(langpack_offs);
1761 return false;
1763 if (len >= 0xC0) {
1764 len = ((len & 0x3F) << 8) + (byte)*s++;
1765 if (s + len >= end) {
1766 free(lang_pack);
1767 free(langpack_offs);
1768 return false;
1771 langpack_offs[i] = s;
1772 s += len;
1773 len = (byte)*s;
1774 *s++ = '\0'; // zero terminate the string
1777 free(_langpack);
1778 _langpack = lang_pack;
1780 free(_langpack_offs);
1781 _langpack_offs = langpack_offs;
1783 _current_language = lang;
1784 _current_text_dir = (TextDirection)_current_language->text_dir;
1785 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
1786 strecpy(_config_language_file, c_file, lastof(_config_language_file));
1787 SetCurrentGrfLangID(_current_language->newgrflangid);
1789 #ifdef WITH_ICU_SORT
1790 /* Delete previous collator. */
1791 if (_current_collator != NULL) {
1792 delete _current_collator;
1793 _current_collator = NULL;
1796 /* Create a collator instance for our current locale. */
1797 UErrorCode status = U_ZERO_ERROR;
1798 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
1799 /* Sort number substrings by their numerical value. */
1800 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
1801 /* Avoid using the collator if it is not correctly set. */
1802 if (U_FAILURE(status)) {
1803 delete _current_collator;
1804 _current_collator = NULL;
1806 #endif /* WITH_ICU_SORT */
1808 /* Some lists need to be sorted again after a language change. */
1809 ReconsiderGameScriptLanguage();
1810 InitializeSortedCargoSpecs();
1811 SortIndustryTypes();
1812 BuildIndustriesLegend();
1813 SortNetworkLanguages();
1814 #ifdef ENABLE_NETWORK
1815 BuildContentTypeStringList();
1816 #endif /* ENABLE_NETWORK */
1817 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
1818 InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
1819 InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
1820 InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
1821 InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
1822 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
1823 InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
1825 return true;
1828 /* Win32 implementation in win32.cpp.
1829 * OS X implementation in os/macosx/macos.mm. */
1830 #if !(defined(WIN32) || defined(__APPLE__))
1832 * Determine the current charset based on the environment
1833 * First check some default values, after this one we passed ourselves
1834 * and if none exist return the value for $LANG
1835 * @param param environment variable to check conditionally if default ones are not
1836 * set. Pass NULL if you don't want additional checks.
1837 * @return return string containing current charset, or NULL if not-determinable
1839 const char *GetCurrentLocale(const char *param)
1841 const char *env;
1843 env = getenv("LANGUAGE");
1844 if (env != NULL) return env;
1846 env = getenv("LC_ALL");
1847 if (env != NULL) return env;
1849 if (param != NULL) {
1850 env = getenv(param);
1851 if (env != NULL) return env;
1854 return getenv("LANG");
1856 #else
1857 const char *GetCurrentLocale(const char *param);
1858 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
1860 int CDECL StringIDSorter(const StringID *a, const StringID *b)
1862 char stra[512];
1863 char strb[512];
1864 GetString(stra, *a, lastof(stra));
1865 GetString(strb, *b, lastof(strb));
1867 return strnatcmp(stra, strb);
1871 * Get the language with the given NewGRF language ID.
1872 * @param newgrflangid NewGRF languages ID to check.
1873 * @return The language's metadata, or NULL if it is not known.
1875 const LanguageMetadata *GetLanguage(byte newgrflangid)
1877 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
1878 if (newgrflangid == lang->newgrflangid) return lang;
1881 return NULL;
1885 * Reads the language file header and checks compatibility.
1886 * @param file the file to read
1887 * @param hdr the place to write the header information to
1888 * @return true if and only if the language file is of a compatible version
1890 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
1892 FILE *f = fopen(file, "rb");
1893 if (f == NULL) return false;
1895 size_t read = fread(hdr, sizeof(*hdr), 1, f);
1896 fclose(f);
1898 bool ret = read == 1 && hdr->IsValid();
1900 /* Convert endianness for the windows language ID */
1901 if (ret) {
1902 hdr->missing = FROM_LE16(hdr->missing);
1903 hdr->winlangid = FROM_LE16(hdr->winlangid);
1905 return ret;
1909 * Gets a list of languages from the given directory.
1910 * @param path the base directory to search in
1912 static void GetLanguageList(const char *path)
1914 DIR *dir = ttd_opendir(path);
1915 if (dir != NULL) {
1916 struct dirent *dirent;
1917 while ((dirent = readdir(dir)) != NULL) {
1918 const char *d_name = FS2OTTD(dirent->d_name);
1919 const char *extension = strrchr(d_name, '.');
1921 /* Not a language file */
1922 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
1924 LanguageMetadata lmd;
1925 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
1927 /* Check whether the file is of the correct version */
1928 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
1929 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
1930 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
1931 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
1932 } else {
1933 *_languages.Append() = lmd;
1936 closedir(dir);
1941 * Make a list of the available language packs. Put the data in
1942 * #_languages list.
1944 void InitializeLanguagePacks()
1946 Searchpath sp;
1948 FOR_ALL_SEARCHPATHS(sp) {
1949 char path[MAX_PATH];
1950 FioAppendDirectory(path, lastof(path), sp, LANG_DIR);
1951 GetLanguageList(path);
1953 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
1955 /* Acquire the locale of the current system */
1956 const char *lang = GetCurrentLocale("LC_MESSAGES");
1957 if (lang == NULL) lang = "en_GB";
1959 const LanguageMetadata *chosen_language = NULL; ///< Matching the language in the configuration file or the current locale
1960 const LanguageMetadata *language_fallback = NULL; ///< Using pt_PT for pt_BR locale when pt_BR is not available
1961 const LanguageMetadata *en_GB_fallback = _languages.Begin(); ///< Fallback when no locale-matching language has been found
1963 /* Find a proper language. */
1964 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
1965 /* We are trying to find a default language. The priority is by
1966 * configuration file, local environment and last, if nothing found,
1967 * English. */
1968 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
1969 if (strcmp(lang_file, _config_language_file) == 0) {
1970 chosen_language = lng;
1971 break;
1974 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
1975 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
1976 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
1979 /* We haven't found the language in the config nor the one in the locale.
1980 * Now we set it to one of the fallback languages */
1981 if (chosen_language == NULL) {
1982 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
1985 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
1989 * Get the ISO language code of the currently loaded language.
1990 * @return the ISO code.
1992 const char *GetCurrentLanguageIsoCode()
1994 return _langpack->isocode;
1998 * Check whether there are glyphs missing in the current language.
1999 * @param Pointer to an address for storing the text pointer.
2000 * @return If glyphs are missing, return \c true, else return \c false.
2001 * @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.
2003 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
2005 InitFreeType(this->Monospace());
2006 const Sprite *question_mark[FS_END];
2008 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
2009 question_mark[size] = GetGlyph(size, '?');
2012 this->Reset();
2013 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
2014 FontSize size = this->DefaultSize();
2015 if (str != NULL) *str = text;
2016 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
2017 if (c == SCC_TINYFONT) {
2018 size = FS_SMALL;
2019 } else if (c == SCC_BIGFONT) {
2020 size = FS_LARGE;
2021 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
2022 /* The character is printable, but not in the normal font. This is the case we were testing for. */
2023 return true;
2027 return false;
2030 /** Helper for searching through the language pack. */
2031 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
2032 uint i; ///< Iterator for the primary language tables.
2033 uint j; ///< Iterator for the secondary language tables.
2035 /* virtual */ void Reset()
2037 this->i = 0;
2038 this->j = 0;
2041 /* virtual */ FontSize DefaultSize()
2043 return FS_NORMAL;
2046 /* virtual */ const char *NextString()
2048 if (this->i >= TEXT_TAB_END) return NULL;
2050 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
2052 this->j++;
2053 while (this->i < TEXT_TAB_END && this->j >= _langtab_num[this->i]) {
2054 this->i++;
2055 this->j = 0;
2058 return ret;
2061 /* virtual */ bool Monospace()
2063 return false;
2066 /* virtual */ void SetFontNames(FreeTypeSettings *settings, const char *font_name)
2068 #ifdef WITH_FREETYPE
2069 strecpy(settings->small.font, font_name, lastof(settings->small.font));
2070 strecpy(settings->medium.font, font_name, lastof(settings->medium.font));
2071 strecpy(settings->large.font, font_name, lastof(settings->large.font));
2072 #endif /* WITH_FREETYPE */
2077 * Check whether the currently loaded language pack
2078 * uses characters that the currently loaded font
2079 * does not support. If this is the case an error
2080 * message will be shown in English. The error
2081 * message will not be localized because that would
2082 * mean it might use characters that are not in the
2083 * font, which is the whole reason this check has
2084 * been added.
2085 * @param base_font Whether to look at the base font as well.
2086 * @param searcher The methods to use to search for strings to check.
2087 * If NULL the loaded language pack searcher is used.
2089 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2091 static LanguagePackGlyphSearcher pack_searcher;
2092 if (searcher == NULL) searcher = &pack_searcher;
2093 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
2094 #ifdef WITH_FREETYPE
2095 if (bad_font) {
2096 /* We found an unprintable character... lets try whether we can find
2097 * a fallback font that can print the characters in the current language. */
2098 FreeTypeSettings backup;
2099 memcpy(&backup, &_freetype, sizeof(backup));
2101 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
2103 memcpy(&_freetype, &backup, sizeof(backup));
2105 if (bad_font && base_font) {
2106 /* Our fallback font does miss characters too, so keep the
2107 * user chosen font as that is more likely to be any good than
2108 * the wild guess we made */
2109 InitFreeType(searcher->Monospace());
2112 #endif
2114 if (bad_font) {
2115 /* All attempts have failed. Display an error. As we do not want the string to be translated by
2116 * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
2117 * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
2118 * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
2119 * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
2120 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.");
2121 Utf8Encode(err_str, SCC_YELLOW);
2122 SetDParamStr(0, err_str);
2123 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2125 /* Reset the font width */
2126 LoadStringWidthTable(searcher->Monospace());
2127 return;
2130 /* Update the font with cache */
2131 LoadStringWidthTable(searcher->Monospace());
2133 #if !defined(WITH_ICU_LAYOUT)
2135 * For right-to-left languages we need the ICU library. If
2136 * we do not have support for that library we warn the user
2137 * about it with a message. As we do not want the string to
2138 * be translated by the translators, we 'force' it into the
2139 * binary and 'load' it via a BindCString. To do this
2140 * properly we have to set the colour of the string,
2141 * otherwise we end up with a lot of artifacts. The colour
2142 * 'character' might change in the future, so for safety
2143 * we just Utf8 Encode it into the string, which takes
2144 * exactly three characters, so it replaces the "XXX" with
2145 * the colour marker.
2147 if (_current_text_dir != TD_LTR) {
2148 static char *err_str = stredup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
2149 Utf8Encode(err_str, SCC_YELLOW);
2150 SetDParamStr(0, err_str);
2151 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
2153 #endif /* !WITH_ICU_LAYOUT */