(svn r27756) -Codechange: Add StringTab enum
[openttd.git] / src / strings.cpp
blob0d115def952dcad60b8ea9fdfda8ade3864609ef
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: 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_NEWGRF1: return GetGRFStringPtr(GetStringIndex(string));
202 case TEXT_TAB_NEWGRF2: return GetGRFStringPtr(GetStringIndex(string) + 0x0800);
203 case TEXT_TAB_NEWGRF3: return GetGRFStringPtr(GetStringIndex(string) + 0x1000);
204 default: return _langpack_offs[_langtab_start[GetStringTab(string)] + GetStringIndex(string)];
209 * Get a parsed string with most special stringcodes replaced by the string parameters.
210 * @param buffr Pointer to a string buffer where the formatted string should be written to.
211 * @param string
212 * @param args Arguments for the string.
213 * @param last Pointer just past the end of \a buffr.
214 * @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case.
215 * @param game_script The string is coming directly from a game script.
216 * @return Pointer to the final zero byte of the formatted string.
218 char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
220 if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
222 uint index = GetStringIndex(string);
223 StringTab tab = GetStringTab(string);
225 switch (tab) {
226 case TEXT_TAB_TOWN:
227 if (index >= 0xC0 && !game_script) {
228 return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
230 break;
232 case TEXT_TAB_SPECIAL:
233 if (index >= 0xE4 && !game_script) {
234 return GetSpecialNameString(buffr, index - 0xE4, args, last);
236 break;
238 case TEXT_TAB_OLD_CUSTOM:
239 /* Old table for custom names. This is no longer used */
240 if (!game_script) {
241 error("Incorrect conversion of custom name string.");
243 break;
245 case TEXT_TAB_GAMESCRIPT:
246 return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
248 case TEXT_TAB_OLD_NEWGRF:
249 NOT_REACHED();
251 case TEXT_TAB_NEWGRF1:
252 return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
254 case TEXT_TAB_NEWGRF2:
255 return FormatString(buffr, GetGRFStringPtr(index + 0x0800), args, last, case_index);
257 case TEXT_TAB_NEWGRF3:
258 return FormatString(buffr, GetGRFStringPtr(index + 0x1000), args, last, case_index);
260 default:
261 break;
264 if (index >= _langtab_num[tab]) {
265 if (game_script) {
266 return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
268 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
271 return FormatString(buffr, GetStringPtr(string), args, last, case_index);
274 char *GetString(char *buffr, StringID string, const char *last)
276 _global_string_params.ClearTypeInformation();
277 _global_string_params.offset = 0;
278 return GetStringWithArgs(buffr, string, &_global_string_params, last);
283 * This function is used to "bind" a C string to a OpenTTD dparam slot.
284 * @param n slot of the string
285 * @param str string to bind
287 void SetDParamStr(uint n, const char *str)
289 SetDParam(n, (uint64)(size_t)str);
293 * Shift the string parameters in the global string parameter array by \a amount positions, making room at the beginning.
294 * @param amount Number of positions to shift.
296 void InjectDParam(uint amount)
298 _global_string_params.ShiftParameters(amount);
302 * Format a number into a string.
303 * @param buff the buffer to write to
304 * @param number the number to write down
305 * @param last the last element in the buffer
306 * @param separator the thousands-separator to use
307 * @param zerofill minimum number of digits to print for the integer part. The number will be filled with zeros at the front if necessary.
308 * @param fractional_digits number of fractional digits to display after a decimal separator. The decimal separator is inserted
309 * in front of the \a fractional_digits last digit of \a number.
310 * @return till where we wrote
312 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
314 static const int max_digits = 20;
315 uint64 divisor = 10000000000000000000ULL;
316 zerofill += fractional_digits;
317 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
319 if (number < 0) {
320 buff += seprintf(buff, last, "-");
321 number = -number;
324 uint64 num = number;
325 uint64 tot = 0;
326 for (int i = 0; i < max_digits; i++) {
327 if (i == max_digits - fractional_digits) {
328 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
329 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
330 buff += seprintf(buff, last, "%s", decimal_separator);
333 uint64 quot = 0;
334 if (num >= divisor) {
335 quot = num / divisor;
336 num = num % divisor;
338 if ((tot |= quot) || i >= max_digits - zerofill) {
339 buff += seprintf(buff, last, "%i", (int)quot);
340 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
343 divisor /= 10;
346 *buff = '\0';
348 return buff;
351 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
353 const char *separator = _settings_game.locale.digit_group_separator;
354 if (separator == NULL) separator = _langpack->digit_group_separator;
355 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
358 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
360 return FormatNumber(buff, number, last, "");
363 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
365 return FormatNumber(buff, number, last, "", count);
368 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
370 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
374 * Format a given number as a number of bytes with the SI prefix.
375 * @param buff the buffer to write to
376 * @param number the number of bytes to write down
377 * @param last the last element in the buffer
378 * @return till where we wrote
380 static char *FormatBytes(char *buff, int64 number, const char *last)
382 assert(number >= 0);
384 /* 1 2^10 2^20 2^30 2^40 2^50 2^60 */
385 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
386 uint id = 1;
387 while (number >= 1024 * 1024) {
388 number /= 1024;
389 id++;
392 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
393 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
395 if (number < 1024) {
396 id = 0;
397 buff += seprintf(buff, last, "%i", (int)number);
398 } else if (number < 1024 * 10) {
399 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
400 } else if (number < 1024 * 100) {
401 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
402 } else {
403 assert(number < 1024 * 1024);
404 buff += seprintf(buff, last, "%i", (int)number / 1024);
407 assert(id < lengthof(iec_prefixes));
408 buff += seprintf(buff, last, NBSP "%sB", iec_prefixes[id]);
410 return buff;
413 static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
415 YearMonthDay ymd;
416 ConvertDateToYMD(date, &ymd);
418 int64 args[] = {ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
419 StringParameters tmp_params(args);
420 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
423 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
425 YearMonthDay ymd;
426 ConvertDateToYMD(date, &ymd);
428 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
429 StringParameters tmp_params(args);
430 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
433 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
435 YearMonthDay ymd;
436 ConvertDateToYMD(date, &ymd);
438 char day[3];
439 char month[3];
440 /* We want to zero-pad the days and months */
441 seprintf(day, lastof(day), "%02i", ymd.day);
442 seprintf(month, lastof(month), "%02i", ymd.month + 1);
444 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
445 StringParameters tmp_params(args);
446 return FormatString(buff, GetStringPtr(str), &tmp_params, last);
449 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
451 /* We are going to make number absolute for printing, so
452 * keep this piece of data as we need it later on */
453 bool negative = number < 0;
454 const char *multiplier = "";
456 number *= spec->rate;
458 /* convert from negative */
459 if (number < 0) {
460 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
461 buff += Utf8Encode(buff, SCC_RED);
462 buff = strecpy(buff, "-", last);
463 number = -number;
466 /* Add prefix part, following symbol_pos specification.
467 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
468 * The only remaining value is 1 (suffix), so everything that is not 1 */
469 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
471 /* for huge numbers, compact the number into k or M */
472 if (compact) {
473 /* Take care of the 'k' rounding. Having 1 000 000 k
474 * and 1 000 M is inconsistent, so always use 1 000 M. */
475 if (number >= 1000000000 - 500) {
476 number = (number + 500000) / 1000000;
477 multiplier = NBSP "M";
478 } else if (number >= 1000000) {
479 number = (number + 500) / 1000;
480 multiplier = NBSP "k";
484 const char *separator = _settings_game.locale.digit_group_separator_currency;
485 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
486 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
487 buff = FormatNumber(buff, number, last, separator);
488 buff = strecpy(buff, multiplier, last);
490 /* Add suffix part, following symbol_pos specification.
491 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
492 * The only remaining value is 1 (prefix), so everything that is not 0 */
493 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
495 if (negative) {
496 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
497 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
498 *buff = '\0';
501 return buff;
505 * Determine the "plural" index given a plural form and a number.
506 * @param count The number to get the plural index of.
507 * @param plural_form The plural form we want an index for.
508 * @return The plural index for the given form.
510 static int DeterminePluralForm(int64 count, int plural_form)
512 /* The absolute value determines plurality */
513 uint64 n = abs(count);
515 switch (plural_form) {
516 default:
517 NOT_REACHED();
519 /* Two forms: singular used for one only.
520 * Used in:
521 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
522 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
523 case 0:
524 return n != 1 ? 1 : 0;
526 /* Only one form.
527 * Used in:
528 * Hungarian, Japanese, Korean, Turkish */
529 case 1:
530 return 0;
532 /* Two forms: singular used for 0 and 1.
533 * Used in:
534 * French, Brazilian Portuguese */
535 case 2:
536 return n > 1 ? 1 : 0;
538 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
539 * Note: Cases are out of order for hysterical reasons. '0' is last.
540 * Used in:
541 * Latvian */
542 case 3:
543 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
545 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
546 * Used in:
547 * Gaelige (Irish) */
548 case 4:
549 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
551 /* 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.
552 * Used in:
553 * Lithuanian */
554 case 5:
555 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
557 /* 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.
558 * Used in:
559 * Croatian, Russian, Ukrainian */
560 case 6:
561 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
563 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
564 * Used in:
565 * Polish */
566 case 7:
567 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
569 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
570 * Used in:
571 * Slovenian */
572 case 8:
573 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
575 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
576 * Used in:
577 * Icelandic */
578 case 9:
579 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
581 /* Three forms: special cases for 1, and 2 to 4
582 * Used in:
583 * Czech, Slovak */
584 case 10:
585 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
587 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
588 * Korean doesn't have the concept of plural, but depending on how a
589 * number is pronounced it needs another version of a particle.
590 * As such the plural system is misused to give this distinction.
592 case 11:
593 switch (n % 10) {
594 case 0: // yeong
595 case 1: // il
596 case 3: // sam
597 case 6: // yuk
598 case 7: // chil
599 case 8: // pal
600 return 0;
602 case 2: // i
603 case 4: // sa
604 case 5: // o
605 case 9: // gu
606 return 1;
608 default:
609 NOT_REACHED();
612 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
613 * Used in:
614 * Maltese */
615 case 12:
616 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
617 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
618 * Used in:
619 * Scottish Gaelic */
620 case 13:
621 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
625 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
627 /* <NUM> {Length of each string} {each string} */
628 uint n = (byte)*b++;
629 uint pos, i, mypos = 0;
631 for (i = pos = 0; i != n; i++) {
632 uint len = (byte)*b++;
633 if (i == form) mypos = pos;
634 pos += len;
637 *dst += seprintf(*dst, last, "%s", b + mypos);
638 return b + pos;
641 /** Helper for unit conversion. */
642 struct UnitConversion {
643 int multiplier; ///< Amount to multiply upon conversion.
644 int shift; ///< Amount to shift upon conversion.
647 * Convert value from OpenTTD's internal unit into the displayed value.
648 * @param input The input to convert.
649 * @param round Whether to round the value or not.
650 * @return The converted value.
652 int64 ToDisplay(int64 input, bool round = true) const
654 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
658 * Convert the displayed value back into a value of OpenTTD's internal unit.
659 * @param input The input to convert.
660 * @param round Whether to round the value up or not.
661 * @param divider Divide the return value by this.
662 * @return The converted value.
664 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
666 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
670 /** Information about a specific unit system. */
671 struct Units {
672 UnitConversion c; ///< Conversion
673 StringID s; ///< String for the unit
676 /** Information about a specific unit system with a long variant. */
677 struct UnitsLong {
678 UnitConversion c; ///< Conversion
679 StringID s; ///< String for the short variant of the unit
680 StringID l; ///< String for the long variant of the unit
683 /** Unit conversions for velocity. */
684 static const Units _units_velocity[] = {
685 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL },
686 { { 103, 6}, STR_UNITS_VELOCITY_METRIC },
687 { {1831, 12}, STR_UNITS_VELOCITY_SI },
690 /** Unit conversions for velocity. */
691 static const Units _units_power[] = {
692 { { 1, 0}, STR_UNITS_POWER_IMPERIAL },
693 { {4153, 12}, STR_UNITS_POWER_METRIC },
694 { {6109, 13}, STR_UNITS_POWER_SI },
697 /** Unit conversions for weight. */
698 static const UnitsLong _units_weight[] = {
699 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
700 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC },
701 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI },
704 /** Unit conversions for volume. */
705 static const UnitsLong _units_volume[] = {
706 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
707 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC },
708 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI },
711 /** Unit conversions for force. */
712 static const Units _units_force[] = {
713 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL },
714 { {3263, 5}, STR_UNITS_FORCE_METRIC },
715 { { 1, 0}, STR_UNITS_FORCE_SI },
718 /** Unit conversions for height. */
719 static const Units _units_height[] = {
720 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL }, // "Wrong" conversion factor for more nicer GUI values
721 { { 1, 0}, STR_UNITS_HEIGHT_METRIC },
722 { { 1, 0}, STR_UNITS_HEIGHT_SI },
726 * Convert the given (internal) speed to the display speed.
727 * @param speed the speed to convert
728 * @return the converted speed.
730 uint ConvertSpeedToDisplaySpeed(uint speed)
732 /* For historical reasons we don't want to mess with the
733 * conversion for speed. So, don't round it and keep the
734 * original conversion factors instead of the real ones. */
735 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed, false);
739 * Convert the given display speed to the (internal) speed.
740 * @param speed the speed to convert
741 * @return the converted speed.
743 uint ConvertDisplaySpeedToSpeed(uint speed)
745 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed);
749 * Convert the given km/h-ish speed to the display speed.
750 * @param speed the speed to convert
751 * @return the converted speed.
753 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
755 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed * 10, false) / 16;
759 * Convert the given display speed to the km/h-ish speed.
760 * @param speed the speed to convert
761 * @return the converted speed.
763 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
765 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed * 16, true, 10);
768 * Parse most format codes within a string and write the result to a buffer.
769 * @param buff The buffer to write the final string to.
770 * @param str The original string with format codes.
771 * @param args Pointer to extra arguments used by various string codes.
772 * @param case_index
773 * @param last Pointer to just past the end of the buff array.
774 * @param dry_run True when the argt array is not yet initialized.
776 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
778 uint orig_offset = args->offset;
780 /* When there is no array with types there is no need to do a dry run. */
781 if (args->HasTypeInformation() && !dry_run) {
782 if (UsingNewGRFTextStack()) {
783 /* Values from the NewGRF text stack are only copied to the normal
784 * argv array at the time they are encountered. That means that if
785 * another string command references a value later in the string it
786 * would fail. We solve that by running FormatString twice. The first
787 * pass makes sure the argv array is correctly filled and the second
788 * pass can reference later values without problems. */
789 struct TextRefStack *backup = CreateTextRefStackBackup();
790 FormatString(buff, str_arg, args, last, case_index, game_script, true);
791 RestoreTextRefStackBackup(backup);
792 } else {
793 FormatString(buff, str_arg, args, last, case_index, game_script, true);
795 /* We have to restore the original offset here to to read the correct values. */
796 args->offset = orig_offset;
798 WChar b = '\0';
799 uint next_substr_case_index = 0;
800 char *buf_start = buff;
801 std::stack<const char *> str_stack;
802 str_stack.push(str_arg);
804 for (;;) {
805 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
806 str_stack.pop();
808 if (str_stack.empty()) break;
809 const char *&str = str_stack.top();
811 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
812 /* We need to pass some stuff as it might be modified; oh boy. */
813 //todo: should argve be passed here too?
814 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), args->GetDataLeft(), dry_run);
815 if (b == 0) continue;
818 switch (b) {
819 case SCC_ENCODED: {
820 uint64 sub_args_data[20];
821 WChar sub_args_type[20];
822 bool sub_args_need_free[20];
823 StringParameters sub_args(sub_args_data, 20, sub_args_type);
825 sub_args.ClearTypeInformation();
826 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
828 uint16 stringid;
829 const char *s = str;
830 char *p;
831 stringid = strtol(str, &p, 16);
832 if (*p != ':' && *p != '\0') {
833 while (*p != '\0') p++;
834 str = p;
835 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
836 break;
838 if (stringid >= TAB_SIZE) {
839 while (*p != '\0') p++;
840 str = p;
841 buff = strecat(buff, "(invalid StringID)", last);
842 break;
845 int i = 0;
846 while (*p != '\0' && i < 20) {
847 uint64 param;
848 s = ++p;
850 /* Find the next value */
851 bool instring = false;
852 bool escape = false;
853 for (;; p++) {
854 if (*p == '\\') {
855 escape = true;
856 continue;
858 if (*p == '"' && escape) {
859 escape = false;
860 continue;
862 escape = false;
864 if (*p == '"') {
865 instring = !instring;
866 continue;
868 if (instring) {
869 continue;
872 if (*p == ':') break;
873 if (*p == '\0') break;
876 if (*s != '"') {
877 /* Check if we want to look up another string */
878 WChar l;
879 size_t len = Utf8Decode(&l, s);
880 bool lookup = (l == SCC_ENCODED);
881 if (lookup) s += len;
883 param = strtoull(s, &p, 16);
885 if (lookup) {
886 if (param >= TAB_SIZE) {
887 while (*p != '\0') p++;
888 str = p;
889 buff = strecat(buff, "(invalid sub-StringID)", last);
890 break;
892 param = MakeStringID(TEXT_TAB_GAMESCRIPT, param);
895 sub_args.SetParam(i++, param);
896 } else {
897 char *g = stredup(s);
898 g[p - s] = '\0';
900 sub_args_need_free[i] = true;
901 sub_args.SetParam(i++, (uint64)(size_t)g);
904 /* If we didn't error out, we can actually print the string. */
905 if (*str != '\0') {
906 str = p;
907 buff = GetStringWithArgs(buff, MakeStringID(TEXT_TAB_GAMESCRIPT, stringid), &sub_args, last, true);
910 for (int i = 0; i < 20; i++) {
911 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
913 break;
916 case SCC_NEWGRF_STRINL: {
917 StringID substr = Utf8Consume(&str);
918 str_stack.push(GetStringPtr(substr));
919 break;
922 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
923 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
924 str_stack.push(GetStringPtr(substr));
925 case_index = next_substr_case_index;
926 next_substr_case_index = 0;
927 break;
931 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
932 /* First read the meta data from the language file. */
933 uint offset = orig_offset + (byte)*str++;
934 int gender = 0;
935 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
936 /* Now we need to figure out what text to resolve, i.e.
937 * what do we need to draw? So get the actual raw string
938 * first using the control code to get said string. */
939 char input[4 + 1];
940 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
941 *p = '\0';
943 /* Now do the string formatting. */
944 char buf[256];
945 bool old_sgd = _scan_for_gender_data;
946 _scan_for_gender_data = true;
947 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
948 p = FormatString(buf, input, &tmp_params, lastof(buf));
949 _scan_for_gender_data = old_sgd;
950 *p = '\0';
952 /* And determine the string. */
953 const char *s = buf;
954 WChar c = Utf8Consume(&s);
955 /* Does this string have a gender, if so, set it */
956 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
958 str = ParseStringChoice(str, gender, &buff, last);
959 break;
962 /* This sets up the gender for the string.
963 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
964 case SCC_GENDER_INDEX: // {GENDER 0}
965 if (_scan_for_gender_data) {
966 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
967 *buff++ = *str++;
968 } else {
969 str++;
971 break;
973 case SCC_PLURAL_LIST: { // {P}
974 int plural_form = *str++; // contains the plural form for this string
975 uint offset = orig_offset + (byte)*str++;
976 int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
977 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
978 break;
981 case SCC_ARG_INDEX: { // Move argument pointer
982 args->offset = orig_offset + (byte)*str++;
983 break;
986 case SCC_SET_CASE: { // {SET_CASE}
987 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
988 * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
989 next_substr_case_index = (byte)*str++;
990 break;
993 case SCC_SWITCH_CASE: { // {Used to implement case switching}
994 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
995 * Each LEN is printed using 2 bytes in big endian order. */
996 uint num = (byte)*str++;
997 while (num) {
998 if ((byte)str[0] == case_index) {
999 /* Found the case, adjust str pointer and continue */
1000 str += 3;
1001 break;
1003 /* Otherwise skip to the next case */
1004 str += 3 + (str[1] << 8) + str[2];
1005 num--;
1007 break;
1010 case SCC_REVISION: // {REV}
1011 buff = strecpy(buff, _openttd_revision, last);
1012 break;
1014 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1015 if (game_script) break;
1016 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
1017 buff = FormatString(buff, str, args, last);
1018 break;
1021 case SCC_STRING: {// {STRING}
1022 StringID str = args->GetInt32(SCC_STRING);
1023 if (game_script && GetStringTab(str) != TEXT_TAB_GAMESCRIPT) break;
1024 /* WARNING. It's prohibited for the included string to consume any arguments.
1025 * For included strings that consume argument, you should use STRING1, STRING2 etc.
1026 * To debug stuff you can set argv to NULL and it will tell you */
1027 StringParameters tmp_params(args->GetDataPointer(), args->GetDataLeft(), NULL);
1028 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
1029 next_substr_case_index = 0;
1030 break;
1033 case SCC_STRING1:
1034 case SCC_STRING2:
1035 case SCC_STRING3:
1036 case SCC_STRING4:
1037 case SCC_STRING5:
1038 case SCC_STRING6:
1039 case SCC_STRING7: { // {STRING1..7}
1040 /* Strings that consume arguments */
1041 StringID str = args->GetInt32(b);
1042 if (game_script && GetStringTab(str) != TEXT_TAB_GAMESCRIPT) break;
1043 uint size = b - SCC_STRING1 + 1;
1044 if (game_script && size > args->GetDataLeft()) {
1045 buff = strecat(buff, "(too many parameters)", last);
1046 } else {
1047 StringParameters sub_args(*args, size);
1048 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
1050 next_substr_case_index = 0;
1051 break;
1054 case SCC_COMMA: // {COMMA}
1055 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
1056 break;
1058 case SCC_DECIMAL: {// {DECIMAL}
1059 int64 number = args->GetInt64(SCC_DECIMAL);
1060 int digits = args->GetInt32(SCC_DECIMAL);
1061 buff = FormatCommaNumber(buff, number, last, digits);
1062 break;
1065 case SCC_NUM: // {NUM}
1066 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
1067 break;
1069 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1070 int64 num = args->GetInt64();
1071 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
1072 break;
1075 case SCC_HEX: // {HEX}
1076 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
1077 break;
1079 case SCC_BYTES: // {BYTES}
1080 buff = FormatBytes(buff, args->GetInt64(), last);
1081 break;
1083 case SCC_CARGO_TINY: { // {CARGO_TINY}
1084 /* Tiny description of cargotypes. Layout:
1085 * param 1: cargo type
1086 * param 2: cargo count */
1087 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
1088 if (cargo >= CargoSpec::GetArraySize()) break;
1090 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1091 int64 amount = 0;
1092 switch (cargo_str) {
1093 case STR_TONS:
1094 amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64());
1095 break;
1097 case STR_LITERS:
1098 amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64());
1099 break;
1101 default: {
1102 amount = args->GetInt64();
1103 break;
1107 buff = FormatCommaNumber(buff, amount, last);
1108 break;
1111 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1112 /* Short description of cargotypes. Layout:
1113 * param 1: cargo type
1114 * param 2: cargo count */
1115 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
1116 if (cargo >= CargoSpec::GetArraySize()) break;
1118 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1119 switch (cargo_str) {
1120 case STR_TONS: {
1121 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1122 int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1123 StringParameters tmp_params(args_array);
1124 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1125 break;
1128 case STR_LITERS: {
1129 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1130 int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1131 StringParameters tmp_params(args_array);
1132 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1133 break;
1136 default: {
1137 StringParameters tmp_params(*args, 1);
1138 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
1139 break;
1142 break;
1145 case SCC_CARGO_LONG: { // {CARGO_LONG}
1146 /* First parameter is cargo type, second parameter is cargo count */
1147 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
1148 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
1150 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1151 StringParameters tmp_args(*args, 1);
1152 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
1153 break;
1156 case SCC_CARGO_LIST: { // {CARGO_LIST}
1157 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
1158 bool first = true;
1160 const CargoSpec *cs;
1161 FOR_ALL_SORTED_CARGOSPECS(cs) {
1162 if (!HasBit(cmask, cs->Index())) continue;
1164 if (buff >= last - 2) break; // ',' and ' '
1166 if (first) {
1167 first = false;
1168 } else {
1169 /* Add a comma if this is not the first item */
1170 *buff++ = ',';
1171 *buff++ = ' ';
1174 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
1177 /* If first is still true then no cargo is accepted */
1178 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
1180 *buff = '\0';
1181 next_substr_case_index = 0;
1183 /* Make sure we detect any buffer overflow */
1184 assert(buff < last);
1185 break;
1188 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1189 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
1190 break;
1192 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1193 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
1194 break;
1196 case SCC_DATE_TINY: // {DATE_TINY}
1197 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
1198 break;
1200 case SCC_DATE_SHORT: // {DATE_SHORT}
1201 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
1202 next_substr_case_index = 0;
1203 break;
1205 case SCC_DATE_LONG: // {DATE_LONG}
1206 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
1207 next_substr_case_index = 0;
1208 break;
1210 case SCC_DATE_ISO: // {DATE_ISO}
1211 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
1212 break;
1214 case SCC_FORCE: { // {FORCE}
1215 assert(_settings_game.locale.units_force < lengthof(_units_force));
1216 int64 args_array[1] = {_units_force[_settings_game.locale.units_force].c.ToDisplay(args->GetInt64())};
1217 StringParameters tmp_params(args_array);
1218 buff = FormatString(buff, GetStringPtr(_units_force[_settings_game.locale.units_force].s), &tmp_params, last);
1219 break;
1222 case SCC_HEIGHT: { // {HEIGHT}
1223 assert(_settings_game.locale.units_height < lengthof(_units_height));
1224 int64 args_array[] = {_units_height[_settings_game.locale.units_height].c.ToDisplay(args->GetInt64())};
1225 StringParameters tmp_params(args_array);
1226 buff = FormatString(buff, GetStringPtr(_units_height[_settings_game.locale.units_height].s), &tmp_params, last);
1227 break;
1230 case SCC_POWER: { // {POWER}
1231 assert(_settings_game.locale.units_power < lengthof(_units_power));
1232 int64 args_array[1] = {_units_power[_settings_game.locale.units_power].c.ToDisplay(args->GetInt64())};
1233 StringParameters tmp_params(args_array);
1234 buff = FormatString(buff, GetStringPtr(_units_power[_settings_game.locale.units_power].s), &tmp_params, last);
1235 break;
1238 case SCC_VELOCITY: { // {VELOCITY}
1239 assert(_settings_game.locale.units_velocity < lengthof(_units_velocity));
1240 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
1241 StringParameters tmp_params(args_array);
1242 buff = FormatString(buff, GetStringPtr(_units_velocity[_settings_game.locale.units_velocity].s), &tmp_params, last);
1243 break;
1246 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1247 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1248 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1249 StringParameters tmp_params(args_array);
1250 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].s), &tmp_params, last);
1251 break;
1254 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1255 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1256 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
1257 StringParameters tmp_params(args_array);
1258 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1259 break;
1262 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1263 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1264 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1265 StringParameters tmp_params(args_array);
1266 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].s), &tmp_params, last);
1267 break;
1270 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1271 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1272 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
1273 StringParameters tmp_params(args_array);
1274 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1275 break;
1278 case SCC_COMPANY_NAME: { // {COMPANY}
1279 const Company *c = Company::GetIfValid(args->GetInt32());
1280 if (c == NULL) break;
1282 if (c->name != NULL) {
1283 int64 args_array[] = {(int64)(size_t)c->name};
1284 StringParameters tmp_params(args_array);
1285 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1286 } else {
1287 int64 args_array[] = {c->name_2};
1288 StringParameters tmp_params(args_array);
1289 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
1291 break;
1294 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1295 CompanyID company = (CompanyID)args->GetInt32();
1297 /* Nothing is added for AI or inactive companies */
1298 if (Company::IsValidHumanID(company)) {
1299 int64 args_array[] = {company + 1};
1300 StringParameters tmp_params(args_array);
1301 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
1303 break;
1306 case SCC_DEPOT_NAME: { // {DEPOT}
1307 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
1308 if (vt == VEH_AIRCRAFT) {
1309 uint64 args_array[] = {(uint64)args->GetInt32()};
1310 WChar types_array[] = {SCC_STATION_NAME};
1311 StringParameters tmp_params(args_array, 1, types_array);
1312 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
1313 break;
1316 const Depot *d = Depot::Get(args->GetInt32());
1317 if (d->name != NULL) {
1318 int64 args_array[] = {(int64)(size_t)d->name};
1319 StringParameters tmp_params(args_array);
1320 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1321 } else {
1322 int64 args_array[] = {d->town->index, d->town_cn + 1};
1323 StringParameters tmp_params(args_array);
1324 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
1326 break;
1329 case SCC_ENGINE_NAME: { // {ENGINE}
1330 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
1331 if (e == NULL) break;
1333 if (e->name != NULL && e->IsEnabled()) {
1334 int64 args_array[] = {(int64)(size_t)e->name};
1335 StringParameters tmp_params(args_array);
1336 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1337 } else {
1338 StringParameters tmp_params(NULL, 0, NULL);
1339 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
1341 break;
1344 case SCC_GROUP_NAME: { // {GROUP}
1345 const Group *g = Group::GetIfValid(args->GetInt32());
1346 if (g == NULL) break;
1348 if (g->name != NULL) {
1349 int64 args_array[] = {(int64)(size_t)g->name};
1350 StringParameters tmp_params(args_array);
1351 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1352 } else {
1353 int64 args_array[] = {g->index};
1354 StringParameters tmp_params(args_array);
1356 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
1358 break;
1361 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1362 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
1363 if (i == NULL) break;
1365 if (_scan_for_gender_data) {
1366 /* Gender is defined by the industry type.
1367 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1368 StringParameters tmp_params(NULL, 0, NULL);
1369 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
1370 } else {
1371 /* First print the town name and the industry type name. */
1372 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
1373 StringParameters tmp_params(args_array);
1375 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
1377 next_substr_case_index = 0;
1378 break;
1381 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1382 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
1383 if (c == NULL) break;
1385 if (c->president_name != NULL) {
1386 int64 args_array[] = {(int64)(size_t)c->president_name};
1387 StringParameters tmp_params(args_array);
1388 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1389 } else {
1390 int64 args_array[] = {c->president_name_2};
1391 StringParameters tmp_params(args_array);
1392 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
1394 break;
1397 case SCC_STATION_NAME: { // {STATION}
1398 StationID sid = args->GetInt32(SCC_STATION_NAME);
1399 const Station *st = Station::GetIfValid(sid);
1401 if (st == NULL) {
1402 /* The station doesn't exist anymore. The only place where we might
1403 * be "drawing" an invalid station is in the case of cargo that is
1404 * in transit. */
1405 StringParameters tmp_params(NULL, 0, NULL);
1406 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
1407 break;
1410 if (st->name != NULL) {
1411 int64 args_array[] = {(int64)(size_t)st->name};
1412 StringParameters tmp_params(args_array);
1413 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1414 } else {
1415 StringID str = st->string_id;
1416 if (st->indtype != IT_INVALID) {
1417 /* Special case where the industry provides the name for the station */
1418 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1420 /* Industry GRFs can change which might remove the station name and
1421 * thus cause very strange things. Here we check for that before we
1422 * actually set the station name. */
1423 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1424 str = indsp->station_name;
1428 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
1429 StringParameters tmp_params(args_array);
1430 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1432 break;
1435 case SCC_TOWN_NAME: { // {TOWN}
1436 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
1437 if (t == NULL) break;
1439 if (t->name != NULL) {
1440 int64 args_array[] = {(int64)(size_t)t->name};
1441 StringParameters tmp_params(args_array);
1442 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1443 } else {
1444 buff = GetTownName(buff, t, last);
1446 break;
1449 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1450 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
1451 if (wp == NULL) break;
1453 if (wp->name != NULL) {
1454 int64 args_array[] = {(int64)(size_t)wp->name};
1455 StringParameters tmp_params(args_array);
1456 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1457 } else {
1458 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
1459 StringParameters tmp_params(args_array);
1460 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1461 if (wp->town_cn != 0) str++;
1462 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1464 break;
1467 case SCC_VEHICLE_NAME: { // {VEHICLE}
1468 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
1469 if (v == NULL) break;
1471 if (v->name != NULL) {
1472 int64 args_array[] = {(int64)(size_t)v->name};
1473 StringParameters tmp_params(args_array);
1474 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1475 } else {
1476 int64 args_array[] = {v->unitnumber};
1477 StringParameters tmp_params(args_array);
1479 StringID str;
1480 switch (v->type) {
1481 default: str = STR_INVALID_VEHICLE; break;
1482 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
1483 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
1484 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
1485 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
1488 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1490 break;
1493 case SCC_SIGN_NAME: { // {SIGN}
1494 const Sign *si = Sign::GetIfValid(args->GetInt32());
1495 if (si == NULL) break;
1497 if (si->name != NULL) {
1498 int64 args_array[] = {(int64)(size_t)si->name};
1499 StringParameters tmp_params(args_array);
1500 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1501 } else {
1502 StringParameters tmp_params(NULL, 0, NULL);
1503 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
1505 break;
1508 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1509 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
1510 break;
1513 default:
1514 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
1515 break;
1518 *buff = '\0';
1519 return buff;
1523 static char *StationGetSpecialString(char *buff, int x, const char *last)
1525 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
1526 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
1527 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
1528 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
1529 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
1530 *buff = '\0';
1531 return buff;
1534 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
1536 return GenerateTownNameString(buff, last, ind, seed);
1539 static const char * const _silly_company_names[] = {
1540 "Bloggs Brothers",
1541 "Tiny Transport Ltd.",
1542 "Express Travel",
1543 "Comfy-Coach & Co.",
1544 "Crush & Bump Ltd.",
1545 "Broken & Late Ltd.",
1546 "Sam Speedy & Son",
1547 "Supersonic Travel",
1548 "Mike's Motors",
1549 "Lightning International",
1550 "Pannik & Loozit Ltd.",
1551 "Inter-City Transport",
1552 "Getout & Pushit Ltd."
1555 static const char * const _surname_list[] = {
1556 "Adams",
1557 "Allan",
1558 "Baker",
1559 "Bigwig",
1560 "Black",
1561 "Bloggs",
1562 "Brown",
1563 "Campbell",
1564 "Gordon",
1565 "Hamilton",
1566 "Hawthorn",
1567 "Higgins",
1568 "Green",
1569 "Gribble",
1570 "Jones",
1571 "McAlpine",
1572 "MacDonald",
1573 "McIntosh",
1574 "Muir",
1575 "Murphy",
1576 "Nelson",
1577 "O'Donnell",
1578 "Parker",
1579 "Phillips",
1580 "Pilkington",
1581 "Quigley",
1582 "Sharkey",
1583 "Thomson",
1584 "Watkins"
1587 static const char * const _silly_surname_list[] = {
1588 "Grumpy",
1589 "Dozy",
1590 "Speedy",
1591 "Nosey",
1592 "Dribble",
1593 "Mushroom",
1594 "Cabbage",
1595 "Sniffle",
1596 "Fishy",
1597 "Swindle",
1598 "Sneaky",
1599 "Nutkins"
1602 static const char _initial_name_letters[] = {
1603 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1604 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1607 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
1609 const char * const *base;
1610 uint num;
1612 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1613 base = _silly_surname_list;
1614 num = lengthof(_silly_surname_list);
1615 } else {
1616 base = _surname_list;
1617 num = lengthof(_surname_list);
1620 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
1621 buff = strecpy(buff, " & Co.", last);
1623 return buff;
1626 static char *GenPresidentName(char *buff, uint32 x, const char *last)
1628 char initial[] = "?. ";
1629 const char * const *base;
1630 uint num;
1631 uint i;
1633 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
1634 buff = strecpy(buff, initial, last);
1636 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
1637 if (i < sizeof(_initial_name_letters)) {
1638 initial[0] = _initial_name_letters[i];
1639 buff = strecpy(buff, initial, last);
1642 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1643 base = _silly_surname_list;
1644 num = lengthof(_silly_surname_list);
1645 } else {
1646 base = _surname_list;
1647 num = lengthof(_surname_list);
1650 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
1652 return buff;
1655 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
1657 switch (ind) {
1658 case 1: // not used
1659 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
1661 case 2: // used for Foobar & Co company names
1662 return GenAndCoName(buff, args->GetInt32(), last);
1664 case 3: // President name
1665 return GenPresidentName(buff, args->GetInt32(), last);
1668 /* town name? */
1669 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1670 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
1671 return strecpy(buff, " Transport", last);
1674 /* language name? */
1675 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
1676 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
1677 return strecpy(buff,
1678 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
1681 /* resolution size? */
1682 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
1683 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
1684 buff += seprintf(
1685 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
1687 return buff;
1690 NOT_REACHED();
1693 #ifdef ENABLE_NETWORK
1694 extern void SortNetworkLanguages();
1695 #else /* ENABLE_NETWORK */
1696 static inline void SortNetworkLanguages() {}
1697 #endif /* ENABLE_NETWORK */
1700 * Check whether the header is a valid header for OpenTTD.
1701 * @return true iff the header is deemed valid.
1703 bool LanguagePackHeader::IsValid() const
1705 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1706 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1707 this->plural_form < LANGUAGE_MAX_PLURAL &&
1708 this->text_dir <= 1 &&
1709 this->newgrflangid < MAX_LANG &&
1710 this->num_genders < MAX_NUM_GENDERS &&
1711 this->num_cases < MAX_NUM_CASES &&
1712 StrValid(this->name, lastof(this->name)) &&
1713 StrValid(this->own_name, lastof(this->own_name)) &&
1714 StrValid(this->isocode, lastof(this->isocode)) &&
1715 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
1716 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
1717 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
1721 * Read a particular language.
1722 * @param lang The metadata about the language.
1723 * @return Whether the loading went okay or not.
1725 bool ReadLanguagePack(const LanguageMetadata *lang)
1727 /* Current language pack */
1728 size_t len;
1729 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
1730 if (lang_pack == NULL) return false;
1732 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1733 const char *end = (char *)lang_pack + len + 1;
1735 /* We need at least one byte of lang_pack->data */
1736 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1737 free(lang_pack);
1738 return false;
1741 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1742 for (uint i = 0; i < TEXT_TAB_END; i++) {
1743 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
1745 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
1747 uint count = 0;
1748 for (uint i = 0; i < TEXT_TAB_END; i++) {
1749 uint16 num = lang_pack->offsets[i];
1750 if (num > TAB_SIZE) {
1751 free(lang_pack);
1752 return false;
1755 _langtab_start[i] = count;
1756 _langtab_num[i] = num;
1757 count += num;
1760 /* Allocate offsets */
1761 char **langpack_offs = MallocT<char *>(count);
1763 /* Fill offsets */
1764 char *s = lang_pack->data;
1765 len = (byte)*s++;
1766 for (uint i = 0; i < count; i++) {
1767 if (s + len >= end) {
1768 free(lang_pack);
1769 free(langpack_offs);
1770 return false;
1772 if (len >= 0xC0) {
1773 len = ((len & 0x3F) << 8) + (byte)*s++;
1774 if (s + len >= end) {
1775 free(lang_pack);
1776 free(langpack_offs);
1777 return false;
1780 langpack_offs[i] = s;
1781 s += len;
1782 len = (byte)*s;
1783 *s++ = '\0'; // zero terminate the string
1786 free(_langpack);
1787 _langpack = lang_pack;
1789 free(_langpack_offs);
1790 _langpack_offs = langpack_offs;
1792 _current_language = lang;
1793 _current_text_dir = (TextDirection)_current_language->text_dir;
1794 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
1795 strecpy(_config_language_file, c_file, lastof(_config_language_file));
1796 SetCurrentGrfLangID(_current_language->newgrflangid);
1798 #ifdef WITH_ICU_SORT
1799 /* Delete previous collator. */
1800 if (_current_collator != NULL) {
1801 delete _current_collator;
1802 _current_collator = NULL;
1805 /* Create a collator instance for our current locale. */
1806 UErrorCode status = U_ZERO_ERROR;
1807 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
1808 /* Sort number substrings by their numerical value. */
1809 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
1810 /* Avoid using the collator if it is not correctly set. */
1811 if (U_FAILURE(status)) {
1812 delete _current_collator;
1813 _current_collator = NULL;
1815 #endif /* WITH_ICU_SORT */
1817 /* Some lists need to be sorted again after a language change. */
1818 ReconsiderGameScriptLanguage();
1819 InitializeSortedCargoSpecs();
1820 SortIndustryTypes();
1821 BuildIndustriesLegend();
1822 SortNetworkLanguages();
1823 #ifdef ENABLE_NETWORK
1824 BuildContentTypeStringList();
1825 #endif /* ENABLE_NETWORK */
1826 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
1827 InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
1828 InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
1829 InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
1830 InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
1831 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
1832 InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
1834 return true;
1837 /* Win32 implementation in win32.cpp.
1838 * OS X implementation in os/macosx/macos.mm. */
1839 #if !(defined(WIN32) || defined(__APPLE__))
1841 * Determine the current charset based on the environment
1842 * First check some default values, after this one we passed ourselves
1843 * and if none exist return the value for $LANG
1844 * @param param environment variable to check conditionally if default ones are not
1845 * set. Pass NULL if you don't want additional checks.
1846 * @return return string containing current charset, or NULL if not-determinable
1848 const char *GetCurrentLocale(const char *param)
1850 const char *env;
1852 env = getenv("LANGUAGE");
1853 if (env != NULL) return env;
1855 env = getenv("LC_ALL");
1856 if (env != NULL) return env;
1858 if (param != NULL) {
1859 env = getenv(param);
1860 if (env != NULL) return env;
1863 return getenv("LANG");
1865 #else
1866 const char *GetCurrentLocale(const char *param);
1867 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
1869 int CDECL StringIDSorter(const StringID *a, const StringID *b)
1871 char stra[512];
1872 char strb[512];
1873 GetString(stra, *a, lastof(stra));
1874 GetString(strb, *b, lastof(strb));
1876 return strnatcmp(stra, strb);
1880 * Get the language with the given NewGRF language ID.
1881 * @param newgrflangid NewGRF languages ID to check.
1882 * @return The language's metadata, or NULL if it is not known.
1884 const LanguageMetadata *GetLanguage(byte newgrflangid)
1886 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
1887 if (newgrflangid == lang->newgrflangid) return lang;
1890 return NULL;
1894 * Reads the language file header and checks compatibility.
1895 * @param file the file to read
1896 * @param hdr the place to write the header information to
1897 * @return true if and only if the language file is of a compatible version
1899 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
1901 FILE *f = fopen(file, "rb");
1902 if (f == NULL) return false;
1904 size_t read = fread(hdr, sizeof(*hdr), 1, f);
1905 fclose(f);
1907 bool ret = read == 1 && hdr->IsValid();
1909 /* Convert endianness for the windows language ID */
1910 if (ret) {
1911 hdr->missing = FROM_LE16(hdr->missing);
1912 hdr->winlangid = FROM_LE16(hdr->winlangid);
1914 return ret;
1918 * Gets a list of languages from the given directory.
1919 * @param path the base directory to search in
1921 static void GetLanguageList(const char *path)
1923 DIR *dir = ttd_opendir(path);
1924 if (dir != NULL) {
1925 struct dirent *dirent;
1926 while ((dirent = readdir(dir)) != NULL) {
1927 const char *d_name = FS2OTTD(dirent->d_name);
1928 const char *extension = strrchr(d_name, '.');
1930 /* Not a language file */
1931 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
1933 LanguageMetadata lmd;
1934 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
1936 /* Check whether the file is of the correct version */
1937 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
1938 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
1939 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
1940 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
1941 } else {
1942 *_languages.Append() = lmd;
1945 closedir(dir);
1950 * Make a list of the available language packs. Put the data in
1951 * #_languages list.
1953 void InitializeLanguagePacks()
1955 Searchpath sp;
1957 FOR_ALL_SEARCHPATHS(sp) {
1958 char path[MAX_PATH];
1959 FioAppendDirectory(path, lastof(path), sp, LANG_DIR);
1960 GetLanguageList(path);
1962 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
1964 /* Acquire the locale of the current system */
1965 const char *lang = GetCurrentLocale("LC_MESSAGES");
1966 if (lang == NULL) lang = "en_GB";
1968 const LanguageMetadata *chosen_language = NULL; ///< Matching the language in the configuration file or the current locale
1969 const LanguageMetadata *language_fallback = NULL; ///< Using pt_PT for pt_BR locale when pt_BR is not available
1970 const LanguageMetadata *en_GB_fallback = _languages.Begin(); ///< Fallback when no locale-matching language has been found
1972 /* Find a proper language. */
1973 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
1974 /* We are trying to find a default language. The priority is by
1975 * configuration file, local environment and last, if nothing found,
1976 * English. */
1977 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
1978 if (strcmp(lang_file, _config_language_file) == 0) {
1979 chosen_language = lng;
1980 break;
1983 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
1984 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
1985 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
1988 /* We haven't found the language in the config nor the one in the locale.
1989 * Now we set it to one of the fallback languages */
1990 if (chosen_language == NULL) {
1991 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
1994 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
1998 * Get the ISO language code of the currently loaded language.
1999 * @return the ISO code.
2001 const char *GetCurrentLanguageIsoCode()
2003 return _langpack->isocode;
2007 * Check whether there are glyphs missing in the current language.
2008 * @param Pointer to an address for storing the text pointer.
2009 * @return If glyphs are missing, return \c true, else return \c false.
2010 * @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.
2012 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
2014 InitFreeType(this->Monospace());
2015 const Sprite *question_mark[FS_END];
2017 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
2018 question_mark[size] = GetGlyph(size, '?');
2021 this->Reset();
2022 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
2023 FontSize size = this->DefaultSize();
2024 if (str != NULL) *str = text;
2025 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
2026 if (c == SCC_TINYFONT) {
2027 size = FS_SMALL;
2028 } else if (c == SCC_BIGFONT) {
2029 size = FS_LARGE;
2030 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
2031 /* The character is printable, but not in the normal font. This is the case we were testing for. */
2032 return true;
2036 return false;
2039 /** Helper for searching through the language pack. */
2040 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
2041 uint i; ///< Iterator for the primary language tables.
2042 uint j; ///< Iterator for the secondary language tables.
2044 /* virtual */ void Reset()
2046 this->i = 0;
2047 this->j = 0;
2050 /* virtual */ FontSize DefaultSize()
2052 return FS_NORMAL;
2055 /* virtual */ const char *NextString()
2057 if (this->i >= TEXT_TAB_END) return NULL;
2059 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
2061 this->j++;
2062 while (this->i < TEXT_TAB_END && this->j >= _langtab_num[this->i]) {
2063 this->i++;
2064 this->j = 0;
2067 return ret;
2070 /* virtual */ bool Monospace()
2072 return false;
2075 /* virtual */ void SetFontNames(FreeTypeSettings *settings, const char *font_name)
2077 #ifdef WITH_FREETYPE
2078 strecpy(settings->small.font, font_name, lastof(settings->small.font));
2079 strecpy(settings->medium.font, font_name, lastof(settings->medium.font));
2080 strecpy(settings->large.font, font_name, lastof(settings->large.font));
2081 #endif /* WITH_FREETYPE */
2086 * Check whether the currently loaded language pack
2087 * uses characters that the currently loaded font
2088 * does not support. If this is the case an error
2089 * message will be shown in English. The error
2090 * message will not be localized because that would
2091 * mean it might use characters that are not in the
2092 * font, which is the whole reason this check has
2093 * been added.
2094 * @param base_font Whether to look at the base font as well.
2095 * @param searcher The methods to use to search for strings to check.
2096 * If NULL the loaded language pack searcher is used.
2098 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2100 static LanguagePackGlyphSearcher pack_searcher;
2101 if (searcher == NULL) searcher = &pack_searcher;
2102 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
2103 #ifdef WITH_FREETYPE
2104 if (bad_font) {
2105 /* We found an unprintable character... lets try whether we can find
2106 * a fallback font that can print the characters in the current language. */
2107 FreeTypeSettings backup;
2108 memcpy(&backup, &_freetype, sizeof(backup));
2110 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
2112 memcpy(&_freetype, &backup, sizeof(backup));
2114 if (bad_font && base_font) {
2115 /* Our fallback font does miss characters too, so keep the
2116 * user chosen font as that is more likely to be any good than
2117 * the wild guess we made */
2118 InitFreeType(searcher->Monospace());
2121 #endif
2123 if (bad_font) {
2124 /* All attempts have failed. Display an error. As we do not want the string to be translated by
2125 * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
2126 * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
2127 * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
2128 * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
2129 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.");
2130 Utf8Encode(err_str, SCC_YELLOW);
2131 SetDParamStr(0, err_str);
2132 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2134 /* Reset the font width */
2135 LoadStringWidthTable(searcher->Monospace());
2136 return;
2139 /* Update the font with cache */
2140 LoadStringWidthTable(searcher->Monospace());
2142 #if !defined(WITH_ICU_LAYOUT)
2144 * For right-to-left languages we need the ICU library. If
2145 * we do not have support for that library we warn the user
2146 * about it with a message. As we do not want the string to
2147 * be translated by the translators, we 'force' it into the
2148 * binary and 'load' it via a BindCString. To do this
2149 * properly we have to set the colour of the string,
2150 * otherwise we end up with a lot of artifacts. The colour
2151 * 'character' might change in the future, so for safety
2152 * we just Utf8 Encode it into the string, which takes
2153 * exactly three characters, so it replaces the "XXX" with
2154 * the colour marker.
2156 if (_current_text_dir != TD_LTR) {
2157 static char *err_str = stredup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
2158 Utf8Encode(err_str, SCC_YELLOW);
2159 SetDParamStr(0, err_str);
2160 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
2162 #endif /* !WITH_ICU_LAYOUT */