Remove unused string code SCC_STRING_ID
[openttd/fttd.git] / src / strings.cpp
blobd29381cdb2085e7733b7447faa11cfbe22f05643
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /** @file strings.cpp Handling of translated strings. */
12 #include "stdafx.h"
13 #include "currency.h"
14 #include "station_base.h"
15 #include "town.h"
16 #include "screenshot.h"
17 #include "waypoint_base.h"
18 #include "depot_base.h"
19 #include "industry.h"
20 #include "newgrf_text.h"
21 #include "fileio_func.h"
22 #include "signs_base.h"
23 #include "fontdetection.h"
24 #include "error.h"
25 #include "strings_func.h"
26 #include "rev.h"
27 #include "core/endian_func.hpp"
28 #include "date_func.h"
29 #include "vehicle_base.h"
30 #include "engine_base.h"
31 #include "language.h"
32 #include "townname_func.h"
33 #include "string_func.h"
34 #include "company_base.h"
35 #include "smallmap_gui.h"
36 #include "window_func.h"
37 #include "debug.h"
38 #include "game/game_text.hpp"
39 #include <stack>
41 #include "table/strings.h"
42 #include "table/control_codes.h"
44 char _config_language_file[MAX_PATH]; ///< The file (name) stored in the configuration.
45 LanguageList _languages; ///< The actual list of language meta data.
46 const LanguageMetadata *_current_language = NULL; ///< The currently loaded language.
48 TextDirection _current_text_dir; ///< Text direction of the currently selected language.
50 #ifdef WITH_ICU
51 Collator *_current_collator = NULL; ///< Collator for the language currently in use.
52 #endif /* WITH_ICU */
54 static uint64 _global_string_params_data[20]; ///< Global array of string parameters. To access, use #SetDParam.
55 static WChar _global_string_params_type[20]; ///< Type of parameters stored in #_decode_parameters
56 StringParameters _global_string_params(_global_string_params_data, 20, _global_string_params_type);
58 /** Reset the type array. */
59 void StringParameters::ClearTypeInformation()
61 assert(this->type != NULL);
62 MemSetT(this->type, 0, this->num_param);
66 /**
67 * Read an int64 from the argument array. The offset is increased
68 * so the next time GetInt64 is called the next value is read.
70 int64 StringParameters::GetInt64(WChar type)
72 if (this->offset >= this->num_param) {
73 DEBUG(misc, 0, "Trying to read invalid string parameter");
74 return 0;
76 if (this->type != NULL) {
77 assert(this->type[this->offset] == 0 || this->type[this->offset] == type);
78 this->type[this->offset] = type;
80 return this->data[this->offset++];
83 /**
84 * Shift all data in the data array by the given amount to make
85 * room for some extra parameters.
87 void StringParameters::ShiftParameters(uint amount)
89 assert(amount <= this->num_param);
90 MemMoveT(this->data + amount, this->data, this->num_param - amount);
93 /**
94 * Set DParam n to some number that is suitable for string size computations.
95 * @param n Index of the string parameter.
96 * @param max_value The biggest value which shall be displayed.
97 * For the result only the number of digits of \a max_value matter.
98 * @param min_count Minimum number of digits independent of \a max.
99 * @param size Font of the number
101 void SetDParamMaxValue(uint n, uint64 max_value, uint min_count, FontSize size)
103 uint num_digits = 1;
104 while (max_value >= 10) {
105 num_digits++;
106 max_value /= 10;
108 SetDParamMaxDigits(n, max(min_count, num_digits), size);
112 * Set DParam n to some number that is suitable for string size computations.
113 * @param n Index of the string parameter.
114 * @param count Number of digits which shall be displayable.
115 * @param size Font of the number
117 void SetDParamMaxDigits(uint n, uint count, FontSize size)
119 uint front, next;
120 GetBroadestDigit(&front, &next, size);
121 uint64 val = count > 1 ? front : next;
122 for (; count > 1; count--) {
123 val = 10 * val + next;
125 SetDParam(n, val);
129 * Copy \a num string parameters from array \a src into the global string parameter array.
130 * @param offs Index in the global array to copy the first string parameter to.
131 * @param src Source array of string parameters.
132 * @param num Number of string parameters to copy.
134 void CopyInDParam(int offs, const uint64 *src, int num)
136 MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
140 * Copy \a num string parameters from the global string parameter array to the \a dst array.
141 * @param dst Destination array of string parameters.
142 * @param offs Index in the global array to copy the first string parameter from.
143 * @param num Number of string parameters to copy.
145 void CopyOutDParam(uint64 *dst, int offs, int num)
147 MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
151 * Copy \a num string parameters from the global string parameter array to the \a dst array.
152 * Furthermore clone raw string parameters into \a strings and amend the data in \a dst.
153 * @param dst Destination array of string parameters.
154 * @param strings Destination array for clone of the raw strings. Must be of same length as dst. Deallocation left to the caller.
155 * @param string The string used to determine where raw strings are and where there are no raw strings.
156 * @param num Number of string parameters to copy.
158 void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
160 char buf[DRAW_STRING_BUFFER];
161 GetString(buf, string, lastof(buf));
163 MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num);
164 for (int i = 0; i < num; i++) {
165 if (_global_string_params.HasTypeInformation() && _global_string_params.GetTypeAtOffset(i) == SCC_RAW_STRING_POINTER) {
166 strings[i] = strdup((const char *)(size_t)_global_string_params.GetParam(i));
167 dst[i] = (size_t)strings[i];
168 } else {
169 strings[i] = NULL;
174 static char *StationGetSpecialString(char *buff, int x, const char *last);
175 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
176 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
178 static char *FormatString(char *buff, const char *str, StringParameters *args, const char *last, uint case_index = 0, bool game_script = false, bool dry_run = false);
180 struct LanguagePack : public LanguagePackHeader {
181 char data[]; // list of strings
184 static char **_langpack_offs;
185 static LanguagePack *_langpack;
186 static uint _langtab_num[TAB_COUNT]; ///< Offset into langpack offs
187 static uint _langtab_start[TAB_COUNT]; ///< Offset into langpack offs
188 static bool _scan_for_gender_data = false; ///< Are we scanning for the gender of the current string? (instead of formatting it)
191 const char *GetStringPtr(StringID string)
193 switch (GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)) {
194 case GAME_TEXT_TAB: return GetGameStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
195 /* GetGRFStringPtr doesn't handle 0xD4xx ids, we need to convert those to 0xD0xx. */
196 case 26: return GetStringPtr(GetGRFStringID(0, 0xD000 + GB(string, TAB_SIZE_OFFSET, 10)));
197 case 28: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
198 case 29: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x0800);
199 case 30: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x1000);
200 default: return _langpack_offs[_langtab_start[GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)] + GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS)];
205 * Get a parsed string with most special stringcodes replaced by the string parameters.
206 * @param buffr Pointer to a string buffer where the formatted string should be written to.
207 * @param string
208 * @param args Arguments for the string.
209 * @param last Pointer just past the end of \a buffr.
210 * @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case.
211 * @param game_script The string is coming directly from a game script.
212 * @return Pointer to the final zero byte of the formatted string.
214 char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
216 if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
218 uint index = GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS);
219 uint tab = GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS);
221 switch (tab) {
222 case 4:
223 if (index >= 0xC0 && !game_script) {
224 return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
226 break;
228 case 14:
229 if (index >= 0xE4 && !game_script) {
230 return GetSpecialNameString(buffr, index - 0xE4, args, last);
232 break;
234 case 15:
235 /* Old table for custom names. This is no longer used */
236 if (!game_script) {
237 error("Incorrect conversion of custom name string.");
239 break;
241 case GAME_TEXT_TAB:
242 return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
244 case 26:
245 /* Include string within newgrf text (format code 81) */
246 if (HasBit(index, 10)) {
247 StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
248 return GetStringWithArgs(buffr, string, args, last, case_index);
250 break;
252 case 28:
253 return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
255 case 29:
256 return FormatString(buffr, GetGRFStringPtr(index + 0x0800), args, last, case_index);
258 case 30:
259 return FormatString(buffr, GetGRFStringPtr(index + 0x1000), args, last, case_index);
262 if (index >= _langtab_num[tab]) {
263 if (game_script) {
264 return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
266 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
269 return FormatString(buffr, GetStringPtr(string), args, last, case_index);
272 char *GetString(char *buffr, StringID string, const char *last)
274 _global_string_params.ClearTypeInformation();
275 _global_string_params.offset = 0;
276 return GetStringWithArgs(buffr, string, &_global_string_params, last);
281 * This function is used to "bind" a C string to a OpenTTD dparam slot.
282 * @param n slot of the string
283 * @param str string to bind
285 void SetDParamStr(uint n, const char *str)
287 SetDParam(n, (uint64)(size_t)str);
291 * Shift the string parameters in the global string parameter array by \a amount positions, making room at the beginning.
292 * @param amount Number of positions to shift.
294 void InjectDParam(uint amount)
296 _global_string_params.ShiftParameters(amount);
300 * Format a number into a string.
301 * @param buff the buffer to write to
302 * @param number the number to write down
303 * @param last the last element in the buffer
304 * @param separator the thousands-separator to use
305 * @param zerofill minimum number of digits to print for the integer part. The number will be filled with zeros at the front if necessary.
306 * @param fractional_digits number of fractional digits to display after a decimal separator. The decimal separator is inserted
307 * in front of the \a fractional_digits last digit of \a number.
308 * @return till where we wrote
310 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
312 static const int max_digits = 20;
313 uint64 divisor = 10000000000000000000ULL;
314 zerofill += fractional_digits;
315 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
317 if (number < 0) {
318 buff += seprintf(buff, last, "-");
319 number = -number;
322 uint64 num = number;
323 uint64 tot = 0;
324 for (int i = 0; i < max_digits; i++) {
325 if (i == max_digits - fractional_digits) {
326 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
327 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
328 buff += seprintf(buff, last, "%s", decimal_separator);
331 uint64 quot = 0;
332 if (num >= divisor) {
333 quot = num / divisor;
334 num = num % divisor;
336 if ((tot |= quot) || i >= max_digits - zerofill) {
337 buff += seprintf(buff, last, "%i", (int)quot);
338 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
341 divisor /= 10;
344 *buff = '\0';
346 return buff;
349 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
351 const char *separator = _settings_game.locale.digit_group_separator;
352 if (separator == NULL) separator = _langpack->digit_group_separator;
353 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
356 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
358 return FormatNumber(buff, number, last, "");
361 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
363 return FormatNumber(buff, number, last, "", count);
366 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
368 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
372 * Format a given number as a number of bytes with the SI prefix.
373 * @param buff the buffer to write to
374 * @param number the number of bytes to write down
375 * @param last the last element in the buffer
376 * @return till where we wrote
378 static char *FormatBytes(char *buff, int64 number, const char *last)
380 assert(number >= 0);
382 /* 1 2^10 2^20 2^30 2^40 2^50 2^60 */
383 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
384 uint id = 1;
385 while (number >= 1024 * 1024) {
386 number /= 1024;
387 id++;
390 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
391 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
393 if (number < 1024) {
394 id = 0;
395 buff += seprintf(buff, last, "%i", (int)number);
396 } else if (number < 1024 * 10) {
397 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
398 } else if (number < 1024 * 100) {
399 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
400 } else {
401 assert(number < 1024 * 1024);
402 buff += seprintf(buff, last, "%i", (int)number / 1024);
405 assert(id < lengthof(iec_prefixes));
406 buff += seprintf(buff, last, " %sB", iec_prefixes[id]);
408 return buff;
411 static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
413 YearMonthDay ymd;
414 ConvertDateToYMD(date, &ymd);
416 int64 args[] = {ymd.day + STR_ORDINAL_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
417 StringParameters tmp_params(args);
418 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
421 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
423 YearMonthDay ymd;
424 ConvertDateToYMD(date, &ymd);
426 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
427 StringParameters tmp_params(args);
428 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
431 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
433 YearMonthDay ymd;
434 ConvertDateToYMD(date, &ymd);
436 char day[3];
437 char month[3];
438 /* We want to zero-pad the days and months */
439 snprintf(day, lengthof(day), "%02i", ymd.day);
440 snprintf(month, lengthof(month), "%02i", ymd.month + 1);
442 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
443 StringParameters tmp_params(args);
444 return FormatString(buff, GetStringPtr(str), &tmp_params, last);
447 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
449 /* We are going to make number absolute for printing, so
450 * keep this piece of data as we need it later on */
451 bool negative = number < 0;
452 const char *multiplier = "";
454 number *= spec->rate;
456 /* convert from negative */
457 if (number < 0) {
458 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
459 buff += Utf8Encode(buff, SCC_RED);
460 buff = strecpy(buff, "-", last);
461 number = -number;
464 /* Add prefix part, following symbol_pos specification.
465 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
466 * The only remaining value is 1 (suffix), so everything that is not 1 */
467 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
469 /* for huge numbers, compact the number into k or M */
470 if (compact) {
471 /* Take care of the 'k' rounding. Having 1 000 000 k
472 * and 1 000 M is inconsistent, so always use 1 000 M. */
473 if (number >= 1000000000 - 500) {
474 number = (number + 500000) / 1000000;
475 multiplier = "M";
476 } else if (number >= 1000000) {
477 number = (number + 500) / 1000;
478 multiplier = "k";
482 const char *separator = _settings_game.locale.digit_group_separator_currency;
483 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
484 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
485 buff = FormatNumber(buff, number, last, separator);
486 buff = strecpy(buff, multiplier, last);
488 /* Add suffix part, following symbol_pos specification.
489 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
490 * The only remaining value is 1 (prefix), so everything that is not 0 */
491 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
493 if (negative) {
494 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
495 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
496 *buff = '\0';
499 return buff;
503 * Determine the "plural" index given a plural form and a number.
504 * @param count The number to get the plural index of.
505 * @param plural_form The plural form we want an index for.
506 * @return The plural index for the given form.
508 static int DeterminePluralForm(int64 count, int plural_form)
510 /* The absolute value determines plurality */
511 uint64 n = abs(count);
513 switch (plural_form) {
514 default:
515 NOT_REACHED();
517 /* Two forms: singular used for one only.
518 * Used in:
519 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
520 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
521 case 0:
522 return n != 1 ? 1 : 0;
524 /* Only one form.
525 * Used in:
526 * Hungarian, Japanese, Korean, Turkish */
527 case 1:
528 return 0;
530 /* Two forms: singular used for 0 and 1.
531 * Used in:
532 * French, Brazilian Portuguese */
533 case 2:
534 return n > 1 ? 1 : 0;
536 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
537 * Note: Cases are out of order for hysterical reasons. '0' is last.
538 * Used in:
539 * Latvian */
540 case 3:
541 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
543 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
544 * Used in:
545 * Gaelige (Irish) */
546 case 4:
547 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
549 /* 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.
550 * Used in:
551 * Lithuanian */
552 case 5:
553 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
555 /* 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.
556 * Used in:
557 * Croatian, Russian, Ukrainian */
558 case 6:
559 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
561 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
562 * Used in:
563 * Polish */
564 case 7:
565 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
567 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
568 * Used in:
569 * Slovenian */
570 case 8:
571 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
573 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
574 * Used in:
575 * Icelandic */
576 case 9:
577 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
579 /* Three forms: special cases for 1, and 2 to 4
580 * Used in:
581 * Czech, Slovak */
582 case 10:
583 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
585 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
586 * Korean doesn't have the concept of plural, but depending on how a
587 * number is pronounced it needs another version of a particle.
588 * As such the plural system is misused to give this distinction.
590 case 11:
591 switch (n % 10) {
592 case 0: // yeong
593 case 1: // il
594 case 3: // sam
595 case 6: // yuk
596 case 7: // chil
597 case 8: // pal
598 return 0;
600 case 2: // i
601 case 4: // sa
602 case 5: // o
603 case 9: // gu
604 return 1;
606 default:
607 NOT_REACHED();
610 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
611 * Used in:
612 * Maltese */
613 case 12:
614 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
615 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
616 * Used in:
617 * Scottish Gaelic */
618 case 13:
619 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
623 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
625 /* <NUM> {Length of each string} {each string} */
626 uint n = (byte)*b++;
627 uint pos, i, mypos = 0;
629 for (i = pos = 0; i != n; i++) {
630 uint len = (byte)*b++;
631 if (i == form) mypos = pos;
632 pos += len;
635 *dst += seprintf(*dst, last, "%s", b + mypos);
636 return b + pos;
639 /** Helper for unit conversion. */
640 struct UnitConversion {
641 int multiplier; ///< Amount to multiply upon conversion.
642 int shift; ///< Amount to shift upon conversion.
645 * Convert value from OpenTTD's internal unit into the displayed value.
646 * @param input The input to convert.
647 * @param round Whether to round the value or not.
648 * @return The converted value.
650 int64 ToDisplay(int64 input, bool round = true) const
652 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
656 * Convert the displayed value back into a value of OpenTTD's internal unit.
657 * @param input The input to convert.
658 * @param round Whether to round the value up or not.
659 * @param divider Divide the return value by this.
660 * @return The converted value.
662 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
664 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
668 /** Information about a specific unit system. */
669 struct Units {
670 UnitConversion c; ///< Conversion
671 StringID s; ///< String for the unit
674 /** Information about a specific unit system with a long variant. */
675 struct UnitsLong {
676 UnitConversion c; ///< Conversion
677 StringID s; ///< String for the short variant of the unit
678 StringID l; ///< String for the long variant of the unit
681 /** Unit conversions for velocity. */
682 static const Units _units_velocity[] = {
683 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL },
684 { { 103, 6}, STR_UNITS_VELOCITY_METRIC },
685 { {1831, 12}, STR_UNITS_VELOCITY_SI },
688 /** Unit conversions for velocity. */
689 static const Units _units_power[] = {
690 { { 1, 0}, STR_UNITS_POWER_IMPERIAL },
691 { {4153, 12}, STR_UNITS_POWER_METRIC },
692 { {6109, 13}, STR_UNITS_POWER_SI },
695 /** Unit conversions for weight. */
696 static const UnitsLong _units_weight[] = {
697 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
698 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC },
699 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI },
702 /** Unit conversions for volume. */
703 static const UnitsLong _units_volume[] = {
704 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
705 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC },
706 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI },
709 /** Unit conversions for force. */
710 static const Units _units_force[] = {
711 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL },
712 { {3263, 5}, STR_UNITS_FORCE_METRIC },
713 { { 1, 0}, STR_UNITS_FORCE_SI },
716 /** Unit conversions for height. */
717 static const Units _units_height[] = {
718 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL }, // "Wrong" conversion factor for more nicer GUI values
719 { { 1, 0}, STR_UNITS_HEIGHT_METRIC },
720 { { 1, 0}, STR_UNITS_HEIGHT_SI },
724 * Convert the given (internal) speed to the display speed.
725 * @param speed the speed to convert
726 * @return the converted speed.
728 uint ConvertSpeedToDisplaySpeed(uint speed)
730 /* For historical reasons we don't want to mess with the
731 * conversion for speed. So, don't round it and keep the
732 * original conversion factors instead of the real ones. */
733 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed, false);
737 * Convert the given display speed to the (internal) speed.
738 * @param speed the speed to convert
739 * @return the converted speed.
741 uint ConvertDisplaySpeedToSpeed(uint speed)
743 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed);
747 * Convert the given km/h-ish speed to the display speed.
748 * @param speed the speed to convert
749 * @return the converted speed.
751 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
753 return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed * 10, false) / 16;
757 * Convert the given display speed to the km/h-ish speed.
758 * @param speed the speed to convert
759 * @return the converted speed.
761 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
763 return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed * 16, true, 10);
766 * Parse most format codes within a string and write the result to a buffer.
767 * @param buff The buffer to write the final string to.
768 * @param str The original string with format codes.
769 * @param args Pointer to extra arguments used by various string codes.
770 * @param case_index
771 * @param last Pointer to just past the end of the buff array.
772 * @param dry_run True when the argt array is not yet initialized.
774 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
776 uint orig_offset = args->offset;
778 /* When there is no array with types there is no need to do a dry run. */
779 if (args->HasTypeInformation() && !dry_run) {
780 if (UsingNewGRFTextStack()) {
781 /* Values from the NewGRF text stack are only copied to the normal
782 * argv array at the time they are encountered. That means that if
783 * another string command references a value later in the string it
784 * would fail. We solve that by running FormatString twice. The first
785 * pass makes sure the argv array is correctly filled and the second
786 * pass can reference later values without problems. */
787 struct TextRefStack *backup = CreateTextRefStackBackup();
788 FormatString(buff, str_arg, args, last, case_index, game_script, true);
789 RestoreTextRefStackBackup(backup);
790 } else {
791 FormatString(buff, str_arg, args, last, case_index, game_script, true);
793 /* We have to restore the original offset here to to read the correct values. */
794 args->offset = orig_offset;
796 WChar b = '\0';
797 uint next_substr_case_index = 0;
798 char *buf_start = buff;
799 std::stack<const char *> str_stack;
800 str_stack.push(str_arg);
802 for (;;) {
803 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
804 str_stack.pop();
806 if (str_stack.empty()) break;
807 const char *&str = str_stack.top();
809 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
810 /* We need to pass some stuff as it might be modified; oh boy. */
811 //todo: should argve be passed here too?
812 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), dry_run);
813 if (b == 0) continue;
816 switch (b) {
817 case SCC_ENCODED: {
818 uint64 sub_args_data[20];
819 WChar sub_args_type[20];
820 bool sub_args_need_free[20];
821 StringParameters sub_args(sub_args_data, 20, sub_args_type);
823 sub_args.ClearTypeInformation();
824 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
826 uint16 stringid;
827 const char *s = str;
828 char *p;
829 stringid = strtol(str, &p, 16);
830 if (*p != ':' && *p != '\0') {
831 while (*p != '\0') p++;
832 str = p;
833 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
834 break;
836 if (stringid >= TAB_SIZE) {
837 while (*p != '\0') p++;
838 str = p;
839 buff = strecat(buff, "(invalid StringID)", last);
840 break;
843 int i = 0;
844 while (*p != '\0' && i < 20) {
845 uint64 param;
846 s = ++p;
848 /* Find the next value */
849 bool instring = false;
850 bool escape = false;
851 for (;; p++) {
852 if (*p == '\\') {
853 escape = true;
854 continue;
856 if (*p == '"' && escape) {
857 escape = false;
858 continue;
860 escape = false;
862 if (*p == '"') {
863 instring = !instring;
864 continue;
866 if (instring) {
867 continue;
870 if (*p == ':') break;
871 if (*p == '\0') break;
874 if (*s != '"') {
875 /* Check if we want to look up another string */
876 WChar l;
877 size_t len = Utf8Decode(&l, s);
878 bool lookup = (l == SCC_ENCODED);
879 if (lookup) s += len;
881 param = (int32)strtoul(s, &p, 16);
883 if (lookup) {
884 if (param >= TAB_SIZE) {
885 while (*p != '\0') p++;
886 str = p;
887 buff = strecat(buff, "(invalid sub-StringID)", last);
888 break;
890 param = (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + param;
893 sub_args.SetParam(i++, param);
894 } else {
895 char *g = strdup(s);
896 g[p - s] = '\0';
898 sub_args_need_free[i] = true;
899 sub_args.SetParam(i++, (uint64)(size_t)g);
902 /* If we didn't error out, we can actually print the string. */
903 if (*str != '\0') {
904 str = p;
905 buff = GetStringWithArgs(buff, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, last, true);
908 for (int i = 0; i < 20; i++) {
909 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
911 break;
914 case SCC_NEWGRF_STRINL: {
915 StringID substr = Utf8Consume(&str);
916 str_stack.push(GetStringPtr(substr));
917 break;
920 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
921 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
922 str_stack.push(GetStringPtr(substr));
923 case_index = next_substr_case_index;
924 next_substr_case_index = 0;
925 break;
929 case SCC_GENDER_LIST: { // {G 0 Der Die Das}
930 /* First read the meta data from the language file. */
931 uint offset = orig_offset + (byte)*str++;
932 int gender = 0;
933 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
934 /* Now we need to figure out what text to resolve, i.e.
935 * what do we need to draw? So get the actual raw string
936 * first using the control code to get said string. */
937 char input[4 + 1];
938 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
939 *p = '\0';
941 /* Now do the string formatting. */
942 char buf[256];
943 bool old_sgd = _scan_for_gender_data;
944 _scan_for_gender_data = true;
945 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
946 p = FormatString(buf, input, &tmp_params, lastof(buf));
947 _scan_for_gender_data = old_sgd;
948 *p = '\0';
950 /* And determine the string. */
951 const char *s = buf;
952 WChar c = Utf8Consume(&s);
953 /* Does this string have a gender, if so, set it */
954 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
956 str = ParseStringChoice(str, gender, &buff, last);
957 break;
960 /* This sets up the gender for the string.
961 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
962 case SCC_GENDER_INDEX: // {GENDER 0}
963 if (_scan_for_gender_data) {
964 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
965 *buff++ = *str++;
966 } else {
967 str++;
969 break;
971 case SCC_PLURAL_LIST: { // {P}
972 int plural_form = *str++; // contains the plural form for this string
973 uint offset = orig_offset + (byte)*str++;
974 int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
975 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
976 break;
979 case SCC_ARG_INDEX: { // Move argument pointer
980 args->offset = orig_offset + (byte)*str++;
981 break;
984 case SCC_SET_CASE: { // {SET_CASE}
985 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
986 * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
987 next_substr_case_index = (byte)*str++;
988 break;
991 case SCC_SWITCH_CASE: { // {Used to implement case switching}
992 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
993 * Each LEN is printed using 2 bytes in big endian order. */
994 uint num = (byte)*str++;
995 while (num) {
996 if ((byte)str[0] == case_index) {
997 /* Found the case, adjust str pointer and continue */
998 str += 3;
999 break;
1001 /* Otherwise skip to the next case */
1002 str += 3 + (str[1] << 8) + str[2];
1003 num--;
1005 break;
1008 case SCC_REVISION: // {REV}
1009 buff = strecpy(buff, _openttd_revision, last);
1010 break;
1012 case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
1013 if (game_script) break;
1014 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
1015 buff = FormatString(buff, str, args, last);
1016 break;
1019 case SCC_STRING: {// {STRING}
1020 StringID str = args->GetInt32(SCC_STRING);
1021 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
1022 /* WARNING. It's prohibited for the included string to consume any arguments.
1023 * For included strings that consume argument, you should use STRING1, STRING2 etc.
1024 * To debug stuff you can set argv to NULL and it will tell you */
1025 StringParameters tmp_params(args->GetDataPointer(), args->num_param - args->offset, NULL);
1026 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
1027 next_substr_case_index = 0;
1028 break;
1031 case SCC_STRING1:
1032 case SCC_STRING2:
1033 case SCC_STRING3:
1034 case SCC_STRING4:
1035 case SCC_STRING5:
1036 case SCC_STRING6:
1037 case SCC_STRING7: { // {STRING1..7}
1038 /* Strings that consume arguments */
1039 StringID str = args->GetInt32(b);
1040 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
1041 uint size = b - SCC_STRING1 + 1;
1042 if (game_script && size > args->num_param - args->offset) {
1043 buff = strecat(buff, "(too many parameters)", last);
1044 } else {
1045 StringParameters sub_args(*args, size);
1046 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
1048 next_substr_case_index = 0;
1049 break;
1052 case SCC_COMMA: // {COMMA}
1053 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
1054 break;
1056 case SCC_DECIMAL: {// {DECIMAL}
1057 int64 number = args->GetInt64(SCC_DECIMAL);
1058 int digits = args->GetInt32(SCC_DECIMAL);
1059 buff = FormatCommaNumber(buff, number, last, digits);
1060 break;
1063 case SCC_NUM: // {NUM}
1064 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
1065 break;
1067 case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
1068 int64 num = args->GetInt64();
1069 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
1070 break;
1073 case SCC_HEX: // {HEX}
1074 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
1075 break;
1077 case SCC_BYTES: // {BYTES}
1078 buff = FormatBytes(buff, args->GetInt64(), last);
1079 break;
1081 case SCC_CARGO_TINY: { // {CARGO_TINY}
1082 /* Tiny description of cargotypes. Layout:
1083 * param 1: cargo type
1084 * param 2: cargo count */
1085 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
1086 if (cargo >= CargoSpec::GetArraySize()) break;
1088 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1089 int64 amount = 0;
1090 switch (cargo_str) {
1091 case STR_TONS:
1092 amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64());
1093 break;
1095 case STR_LITERS:
1096 amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64());
1097 break;
1099 default: {
1100 amount = args->GetInt64();
1101 break;
1105 buff = FormatCommaNumber(buff, amount, last);
1106 break;
1109 case SCC_CARGO_SHORT: { // {CARGO_SHORT}
1110 /* Short description of cargotypes. Layout:
1111 * param 1: cargo type
1112 * param 2: cargo count */
1113 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
1114 if (cargo >= CargoSpec::GetArraySize()) break;
1116 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
1117 switch (cargo_str) {
1118 case STR_TONS: {
1119 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1120 int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1121 StringParameters tmp_params(args_array);
1122 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1123 break;
1126 case STR_LITERS: {
1127 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1128 int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1129 StringParameters tmp_params(args_array);
1130 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1131 break;
1134 default: {
1135 StringParameters tmp_params(*args, 1);
1136 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
1137 break;
1140 break;
1143 case SCC_CARGO_LONG: { // {CARGO_LONG}
1144 /* First parameter is cargo type, second parameter is cargo count */
1145 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
1146 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
1148 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
1149 StringParameters tmp_args(*args, 1);
1150 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
1151 break;
1154 case SCC_CARGO_LIST: { // {CARGO_LIST}
1155 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
1156 bool first = true;
1158 const CargoSpec *cs;
1159 FOR_ALL_SORTED_CARGOSPECS(cs) {
1160 if (!HasBit(cmask, cs->Index())) continue;
1162 if (buff >= last - 2) break; // ',' and ' '
1164 if (first) {
1165 first = false;
1166 } else {
1167 /* Add a comma if this is not the first item */
1168 *buff++ = ',';
1169 *buff++ = ' ';
1172 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
1175 /* If first is still true then no cargo is accepted */
1176 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
1178 *buff = '\0';
1179 next_substr_case_index = 0;
1181 /* Make sure we detect any buffer overflow */
1182 assert(buff < last);
1183 break;
1186 case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
1187 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
1188 break;
1190 case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
1191 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
1192 break;
1194 case SCC_DATE_TINY: // {DATE_TINY}
1195 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
1196 break;
1198 case SCC_DATE_SHORT: // {DATE_SHORT}
1199 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
1200 next_substr_case_index = 0;
1201 break;
1203 case SCC_DATE_LONG: // {DATE_LONG}
1204 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
1205 next_substr_case_index = 0;
1206 break;
1208 case SCC_DATE_ISO: // {DATE_ISO}
1209 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
1210 break;
1212 case SCC_FORCE: { // {FORCE}
1213 assert(_settings_game.locale.units_force < lengthof(_units_force));
1214 int64 args_array[1] = {_units_force[_settings_game.locale.units_force].c.ToDisplay(args->GetInt64())};
1215 StringParameters tmp_params(args_array);
1216 buff = FormatString(buff, GetStringPtr(_units_force[_settings_game.locale.units_force].s), &tmp_params, last);
1217 break;
1220 case SCC_HEIGHT: { // {HEIGHT}
1221 assert(_settings_game.locale.units_height < lengthof(_units_height));
1222 int64 args_array[] = {_units_height[_settings_game.locale.units_height].c.ToDisplay(args->GetInt64())};
1223 StringParameters tmp_params(args_array);
1224 buff = FormatString(buff, GetStringPtr(_units_height[_settings_game.locale.units_height].s), &tmp_params, last);
1225 break;
1228 case SCC_POWER: { // {POWER}
1229 assert(_settings_game.locale.units_power < lengthof(_units_power));
1230 int64 args_array[1] = {_units_power[_settings_game.locale.units_power].c.ToDisplay(args->GetInt64())};
1231 StringParameters tmp_params(args_array);
1232 buff = FormatString(buff, GetStringPtr(_units_power[_settings_game.locale.units_power].s), &tmp_params, last);
1233 break;
1236 case SCC_VELOCITY: { // {VELOCITY}
1237 assert(_settings_game.locale.units_velocity < lengthof(_units_velocity));
1238 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
1239 StringParameters tmp_params(args_array);
1240 buff = FormatString(buff, GetStringPtr(_units_velocity[_settings_game.locale.units_velocity].s), &tmp_params, last);
1241 break;
1244 case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
1245 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1246 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
1247 StringParameters tmp_params(args_array);
1248 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].s), &tmp_params, last);
1249 break;
1252 case SCC_VOLUME_LONG: { // {VOLUME_LONG}
1253 assert(_settings_game.locale.units_volume < lengthof(_units_volume));
1254 int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
1255 StringParameters tmp_params(args_array);
1256 buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
1257 break;
1260 case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
1261 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1262 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
1263 StringParameters tmp_params(args_array);
1264 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].s), &tmp_params, last);
1265 break;
1268 case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
1269 assert(_settings_game.locale.units_weight < lengthof(_units_weight));
1270 int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
1271 StringParameters tmp_params(args_array);
1272 buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
1273 break;
1276 case SCC_COMPANY_NAME: { // {COMPANY}
1277 const Company *c = Company::GetIfValid(args->GetInt32());
1278 if (c == NULL) break;
1280 if (c->name != NULL) {
1281 int64 args_array[] = {(uint64)(size_t)c->name};
1282 StringParameters tmp_params(args_array);
1283 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1284 } else {
1285 int64 args_array[] = {c->name_2};
1286 StringParameters tmp_params(args_array);
1287 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
1289 break;
1292 case SCC_COMPANY_NUM: { // {COMPANY_NUM}
1293 CompanyID company = (CompanyID)args->GetInt32();
1295 /* Nothing is added for AI or inactive companies */
1296 if (Company::IsValidHumanID(company)) {
1297 int64 args_array[] = {company + 1};
1298 StringParameters tmp_params(args_array);
1299 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
1301 break;
1304 case SCC_DEPOT_NAME: { // {DEPOT}
1305 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
1306 if (vt == VEH_AIRCRAFT) {
1307 uint64 args_array[] = {args->GetInt32()};
1308 WChar types_array[] = {SCC_STATION_NAME};
1309 StringParameters tmp_params(args_array, 1, types_array);
1310 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
1311 break;
1314 const Depot *d = Depot::Get(args->GetInt32());
1315 if (d->name != NULL) {
1316 int64 args_array[] = {(uint64)(size_t)d->name};
1317 StringParameters tmp_params(args_array);
1318 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1319 } else {
1320 int64 args_array[] = {d->town->index, d->town_cn + 1};
1321 StringParameters tmp_params(args_array);
1322 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
1324 break;
1327 case SCC_ENGINE_NAME: { // {ENGINE}
1328 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
1329 if (e == NULL) break;
1331 if (e->name != NULL && e->IsEnabled()) {
1332 int64 args_array[] = {(uint64)(size_t)e->name};
1333 StringParameters tmp_params(args_array);
1334 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1335 } else {
1336 StringParameters tmp_params(NULL, 0, NULL);
1337 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
1339 break;
1342 case SCC_GROUP_NAME: { // {GROUP}
1343 const Group *g = Group::GetIfValid(args->GetInt32());
1344 if (g == NULL) break;
1346 if (g->name != NULL) {
1347 int64 args_array[] = {(uint64)(size_t)g->name};
1348 StringParameters tmp_params(args_array);
1349 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1350 } else {
1351 int64 args_array[] = {g->index};
1352 StringParameters tmp_params(args_array);
1354 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
1356 break;
1359 case SCC_INDUSTRY_NAME: { // {INDUSTRY}
1360 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
1361 if (i == NULL) break;
1363 if (_scan_for_gender_data) {
1364 /* Gender is defined by the industry type.
1365 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1366 StringParameters tmp_params(NULL, 0, NULL);
1367 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
1368 } else {
1369 /* First print the town name and the industry type name. */
1370 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
1371 StringParameters tmp_params(args_array);
1373 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
1375 next_substr_case_index = 0;
1376 break;
1379 case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
1380 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
1381 if (c == NULL) break;
1383 if (c->president_name != NULL) {
1384 int64 args_array[] = {(uint64)(size_t)c->president_name};
1385 StringParameters tmp_params(args_array);
1386 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1387 } else {
1388 int64 args_array[] = {c->president_name_2};
1389 StringParameters tmp_params(args_array);
1390 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
1392 break;
1395 case SCC_STATION_NAME: { // {STATION}
1396 StationID sid = args->GetInt32(SCC_STATION_NAME);
1397 const Station *st = Station::GetIfValid(sid);
1399 if (st == NULL) {
1400 /* The station doesn't exist anymore. The only place where we might
1401 * be "drawing" an invalid station is in the case of cargo that is
1402 * in transit. */
1403 StringParameters tmp_params(NULL, 0, NULL);
1404 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
1405 break;
1408 if (st->name != NULL) {
1409 int64 args_array[] = {(uint64)(size_t)st->name};
1410 StringParameters tmp_params(args_array);
1411 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1412 } else {
1413 StringID str = st->string_id;
1414 if (st->indtype != IT_INVALID) {
1415 /* Special case where the industry provides the name for the station */
1416 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
1418 /* Industry GRFs can change which might remove the station name and
1419 * thus cause very strange things. Here we check for that before we
1420 * actually set the station name. */
1421 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
1422 str = indsp->station_name;
1426 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
1427 StringParameters tmp_params(args_array);
1428 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1430 break;
1433 case SCC_TOWN_NAME: { // {TOWN}
1434 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
1435 if (t == NULL) break;
1437 if (t->name != NULL) {
1438 int64 args_array[] = {(uint64)(size_t)t->name};
1439 StringParameters tmp_params(args_array);
1440 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1441 } else {
1442 buff = GetTownName(buff, t, last);
1444 break;
1447 case SCC_WAYPOINT_NAME: { // {WAYPOINT}
1448 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
1449 if (wp == NULL) break;
1451 if (wp->name != NULL) {
1452 int64 args_array[] = {(uint64)(size_t)wp->name};
1453 StringParameters tmp_params(args_array);
1454 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1455 } else {
1456 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
1457 StringParameters tmp_params(args_array);
1458 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1459 if (wp->town_cn != 0) str++;
1460 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1462 break;
1465 case SCC_VEHICLE_NAME: { // {VEHICLE}
1466 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
1467 if (v == NULL) break;
1469 if (v->name != NULL) {
1470 int64 args_array[] = {(uint64)(size_t)v->name};
1471 StringParameters tmp_params(args_array);
1472 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1473 } else {
1474 int64 args_array[] = {v->unitnumber};
1475 StringParameters tmp_params(args_array);
1477 StringID str;
1478 switch (v->type) {
1479 default: str = STR_INVALID_VEHICLE; break;
1480 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
1481 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
1482 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
1483 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
1486 buff = GetStringWithArgs(buff, str, &tmp_params, last);
1488 break;
1491 case SCC_SIGN_NAME: { // {SIGN}
1492 const Sign *si = Sign::GetIfValid(args->GetInt32());
1493 if (si == NULL) break;
1495 if (si->name != NULL) {
1496 int64 args_array[] = {(uint64)(size_t)si->name};
1497 StringParameters tmp_params(args_array);
1498 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
1499 } else {
1500 StringParameters tmp_params(NULL, 0, NULL);
1501 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
1503 break;
1506 case SCC_STATION_FEATURES: { // {STATIONFEATURES}
1507 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
1508 break;
1511 default:
1512 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
1513 break;
1516 *buff = '\0';
1517 return buff;
1521 static char *StationGetSpecialString(char *buff, int x, const char *last)
1523 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
1524 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
1525 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
1526 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
1527 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
1528 *buff = '\0';
1529 return buff;
1532 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
1534 return GenerateTownNameString(buff, last, ind, seed);
1537 static const char * const _silly_company_names[] = {
1538 "Bloggs Brothers",
1539 "Tiny Transport Ltd.",
1540 "Express Travel",
1541 "Comfy-Coach & Co.",
1542 "Crush & Bump Ltd.",
1543 "Broken & Late Ltd.",
1544 "Sam Speedy & Son",
1545 "Supersonic Travel",
1546 "Mike's Motors",
1547 "Lightning International",
1548 "Pannik & Loozit Ltd.",
1549 "Inter-City Transport",
1550 "Getout & Pushit Ltd."
1553 static const char * const _surname_list[] = {
1554 "Adams",
1555 "Allan",
1556 "Baker",
1557 "Bigwig",
1558 "Black",
1559 "Bloggs",
1560 "Brown",
1561 "Campbell",
1562 "Gordon",
1563 "Hamilton",
1564 "Hawthorn",
1565 "Higgins",
1566 "Green",
1567 "Gribble",
1568 "Jones",
1569 "McAlpine",
1570 "MacDonald",
1571 "McIntosh",
1572 "Muir",
1573 "Murphy",
1574 "Nelson",
1575 "O'Donnell",
1576 "Parker",
1577 "Phillips",
1578 "Pilkington",
1579 "Quigley",
1580 "Sharkey",
1581 "Thomson",
1582 "Watkins"
1585 static const char * const _silly_surname_list[] = {
1586 "Grumpy",
1587 "Dozy",
1588 "Speedy",
1589 "Nosey",
1590 "Dribble",
1591 "Mushroom",
1592 "Cabbage",
1593 "Sniffle",
1594 "Fishy",
1595 "Swindle",
1596 "Sneaky",
1597 "Nutkins"
1600 static const char _initial_name_letters[] = {
1601 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1602 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1605 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
1607 const char * const *base;
1608 uint num;
1610 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1611 base = _silly_surname_list;
1612 num = lengthof(_silly_surname_list);
1613 } else {
1614 base = _surname_list;
1615 num = lengthof(_surname_list);
1618 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
1619 buff = strecpy(buff, " & Co.", last);
1621 return buff;
1624 static char *GenPresidentName(char *buff, uint32 x, const char *last)
1626 char initial[] = "?. ";
1627 const char * const *base;
1628 uint num;
1629 uint i;
1631 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
1632 buff = strecpy(buff, initial, last);
1634 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
1635 if (i < sizeof(_initial_name_letters)) {
1636 initial[0] = _initial_name_letters[i];
1637 buff = strecpy(buff, initial, last);
1640 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
1641 base = _silly_surname_list;
1642 num = lengthof(_silly_surname_list);
1643 } else {
1644 base = _surname_list;
1645 num = lengthof(_surname_list);
1648 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
1650 return buff;
1653 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
1655 switch (ind) {
1656 case 1: // not used
1657 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
1659 case 2: // used for Foobar & Co company names
1660 return GenAndCoName(buff, args->GetInt32(), last);
1662 case 3: // President name
1663 return GenPresidentName(buff, args->GetInt32(), last);
1666 /* town name? */
1667 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1668 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
1669 return strecpy(buff, " Transport", last);
1672 /* language name? */
1673 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
1674 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
1675 return strecpy(buff,
1676 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
1679 /* resolution size? */
1680 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
1681 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
1682 buff += seprintf(
1683 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
1685 return buff;
1688 /* screenshot format name? */
1689 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
1690 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
1691 return strecpy(buff, GetScreenshotFormatDesc(i), last);
1694 NOT_REACHED();
1697 #ifdef ENABLE_NETWORK
1698 extern void SortNetworkLanguages();
1699 #else /* ENABLE_NETWORK */
1700 static inline void SortNetworkLanguages() {}
1701 #endif /* ENABLE_NETWORK */
1704 * Check whether the header is a valid header for OpenTTD.
1705 * @return true iff the header is deemed valid.
1707 bool LanguagePackHeader::IsValid() const
1709 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
1710 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1711 this->plural_form < LANGUAGE_MAX_PLURAL &&
1712 this->text_dir <= 1 &&
1713 this->newgrflangid < MAX_LANG &&
1714 this->num_genders < MAX_NUM_GENDERS &&
1715 this->num_cases < MAX_NUM_CASES &&
1716 StrValid(this->name, lastof(this->name)) &&
1717 StrValid(this->own_name, lastof(this->own_name)) &&
1718 StrValid(this->isocode, lastof(this->isocode)) &&
1719 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
1720 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
1721 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
1725 * Read a particular language.
1726 * @param lang The metadata about the language.
1727 * @return Whether the loading went okay or not.
1729 bool ReadLanguagePack(const LanguageMetadata *lang)
1731 /* Current language pack */
1732 size_t len;
1733 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
1734 if (lang_pack == NULL) return false;
1736 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1737 const char *end = (char *)lang_pack + len + 1;
1739 /* We need at least one byte of lang_pack->data */
1740 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1741 free(lang_pack);
1742 return false;
1745 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1746 for (uint i = 0; i < TAB_COUNT; i++) {
1747 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
1749 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
1751 uint count = 0;
1752 for (uint i = 0; i < TAB_COUNT; i++) {
1753 uint16 num = lang_pack->offsets[i];
1754 if (num > TAB_SIZE) {
1755 free(lang_pack);
1756 return false;
1759 _langtab_start[i] = count;
1760 _langtab_num[i] = num;
1761 count += num;
1764 /* Allocate offsets */
1765 char **langpack_offs = MallocT<char *>(count);
1767 /* Fill offsets */
1768 char *s = lang_pack->data;
1769 len = (byte)*s++;
1770 for (uint i = 0; i < count; i++) {
1771 if (s + len >= end) {
1772 free(lang_pack);
1773 free(langpack_offs);
1774 return false;
1776 if (len >= 0xC0) {
1777 len = ((len & 0x3F) << 8) + (byte)*s++;
1778 if (s + len >= end) {
1779 free(lang_pack);
1780 free(langpack_offs);
1781 return false;
1784 langpack_offs[i] = s;
1785 s += len;
1786 len = (byte)*s;
1787 *s++ = '\0'; // zero terminate the string
1790 free(_langpack);
1791 _langpack = lang_pack;
1793 free(_langpack_offs);
1794 _langpack_offs = langpack_offs;
1796 _current_language = lang;
1797 _current_text_dir = (TextDirection)_current_language->text_dir;
1798 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
1799 strecpy(_config_language_file, c_file, lastof(_config_language_file));
1800 SetCurrentGrfLangID(_current_language->newgrflangid);
1802 #ifdef WITH_ICU
1803 /* Delete previous collator. */
1804 if (_current_collator != NULL) {
1805 delete _current_collator;
1806 _current_collator = NULL;
1809 /* Create a collator instance for our current locale. */
1810 UErrorCode status = U_ZERO_ERROR;
1811 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
1812 /* Sort number substrings by their numerical value. */
1813 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
1814 /* Avoid using the collator if it is not correctly set. */
1815 if (U_FAILURE(status)) {
1816 delete _current_collator;
1817 _current_collator = NULL;
1819 #endif /* WITH_ICU */
1821 /* Some lists need to be sorted again after a language change. */
1822 ReconsiderGameScriptLanguage();
1823 InitializeSortedCargoSpecs();
1824 SortIndustryTypes();
1825 BuildIndustriesLegend();
1826 SortNetworkLanguages();
1827 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window.
1828 InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window.
1829 InvalidateWindowClassesData(WC_ROADVEH_LIST); // Road vehicle group window.
1830 InvalidateWindowClassesData(WC_SHIPS_LIST); // Ship group window.
1831 InvalidateWindowClassesData(WC_AIRCRAFT_LIST); // Aircraft group window.
1832 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
1833 InvalidateWindowClassesData(WC_STATION_LIST); // Station list window.
1835 return true;
1838 /* Win32 implementation in win32.cpp.
1839 * OS X implementation in os/macosx/macos.mm. */
1840 #if !(defined(WIN32) || defined(__APPLE__))
1842 * Determine the current charset based on the environment
1843 * First check some default values, after this one we passed ourselves
1844 * and if none exist return the value for $LANG
1845 * @param param environment variable to check conditionally if default ones are not
1846 * set. Pass NULL if you don't want additional checks.
1847 * @return return string containing current charset, or NULL if not-determinable
1849 const char *GetCurrentLocale(const char *param)
1851 const char *env;
1853 env = getenv("LANGUAGE");
1854 if (env != NULL) return env;
1856 env = getenv("LC_ALL");
1857 if (env != NULL) return env;
1859 if (param != NULL) {
1860 env = getenv(param);
1861 if (env != NULL) return env;
1864 return getenv("LANG");
1866 #else
1867 const char *GetCurrentLocale(const char *param);
1868 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
1870 int CDECL StringIDSorter(const StringID *a, const StringID *b)
1872 char stra[512];
1873 char strb[512];
1874 GetString(stra, *a, lastof(stra));
1875 GetString(strb, *b, lastof(strb));
1877 return strcmp(stra, strb);
1881 * Get the language with the given NewGRF language ID.
1882 * @param newgrflangid NewGRF languages ID to check.
1883 * @return The language's metadata, or NULL if it is not known.
1885 const LanguageMetadata *GetLanguage(byte newgrflangid)
1887 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
1888 if (newgrflangid == lang->newgrflangid) return lang;
1891 return NULL;
1895 * Reads the language file header and checks compatibility.
1896 * @param file the file to read
1897 * @param hdr the place to write the header information to
1898 * @return true if and only if the language file is of a compatible version
1900 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
1902 FILE *f = fopen(file, "rb");
1903 if (f == NULL) return false;
1905 size_t read = fread(hdr, sizeof(*hdr), 1, f);
1906 fclose(f);
1908 bool ret = read == 1 && hdr->IsValid();
1910 /* Convert endianness for the windows language ID */
1911 if (ret) {
1912 hdr->missing = FROM_LE16(hdr->missing);
1913 hdr->winlangid = FROM_LE16(hdr->winlangid);
1915 return ret;
1919 * Gets a list of languages from the given directory.
1920 * @param path the base directory to search in
1922 static void GetLanguageList(const char *path)
1924 DIR *dir = ttd_opendir(path);
1925 if (dir != NULL) {
1926 struct dirent *dirent;
1927 while ((dirent = readdir(dir)) != NULL) {
1928 const char *d_name = FS2OTTD(dirent->d_name);
1929 const char *extension = strrchr(d_name, '.');
1931 /* Not a language file */
1932 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
1934 LanguageMetadata lmd;
1935 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
1937 /* Check whether the file is of the correct version */
1938 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
1939 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
1940 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
1941 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
1942 } else {
1943 *_languages.Append() = lmd;
1946 closedir(dir);
1951 * Make a list of the available language packs. Put the data in
1952 * #_languages list.
1954 void InitializeLanguagePacks()
1956 Searchpath sp;
1958 FOR_ALL_SEARCHPATHS(sp) {
1959 char path[MAX_PATH];
1960 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
1961 GetLanguageList(path);
1963 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
1965 /* Acquire the locale of the current system */
1966 const char *lang = GetCurrentLocale("LC_MESSAGES");
1967 if (lang == NULL) lang = "en_GB";
1969 const LanguageMetadata *chosen_language = NULL; ///< Matching the language in the configuration file or the current locale
1970 const LanguageMetadata *language_fallback = NULL; ///< Using pt_PT for pt_BR locale when pt_BR is not available
1971 const LanguageMetadata *en_GB_fallback = _languages.Begin(); ///< Fallback when no locale-matching language has been found
1973 /* Find a proper language. */
1974 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
1975 /* We are trying to find a default language. The priority is by
1976 * configuration file, local environment and last, if nothing found,
1977 * English. */
1978 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
1979 if (strcmp(lang_file, _config_language_file) == 0) {
1980 chosen_language = lng;
1981 break;
1984 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
1985 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
1986 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
1989 /* We haven't found the language in the config nor the one in the locale.
1990 * Now we set it to one of the fallback languages */
1991 if (chosen_language == NULL) {
1992 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
1995 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
1999 * Get the ISO language code of the currently loaded language.
2000 * @return the ISO code.
2002 const char *GetCurrentLanguageIsoCode()
2004 return _langpack->isocode;
2008 * Check whether there are glyphs missing in the current language.
2009 * @param Pointer to an address for storing the text pointer.
2010 * @return If glyphs are missing, return \c true, else return \c false.
2011 * @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.
2013 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
2015 InitFreeType(this->Monospace());
2016 const Sprite *question_mark[FS_END];
2018 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
2019 question_mark[size] = GetGlyph(size, '?');
2022 this->Reset();
2023 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
2024 FontSize size = this->DefaultSize();
2025 if (str != NULL) *str = text;
2026 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
2027 if (c == SCC_TINYFONT) {
2028 size = FS_SMALL;
2029 } else if (c == SCC_BIGFONT) {
2030 size = FS_LARGE;
2031 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
2032 /* The character is printable, but not in the normal font. This is the case we were testing for. */
2033 return true;
2037 return false;
2040 /** Helper for searching through the language pack. */
2041 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
2042 uint i; ///< Iterator for the primary language tables.
2043 uint j; ///< Iterator for the secondary language tables.
2045 /* virtual */ void Reset()
2047 this->i = 0;
2048 this->j = 0;
2051 /* virtual */ FontSize DefaultSize()
2053 return FS_NORMAL;
2056 /* virtual */ const char *NextString()
2058 if (this->i >= TAB_COUNT) return NULL;
2060 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
2062 this->j++;
2063 while (this->i < TAB_COUNT && this->j >= _langtab_num[this->i]) {
2064 this->i++;
2065 this->j = 0;
2068 return ret;
2071 /* virtual */ bool Monospace()
2073 return false;
2076 /* virtual */ void SetFontNames(FreeTypeSettings *settings, const char *font_name)
2078 #ifdef WITH_FREETYPE
2079 strecpy(settings->small.font, font_name, lastof(settings->small.font));
2080 strecpy(settings->medium.font, font_name, lastof(settings->medium.font));
2081 strecpy(settings->large.font, font_name, lastof(settings->large.font));
2082 #endif /* WITH_FREETYPE */
2087 * Check whether the currently loaded language pack
2088 * uses characters that the currently loaded font
2089 * does not support. If this is the case an error
2090 * message will be shown in English. The error
2091 * message will not be localized because that would
2092 * mean it might use characters that are not in the
2093 * font, which is the whole reason this check has
2094 * been added.
2095 * @param base_font Whether to look at the base font as well.
2096 * @param searcher The methods to use to search for strings to check.
2097 * If NULL the loaded language pack searcher is used.
2099 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
2101 static LanguagePackGlyphSearcher pack_searcher;
2102 if (searcher == NULL) searcher = &pack_searcher;
2103 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
2104 #ifdef WITH_FREETYPE
2105 if (bad_font) {
2106 /* We found an unprintable character... lets try whether we can find
2107 * a fallback font that can print the characters in the current language. */
2108 FreeTypeSettings backup;
2109 memcpy(&backup, &_freetype, sizeof(backup));
2111 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
2113 memcpy(&_freetype, &backup, sizeof(backup));
2115 if (bad_font && base_font) {
2116 /* Our fallback font does miss characters too, so keep the
2117 * user chosen font as that is more likely to be any good than
2118 * the wild guess we made */
2119 InitFreeType(searcher->Monospace());
2122 #endif
2124 if (bad_font) {
2125 /* All attempts have failed. Display an error. As we do not want the string to be translated by
2126 * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
2127 * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
2128 * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
2129 * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
2130 static char *err_str = strdup("XXXThe current font is missing some of the characters used in the texts for this language. Read the readme to see how to solve this.");
2131 Utf8Encode(err_str, SCC_YELLOW);
2132 SetDParamStr(0, err_str);
2133 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
2135 /* Reset the font width */
2136 LoadStringWidthTable(searcher->Monospace());
2137 return;
2140 /* Update the font with cache */
2141 LoadStringWidthTable(searcher->Monospace());
2143 #if !defined(WITH_ICU)
2145 * For right-to-left languages we need the ICU library. If
2146 * we do not have support for that library we warn the user
2147 * about it with a message. As we do not want the string to
2148 * be translated by the translators, we 'force' it into the
2149 * binary and 'load' it via a BindCString. To do this
2150 * properly we have to set the colour of the string,
2151 * otherwise we end up with a lot of artifacts. The colour
2152 * 'character' might change in the future, so for safety
2153 * we just Utf8 Encode it into the string, which takes
2154 * exactly three characters, so it replaces the "XXX" with
2155 * the colour marker.
2157 if (_current_text_dir != TD_LTR) {
2158 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
2159 Utf8Encode(err_str, SCC_YELLOW);
2160 SetDParamStr(0, err_str);
2161 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
2163 #endif