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/>.
10 /** @file strings.cpp Handling of translated strings. */
14 #include "station_base.h"
16 #include "screenshot.h"
17 #include "waypoint_base.h"
18 #include "depot_base.h"
20 #include "newgrf_text.h"
21 #include "fileio_func.h"
22 #include "signs_base.h"
25 #include "strings_func.h"
27 #include "core/endian_func.hpp"
28 #include "date_func.h"
29 #include "vehicle_base.h"
30 #include "engine_base.h"
32 #include "townnamegen.h"
33 #include "townname_func.h"
35 #include "company_base.h"
36 #include "smallmap_gui.h"
37 #include "window_func.h"
39 #include "game/game_text.hpp"
40 #include "network/network_content_gui.h"
43 #include "table/strings.h"
44 #include "table/control_codes.h"
46 char _config_language_file
[MAX_PATH
]; ///< The file (name) stored in the configuration.
47 LanguageList _languages
; ///< The actual list of language meta data.
48 const LanguageMetadata
*_current_language
= NULL
; ///< The currently loaded language.
50 TextDirection _current_text_dir
; ///< Text direction of the currently selected language.
53 Collator
*_current_collator
= NULL
; ///< Collator for the language currently in use.
54 #endif /* WITH_ICU_SORT */
56 static uint64 _global_string_params_data
[20]; ///< Global array of string parameters. To access, use #SetDParam.
57 static WChar _global_string_params_type
[20]; ///< Type of parameters stored in #_decode_parameters
58 StringParameters
_global_string_params(_global_string_params_data
, 20, _global_string_params_type
);
60 /** Reset the type array. */
61 void StringParameters::ClearTypeInformation()
63 assert(this->type
!= NULL
);
64 MemSetT(this->type
, 0, this->num_param
);
69 * Read an int64 from the argument array. The offset is increased
70 * so the next time GetInt64 is called the next value is read.
72 int64
StringParameters::GetInt64(WChar type
)
74 if (this->offset
>= this->num_param
) {
75 DEBUG(misc
, 0, "Trying to read invalid string parameter");
78 if (this->type
!= NULL
) {
79 assert(this->type
[this->offset
] == 0 || this->type
[this->offset
] == type
);
80 this->type
[this->offset
] = type
;
82 return this->data
[this->offset
++];
86 * Set DParam n to some number that is suitable for string size computations.
87 * @param n Index of the string parameter.
88 * @param max_value The biggest value which shall be displayed.
89 * For the result only the number of digits of \a max_value matter.
90 * @param min_count Minimum number of digits independent of \a max.
91 * @param size Font of the number
93 void SetDParamMaxValue(uint n
, uint64 max_value
, uint min_count
, FontSize size
)
96 while (max_value
>= 10) {
100 SetDParamMaxDigits(n
, max(min_count
, num_digits
), size
);
104 * Set DParam n to some number that is suitable for string size computations.
105 * @param n Index of the string parameter.
106 * @param count Number of digits which shall be displayable.
107 * @param size Font of the number
109 void SetDParamMaxDigits(uint n
, uint count
, FontSize size
)
111 SetDParam (n
, FontCache::Get(size
)->GetBroadestValue (count
));
115 * Copy \a num string parameters from array \a src into the global string parameter array.
116 * @param offs Index in the global array to copy the first string parameter to.
117 * @param src Source array of string parameters.
118 * @param num Number of string parameters to copy.
120 void CopyInDParam(int offs
, const uint64
*src
, int num
)
122 MemCpyT(_global_string_params
.GetPointerToOffset(offs
), src
, num
);
126 * Copy \a num string parameters from the global string parameter array to the \a dst array.
127 * @param dst Destination array of string parameters.
128 * @param offs Index in the global array to copy the first string parameter from.
129 * @param num Number of string parameters to copy.
131 void CopyOutDParam(uint64
*dst
, int offs
, int num
)
133 MemCpyT(dst
, _global_string_params
.GetPointerToOffset(offs
), num
);
137 * Copy \a num string parameters from the global string parameter array to the \a dst array.
138 * Furthermore clone raw string parameters into \a strings and amend the data in \a dst.
139 * @param dst Destination array of string parameters.
140 * @param strings Destination array for clone of the raw strings. Must be of same length as dst. Deallocation left to the caller.
141 * @param string The string used to determine where raw strings are and where there are no raw strings.
142 * @param num Number of string parameters to copy.
144 void CopyOutDParam(uint64
*dst
, const char **strings
, StringID string
, int num
)
146 char buf
[DRAW_STRING_BUFFER
];
147 GetString (buf
, string
);
149 MemCpyT(dst
, _global_string_params
.GetPointerToOffset(0), num
);
150 for (int i
= 0; i
< num
; i
++) {
151 if (_global_string_params
.HasTypeInformation() && _global_string_params
.GetTypeAtOffset(i
) == SCC_RAW_STRING_POINTER
) {
152 strings
[i
] = xstrdup((const char *)(size_t)_global_string_params
.GetParam(i
));
153 dst
[i
] = (size_t)strings
[i
];
160 static void AppendStationSpecialString (stringb
*buf
, int x
);
161 static void AppendSpecialNameString (stringb
*buf
, int ind
, StringParameters
*args
);
163 static void FormatString (stringb
*buf
, const char *str
, StringParameters
*args
, uint case_index
= 0, bool game_script
= false, bool dry_run
= false);
165 struct LanguagePack
: public LanguagePackHeader
{
166 char data
[]; // list of strings
169 static char **_langpack_offs
;
170 static LanguagePack
*_langpack
;
171 static uint _langtab_num
[TEXT_TAB_END
]; ///< Offset into langpack offs
172 static uint _langtab_start
[TEXT_TAB_END
]; ///< Offset into langpack offs
173 static bool _scan_for_gender_data
= false; ///< Are we scanning for the gender of the current string? (instead of formatting it)
176 const char *GetStringPtr(StringID string
)
178 switch (GetStringTab(string
)) {
179 case TEXT_TAB_GAMESCRIPT_START
: return GetGameStringPtr(GetStringIndex(string
));
180 /* 0xD0xx and 0xD4xx IDs have been converted earlier. */
181 case TEXT_TAB_OLD_NEWGRF
: NOT_REACHED();
182 case TEXT_TAB_NEWGRF_START
: return GetGRFStringPtr(GetStringIndex(string
));
183 default: return _langpack_offs
[_langtab_start
[GetStringTab(string
)] + GetStringIndex(string
)];
188 * Get a parsed string with most special stringcodes replaced by the string parameters.
189 * @param buf Buffer where the formatted string should be written to.
191 * @param args Arguments for the string.
192 * @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case.
193 * @param game_script The string is coming directly from a game script.
195 void AppendStringWithArgs (stringb
*buf
, StringID string
, StringParameters
*args
, uint case_index
, bool game_script
)
198 AppendStringWithArgs (buf
, STR_UNDEFINED
, args
);
202 uint index
= GetStringIndex(string
);
203 StringTab tab
= GetStringTab(string
);
207 if (index
>= 0xC0 && !game_script
) {
208 GenerateTownNameString (buf
, index
- 0xC0, args
->GetInt32());
213 case TEXT_TAB_SPECIAL
:
214 if (index
>= 0xE4 && !game_script
) {
215 AppendSpecialNameString (buf
, index
- 0xE4, args
);
220 case TEXT_TAB_OLD_CUSTOM
:
221 /* Old table for custom names. This is no longer used */
223 error("Incorrect conversion of custom name string.");
227 case TEXT_TAB_GAMESCRIPT_START
:
228 FormatString (buf
, GetGameStringPtr(index
), args
, case_index
, true);
231 case TEXT_TAB_OLD_NEWGRF
:
234 case TEXT_TAB_NEWGRF_START
:
235 FormatString (buf
, GetGRFStringPtr(index
), args
, case_index
);
242 if (index
>= _langtab_num
[tab
]) {
244 AppendStringWithArgs (buf
, STR_UNDEFINED
, args
);
247 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string
);
250 FormatString (buf
, GetStringPtr(string
), args
, case_index
);
253 void AppendString (stringb
*buf
, StringID string
)
255 _global_string_params
.ClearTypeInformation();
256 _global_string_params
.offset
= 0;
257 AppendStringWithArgs (buf
, string
, &_global_string_params
);
262 * This function is used to "bind" a C string to a OpenTTD dparam slot.
263 * @param n slot of the string
264 * @param str string to bind
266 void SetDParamStr(uint n
, const char *str
)
268 SetDParam(n
, (uint64
)(size_t)str
);
272 * Format a number into a string.
273 * @param buf the buffer to write to
274 * @param number the number to write down
275 * @param separator the thousands-separator to use
276 * @param zerofill minimum number of digits to print for the integer part. The number will be filled with zeros at the front if necessary.
277 * @param fractional_digits number of fractional digits to display after a decimal separator. The decimal separator is inserted
278 * in front of the \a fractional_digits last digit of \a number.
280 static void FormatNumber (stringb
*buf
, int64 number
, const char *separator
, int zerofill
= 1, int fractional_digits
= 0)
282 static const int max_digits
= 20;
283 uint64 divisor
= 10000000000000000000ULL;
284 zerofill
+= fractional_digits
;
285 int thousands_offset
= (max_digits
- fractional_digits
- 1) % 3;
294 for (int i
= 0; i
< max_digits
; i
++) {
295 if (i
== max_digits
- fractional_digits
) {
296 const char *decimal_separator
= _settings_game
.locale
.digit_decimal_separator
;
297 if (decimal_separator
== NULL
) decimal_separator
= _langpack
->digit_decimal_separator
;
298 buf
->append (decimal_separator
);
302 if (num
>= divisor
) {
303 quot
= num
/ divisor
;
306 if ((tot
|= quot
) || i
>= max_digits
- zerofill
) {
307 buf
->append_fmt ("%i", (int)quot
);
308 if ((i
% 3) == thousands_offset
&& i
< max_digits
- 1 - fractional_digits
) buf
->append (separator
);
315 static void FormatCommaNumber (stringb
*buf
, int64 number
, int fractional_digits
= 0)
317 const char *separator
= _settings_game
.locale
.digit_group_separator
;
318 if (separator
== NULL
) separator
= _langpack
->digit_group_separator
;
319 FormatNumber (buf
, number
, separator
, 1, fractional_digits
);
322 static void FormatNoCommaNumber (stringb
*buf
, int64 number
)
324 FormatNumber (buf
, number
, "");
327 static void FormatZerofillNumber (stringb
*buf
, int64 number
, int64 count
)
329 FormatNumber (buf
, number
, "", count
);
332 static void FormatHexNumber (stringb
*buf
, uint64 number
)
334 buf
->append_fmt ("0x" OTTD_PRINTFHEX64
, number
);
338 * Format a given number as a number of bytes with the SI prefix.
339 * @param buf the buffer to write to
340 * @param number the number of bytes to write down
342 static void FormatBytes (stringb
*buf
, int64 number
)
347 buf
->append_fmt ("%i" NBSP
"B", (int)number
);
351 /* 2^10 2^20 2^30 2^40 2^50 2^60 */
352 static const char iec_prefixes
[] = {'K', 'M', 'G', 'T', 'P', 'E'};
354 while (number
>= 1024 * 1024) {
359 const char *decimal_separator
= _settings_game
.locale
.digit_decimal_separator
;
360 if (decimal_separator
== NULL
) decimal_separator
= _langpack
->digit_decimal_separator
;
362 if (number
< 1024 * 10) {
363 buf
->append_fmt ("%i%s%02i", (int)number
/ 1024, decimal_separator
, (int)(number
% 1024) * 100 / 1024);
364 } else if (number
< 1024 * 100) {
365 buf
->append_fmt ("%i%s%01i", (int)number
/ 1024, decimal_separator
, (int)(number
% 1024) * 10 / 1024);
367 assert(number
< 1024 * 1024);
368 buf
->append_fmt ("%i", (int)number
/ 1024);
371 assert(id
< lengthof(iec_prefixes
));
372 buf
->append_fmt (NBSP
"%ciB", iec_prefixes
[id
]);
375 static void FormatYmdString (stringb
*buf
, Date date
, uint case_index
)
378 ConvertDateToYMD(date
, &ymd
);
380 int64 args
[] = {ymd
.day
+ STR_DAY_NUMBER_1ST
- 1, STR_MONTH_ABBREV_JAN
+ ymd
.month
, ymd
.year
};
381 StringParameters
tmp_params(args
);
382 FormatString (buf
, GetStringPtr(STR_FORMAT_DATE_LONG
), &tmp_params
, case_index
);
385 static void FormatMonthAndYear (stringb
*buf
, Date date
, uint case_index
)
388 ConvertDateToYMD(date
, &ymd
);
390 int64 args
[] = {STR_MONTH_JAN
+ ymd
.month
, ymd
.year
};
391 StringParameters
tmp_params(args
);
392 FormatString (buf
, GetStringPtr(STR_FORMAT_DATE_SHORT
), &tmp_params
, case_index
);
395 static void FormatTinyOrISODate (stringb
*buf
, Date date
, StringID str
)
398 ConvertDateToYMD(date
, &ymd
);
402 /* We want to zero-pad the days and months */
403 snprintf(day
, lengthof(day
), "%02i", ymd
.day
);
404 snprintf(month
, lengthof(month
), "%02i", ymd
.month
+ 1);
406 int64 args
[] = {(int64
)(size_t)day
, (int64
)(size_t)month
, ymd
.year
};
407 StringParameters
tmp_params(args
);
408 FormatString (buf
, GetStringPtr(str
), &tmp_params
);
411 static void FormatGenericCurrency (stringb
*buf
, const CurrencySpec
*spec
, Money number
, bool compact
)
413 /* We are going to make number absolute for printing, so
414 * keep this piece of data as we need it later on */
415 bool negative
= number
< 0;
416 const char *multiplier
= "";
418 number
*= spec
->rate
;
420 /* convert from negative */
422 if (!buf
->append_utf8(SCC_RED
)) return;
427 /* Add prefix part, following symbol_pos specification.
428 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
429 * The only remaining value is 1 (suffix), so everything that is not 1 */
430 if (spec
->symbol_pos
!= 1) buf
->append (spec
->prefix
);
432 /* for huge numbers, compact the number into k or M */
434 /* Take care of the 'k' rounding. Having 1 000 000 k
435 * and 1 000 M is inconsistent, so always use 1 000 M. */
436 if (number
>= 1000000000 - 500) {
437 number
= (number
+ 500000) / 1000000;
438 multiplier
= NBSP
"M";
439 } else if (number
>= 1000000) {
440 number
= (number
+ 500) / 1000;
441 multiplier
= NBSP
"k";
445 const char *separator
= _settings_game
.locale
.digit_group_separator_currency
;
446 if (separator
== NULL
&& !StrEmpty(_currency
->separator
)) separator
= _currency
->separator
;
447 if (separator
== NULL
) separator
= _langpack
->digit_group_separator_currency
;
448 FormatNumber (buf
, number
, separator
);
449 buf
->append (multiplier
);
451 /* Add suffix part, following symbol_pos specification.
452 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
453 * The only remaining value is 1 (prefix), so everything that is not 0 */
454 if (spec
->symbol_pos
!= 0) buf
->append (spec
->suffix
);
456 if (negative
) buf
->append_utf8(SCC_PREVIOUS_COLOUR
);
460 * Determine the "plural" index given a plural form and a number.
461 * @param count The number to get the plural index of.
462 * @param plural_form The plural form we want an index for.
463 * @return The plural index for the given form.
465 static int DeterminePluralForm(int64 count
, int plural_form
)
467 /* The absolute value determines plurality */
468 uint64 n
= abs(count
);
470 switch (plural_form
) {
474 /* Two forms: singular used for one only.
476 * Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
477 * Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
479 return n
!= 1 ? 1 : 0;
483 * Hungarian, Japanese, Korean, Turkish */
487 /* Two forms: singular used for 0 and 1.
489 * French, Brazilian Portuguese */
491 return n
> 1 ? 1 : 0;
493 /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
494 * Note: Cases are out of order for hysterical reasons. '0' is last.
498 return n
% 10 == 1 && n
% 100 != 11 ? 0 : n
!= 0 ? 1 : 2;
500 /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
504 return n
== 1 ? 0 : n
== 2 ? 1 : n
< 7 ? 2 : n
< 11 ? 3 : 4;
506 /* 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.
510 return n
% 10 == 1 && n
% 100 != 11 ? 0 : n
% 10 >= 2 && (n
% 100 < 10 || n
% 100 >= 20) ? 1 : 2;
512 /* 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.
514 * Croatian, Russian, Ukrainian */
516 return n
% 10 == 1 && n
% 100 != 11 ? 0 : n
% 10 >= 2 && n
% 10 <= 4 && (n
% 100 < 10 || n
% 100 >= 20) ? 1 : 2;
518 /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
522 return n
== 1 ? 0 : n
% 10 >= 2 && n
% 10 <= 4 && (n
% 100 < 10 || n
% 100 >= 20) ? 1 : 2;
524 /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
528 return n
% 100 == 1 ? 0 : n
% 100 == 2 ? 1 : n
% 100 == 3 || n
% 100 == 4 ? 2 : 3;
530 /* Two forms: singular used for numbers ending in 1 except when ending in 11.
534 return n
% 10 == 1 && n
% 100 != 11 ? 0 : 1;
536 /* Three forms: special cases for 1, and 2 to 4
540 return n
== 1 ? 0 : n
>= 2 && n
<= 4 ? 1 : 2;
542 /* Two forms: cases for numbers ending with a consonant, and with a vowel.
543 * Korean doesn't have the concept of plural, but depending on how a
544 * number is pronounced it needs another version of a particle.
545 * As such the plural system is misused to give this distinction.
567 /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
571 return (n
== 1 ? 0 : n
== 0 || (n
% 100 > 1 && n
% 100 < 11) ? 1 : (n
% 100 > 10 && n
% 100 < 20) ? 2 : 3);
572 /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
576 return ((n
== 1 || n
== 11) ? 0 : (n
== 2 || n
== 12) ? 1 : ((n
> 2 && n
< 11) || (n
> 12 && n
< 20)) ? 2 : 3);
580 static const char *ParseStringChoice (const char *b
, uint form
, stringb
*dst
)
582 /* <NUM> {Length of each string} {each string} */
584 uint pos
, i
, mypos
= 0;
586 for (i
= pos
= 0; i
!= n
; i
++) {
587 uint len
= (byte
)*b
++;
588 if (i
== form
) mypos
= pos
;
592 dst
->append (b
+ mypos
);
596 /** Helper for unit conversion. */
597 struct UnitConversion
{
598 int multiplier
; ///< Amount to multiply upon conversion.
599 int shift
; ///< Amount to shift upon conversion.
602 * Convert value from OpenTTD's internal unit into the displayed value.
603 * @param input The input to convert.
604 * @param round Whether to round the value or not.
605 * @return The converted value.
607 int64
ToDisplay(int64 input
, bool round
= true) const
609 return ((input
* this->multiplier
) + (round
&& this->shift
!= 0 ? 1 << (this->shift
- 1) : 0)) >> this->shift
;
613 * Convert the displayed value back into a value of OpenTTD's internal unit.
614 * @param input The input to convert.
615 * @param round Whether to round the value up or not.
616 * @param divider Divide the return value by this.
617 * @return The converted value.
619 int64
FromDisplay(int64 input
, bool round
= true, int64 divider
= 1) const
621 return ((input
<< this->shift
) + (round
? (this->multiplier
* divider
) - 1 : 0)) / (this->multiplier
* divider
);
625 /** Information about a specific unit system. */
627 UnitConversion c
; ///< Conversion
628 StringID s
; ///< String for the unit
631 /** Information about a specific unit system with a long variant. */
633 UnitConversion c
; ///< Conversion
634 StringID s
; ///< String for the short variant of the unit
635 StringID l
; ///< String for the long variant of the unit
638 /** Unit conversions for velocity. */
639 static const Units _units_velocity
[] = {
640 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL
},
641 { { 103, 6}, STR_UNITS_VELOCITY_METRIC
},
642 { {1831, 12}, STR_UNITS_VELOCITY_SI
},
645 /** Unit conversions for velocity. */
646 static const Units _units_power
[] = {
647 { { 1, 0}, STR_UNITS_POWER_IMPERIAL
},
648 { {4153, 12}, STR_UNITS_POWER_METRIC
},
649 { {6109, 13}, STR_UNITS_POWER_SI
},
652 /** Unit conversions for weight. */
653 static const UnitsLong _units_weight
[] = {
654 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL
, STR_UNITS_WEIGHT_LONG_IMPERIAL
},
655 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC
, STR_UNITS_WEIGHT_LONG_METRIC
},
656 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI
, STR_UNITS_WEIGHT_LONG_SI
},
659 /** Unit conversions for volume. */
660 static const UnitsLong _units_volume
[] = {
661 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL
, STR_UNITS_VOLUME_LONG_IMPERIAL
},
662 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC
, STR_UNITS_VOLUME_LONG_METRIC
},
663 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI
, STR_UNITS_VOLUME_LONG_SI
},
666 /** Unit conversions for force. */
667 static const Units _units_force
[] = {
668 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL
},
669 { {3263, 5}, STR_UNITS_FORCE_METRIC
},
670 { { 1, 0}, STR_UNITS_FORCE_SI
},
673 /** Unit conversions for height. */
674 static const Units _units_height
[] = {
675 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL
}, // "Wrong" conversion factor for more nicer GUI values
676 { { 1, 0}, STR_UNITS_HEIGHT_METRIC
},
677 { { 1, 0}, STR_UNITS_HEIGHT_SI
},
681 * Convert the given (internal) speed to the display speed.
682 * @param speed the speed to convert
683 * @return the converted speed.
685 uint
ConvertSpeedToDisplaySpeed(uint speed
)
687 /* For historical reasons we don't want to mess with the
688 * conversion for speed. So, don't round it and keep the
689 * original conversion factors instead of the real ones. */
690 return _units_velocity
[_settings_game
.locale
.units_velocity
].c
.ToDisplay(speed
, false);
694 * Convert the given display speed to the (internal) speed.
695 * @param speed the speed to convert
696 * @return the converted speed.
698 uint
ConvertDisplaySpeedToSpeed(uint speed
)
700 return _units_velocity
[_settings_game
.locale
.units_velocity
].c
.FromDisplay(speed
);
704 * Convert the given km/h-ish speed to the display speed.
705 * @param speed the speed to convert
706 * @return the converted speed.
708 uint
ConvertKmhishSpeedToDisplaySpeed(uint speed
)
710 return _units_velocity
[_settings_game
.locale
.units_velocity
].c
.ToDisplay(speed
* 10, false) / 16;
714 * Convert the given display speed to the km/h-ish speed.
715 * @param speed the speed to convert
716 * @return the converted speed.
718 uint
ConvertDisplaySpeedToKmhishSpeed(uint speed
)
720 return _units_velocity
[_settings_game
.locale
.units_velocity
].c
.FromDisplay(speed
* 16, true, 10);
723 * Parse most format codes within a string and write the result to a buffer.
724 * @param buf The buffer to write the final string to.
725 * @param str The original string with format codes.
726 * @param args Pointer to extra arguments used by various string codes.
728 * @param dry_run True when the argt array is not yet initialized.
730 static void FormatString (stringb
*buf
, const char *str_arg
, StringParameters
*args
, uint case_index
, bool game_script
, bool dry_run
)
732 uint orig_offset
= args
->offset
;
734 /* When there is no array with types there is no need to do a dry run. */
735 if (args
->HasTypeInformation() && !dry_run
) {
736 size_t length
= buf
->length();
737 if (UsingNewGRFTextStack()) {
738 /* Values from the NewGRF text stack are only copied to the normal
739 * argv array at the time they are encountered. That means that if
740 * another string command references a value later in the string it
741 * would fail. We solve that by running FormatString twice. The first
742 * pass makes sure the argv array is correctly filled and the second
743 * pass can reference later values without problems. */
744 struct TextRefStack
*backup
= CreateTextRefStackBackup();
745 FormatString (buf
, str_arg
, args
, case_index
, game_script
, true);
746 RestoreTextRefStackBackup(backup
);
748 FormatString (buf
, str_arg
, args
, case_index
, game_script
, true);
750 buf
->truncate (length
);
751 /* We have to restore the original offset here to to read the correct values. */
752 args
->offset
= orig_offset
;
755 uint next_substr_case_index
= 0;
756 std::stack
<const char *> str_stack
;
757 str_stack
.push(str_arg
);
760 while (!str_stack
.empty() && (b
= Utf8Consume(&str_stack
.top())) == '\0') {
763 if (str_stack
.empty()) break;
764 const char *&str
= str_stack
.top();
766 if (SCC_NEWGRF_FIRST
<= b
&& b
<= SCC_NEWGRF_LAST
) {
767 /* We need to pass some stuff as it might be modified; oh boy. */
768 //todo: should argve be passed here too?
769 b
= RemapNewGRFStringControlCode (b
, buf
, &str
, (int64
*)args
->GetDataPointer(), args
->GetDataLeft(), dry_run
);
770 if (b
== 0) continue;
775 uint64 sub_args_data
[20];
776 WChar sub_args_type
[20];
777 bool sub_args_need_free
[20];
778 StringParameters
sub_args(sub_args_data
, 20, sub_args_type
);
780 sub_args
.ClearTypeInformation();
781 memset(sub_args_need_free
, 0, sizeof(sub_args_need_free
));
785 uint32 stringid
= strtoul(str
, &p
, 16);
786 if (*p
!= ':' && *p
!= '\0') {
787 while (*p
!= '\0') p
++;
789 buf
->append ("(invalid SCC_ENCODED)");
792 if (stringid
>= TAB_SIZE_GAMESCRIPT
) {
793 while (*p
!= '\0') p
++;
795 buf
->append ("(invalid StringID)");
800 while (*p
!= '\0' && i
< 20) {
804 /* Find the next value */
805 bool instring
= false;
812 if (*p
== '"' && escape
) {
819 instring
= !instring
;
826 if (*p
== ':') break;
827 if (*p
== '\0') break;
831 /* Check if we want to look up another string */
833 size_t len
= Utf8Decode(&l
, s
);
834 bool lookup
= (l
== SCC_ENCODED
);
835 if (lookup
) s
+= len
;
837 param
= strtoull(s
, &p
, 16);
840 if (param
>= TAB_SIZE_GAMESCRIPT
) {
841 while (*p
!= '\0') p
++;
843 buf
->append ("(invalid sub-StringID)");
846 param
= MakeStringID(TEXT_TAB_GAMESCRIPT_START
, param
);
849 sub_args
.SetParam(i
++, param
);
851 char *g
= xstrmemdup (s
, p
- s
);
853 sub_args_need_free
[i
] = true;
854 sub_args
.SetParam(i
++, (uint64
)(size_t)g
);
857 /* If we didn't error out, we can actually print the string. */
860 AppendStringWithArgs (buf
, MakeStringID(TEXT_TAB_GAMESCRIPT_START
, stringid
), &sub_args
, true);
863 for (int i
= 0; i
< 20; i
++) {
864 if (sub_args_need_free
[i
]) free((void *)sub_args
.GetParam(i
));
869 case SCC_NEWGRF_STRINL
: {
870 StringID substr
= Utf8Consume(&str
);
871 str_stack
.push(GetStringPtr(substr
));
875 case SCC_NEWGRF_PRINT_WORD_STRING_ID
: {
876 StringID substr
= args
->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID
);
877 str_stack
.push(GetStringPtr(substr
));
878 case_index
= next_substr_case_index
;
879 next_substr_case_index
= 0;
884 case SCC_GENDER_LIST
: { // {G 0 Der Die Das}
885 /* First read the meta data from the language file. */
886 uint offset
= orig_offset
+ (byte
)*str
++;
888 if (!dry_run
&& args
->GetTypeAtOffset(offset
) != 0) {
889 /* Now we need to figure out what text to resolve, i.e.
890 * what do we need to draw? So get the actual raw string
891 * first using the control code to get said string. */
893 char *p
= input
+ Utf8Encode(input
, args
->GetTypeAtOffset(offset
));
896 /* Now do the string formatting. */
898 bool old_sgd
= _scan_for_gender_data
;
899 _scan_for_gender_data
= true;
900 StringParameters
tmp_params(args
->GetPointerToOffset(offset
), args
->num_param
- offset
, NULL
);
901 FormatString (&tmp
, input
, &tmp_params
);
902 _scan_for_gender_data
= old_sgd
;
904 /* And determine the string. */
905 const char *s
= tmp
.c_str();
906 WChar c
= Utf8Consume(&s
);
907 /* Does this string have a gender, if so, set it */
908 if (c
== SCC_GENDER_INDEX
) gender
= (byte
)s
[0];
910 str
= ParseStringChoice(str
, gender
, buf
);
914 /* This sets up the gender for the string.
915 * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
916 case SCC_GENDER_INDEX
: // {GENDER 0}
917 if (_scan_for_gender_data
) {
918 buf
->append_utf8 (SCC_GENDER_INDEX
);
919 buf
->append (*str
++);
925 case SCC_PLURAL_LIST
: { // {P}
926 int plural_form
= *str
++; // contains the plural form for this string
927 uint offset
= orig_offset
+ (byte
)*str
++;
928 int64 v
= *args
->GetPointerToOffset(offset
); // contains the number that determines plural
929 str
= ParseStringChoice(str
, DeterminePluralForm(v
, plural_form
), buf
);
933 case SCC_ARG_INDEX
: { // Move argument pointer
934 args
->offset
= orig_offset
+ (byte
)*str
++;
938 case SCC_SET_CASE
: { // {SET_CASE}
939 /* This is a pseudo command, it's outputted when someone does {STRING.ack}
940 * The modifier is added to all subsequent AppendStringWithArgs that accept the modifier. */
941 next_substr_case_index
= (byte
)*str
++;
945 case SCC_SWITCH_CASE
: { // {Used to implement case switching}
946 /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
947 * Each LEN is printed using 2 bytes in big endian order. */
948 uint num
= (byte
)*str
++;
950 if ((byte
)str
[0] == case_index
) {
951 /* Found the case, adjust str pointer and continue */
955 /* Otherwise skip to the next case */
956 str
+= 3 + (str
[1] << 8) + str
[2];
962 case SCC_REVISION
: // {REV}
963 buf
->append (_openttd_revision
);
966 case SCC_RAW_STRING_POINTER
: { // {RAW_STRING}
967 if (game_script
) break;
968 const char *str
= (const char *)(size_t)args
->GetInt64(SCC_RAW_STRING_POINTER
);
969 FormatString (buf
, str
, args
);
973 case SCC_STRING
: {// {STRING}
974 StringID str
= args
->GetInt32(SCC_STRING
);
975 if (game_script
&& GetStringTab(str
) != TEXT_TAB_GAMESCRIPT_START
) break;
976 /* WARNING. It's prohibited for the included string to consume any arguments.
977 * For included strings that consume argument, you should use STRING1, STRING2 etc.
978 * To debug stuff you can set argv to NULL and it will tell you */
979 StringParameters
tmp_params(args
->GetDataPointer(), args
->GetDataLeft(), NULL
);
980 AppendStringWithArgs (buf
, str
, &tmp_params
, next_substr_case_index
, game_script
);
981 next_substr_case_index
= 0;
991 case SCC_STRING7
: { // {STRING1..7}
992 /* Strings that consume arguments */
993 StringID str
= args
->GetInt32(b
);
994 if (game_script
&& GetStringTab(str
) != TEXT_TAB_GAMESCRIPT_START
) break;
995 uint size
= b
- SCC_STRING1
+ 1;
996 if (game_script
&& size
> args
->GetDataLeft()) {
997 buf
->append ("(too many parameters)");
999 StringParameters
sub_args(*args
, size
);
1000 AppendStringWithArgs (buf
, str
, &sub_args
, next_substr_case_index
, game_script
);
1002 next_substr_case_index
= 0;
1006 case SCC_COMMA
: // {COMMA}
1007 FormatCommaNumber (buf
, args
->GetInt64(SCC_COMMA
));
1010 case SCC_DECIMAL
: {// {DECIMAL}
1011 int64 number
= args
->GetInt64(SCC_DECIMAL
);
1012 int digits
= args
->GetInt32(SCC_DECIMAL
);
1013 FormatCommaNumber (buf
, number
, digits
);
1017 case SCC_NUM
: // {NUM}
1018 FormatNoCommaNumber (buf
, args
->GetInt64(SCC_NUM
));
1021 case SCC_ZEROFILL_NUM
: { // {ZEROFILL_NUM}
1022 int64 num
= args
->GetInt64();
1023 FormatZerofillNumber (buf
, num
, args
->GetInt64());
1027 case SCC_HEX
: // {HEX}
1028 FormatHexNumber (buf
, (uint64
)args
->GetInt64(SCC_HEX
));
1031 case SCC_BYTES
: // {BYTES}
1032 FormatBytes (buf
, args
->GetInt64());
1035 case SCC_CARGO_TINY
: { // {CARGO_TINY}
1036 /* Tiny description of cargotypes. Layout:
1037 * param 1: cargo type
1038 * param 2: cargo count */
1039 CargoID cargo
= args
->GetInt32(SCC_CARGO_TINY
);
1040 if (cargo
>= CargoSpec::GetArraySize()) break;
1042 StringID cargo_str
= CargoSpec::Get(cargo
)->units_volume
;
1044 switch (cargo_str
) {
1046 amount
= _units_weight
[_settings_game
.locale
.units_weight
].c
.ToDisplay(args
->GetInt64());
1050 amount
= _units_volume
[_settings_game
.locale
.units_volume
].c
.ToDisplay(args
->GetInt64());
1054 amount
= args
->GetInt64();
1059 FormatCommaNumber (buf
, amount
);
1063 case SCC_CARGO_SHORT
: { // {CARGO_SHORT}
1064 /* Short description of cargotypes. Layout:
1065 * param 1: cargo type
1066 * param 2: cargo count */
1067 CargoID cargo
= args
->GetInt32(SCC_CARGO_SHORT
);
1068 if (cargo
>= CargoSpec::GetArraySize()) break;
1070 StringID cargo_str
= CargoSpec::Get(cargo
)->units_volume
;
1071 switch (cargo_str
) {
1073 assert(_settings_game
.locale
.units_weight
< lengthof(_units_weight
));
1074 int64 args_array
[] = {_units_weight
[_settings_game
.locale
.units_weight
].c
.ToDisplay(args
->GetInt64())};
1075 StringParameters
tmp_params(args_array
);
1076 FormatString (buf
, GetStringPtr(_units_weight
[_settings_game
.locale
.units_weight
].l
), &tmp_params
);
1081 assert(_settings_game
.locale
.units_volume
< lengthof(_units_volume
));
1082 int64 args_array
[] = {_units_volume
[_settings_game
.locale
.units_volume
].c
.ToDisplay(args
->GetInt64())};
1083 StringParameters
tmp_params(args_array
);
1084 FormatString (buf
, GetStringPtr(_units_volume
[_settings_game
.locale
.units_volume
].l
), &tmp_params
);
1089 StringParameters
tmp_params(*args
, 1);
1090 AppendStringWithArgs (buf
, cargo_str
, &tmp_params
);
1097 case SCC_CARGO_LONG
: { // {CARGO_LONG}
1098 /* First parameter is cargo type, second parameter is cargo count */
1099 CargoID cargo
= args
->GetInt32(SCC_CARGO_LONG
);
1100 if (cargo
!= CT_INVALID
&& cargo
>= CargoSpec::GetArraySize()) break;
1102 StringID cargo_str
= (cargo
== CT_INVALID
) ? STR_QUANTITY_N_A
: CargoSpec::Get(cargo
)->quantifier
;
1103 StringParameters
tmp_args(*args
, 1);
1104 AppendStringWithArgs (buf
, cargo_str
, &tmp_args
);
1108 case SCC_CARGO_LIST
: { // {CARGO_LIST}
1109 uint32 cmask
= args
->GetInt32(SCC_CARGO_LIST
);
1112 const CargoSpec
*cs
;
1113 FOR_ALL_SORTED_CARGOSPECS(cs
) {
1114 if (!HasBit(cmask
, cs
->Index())) continue;
1116 if (buf
->length() >= buf
->capacity
- 3) break; // ',' and ' '
1121 /* Add a comma if this is not the first item */
1125 AppendStringWithArgs (buf
, cs
->name
, args
, next_substr_case_index
, game_script
);
1128 /* If first is still true then no cargo is accepted */
1129 if (first
) AppendStringWithArgs (buf
, STR_JUST_NOTHING
, args
, next_substr_case_index
, game_script
);
1131 next_substr_case_index
= 0;
1135 case SCC_CURRENCY_SHORT
: // {CURRENCY_SHORT}
1136 FormatGenericCurrency (buf
, _currency
, args
->GetInt64(), true);
1139 case SCC_CURRENCY_LONG
: // {CURRENCY_LONG}
1140 FormatGenericCurrency (buf
, _currency
, args
->GetInt64(SCC_CURRENCY_LONG
), false);
1143 case SCC_DATE_TINY
: // {DATE_TINY}
1144 FormatTinyOrISODate (buf
, args
->GetInt32(SCC_DATE_TINY
), STR_FORMAT_DATE_TINY
);
1147 case SCC_DATE_SHORT
: // {DATE_SHORT}
1148 FormatMonthAndYear (buf
, args
->GetInt32(SCC_DATE_SHORT
), next_substr_case_index
);
1149 next_substr_case_index
= 0;
1152 case SCC_DATE_LONG
: // {DATE_LONG}
1153 FormatYmdString (buf
, args
->GetInt32(SCC_DATE_LONG
), next_substr_case_index
);
1154 next_substr_case_index
= 0;
1157 case SCC_DATE_ISO
: // {DATE_ISO}
1158 FormatTinyOrISODate (buf
, args
->GetInt32(), STR_FORMAT_DATE_ISO
);
1161 case SCC_FORCE
: { // {FORCE}
1162 assert(_settings_game
.locale
.units_force
< lengthof(_units_force
));
1163 int64 args_array
[1] = {_units_force
[_settings_game
.locale
.units_force
].c
.ToDisplay(args
->GetInt64())};
1164 StringParameters
tmp_params(args_array
);
1165 FormatString (buf
, GetStringPtr(_units_force
[_settings_game
.locale
.units_force
].s
), &tmp_params
);
1169 case SCC_HEIGHT
: { // {HEIGHT}
1170 assert(_settings_game
.locale
.units_height
< lengthof(_units_height
));
1171 int64 args_array
[] = {_units_height
[_settings_game
.locale
.units_height
].c
.ToDisplay(args
->GetInt64())};
1172 StringParameters
tmp_params(args_array
);
1173 FormatString (buf
, GetStringPtr(_units_height
[_settings_game
.locale
.units_height
].s
), &tmp_params
);
1177 case SCC_POWER
: { // {POWER}
1178 assert(_settings_game
.locale
.units_power
< lengthof(_units_power
));
1179 int64 args_array
[1] = {_units_power
[_settings_game
.locale
.units_power
].c
.ToDisplay(args
->GetInt64())};
1180 StringParameters
tmp_params(args_array
);
1181 FormatString (buf
, GetStringPtr(_units_power
[_settings_game
.locale
.units_power
].s
), &tmp_params
);
1185 case SCC_VELOCITY
: { // {VELOCITY}
1186 assert(_settings_game
.locale
.units_velocity
< lengthof(_units_velocity
));
1187 int64 args_array
[] = {ConvertKmhishSpeedToDisplaySpeed(args
->GetInt64(SCC_VELOCITY
))};
1188 StringParameters
tmp_params(args_array
);
1189 FormatString (buf
, GetStringPtr(_units_velocity
[_settings_game
.locale
.units_velocity
].s
), &tmp_params
);
1193 case SCC_VOLUME_SHORT
: { // {VOLUME_SHORT}
1194 assert(_settings_game
.locale
.units_volume
< lengthof(_units_volume
));
1195 int64 args_array
[1] = {_units_volume
[_settings_game
.locale
.units_volume
].c
.ToDisplay(args
->GetInt64())};
1196 StringParameters
tmp_params(args_array
);
1197 FormatString (buf
, GetStringPtr(_units_volume
[_settings_game
.locale
.units_volume
].s
), &tmp_params
);
1201 case SCC_VOLUME_LONG
: { // {VOLUME_LONG}
1202 assert(_settings_game
.locale
.units_volume
< lengthof(_units_volume
));
1203 int64 args_array
[1] = {_units_volume
[_settings_game
.locale
.units_volume
].c
.ToDisplay(args
->GetInt64(SCC_VOLUME_LONG
))};
1204 StringParameters
tmp_params(args_array
);
1205 FormatString (buf
, GetStringPtr(_units_volume
[_settings_game
.locale
.units_volume
].l
), &tmp_params
);
1209 case SCC_WEIGHT_SHORT
: { // {WEIGHT_SHORT}
1210 assert(_settings_game
.locale
.units_weight
< lengthof(_units_weight
));
1211 int64 args_array
[1] = {_units_weight
[_settings_game
.locale
.units_weight
].c
.ToDisplay(args
->GetInt64())};
1212 StringParameters
tmp_params(args_array
);
1213 FormatString (buf
, GetStringPtr(_units_weight
[_settings_game
.locale
.units_weight
].s
), &tmp_params
);
1217 case SCC_WEIGHT_LONG
: { // {WEIGHT_LONG}
1218 assert(_settings_game
.locale
.units_weight
< lengthof(_units_weight
));
1219 int64 args_array
[1] = {_units_weight
[_settings_game
.locale
.units_weight
].c
.ToDisplay(args
->GetInt64(SCC_WEIGHT_LONG
))};
1220 StringParameters
tmp_params(args_array
);
1221 FormatString (buf
, GetStringPtr(_units_weight
[_settings_game
.locale
.units_weight
].l
), &tmp_params
);
1225 case SCC_COMPANY_NAME
: { // {COMPANY}
1226 const Company
*c
= Company::GetIfValid(args
->GetInt32());
1227 if (c
== NULL
) break;
1229 if (c
->name
!= NULL
) {
1230 int64 args_array
[] = {(int64
)(size_t)c
->name
};
1231 StringParameters
tmp_params(args_array
);
1232 AppendStringWithArgs (buf
, STR_JUST_RAW_STRING
, &tmp_params
);
1234 int64 args_array
[] = {c
->name_2
};
1235 StringParameters
tmp_params(args_array
);
1236 AppendStringWithArgs (buf
, c
->name_1
, &tmp_params
);
1241 case SCC_COMPANY_NUM
: { // {COMPANY_NUM}
1242 CompanyID company
= (CompanyID
)args
->GetInt32();
1244 /* Nothing is added for AI or inactive companies */
1245 if (Company::IsValidHumanID(company
)) {
1246 int64 args_array
[] = {company
+ 1};
1247 StringParameters
tmp_params(args_array
);
1248 AppendStringWithArgs (buf
, STR_FORMAT_COMPANY_NUM
, &tmp_params
);
1253 case SCC_DEPOT_NAME
: { // {DEPOT}
1254 VehicleType vt
= (VehicleType
)args
->GetInt32(SCC_DEPOT_NAME
);
1255 if (vt
== VEH_AIRCRAFT
) {
1256 uint64 args_array
[] = {(uint64
)args
->GetInt32()};
1257 WChar types_array
[] = {SCC_STATION_NAME
};
1258 StringParameters
tmp_params(args_array
, 1, types_array
);
1259 AppendStringWithArgs (buf
, STR_FORMAT_DEPOT_NAME_AIRCRAFT
, &tmp_params
);
1263 const Depot
*d
= Depot::Get(args
->GetInt32());
1264 if (d
->name
!= NULL
) {
1265 int64 args_array
[] = {(int64
)(size_t)d
->name
};
1266 StringParameters
tmp_params(args_array
);
1267 AppendStringWithArgs (buf
, STR_JUST_RAW_STRING
, &tmp_params
);
1269 int64 args_array
[] = {d
->town
->index
, d
->town_cn
+ 1};
1270 StringParameters
tmp_params(args_array
);
1271 AppendStringWithArgs (buf
, STR_FORMAT_DEPOT_NAME_TRAIN
+ 2 * vt
+ (d
->town_cn
== 0 ? 0 : 1), &tmp_params
);
1276 case SCC_ENGINE_NAME
: { // {ENGINE}
1277 const Engine
*e
= Engine::GetIfValid(args
->GetInt32(SCC_ENGINE_NAME
));
1278 if (e
== NULL
) break;
1280 if (e
->name
!= NULL
&& e
->IsEnabled()) {
1281 int64 args_array
[] = {(int64
)(size_t)e
->name
};
1282 StringParameters
tmp_params(args_array
);
1283 AppendStringWithArgs (buf
, STR_JUST_RAW_STRING
, &tmp_params
);
1285 StringParameters
tmp_params(NULL
, 0, NULL
);
1286 AppendStringWithArgs (buf
, e
->info
.string_id
, &tmp_params
);
1291 case SCC_GROUP_NAME
: { // {GROUP}
1292 const Group
*g
= Group::GetIfValid(args
->GetInt32());
1293 if (g
== NULL
) break;
1295 if (g
->name
!= NULL
) {
1296 int64 args_array
[] = {(int64
)(size_t)g
->name
};
1297 StringParameters
tmp_params(args_array
);
1298 AppendStringWithArgs (buf
, STR_JUST_RAW_STRING
, &tmp_params
);
1300 int64 args_array
[] = {g
->index
};
1301 StringParameters
tmp_params(args_array
);
1303 AppendStringWithArgs (buf
, STR_FORMAT_GROUP_NAME
, &tmp_params
);
1308 case SCC_INDUSTRY_NAME
: { // {INDUSTRY}
1309 const Industry
*i
= Industry::GetIfValid(args
->GetInt32(SCC_INDUSTRY_NAME
));
1310 if (i
== NULL
) break;
1312 if (_scan_for_gender_data
) {
1313 /* Gender is defined by the industry type.
1314 * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
1315 StringParameters
tmp_params(NULL
, 0, NULL
);
1316 FormatString (buf
, GetStringPtr(GetIndustrySpec(i
->type
)->name
), &tmp_params
, next_substr_case_index
);
1318 /* First print the town name and the industry type name. */
1319 int64 args_array
[2] = {i
->town
->index
, GetIndustrySpec(i
->type
)->name
};
1320 StringParameters
tmp_params(args_array
);
1322 FormatString (buf
, GetStringPtr(STR_FORMAT_INDUSTRY_NAME
), &tmp_params
, next_substr_case_index
);
1324 next_substr_case_index
= 0;
1328 case SCC_PRESIDENT_NAME
: { // {PRESIDENT_NAME}
1329 const Company
*c
= Company::GetIfValid(args
->GetInt32(SCC_PRESIDENT_NAME
));
1330 if (c
== NULL
) break;
1332 if (c
->president_name
!= NULL
) {
1333 int64 args_array
[] = {(int64
)(size_t)c
->president_name
};
1334 StringParameters
tmp_params(args_array
);
1335 AppendStringWithArgs (buf
, STR_JUST_RAW_STRING
, &tmp_params
);
1337 int64 args_array
[] = {c
->president_name_2
};
1338 StringParameters
tmp_params(args_array
);
1339 AppendStringWithArgs (buf
, c
->president_name_1
, &tmp_params
);
1344 case SCC_STATION_NAME
: { // {STATION}
1345 StationID sid
= args
->GetInt32(SCC_STATION_NAME
);
1346 const Station
*st
= Station::GetIfValid(sid
);
1349 /* The station doesn't exist anymore. The only place where we might
1350 * be "drawing" an invalid station is in the case of cargo that is
1352 StringParameters
tmp_params(NULL
, 0, NULL
);
1353 AppendStringWithArgs (buf
, STR_UNKNOWN_STATION
, &tmp_params
);
1357 if (st
->name
!= NULL
) {
1358 int64 args_array
[] = {(int64
)(size_t)st
->name
};
1359 StringParameters
tmp_params(args_array
);
1360 AppendStringWithArgs (buf
, STR_JUST_RAW_STRING
, &tmp_params
);
1362 StringID str
= st
->string_id
;
1363 if (st
->indtype
!= IT_INVALID
) {
1364 /* Special case where the industry provides the name for the station */
1365 const IndustrySpec
*indsp
= GetIndustrySpec(st
->indtype
);
1367 /* Industry GRFs can change which might remove the station name and
1368 * thus cause very strange things. Here we check for that before we
1369 * actually set the station name. */
1370 if (indsp
->station_name
!= STR_NULL
&& indsp
->station_name
!= STR_UNDEFINED
) {
1371 str
= indsp
->station_name
;
1375 int64 args_array
[] = {STR_TOWN_NAME
, st
->town
->index
, st
->index
};
1376 StringParameters
tmp_params(args_array
);
1377 AppendStringWithArgs (buf
, str
, &tmp_params
);
1382 case SCC_TOWN_NAME
: { // {TOWN}
1383 const Town
*t
= Town::GetIfValid(args
->GetInt32(SCC_TOWN_NAME
));
1384 if (t
== NULL
) break;
1386 if (t
->name
!= NULL
) {
1387 int64 args_array
[] = {(int64
)(size_t)t
->name
};
1388 StringParameters
tmp_params(args_array
);
1389 AppendStringWithArgs (buf
, STR_JUST_RAW_STRING
, &tmp_params
);
1391 AppendTownName (buf
, t
);
1396 case SCC_WAYPOINT_NAME
: { // {WAYPOINT}
1397 Waypoint
*wp
= Waypoint::GetIfValid(args
->GetInt32(SCC_WAYPOINT_NAME
));
1398 if (wp
== NULL
) break;
1400 if (wp
->name
!= NULL
) {
1401 int64 args_array
[] = {(int64
)(size_t)wp
->name
};
1402 StringParameters
tmp_params(args_array
);
1403 AppendStringWithArgs (buf
, STR_JUST_RAW_STRING
, &tmp_params
);
1405 int64 args_array
[] = {wp
->town
->index
, wp
->town_cn
+ 1};
1406 StringParameters
tmp_params(args_array
);
1407 StringID str
= ((wp
->string_id
== STR_SV_STNAME_BUOY
) ? STR_FORMAT_BUOY_NAME
: STR_FORMAT_WAYPOINT_NAME
);
1408 if (wp
->town_cn
!= 0) str
++;
1409 AppendStringWithArgs (buf
, str
, &tmp_params
);
1414 case SCC_VEHICLE_NAME
: { // {VEHICLE}
1415 const Vehicle
*v
= Vehicle::GetIfValid(args
->GetInt32(SCC_VEHICLE_NAME
));
1416 if (v
== NULL
) break;
1418 if (v
->name
!= NULL
) {
1419 int64 args_array
[] = {(int64
)(size_t)v
->name
};
1420 StringParameters
tmp_params(args_array
);
1421 AppendStringWithArgs (buf
, STR_JUST_RAW_STRING
, &tmp_params
);
1423 int64 args_array
[] = {v
->unitnumber
};
1424 StringParameters
tmp_params(args_array
);
1428 default: str
= STR_INVALID_VEHICLE
; break;
1429 case VEH_TRAIN
: str
= STR_SV_TRAIN_NAME
; break;
1430 case VEH_ROAD
: str
= STR_SV_ROAD_VEHICLE_NAME
; break;
1431 case VEH_SHIP
: str
= STR_SV_SHIP_NAME
; break;
1432 case VEH_AIRCRAFT
: str
= STR_SV_AIRCRAFT_NAME
; break;
1435 AppendStringWithArgs (buf
, str
, &tmp_params
);
1440 case SCC_SIGN_NAME
: { // {SIGN}
1441 const Sign
*si
= Sign::GetIfValid(args
->GetInt32());
1442 if (si
== NULL
) break;
1444 if (si
->name
!= NULL
) {
1445 int64 args_array
[] = {(int64
)(size_t)si
->name
};
1446 StringParameters
tmp_params(args_array
);
1447 AppendStringWithArgs (buf
, STR_JUST_RAW_STRING
, &tmp_params
);
1449 StringParameters
tmp_params(NULL
, 0, NULL
);
1450 AppendStringWithArgs (buf
, STR_DEFAULT_SIGN_NAME
, &tmp_params
);
1455 case SCC_STATION_FEATURES
: { // {STATIONFEATURES}
1456 AppendStationSpecialString (buf
, args
->GetInt32(SCC_STATION_FEATURES
));
1461 buf
->append_utf8 (b
);
1468 static void AppendStationSpecialString (stringb
*buf
, int x
)
1470 if (x
& FACIL_TRAIN
) buf
->append_utf8 (SCC_TRAIN
);
1471 if (x
& FACIL_TRUCK_STOP
) buf
->append_utf8 (SCC_LORRY
);
1472 if (x
& FACIL_BUS_STOP
) buf
->append_utf8 (SCC_BUS
);
1473 if (x
& FACIL_DOCK
) buf
->append_utf8 (SCC_SHIP
);
1474 if (x
& FACIL_AIRPORT
) buf
->append_utf8 (SCC_PLANE
);
1478 static const char *GenSurname (uint32 arg
)
1480 static const char * const surname_list
[] = {
1481 "Adams", "Allan", "Baker", "Bigwig", "Black",
1482 "Bloggs", "Brown", "Campbell", "Gordon", "Hamilton",
1483 "Hawthorn", "Higgins", "Green", "Gribble", "Jones",
1484 "McAlpine", "MacDonald", "McIntosh", "Muir", "Murphy",
1485 "Nelson", "O'Donnell", "Parker", "Phillips", "Pilkington",
1486 "Quigley", "Sharkey", "Thomson", "Watkins",
1489 static const char * const silly_list
[] = {
1490 "Grumpy", "Dozy", "Speedy", "Nosey", "Dribble", "Mushroom",
1491 "Cabbage", "Sniffle", "Fishy", "Swindle", "Sneaky", "Nutkins",
1494 const char * const *base
;
1497 if (_settings_game
.game_creation
.landscape
== LT_TOYLAND
) {
1499 num
= lengthof(silly_list
);
1501 base
= surname_list
;
1502 num
= lengthof(surname_list
);
1505 return base
[num
* GB(arg
, 16, 8) >> 8];
1508 static void GenAndCoName (stringb
*buf
, uint32 arg
)
1510 buf
->append (GenSurname (arg
));
1511 buf
->append (" & Co.");
1514 static void GenPresidentName (stringb
*buf
, uint32 x
)
1516 static const char initials
[] = {
1517 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
1518 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
1521 char initial
[] = "?. ";
1524 initial
[0] = initials
[sizeof(initials
) * GB(x
, 0, 8) >> 8];
1525 buf
->append (initial
);
1527 i
= (sizeof(initials
) + 35) * GB(x
, 8, 8) >> 8;
1528 if (i
< sizeof(initials
)) {
1529 initial
[0] = initials
[i
];
1530 buf
->append (initial
);
1533 buf
->append (GenSurname (x
));
1536 static void AppendSpecialNameString (stringb
*buf
, int ind
, StringParameters
*args
)
1538 static const char * const silly_company_names
[] = {
1540 "Tiny Transport Ltd.",
1542 "Comfy-Coach & Co.",
1543 "Crush & Bump Ltd.",
1544 "Broken & Late Ltd.",
1546 "Supersonic Travel",
1548 "Lightning International",
1549 "Pannik & Loozit Ltd.",
1550 "Inter-City Transport",
1551 "Getout & Pushit Ltd.",
1556 buf
->append (silly_company_names
[min(args
->GetInt32() & 0xFFFF, lengthof(silly_company_names
) - 1)]);
1559 case 2: // used for Foobar & Co company names
1560 GenAndCoName (buf
, args
->GetInt32());
1563 case 3: // President name
1564 GenPresidentName (buf
, args
->GetInt32());
1569 if (IsInsideMM(ind
- 6, 0, SPECSTR_TOWNNAME_LAST
- SPECSTR_TOWNNAME_START
+ 1)) {
1570 GenerateTownNameString (buf
, ind
- 6, args
->GetInt32());
1571 buf
->append (" Transport");
1575 /* language name? */
1576 if (IsInsideMM(ind
, (SPECSTR_LANGUAGE_START
- 0x70E4), (SPECSTR_LANGUAGE_END
- 0x70E4) + 1)) {
1577 int i
= ind
- (SPECSTR_LANGUAGE_START
- 0x70E4);
1578 buf
->append (&_languages
[i
] == _current_language
? _current_language
->own_name
: _languages
[i
].name
);
1582 /* resolution size? */
1583 if (IsInsideMM(ind
, (SPECSTR_RESOLUTION_START
- 0x70E4), (SPECSTR_RESOLUTION_END
- 0x70E4) + 1)) {
1584 int i
= ind
- (SPECSTR_RESOLUTION_START
- 0x70E4);
1585 buf
->append_fmt ("%ux%u", _resolutions
[i
].width
, _resolutions
[i
].height
);
1589 /* screenshot format name? */
1590 if (IsInsideMM(ind
, (SPECSTR_SCREENSHOT_START
- 0x70E4), (SPECSTR_SCREENSHOT_END
- 0x70E4) + 1)) {
1591 int i
= ind
- (SPECSTR_SCREENSHOT_START
- 0x70E4);
1592 buf
->append (GetScreenshotFormatDesc(i
));
1599 #ifdef ENABLE_NETWORK
1600 extern void SortNetworkLanguages();
1601 #else /* ENABLE_NETWORK */
1602 static inline void SortNetworkLanguages() {}
1603 #endif /* ENABLE_NETWORK */
1606 * Check whether the header is a valid header for OpenTTD.
1607 * @return true iff the header is deemed valid.
1609 bool LanguagePackHeader::IsValid() const
1611 return this->ident
== TO_LE32(LanguagePackHeader::IDENT
) &&
1612 this->version
== TO_LE32(LANGUAGE_PACK_VERSION
) &&
1613 this->plural_form
< LANGUAGE_MAX_PLURAL
&&
1614 this->text_dir
<= 1 &&
1615 this->newgrflangid
< MAX_LANG
&&
1616 this->num_genders
< MAX_NUM_GENDERS
&&
1617 this->num_cases
< MAX_NUM_CASES
&&
1618 StrValid(this->name
, lastof(this->name
)) &&
1619 StrValid(this->own_name
, lastof(this->own_name
)) &&
1620 StrValid(this->isocode
, lastof(this->isocode
)) &&
1621 StrValid(this->digit_group_separator
, lastof(this->digit_group_separator
)) &&
1622 StrValid(this->digit_group_separator_currency
, lastof(this->digit_group_separator_currency
)) &&
1623 StrValid(this->digit_decimal_separator
, lastof(this->digit_decimal_separator
));
1627 * Read a particular language.
1628 * @param lang The metadata about the language.
1629 * @return Whether the loading went okay or not.
1631 bool ReadLanguagePack(const LanguageMetadata
*lang
)
1633 /* Current language pack */
1635 LanguagePack
*lang_pack
= (LanguagePack
*)ReadFileToMem(lang
->file
, &len
, 1U << 20);
1636 if (lang_pack
== NULL
) return false;
1638 /* End of read data (+ terminating zero added in ReadFileToMem()) */
1639 const char *end
= (char *)lang_pack
+ len
+ 1;
1641 /* We need at least one byte of lang_pack->data */
1642 if (end
<= lang_pack
->data
|| !lang_pack
->IsValid()) {
1647 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1648 for (uint i
= 0; i
< TEXT_TAB_END
; i
++) {
1649 lang_pack
->offsets
[i
] = ReadLE16Aligned(&lang_pack
->offsets
[i
]);
1651 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
1654 for (uint i
= 0; i
< TEXT_TAB_END
; i
++) {
1655 uint16 num
= lang_pack
->offsets
[i
];
1656 if (num
> TAB_SIZE
) {
1661 _langtab_start
[i
] = count
;
1662 _langtab_num
[i
] = num
;
1666 /* Allocate offsets */
1667 char **langpack_offs
= xmalloct
<char *>(count
);
1670 char *s
= lang_pack
->data
;
1672 for (uint i
= 0; i
< count
; i
++) {
1673 if (s
+ len
>= end
) {
1675 free(langpack_offs
);
1679 len
= ((len
& 0x3F) << 8) + (byte
)*s
++;
1680 if (s
+ len
>= end
) {
1682 free(langpack_offs
);
1686 langpack_offs
[i
] = s
;
1689 *s
++ = '\0'; // zero terminate the string
1693 _langpack
= lang_pack
;
1695 free(_langpack_offs
);
1696 _langpack_offs
= langpack_offs
;
1698 _current_language
= lang
;
1699 _current_text_dir
= (TextDirection
)_current_language
->text_dir
;
1700 const char *c_file
= strrchr(_current_language
->file
, PATHSEPCHAR
) + 1;
1701 bstrcpy (_config_language_file
, c_file
);
1702 SetCurrentGrfLangID(_current_language
->newgrflangid
);
1704 #ifdef WITH_ICU_SORT
1705 /* Delete previous collator. */
1706 if (_current_collator
!= NULL
) {
1707 delete _current_collator
;
1708 _current_collator
= NULL
;
1711 /* Create a collator instance for our current locale. */
1712 UErrorCode status
= U_ZERO_ERROR
;
1713 _current_collator
= Collator::createInstance(Locale(_current_language
->isocode
), status
);
1714 /* Sort number substrings by their numerical value. */
1715 if (_current_collator
!= NULL
) _current_collator
->setAttribute(UCOL_NUMERIC_COLLATION
, UCOL_ON
, status
);
1716 /* Avoid using the collator if it is not correctly set. */
1717 if (U_FAILURE(status
)) {
1718 delete _current_collator
;
1719 _current_collator
= NULL
;
1721 #endif /* WITH_ICU_SORT */
1723 /* Some lists need to be sorted again after a language change. */
1724 ReconsiderGameScriptLanguage();
1725 InitializeSortedCargoSpecs();
1726 SortIndustryTypes();
1727 BuildIndustriesLegend();
1728 SortNetworkLanguages();
1729 BuildContentTypeStringList();
1730 InvalidateWindowClassesData(WC_BUILD_VEHICLE
); // Build vehicle window.
1731 InvalidateWindowClassesData(WC_TRAINS_LIST
); // Train group window.
1732 InvalidateWindowClassesData(WC_ROADVEH_LIST
); // Road vehicle group window.
1733 InvalidateWindowClassesData(WC_SHIPS_LIST
); // Ship group window.
1734 InvalidateWindowClassesData(WC_AIRCRAFT_LIST
); // Aircraft group window.
1735 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY
); // Industry directory window.
1736 InvalidateWindowClassesData(WC_STATION_LIST
); // Station list window.
1741 /* Win32 implementation in win32.cpp.
1742 * OS X implementation in os/macosx/macos.mm. */
1743 #if !(defined(WIN32) || defined(__APPLE__))
1745 * Determine the current charset based on the environment
1746 * First check some default values, after this one we passed ourselves
1747 * and if none exist return the value for $LANG
1748 * @param param environment variable to check conditionally if default ones are not
1749 * set. Pass NULL if you don't want additional checks.
1750 * @return return string containing current charset, or NULL if not-determinable
1752 const char *GetCurrentLocale(const char *param
)
1756 env
= getenv("LANGUAGE");
1757 if (env
!= NULL
) return env
;
1759 env
= getenv("LC_ALL");
1760 if (env
!= NULL
) return env
;
1762 if (param
!= NULL
) {
1763 env
= getenv(param
);
1764 if (env
!= NULL
) return env
;
1767 return getenv("LANG");
1770 const char *GetCurrentLocale(const char *param
);
1771 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
1773 int CDECL
StringIDSorter(const StringID
*a
, const StringID
*b
)
1777 GetString (stra
, *a
);
1778 GetString (strb
, *b
);
1780 return strnatcmp(stra
, strb
);
1784 * Get the language with the given NewGRF language ID.
1785 * @param newgrflangid NewGRF languages ID to check.
1786 * @return The language's metadata, or NULL if it is not known.
1788 const LanguageMetadata
*GetLanguage(byte newgrflangid
)
1790 for (const LanguageMetadata
*lang
= _languages
.Begin(); lang
!= _languages
.End(); lang
++) {
1791 if (newgrflangid
== lang
->newgrflangid
) return lang
;
1798 * Reads the language file header and checks compatibility.
1799 * @param file the file to read
1800 * @param hdr the place to write the header information to
1801 * @return true if and only if the language file is of a compatible version
1803 static bool GetLanguageFileHeader(const char *file
, LanguagePackHeader
*hdr
)
1805 FILE *f
= fopen(file
, "rb");
1806 if (f
== NULL
) return false;
1808 size_t read
= fread(hdr
, sizeof(*hdr
), 1, f
);
1811 bool ret
= read
== 1 && hdr
->IsValid();
1813 /* Convert endianness for the windows language ID */
1815 hdr
->missing
= FROM_LE16(hdr
->missing
);
1816 hdr
->winlangid
= FROM_LE16(hdr
->winlangid
);
1822 * Gets a list of languages from the given directory.
1823 * @param path the base directory to search in
1825 static void GetLanguageList(const char *path
)
1827 DIR *dir
= ttd_opendir(path
);
1829 struct dirent
*dirent
;
1830 while ((dirent
= readdir(dir
)) != NULL
) {
1831 const char *d_name
= FS2OTTD(dirent
->d_name
);
1832 const char *extension
= strrchr(d_name
, '.');
1834 /* Not a language file */
1835 if (extension
== NULL
|| strcmp(extension
, ".lng") != 0) continue;
1837 LanguageMetadata lmd
;
1838 bstrfmt (lmd
.file
, "%s%s", path
, d_name
);
1840 /* Check whether the file is of the correct version */
1841 if (!GetLanguageFileHeader(lmd
.file
, &lmd
)) {
1842 DEBUG(misc
, 3, "%s is not a valid language file", lmd
.file
);
1843 } else if (GetLanguage(lmd
.newgrflangid
) != NULL
) {
1844 DEBUG(misc
, 3, "%s's language ID is already known", lmd
.file
);
1846 *_languages
.Append() = lmd
;
1854 * Make a list of the available language packs. Put the data in
1857 void InitializeLanguagePacks()
1861 FOR_ALL_SEARCHPATHS(sp
) {
1862 char path
[MAX_PATH
];
1863 FioGetFullPath (path
, lengthof(path
), sp
, LANG_DIR
);
1864 GetLanguageList(path
);
1866 if (_languages
.Length() == 0) usererror("No available language packs (invalid versions?)");
1868 /* Acquire the locale of the current system */
1869 const char *lang
= GetCurrentLocale("LC_MESSAGES");
1870 if (lang
== NULL
) lang
= "en_GB";
1872 const LanguageMetadata
*chosen_language
= NULL
; ///< Matching the language in the configuration file or the current locale
1873 const LanguageMetadata
*language_fallback
= NULL
; ///< Using pt_PT for pt_BR locale when pt_BR is not available
1874 const LanguageMetadata
*en_GB_fallback
= _languages
.Begin(); ///< Fallback when no locale-matching language has been found
1876 /* Find a proper language. */
1877 for (const LanguageMetadata
*lng
= _languages
.Begin(); lng
!= _languages
.End(); lng
++) {
1878 /* We are trying to find a default language. The priority is by
1879 * configuration file, local environment and last, if nothing found,
1881 const char *lang_file
= strrchr(lng
->file
, PATHSEPCHAR
) + 1;
1882 if (strcmp(lang_file
, _config_language_file
) == 0) {
1883 chosen_language
= lng
;
1887 if (strcmp (lng
->isocode
, "en_GB") == 0) en_GB_fallback
= lng
;
1888 if (strncmp(lng
->isocode
, lang
, 5) == 0) chosen_language
= lng
;
1889 if (strncmp(lng
->isocode
, lang
, 2) == 0) language_fallback
= lng
;
1892 /* We haven't found the language in the config nor the one in the locale.
1893 * Now we set it to one of the fallback languages */
1894 if (chosen_language
== NULL
) {
1895 chosen_language
= (language_fallback
!= NULL
) ? language_fallback
: en_GB_fallback
;
1898 if (!ReadLanguagePack(chosen_language
)) usererror("Can't read language pack '%s'", chosen_language
->file
);
1902 * Get the ISO language code of the currently loaded language.
1903 * @return the ISO code.
1905 const char *GetCurrentLanguageIsoCode()
1907 return _langpack
->isocode
;
1911 * Check whether there are glyphs missing in the current language.
1912 * @return If glyphs are missing, return \c true, else return \c false.
1914 bool MissingGlyphSearcher::FindMissingGlyphs (void)
1916 InitFreeType(this->Monospace());
1917 const Sprite
*question_mark
[FS_END
];
1919 for (FontSize size
= this->Monospace() ? FS_MONO
: FS_BEGIN
; size
< (this->Monospace() ? FS_END
: FS_MONO
); size
++) {
1920 question_mark
[size
] = FontCache::Get(size
)->GetCharGlyph('?');
1924 for (const char *text
= this->NextString(); text
!= NULL
; text
= this->NextString()) {
1925 FontSize size
= this->DefaultSize();
1927 WChar c
= Utf8Consume(&text
);
1930 } else if (c
== SCC_TINYFONT
) {
1932 } else if (c
== SCC_BIGFONT
) {
1934 } else if (!IsInsideMM (c
, SCC_SPRITE_START
, SCC_SPRITE_END
)
1936 && !IsTextDirectionChar (c
)
1938 && FontCache::Get(size
)->GetCharGlyph(c
) == question_mark
[size
]) {
1939 /* The character is printable, but not in the normal font. This is the case we were testing for. */
1947 /** Helper for searching through the language pack. */
1948 class LanguagePackGlyphSearcher
: public MissingGlyphSearcher
{
1949 uint i
; ///< Iterator for the primary language tables.
1950 uint j
; ///< Iterator for the secondary language tables.
1953 CONSTEXPR
LanguagePackGlyphSearcher()
1954 : MissingGlyphSearcher (FS_NORMAL
, false), i(0), j(0)
1959 /* virtual */ void Reset()
1965 /* virtual */ const char *NextString()
1967 if (this->i
>= TEXT_TAB_END
) return NULL
;
1969 const char *ret
= _langpack_offs
[_langtab_start
[this->i
] + this->j
];
1972 while (this->i
< TEXT_TAB_END
&& this->j
>= _langtab_num
[this->i
]) {
1982 * Check whether the currently loaded language pack
1983 * uses characters that the currently loaded font
1984 * does not support. If this is the case an error
1985 * message will be shown in English. The error
1986 * message will not be localized because that would
1987 * mean it might use characters that are not in the
1988 * font, which is the whole reason this check has
1990 * @param base_font Whether to look at the base font as well.
1991 * @param searcher The methods to use to search for strings to check.
1992 * If NULL the loaded language pack searcher is used.
1994 void CheckForMissingGlyphs(bool base_font
, MissingGlyphSearcher
*searcher
)
1996 static LanguagePackGlyphSearcher pack_searcher
;
1997 if (searcher
== NULL
) searcher
= &pack_searcher
;
1998 bool bad_font
= !base_font
|| searcher
->FindMissingGlyphs();
1999 #ifdef WITH_FREETYPE
2001 /* We found an unprintable character... lets try whether we can find
2002 * a fallback font that can print the characters in the current language. */
2003 FreeTypeSettings backup
;
2004 memcpy(&backup
, &_freetype
, sizeof(backup
));
2006 bad_font
= !SetFallbackFont(&_freetype
, _langpack
->isocode
, _langpack
->winlangid
, searcher
);
2008 memcpy(&_freetype
, &backup
, sizeof(backup
));
2010 if (bad_font
&& base_font
) {
2011 /* Our fallback font does miss characters too, so keep the
2012 * user chosen font as that is more likely to be any good than
2013 * the wild guess we made */
2014 InitFreeType(searcher
->Monospace());
2020 /* All attempts have failed. Display an error. */
2022 SCCSTR_YELLOW
"The current font is missing some of "
2023 "the characters used in the texts for this language. "
2024 "Read the readme to see how to solve this.");
2025 ShowErrorMessage(STR_JUST_RAW_STRING
, INVALID_STRING_ID
, WL_WARNING
);
2027 /* Reset the font width */
2028 LoadStringWidthTable(searcher
->Monospace());
2032 /* Update the font with cache */
2033 LoadStringWidthTable(searcher
->Monospace());
2035 #if !defined(WITH_ICU_LAYOUT)
2037 * For right-to-left languages we need the ICU library. If
2038 * we do not have support for that library we warn the user
2039 * about it with a message.
2041 if (_current_text_dir
!= TD_LTR
) {
2043 SCCSTR_YELLOW
"This version of OpenTTD does not "
2044 "support right-to-left languages. "
2045 "Recompile with icu enabled.");
2046 ShowErrorMessage(STR_JUST_RAW_STRING
, INVALID_STRING_ID
, WL_ERROR
);
2048 #endif /* !WITH_ICU_LAYOUT */