# strings.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "currency.h"
    00014 #include "station_base.h"
    00015 #include "town.h"
    00016 #include "screenshot.h"
    00017 #include "waypoint_base.h"
    00018 #include "depot_base.h"
    00019 #include "industry.h"
    00020 #include "newgrf_text.h"
    00021 #include "fileio_func.h"
    00022 #include "signs_base.h"
    00023 #include "fontcache.h"
    00024 #include "error.h"
    00025 #include "strings_func.h"
    00026 #include "rev.h"
    00027 #include "core/endian_func.hpp"
    00028 #include "date_func.h"
    00029 #include "vehicle_base.h"
    00030 #include "engine_base.h"
    00031 #include "language.h"
    00032 #include "townname_func.h"
    00033 #include "string_func.h"
    00034 #include "company_base.h"
    00035 #include "smallmap_gui.h"
    00036 #include "window_func.h"
    00037 #include "debug.h"
    00038 #include "game/game_text.hpp"
    00039 #include <stack>
    00040
    00041 #include "table/strings.h"
    00042 #include "table/control_codes.h"
    00043
    00044 char _config_language_file[MAX_PATH];
    00045 LanguageList _languages;
    00046 const LanguageMetadata *_current_language = NULL;
    00047
    00048 TextDirection _current_text_dir;
    00049
    00050 #ifdef WITH_ICU
    00051 Collator *_current_collator = NULL;
    00052 #endif /* WITH_ICU */
    00053
    00054 static uint64 _global_string_params_data[20];
    00055 static WChar _global_string_params_type[20];
    00056 StringParameters _global_string_params(_global_string_params_data, 20, _global_string_params_type);
    00057
    00059 void StringParameters::ClearTypeInformation()
    00060 {
    00061   assert(this->type != NULL);
    00062   MemSetT(this->type, 0, this->num_param);
    00063 }
    00064
    00069 void StringParameters::ShiftParameters(uint amount)
    00070 {
    00071   assert(amount <= this->num_param);
    00072   MemMoveT(this->data + amount, this->data, this->num_param - amount);
    00073 }
    00074
    00081 void CopyInDParam(int offs, const uint64 *src, int num)
    00082 {
    00083   MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
    00084 }
    00085
    00092 void CopyOutDParam(uint64 *dst, int offs, int num)
    00093 {
    00094   MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
    00095 }
    00096
    00105 void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
    00106 {
    00107   char buf[DRAW_STRING_BUFFER];
    00108   GetString(buf, string, lastof(buf));
    00109
    00110   MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num);
    00111   for (int i = 0; i < num; i++) {
    00112     if (_global_string_params.HasTypeInformation() && _global_string_params.GetTypeAtOffset(i) == SCC_RAW_STRING_POINTER) {
    00113       strings[i] = strdup((const char *)(size_t)_global_string_params.GetParam(i));
    00114       dst[i] = (size_t)strings[i];
    00115     } else {
    00116       strings[i] = NULL;
    00117     }
    00118   }
    00119 }
    00120
    00121 static char *StationGetSpecialString(char *buff, int x, const char *last);
    00122 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
    00123 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
    00124
    00125 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);
    00126
    00127 struct LanguagePack : public LanguagePackHeader {
    00128   char data[]; // list of strings
    00129 };
    00130
    00131 static char **_langpack_offs;
    00132 static LanguagePack *_langpack;
    00133 static uint _langtab_num[TAB_COUNT];
    00134 static uint _langtab_start[TAB_COUNT];
    00135 static bool _keep_gender_data = false;
    00136
    00137
    00138 const char *GetStringPtr(StringID string)
    00139 {
    00140   switch (GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)) {
    00141     case GAME_TEXT_TAB: return GetGameStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
    00142     /* GetGRFStringPtr doesn't handle 0xD4xx ids, we need to convert those to 0xD0xx. */
    00143     case 26: return GetStringPtr(GetGRFStringID(0, 0xD000 + GB(string, TAB_SIZE_OFFSET, 10)));
    00144     case 28: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
    00145     case 29: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x0800);
    00146     case 30: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x1000);
    00147     default: return _langpack_offs[_langtab_start[GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)] + GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS)];
    00148   }
    00149 }
    00150
    00161 char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
    00162 {
    00163   if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
    00164
    00165   uint index = GB(string, TAB_SIZE_OFFSET,  TAB_SIZE_BITS);
    00166   uint tab   = GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS);
    00167
    00168   switch (tab) {
    00169     case 4:
    00170       if (index >= 0xC0 && !game_script) {
    00171         return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
    00172       }
    00173       break;
    00174
    00175     case 14:
    00176       if (index >= 0xE4 && !game_script) {
    00177         return GetSpecialNameString(buffr, index - 0xE4, args, last);
    00178       }
    00179       break;
    00180
    00181     case 15:
    00182       /* Old table for custom names. This is no longer used */
    00183       error("Incorrect conversion of custom name string.");
    00184
    00185     case GAME_TEXT_TAB:
    00186       return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
    00187
    00188     case 26:
    00189       /* Include string within newgrf text (format code 81) */
    00190       if (HasBit(index, 10)) {
    00191         StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
    00192         return GetStringWithArgs(buffr, string, args, last, case_index);
    00193       }
    00194       break;
    00195
    00196     case 28:
    00197       return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
    00198
    00199     case 29:
    00200       return FormatString(buffr, GetGRFStringPtr(index + 0x0800), args, last, case_index);
    00201
    00202     case 30:
    00203       return FormatString(buffr, GetGRFStringPtr(index + 0x1000), args, last, case_index);
    00204
    00205     case 31:
    00206       NOT_REACHED();
    00207   }
    00208
    00209   if (index >= _langtab_num[tab]) {
    00210     if (game_script) {
    00211       return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
    00212     }
    00213     error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
    00214   }
    00215
    00216   return FormatString(buffr, GetStringPtr(string), args, last, case_index);
    00217 }
    00218
    00219 char *GetString(char *buffr, StringID string, const char *last)
    00220 {
    00221   _global_string_params.ClearTypeInformation();
    00222   _global_string_params.offset = 0;
    00223   return GetStringWithArgs(buffr, string, &_global_string_params, last);
    00224 }
    00225
    00226
    00227 char *InlineString(char *buf, StringID string)
    00228 {
    00229   buf += Utf8Encode(buf, SCC_STRING_ID);
    00230   buf += Utf8Encode(buf, string);
    00231   return buf;
    00232 }
    00233
    00234
    00240 void SetDParamStr(uint n, const char *str)
    00241 {
    00242   SetDParam(n, (uint64)(size_t)str);
    00243 }
    00244
    00249 void InjectDParam(uint amount)
    00250 {
    00251   _global_string_params.ShiftParameters(amount);
    00252 }
    00253
    00265 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
    00266 {
    00267   static const int max_digits = 20;
    00268   uint64 divisor = 10000000000000000000ULL;
    00269   zerofill += fractional_digits;
    00270   int thousands_offset = (max_digits - fractional_digits - 1) % 3;
    00271
    00272   if (number < 0) {
    00273     buff += seprintf(buff, last, "-");
    00274     number = -number;
    00275   }
    00276
    00277   uint64 num = number;
    00278   uint64 tot = 0;
    00279   for (int i = 0; i < max_digits; i++) {
    00280     if (i == max_digits - fractional_digits) {
    00281       const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
    00282       if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
    00283       buff += seprintf(buff, last, "%s", decimal_separator);
    00284     }
    00285
    00286     uint64 quot = 0;
    00287     if (num >= divisor) {
    00288       quot = num / divisor;
    00289       num = num % divisor;
    00290     }
    00291     if ((tot |= quot) || i >= max_digits - zerofill) {
    00292       buff += seprintf(buff, last, "%i", (int)quot);
    00293       if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
    00294     }
    00295
    00296     divisor /= 10;
    00297   }
    00298
    00299   *buff = '\0';
    00300
    00301   return buff;
    00302 }
    00303
    00304 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
    00305 {
    00306   const char *separator = _settings_game.locale.digit_group_separator;
    00307   if (separator == NULL) separator = _langpack->digit_group_separator;
    00308   return FormatNumber(buff, number, last, separator, 1, fractional_digits);
    00309 }
    00310
    00311 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
    00312 {
    00313   return FormatNumber(buff, number, last, "");
    00314 }
    00315
    00316 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
    00317 {
    00318   return FormatNumber(buff, number, last, "", count);
    00319 }
    00320
    00321 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
    00322 {
    00323   return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
    00324 }
    00325
    00333 static char *FormatBytes(char *buff, int64 number, const char *last)
    00334 {
    00335   assert(number >= 0);
    00336
    00337   /*                                   1   2^10  2^20  2^30  2^40  2^50  2^60 */
    00338   const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
    00339   uint id = 1;
    00340   while (number >= 1024 * 1024) {
    00341     number /= 1024;
    00342     id++;
    00343   }
    00344
    00345   const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
    00346   if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
    00347
    00348   if (number < 1024) {
    00349     id = 0;
    00350     buff += seprintf(buff, last, "%i", (int)number);
    00351   } else if (number < 1024 * 10) {
    00352     buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
    00353   } else if (number < 1024 * 100) {
    00354     buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
    00355   } else {
    00356     assert(number < 1024 * 1024);
    00357     buff += seprintf(buff, last, "%i", (int)number / 1024);
    00358   }
    00359
    00360   assert(id < lengthof(iec_prefixes));
    00361   buff += seprintf(buff, last, " %sB", iec_prefixes[id]);
    00362
    00363   return buff;
    00364 }
    00365
    00366 static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
    00367 {
    00368   YearMonthDay ymd;
    00369   ConvertDateToYMD(date, &ymd);
    00370
    00371   int64 args[] = {ymd.day + STR_ORDINAL_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
    00372   StringParameters tmp_params(args);
    00373   return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
    00374 }
    00375
    00376 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
    00377 {
    00378   YearMonthDay ymd;
    00379   ConvertDateToYMD(date, &ymd);
    00380
    00381   int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
    00382   StringParameters tmp_params(args);
    00383   return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
    00384 }
    00385
    00386 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
    00387 {
    00388   YearMonthDay ymd;
    00389   ConvertDateToYMD(date, &ymd);
    00390
    00391   char day[3];
    00392   char month[3];
    00393   /* We want to zero-pad the days and months */
    00394   snprintf(day,   lengthof(day),   "%02i", ymd.day);
    00395   snprintf(month, lengthof(month), "%02i", ymd.month + 1);
    00396
    00397   int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
    00398   StringParameters tmp_params(args);
    00399   return FormatString(buff, GetStringPtr(str), &tmp_params, last);
    00400 }
    00401
    00402 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
    00403 {
    00404   /* We are going to make number absolute for printing, so
    00405    * keep this piece of data as we need it later on */
    00406   bool negative = number < 0;
    00407   const char *multiplier = "";
    00408
    00409   number *= spec->rate;
    00410
    00411   /* convert from negative */
    00412   if (number < 0) {
    00413     if (buff + Utf8CharLen(SCC_RED) > last) return buff;
    00414     buff += Utf8Encode(buff, SCC_RED);
    00415     buff = strecpy(buff, "-", last);
    00416     number = -number;
    00417   }
    00418
    00419   /* Add prefix part, following symbol_pos specification.
    00420    * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
    00421    * The only remaining value is 1 (suffix), so everything that is not 1 */
    00422   if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
    00423
    00424   /* for huge numbers, compact the number into k or M */
    00425   if (compact) {
    00426     /* Take care of the 'k' rounding. Having 1 000 000 k
    00427      * and 1 000 M is inconsistent, so always use 1 000 M. */
    00428     if (number >= 1000000000 - 500) {
    00429       number = (number + 500000) / 1000000;
    00430       multiplier = "M";
    00431     } else if (number >= 1000000) {
    00432       number = (number + 500) / 1000;
    00433       multiplier = "k";
    00434     }
    00435   }
    00436
    00437   const char *separator = _settings_game.locale.digit_group_separator_currency;
    00438   if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
    00439   if (separator == NULL) separator = _langpack->digit_group_separator_currency;
    00440   buff = FormatNumber(buff, number, last, separator);
    00441   buff = strecpy(buff, multiplier, last);
    00442
    00443   /* Add suffix part, following symbol_pos specification.
    00444    * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
    00445    * The only remaining value is 1 (prefix), so everything that is not 0 */
    00446   if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
    00447
    00448   if (negative) {
    00449     if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
    00450     buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
    00451     *buff = '\0';
    00452   }
    00453
    00454   return buff;
    00455 }
    00456
    00463 static int DeterminePluralForm(int64 count, int plural_form)
    00464 {
    00465   /* The absolute value determines plurality */
    00466   uint64 n = abs(count);
    00467
    00468   switch (plural_form) {
    00469     default:
    00470       NOT_REACHED();
    00471
    00472     /* Two forms, singular used for one only
    00473      * Used in:
    00474      *   Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
    00475      *   Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
    00476     case 0:
    00477       return n != 1;
    00478
    00479     /* Only one form
    00480      * Used in:
    00481      *   Hungarian, Japanese, Korean, Turkish */
    00482     case 1:
    00483       return 0;
    00484
    00485     /* Two forms, singular used for zero and one
    00486      * Used in:
    00487      *   French, Brazilian Portuguese */
    00488     case 2:
    00489       return n > 1;
    00490
    00491     /* Three forms, special case for 0 and ending in 1, except those ending in 11
    00492      * Used in:
    00493      *   Latvian */
    00494     case 3:
    00495       return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
    00496
    00497     /* Five forms, special case for one, two, 3 to 6 and 7 to 10
    00498      * Used in:
    00499      *   Gaelige (Irish) */
    00500     case 4:
    00501       return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
    00502
    00503     /* Three forms, special case for numbers ending in 1[2-9]
    00504      * Used in:
    00505      *   Lithuanian */
    00506     case 5:
    00507       return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
    00508
    00509     /* Three forms, special cases for numbers ending in 1 and 2, 3, 4, except those ending in 1[1-4]
    00510      * Used in:
    00511      *   Croatian, Russian, Ukrainian */
    00512     case 6:
    00513       return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
    00514
    00515     /* Three forms, special case for one and some numbers ending in 2, 3, or 4
    00516      * Used in:
    00517      *   Polish */
    00518     case 7:
    00519       return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
    00520
    00521     /* Four forms, special case for one and all numbers ending in 02, 03, or 04
    00522      * Used in:
    00523      *   Slovenian */
    00524     case 8:
    00525       return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
    00526
    00527     /* Two forms; singular used for everything ending in 1 but not in 11.
    00528      * Used in:
    00529      *   Icelandic */
    00530     case 9:
    00531       return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
    00532
    00533     /* Three forms, special cases for one and 2, 3, or 4
    00534      * Used in:
    00535      *   Czech, Slovak */
    00536     case 10:
    00537       return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
    00538
    00539     /* Two forms, special 'hack' for Korean; singular for numbers ending
    00540      *   in a consonant and plural for numbers ending in a vowel.
    00541      * Korean doesn't have the concept of plural, but depending on how a
    00542      * number is pronounced it needs another version of a particle.
    00543      * As such the plural system is misused to give this distinction.
    00544      */
    00545     case 11:
    00546       switch (n % 10) {
    00547         case 0: // yeong
    00548         case 1: // il
    00549         case 3: // sam
    00550         case 6: // yuk
    00551         case 7: // chil
    00552         case 8: // pal
    00553           return 0;
    00554
    00555         case 2: // i
    00556         case 4: // sa
    00557         case 5: // o
    00558         case 9: // gu
    00559           return 1;
    00560
    00561         default:
    00562           NOT_REACHED();
    00563       }
    00564
    00565     /* Four forms: one, 0 and everything ending in 02..10, everything ending in 11..19.
    00566      * Used in:
    00567      *  Maltese */
    00568     case 12:
    00569       return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
    00570   }
    00571 }
    00572
    00573 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
    00574 {
    00575   /* <NUM> {Length of each string} {each string} */
    00576   uint n = (byte)*b++;
    00577   uint pos, i, mypos = 0;
    00578
    00579   for (i = pos = 0; i != n; i++) {
    00580     uint len = (byte)*b++;
    00581     if (i == form) mypos = pos;
    00582     pos += len;
    00583   }
    00584
    00585   *dst += seprintf(*dst, last, "%s", b + mypos);
    00586   return b + pos;
    00587 }
    00588
    00590 struct UnitConversion {
    00591   int multiplier;
    00592   int shift;
    00593
    00600   int64 ToDisplay(int64 input, bool round = true) const
    00601   {
    00602     return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
    00603   }
    00604
    00611   int64 FromDisplay(int64 input, bool round = true) const
    00612   {
    00613     return ((input << this->shift) + (round ? this->multiplier / 2 : 0)) / this->multiplier;
    00614   }
    00615 };
    00616
    00617 struct Units {
    00618   UnitConversion c_velocity;
    00619   StringID velocity;
    00620   UnitConversion c_power;
    00621   StringID power;
    00622   UnitConversion c_weight;
    00623   StringID s_weight;
    00624   StringID l_weight;
    00625   UnitConversion c_volume;
    00626   StringID s_volume;
    00627   StringID l_volume;
    00628   UnitConversion c_force;
    00629   StringID force;
    00630   UnitConversion c_height;
    00631   StringID height;
    00632 };
    00633
    00634 /* Unit conversions */
    00635 static const Units _units[] = {
    00636   { // Imperial (Original, mph, hp, metric ton, litre, kN, ft)
    00637     {   1,  0}, STR_UNITS_VELOCITY_IMPERIAL,
    00638     {   1,  0}, STR_UNITS_POWER_IMPERIAL,
    00639     {   1,  0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
    00640     {1000,  0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
    00641     {   1,  0}, STR_UNITS_FORCE_SI,
    00642     {   3,  0}, STR_UNITS_HEIGHT_IMPERIAL, // "Wrong" conversion factor for more nicer GUI values
    00643   },
    00644   { // Metric (km/h, hp, metric ton, litre, kN, metre)
    00645     { 103,  6}, STR_UNITS_VELOCITY_METRIC,
    00646     {4153, 12}, STR_UNITS_POWER_METRIC,
    00647     {   1,  0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
    00648     {1000,  0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
    00649     {   1,  0}, STR_UNITS_FORCE_SI,
    00650     {   1,  0}, STR_UNITS_HEIGHT_SI,
    00651   },
    00652   { // SI (m/s, kilowatt, kilogram, cubic metre, kilonewton, metre)
    00653     {1831, 12}, STR_UNITS_VELOCITY_SI,
    00654     {6109, 13}, STR_UNITS_POWER_SI,
    00655     {1000,  0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
    00656     {   1,  0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
    00657     {   1,  0}, STR_UNITS_FORCE_SI,
    00658     {   1,  0}, STR_UNITS_HEIGHT_SI,
    00659   },
    00660 };
    00661
    00667 uint ConvertSpeedToDisplaySpeed(uint speed)
    00668 {
    00669   /* For historical reasons we don't want to mess with the
    00670    * conversion for speed. So, don't round it and keep the
    00671    * original conversion factors instead of the real ones. */
    00672   return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed, false);
    00673 }
    00674
    00680 uint ConvertDisplaySpeedToSpeed(uint speed)
    00681 {
    00682   return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed);
    00683 }
    00684
    00690 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
    00691 {
    00692   return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed * 10, false) / 16;
    00693 }
    00694
    00700 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
    00701 {
    00702   return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed * 16) / 10;
    00703 }
    00713 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
    00714 {
    00715   uint orig_offset = args->offset;
    00716
    00717   /* When there is no array with types there is no need to do a dry run. */
    00718   if (args->HasTypeInformation() && !dry_run) {
    00719     if (UsingNewGRFTextStack()) {
    00720       /* Values from the NewGRF text stack are only copied to the normal
    00721        * argv array at the time they are encountered. That means that if
    00722        * another string command references a value later in the string it
    00723        * would fail. We solve that by running FormatString twice. The first
    00724        * pass makes sure the argv array is correctly filled and the second
    00725        * pass can reference later values without problems. */
    00726       struct TextRefStack *backup = CreateTextRefStackBackup();
    00727       FormatString(buff, str_arg, args, last, case_index, game_script, true);
    00728       RestoreTextRefStackBackup(backup);
    00729     } else {
    00730       FormatString(buff, str_arg, args, last, case_index, game_script, true);
    00731     }
    00732     /* We have to restore the original offset here to to read the correct values. */
    00733     args->offset = orig_offset;
    00734   }
    00735   WChar b;
    00736   uint next_substr_case_index = 0;
    00737   char *buf_start = buff;
    00738   std::stack<const char *> str_stack;
    00739   str_stack.push(str_arg);
    00740
    00741   for (;;) {
    00742     while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
    00743       str_stack.pop();
    00744     }
    00745     if (str_stack.empty()) break;
    00746     const char *&str = str_stack.top();
    00747
    00748     if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
    00749       /* We need to pass some stuff as it might be modified; oh boy. */
    00750       //todo: should argve be passed here too?
    00751       b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), dry_run);
    00752       if (b == 0) continue;
    00753     }
    00754
    00755     switch (b) {
    00756       case SCC_ENCODED: {
    00757         uint64 sub_args_data[20];
    00758         WChar sub_args_type[20];
    00759         bool sub_args_need_free[20];
    00760         StringParameters sub_args(sub_args_data, 20, sub_args_type);
    00761
    00762         sub_args.ClearTypeInformation();
    00763         memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
    00764
    00765         uint16 stringid;
    00766         const char *s = str;
    00767         char *p;
    00768         stringid = strtol(str, &p, 16);
    00769         if (*p != ':' && *p != '\0') {
    00770           while (*p != '\0') p++;
    00771           str = p;
    00772           buff = strecat(buff, "(invalid SCC_ENCODED)", last);
    00773           break;
    00774         }
    00775         if (stringid >= TAB_SIZE) {
    00776           while (*p != '\0') p++;
    00777           str = p;
    00778           buff = strecat(buff, "(invalid StringID)", last);
    00779           break;
    00780         }
    00781
    00782         int i = 0;
    00783         while (*p != '\0') {
    00784           uint64 param;
    00785           s = ++p;
    00786
    00787           /* Find the next value */
    00788           bool instring = false;
    00789           bool escape = false;
    00790           for (;; p++) {
    00791             if (*p == '\\') {
    00792               escape = true;
    00793               continue;
    00794             }
    00795             if (*p == '"' && escape) {
    00796               escape = false;
    00797               continue;
    00798             }
    00799             escape = false;
    00800
    00801             if (*p == '"') {
    00802               instring = !instring;
    00803               continue;
    00804             }
    00805             if (instring) {
    00806               continue;
    00807             }
    00808
    00809             if (*p == ':') break;
    00810             if (*p == '\0') break;
    00811           }
    00812
    00813           if (*s != '"') {
    00814             /* Check if we want to look up another string */
    00815             WChar l;
    00816             size_t len = Utf8Decode(&l, s);
    00817             bool lookup = (l == SCC_ENCODED);
    00818             if (lookup) s += len;
    00819
    00820             param = strtol(s, &p, 16);
    00821
    00822             if (lookup) {
    00823               if (param >= TAB_SIZE) {
    00824                 while (*p != '\0') p++;
    00825                 str = p;
    00826                 buff = strecat(buff, "(invalid sub-StringID)", last);
    00827                 break;
    00828               }
    00829               param = (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + param;
    00830             }
    00831
    00832             sub_args.SetParam(i++, param);
    00833           } else {
    00834             char *g = strdup(s);
    00835             g[p - s] = '\0';
    00836
    00837             sub_args_need_free[i] = true;
    00838             sub_args.SetParam(i++, (uint64)(size_t)g);
    00839           }
    00840         }
    00841         /* We error'd out in the while, to error out in themain too */
    00842         if (*str == '\0') break;
    00843
    00844         str = p;
    00845         buff = GetStringWithArgs(buff, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, last, true);
    00846
    00847         for (int i = 0; i < 20; i++) {
    00848           if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
    00849         }
    00850         break;
    00851       }
    00852
    00853       case SCC_NEWGRF_STRINL: {
    00854         StringID substr = Utf8Consume(&str);
    00855         str_stack.push(GetStringPtr(substr));
    00856         break;
    00857       }
    00858
    00859       case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
    00860         StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
    00861         str_stack.push(GetStringPtr(substr));
    00862         case_index = next_substr_case_index;
    00863         next_substr_case_index = 0;
    00864         break;
    00865       }
    00866
    00867
    00868       case SCC_GENDER_LIST: { // {G 0 Der Die Das}
    00869         /* First read the meta data from the language file. */
    00870         uint offset = orig_offset + (byte)*str++;
    00871         int gender = 0;
    00872         if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
    00873           /* Now we need to figure out what text to resolve, i.e.
    00874            * what do we need to draw? So get the actual raw string
    00875            * first using the control code to get said string. */
    00876           char input[4 + 1];
    00877           char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
    00878           *p = '\0';
    00879
    00880           /* Now do the string formatting. */
    00881           char buf[256];
    00882           bool old_kgd = _keep_gender_data;
    00883           _keep_gender_data = true;
    00884           StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
    00885           p = FormatString(buf, input, &tmp_params, lastof(buf));
    00886           _keep_gender_data = old_kgd;
    00887           *p = '\0';
    00888
    00889           /* And determine the string. */
    00890           const char *s = buf;
    00891           WChar c = Utf8Consume(&s);
    00892           /* Does this string have a gender, if so, set it */
    00893           if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
    00894         }
    00895         str = ParseStringChoice(str, gender, &buff, last);
    00896         break;
    00897       }
    00898
    00899       /* This sets up the gender for the string.
    00900        * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
    00901       case SCC_GENDER_INDEX: // {GENDER 0}
    00902         if (_keep_gender_data) {
    00903           buff += Utf8Encode(buff, SCC_GENDER_INDEX);
    00904           *buff++ = *str++;
    00905         } else {
    00906           str++;
    00907         }
    00908         break;
    00909
    00910       case SCC_PLURAL_LIST: { // {P}
    00911         int plural_form = *str++;          // contains the plural form for this string
    00912         uint offset = orig_offset + (byte)*str++;
    00913         int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
    00914         str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
    00915         break;
    00916       }
    00917
    00918       case SCC_ARG_INDEX: { // Move argument pointer
    00919         args->offset = orig_offset + (byte)*str++;
    00920         break;
    00921       }
    00922
    00923       case SCC_SET_CASE: { // {SET_CASE}
    00924         /* This is a pseudo command, it's outputted when someone does {STRING.ack}
    00925          * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
    00926         next_substr_case_index = (byte)*str++;
    00927         break;
    00928       }
    00929
    00930       case SCC_SWITCH_CASE: { // {Used to implement case switching}
    00931         /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
    00932          * Each LEN is printed using 2 bytes in big endian order. */
    00933         uint num = (byte)*str++;
    00934         while (num) {
    00935           if ((byte)str[0] == case_index) {
    00936             /* Found the case, adjust str pointer and continue */
    00937             str += 3;
    00938             break;
    00939           }
    00940           /* Otherwise skip to the next case */
    00941           str += 3 + (str[1] << 8) + str[2];
    00942           num--;
    00943         }
    00944         break;
    00945       }
    00946
    00947       case SCC_SETX: // {SETX}
    00948         if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
    00949           buff += Utf8Encode(buff, SCC_SETX);
    00950           *buff++ = *str++;
    00951         }
    00952         break;
    00953
    00954       case SCC_SETXY: // {SETXY}
    00955         if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
    00956           buff += Utf8Encode(buff, SCC_SETXY);
    00957           *buff++ = *str++;
    00958           *buff++ = *str++;
    00959         }
    00960         break;
    00961
    00962       case SCC_REVISION: // {REV}
    00963         buff = strecpy(buff, _openttd_revision, last);
    00964         break;
    00965
    00966       case SCC_STRING_ID: // {STRINL}
    00967         if (game_script) break;
    00968         buff = GetStringWithArgs(buff, Utf8Consume(&str), args, last);
    00969         break;
    00970
    00971       case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
    00972         if (game_script) break;
    00973         const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
    00974         buff = FormatString(buff, str, args, last);
    00975         break;
    00976       }
    00977
    00978       case SCC_STRING: {// {STRING}
    00979         StringID str = args->GetInt32(SCC_STRING);
    00980         if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
    00981         /* WARNING. It's prohibited for the included string to consume any arguments.
    00982          * For included strings that consume argument, you should use STRING1, STRING2 etc.
    00983          * To debug stuff you can set argv to NULL and it will tell you */
    00984         StringParameters tmp_params(args->GetDataPointer(), args->num_param - args->offset, NULL);
    00985         buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
    00986         next_substr_case_index = 0;
    00987         break;
    00988       }
    00989
    00990       case SCC_STRING1:
    00991       case SCC_STRING2:
    00992       case SCC_STRING3:
    00993       case SCC_STRING4:
    00994       case SCC_STRING5:
    00995       case SCC_STRING6:
    00996       case SCC_STRING7: { // {STRING1..7}
    00997         /* Strings that consume arguments */
    00998         StringID str = args->GetInt32(b);
    00999         if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
    01000         StringParameters sub_args(*args, b - SCC_STRING1 + 1);
    01001         buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
    01002         next_substr_case_index = 0;
    01003         break;
    01004       }
    01005
    01006       case SCC_COMMA: // {COMMA}
    01007         buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
    01008         break;
    01009
    01010       case SCC_DECIMAL: {// {DECIMAL}
    01011         int64 number = args->GetInt64(SCC_DECIMAL);
    01012         int digits = args->GetInt32(SCC_DECIMAL);
    01013         buff = FormatCommaNumber(buff, number, last, digits);
    01014         break;
    01015       }
    01016
    01017       case SCC_NUM: // {NUM}
    01018         buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
    01019         break;
    01020
    01021       case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
    01022         int64 num = args->GetInt64();
    01023         buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
    01024         break;
    01025       }
    01026
    01027       case SCC_HEX: // {HEX}
    01028         buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
    01029         break;
    01030
    01031       case SCC_BYTES: // {BYTES}
    01032         buff = FormatBytes(buff, args->GetInt64(), last);
    01033         break;
    01034
    01035       case SCC_CARGO_TINY: { // {CARGO_TINY}
    01036         /* Tiny description of cargotypes. Layout:
    01037          * param 1: cargo type
    01038          * param 2: cargo count */
    01039         CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
    01040         if (cargo >= CargoSpec::GetArraySize()) break;
    01041
    01042         StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
    01043         int64 amount = 0;
    01044         switch (cargo_str) {
    01045           case STR_TONS:
    01046             amount = _units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64());
    01047             break;
    01048
    01049           case STR_LITERS:
    01050             amount = _units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64());
    01051             break;
    01052
    01053           default: {
    01054             amount = args->GetInt64();
    01055             break;
    01056           }
    01057         }
    01058
    01059         buff = FormatCommaNumber(buff, amount, last);
    01060         break;
    01061       }
    01062
    01063       case SCC_CARGO_SHORT: { // {CARGO_SHORT}
    01064         /* Short description of cargotypes. Layout:
    01065          * param 1: cargo type
    01066          * param 2: cargo count */
    01067         CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
    01068         if (cargo >= CargoSpec::GetArraySize()) break;
    01069
    01070         StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
    01071         switch (cargo_str) {
    01072           case STR_TONS: {
    01073             assert(_settings_game.locale.units < lengthof(_units));
    01074             int64 args_array[] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
    01075             StringParameters tmp_params(args_array);
    01076             buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
    01077             break;
    01078           }
    01079
    01080           case STR_LITERS: {
    01081             assert(_settings_game.locale.units < lengthof(_units));
    01082             int64 args_array[] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
    01083             StringParameters tmp_params(args_array);
    01084             buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
    01085             break;
    01086           }
    01087
    01088           default: {
    01089             StringParameters tmp_params(*args, 1);
    01090             buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
    01091             break;
    01092           }
    01093         }
    01094         break;
    01095       }
    01096
    01097       case SCC_CARGO_LONG: { // {CARGO_LONG}
    01098         /* First parameter is cargo type, second parameter is cargo count */
    01099         CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
    01100         if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
    01101
    01102         StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
    01103         StringParameters tmp_args(*args, 1);
    01104         buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
    01105         break;
    01106       }
    01107
    01108       case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
    01109         buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
    01110         break;
    01111
    01112       case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
    01113         buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
    01114         break;
    01115
    01116       case SCC_DATE_TINY: // {DATE_TINY}
    01117         buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
    01118         break;
    01119
    01120       case SCC_DATE_SHORT: // {DATE_SHORT}
    01121         buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
    01122         next_substr_case_index = 0;
    01123         break;
    01124
    01125       case SCC_DATE_LONG: // {DATE_LONG}
    01126         buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
    01127         next_substr_case_index = 0;
    01128         break;
    01129
    01130       case SCC_DATE_ISO: // {DATE_ISO}
    01131         buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
    01132         break;
    01133
    01134       case SCC_FORCE: { // {FORCE}
    01135         assert(_settings_game.locale.units < lengthof(_units));
    01136         int64 args_array[1] = {_units[_settings_game.locale.units].c_force.ToDisplay(args->GetInt64())};
    01137         StringParameters tmp_params(args_array);
    01138         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].force), &tmp_params, last);
    01139         break;
    01140       }
    01141
    01142       case SCC_HEIGHT: { // {HEIGHT}
    01143         int64 args_array[] = {_units[_settings_game.locale.units].c_height.ToDisplay(args->GetInt64())};
    01144         StringParameters tmp_params(args_array);
    01145         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].height), &tmp_params, last);
    01146         break;
    01147       }
    01148
    01149       case SCC_POWER: { // {POWER}
    01150         assert(_settings_game.locale.units < lengthof(_units));
    01151         int64 args_array[1] = {_units[_settings_game.locale.units].c_power.ToDisplay(args->GetInt64())};
    01152         StringParameters tmp_params(args_array);
    01153         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].power), &tmp_params, last);
    01154         break;
    01155       }
    01156
    01157       case SCC_VELOCITY: { // {VELOCITY}
    01158         assert(_settings_game.locale.units < lengthof(_units));
    01159         int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
    01160         StringParameters tmp_params(args_array);
    01161         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].velocity), &tmp_params, last);
    01162         break;
    01163       }
    01164
    01165       case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
    01166         assert(_settings_game.locale.units < lengthof(_units));
    01167         int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
    01168         StringParameters tmp_params(args_array);
    01169         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_volume), &tmp_params, last);
    01170         break;
    01171       }
    01172
    01173       case SCC_VOLUME_LONG: { // {VOLUME_LONG}
    01174         assert(_settings_game.locale.units < lengthof(_units));
    01175         int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
    01176         StringParameters tmp_params(args_array);
    01177         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
    01178         break;
    01179       }
    01180
    01181       case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
    01182         assert(_settings_game.locale.units < lengthof(_units));
    01183         int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
    01184         StringParameters tmp_params(args_array);
    01185         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_weight), &tmp_params, last);
    01186         break;
    01187       }
    01188
    01189       case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
    01190         assert(_settings_game.locale.units < lengthof(_units));
    01191         int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
    01192         StringParameters tmp_params(args_array);
    01193         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
    01194         break;
    01195       }
    01196
    01197       case SCC_COMPANY_NAME: { // {COMPANY}
    01198         const Company *c = Company::GetIfValid(args->GetInt32());
    01199         if (c == NULL) break;
    01200
    01201         if (c->name != NULL) {
    01202           int64 args_array[] = {(uint64)(size_t)c->name};
    01203           StringParameters tmp_params(args_array);
    01204           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01205         } else {
    01206           int64 args_array[] = {c->name_2};
    01207           StringParameters tmp_params(args_array);
    01208           buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
    01209         }
    01210         break;
    01211       }
    01212
    01213       case SCC_COMPANY_NUM: { // {COMPANY_NUM}
    01214         CompanyID company = (CompanyID)args->GetInt32();
    01215
    01216         /* Nothing is added for AI or inactive companies */
    01217         if (Company::IsValidHumanID(company)) {
    01218           int64 args_array[] = {company + 1};
    01219           StringParameters tmp_params(args_array);
    01220           buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
    01221         }
    01222         break;
    01223       }
    01224
    01225       case SCC_DEPOT_NAME: { // {DEPOT}
    01226         VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
    01227         if (vt == VEH_AIRCRAFT) {
    01228           uint64 args_array[] = {args->GetInt32()};
    01229           WChar types_array[] = {SCC_STATION_NAME};
    01230           StringParameters tmp_params(args_array, 1, types_array);
    01231           buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
    01232           break;
    01233         }
    01234
    01235         const Depot *d = Depot::Get(args->GetInt32());
    01236         if (d->name != NULL) {
    01237           int64 args_array[] = {(uint64)(size_t)d->name};
    01238           StringParameters tmp_params(args_array);
    01239           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01240         } else {
    01241           int64 args_array[] = {d->town->index, d->town_cn + 1};
    01242           StringParameters tmp_params(args_array);
    01243           buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
    01244         }
    01245         break;
    01246       }
    01247
    01248       case SCC_ENGINE_NAME: { // {ENGINE}
    01249         const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
    01250         if (e == NULL) break;
    01251
    01252         if (e->name != NULL && e->IsEnabled()) {
    01253           int64 args_array[] = {(uint64)(size_t)e->name};
    01254           StringParameters tmp_params(args_array);
    01255           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01256         } else {
    01257           StringParameters tmp_params(NULL, 0, NULL);
    01258           buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
    01259         }
    01260         break;
    01261       }
    01262
    01263       case SCC_GROUP_NAME: { // {GROUP}
    01264         const Group *g = Group::GetIfValid(args->GetInt32());
    01265         if (g == NULL) break;
    01266
    01267         if (g->name != NULL) {
    01268           int64 args_array[] = {(uint64)(size_t)g->name};
    01269           StringParameters tmp_params(args_array);
    01270           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01271         } else {
    01272           int64 args_array[] = {g->index};
    01273           StringParameters tmp_params(args_array);
    01274
    01275           buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
    01276         }
    01277         break;
    01278       }
    01279
    01280       case SCC_INDUSTRY_NAME: { // {INDUSTRY}
    01281         const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
    01282         if (i == NULL) break;
    01283
    01284         /* First print the town name and the industry type name. */
    01285         int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
    01286         StringParameters tmp_params(args_array);
    01287
    01288         buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
    01289         next_substr_case_index = 0;
    01290         break;
    01291       }
    01292
    01293       case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
    01294         const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
    01295         if (c == NULL) break;
    01296
    01297         if (c->president_name != NULL) {
    01298           int64 args_array[] = {(uint64)(size_t)c->president_name};
    01299           StringParameters tmp_params(args_array);
    01300           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01301         } else {
    01302           int64 args_array[] = {c->president_name_2};
    01303           StringParameters tmp_params(args_array);
    01304           buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
    01305         }
    01306         break;
    01307       }
    01308
    01309       case SCC_STATION_NAME: { // {STATION}
    01310         StationID sid = args->GetInt32(SCC_STATION_NAME);
    01311         const Station *st = Station::GetIfValid(sid);
    01312
    01313         if (st == NULL) {
    01314           /* The station doesn't exist anymore. The only place where we might
    01315            * be "drawing" an invalid station is in the case of cargo that is
    01316            * in transit. */
    01317           StringParameters tmp_params(NULL, 0, NULL);
    01318           buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
    01319           break;
    01320         }
    01321
    01322         if (st->name != NULL) {
    01323           int64 args_array[] = {(uint64)(size_t)st->name};
    01324           StringParameters tmp_params(args_array);
    01325           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01326         } else {
    01327           StringID str = st->string_id;
    01328           if (st->indtype != IT_INVALID) {
    01329             /* Special case where the industry provides the name for the station */
    01330             const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
    01331
    01332             /* Industry GRFs can change which might remove the station name and
    01333              * thus cause very strange things. Here we check for that before we
    01334              * actually set the station name. */
    01335             if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
    01336               str = indsp->station_name;
    01337             }
    01338           }
    01339
    01340           int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
    01341           StringParameters tmp_params(args_array);
    01342           buff = GetStringWithArgs(buff, str, &tmp_params, last);
    01343         }
    01344         break;
    01345       }
    01346
    01347       case SCC_TOWN_NAME: { // {TOWN}
    01348         const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
    01349         if (t == NULL) break;
    01350
    01351         if (t->name != NULL) {
    01352           int64 args_array[] = {(uint64)(size_t)t->name};
    01353           StringParameters tmp_params(args_array);
    01354           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01355         } else {
    01356           buff = GetTownName(buff, t, last);
    01357         }
    01358         break;
    01359       }
    01360
    01361       case SCC_WAYPOINT_NAME: { // {WAYPOINT}
    01362         Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
    01363         if (wp == NULL) break;
    01364
    01365         if (wp->name != NULL) {
    01366           int64 args_array[] = {(uint64)(size_t)wp->name};
    01367           StringParameters tmp_params(args_array);
    01368           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01369         } else {
    01370           int64 args_array[] = {wp->town->index, wp->town_cn + 1};
    01371           StringParameters tmp_params(args_array);
    01372           StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
    01373           if (wp->town_cn != 0) str++;
    01374           buff = GetStringWithArgs(buff, str, &tmp_params, last);
    01375         }
    01376         break;
    01377       }
    01378
    01379       case SCC_VEHICLE_NAME: { // {VEHICLE}
    01380         const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
    01381         if (v == NULL) break;
    01382
    01383         if (v->name != NULL) {
    01384           int64 args_array[] = {(uint64)(size_t)v->name};
    01385           StringParameters tmp_params(args_array);
    01386           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01387         } else {
    01388           int64 args_array[] = {v->unitnumber};
    01389           StringParameters tmp_params(args_array);
    01390
    01391           StringID str;
    01392           switch (v->type) {
    01393             default: NOT_REACHED();
    01394             case VEH_TRAIN:    str = STR_SV_TRAIN_NAME; break;
    01395             case VEH_ROAD:     str = STR_SV_ROAD_VEHICLE_NAME; break;
    01396             case VEH_SHIP:     str = STR_SV_SHIP_NAME; break;
    01397             case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
    01398           }
    01399
    01400           buff = GetStringWithArgs(buff, str, &tmp_params, last);
    01401         }
    01402         break;
    01403       }
    01404
    01405       case SCC_SIGN_NAME: { // {SIGN}
    01406         const Sign *si = Sign::GetIfValid(args->GetInt32());
    01407         if (si == NULL) break;
    01408
    01409         if (si->name != NULL) {
    01410           int64 args_array[] = {(uint64)(size_t)si->name};
    01411           StringParameters tmp_params(args_array);
    01412           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01413         } else {
    01414           StringParameters tmp_params(NULL, 0, NULL);
    01415           buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
    01416         }
    01417         break;
    01418       }
    01419
    01420       case SCC_STATION_FEATURES: { // {STATIONFEATURES}
    01421         buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
    01422         break;
    01423       }
    01424
    01425       default:
    01426         if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
    01427         break;
    01428     }
    01429   }
    01430   *buff = '\0';
    01431   return buff;
    01432 }
    01433
    01434
    01435 static char *StationGetSpecialString(char *buff, int x, const char *last)
    01436 {
    01437   if ((x & FACIL_TRAIN)      && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
    01438   if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
    01439   if ((x & FACIL_BUS_STOP)   && (buff + Utf8CharLen(SCC_BUS)   < last)) buff += Utf8Encode(buff, SCC_BUS);
    01440   if ((x & FACIL_DOCK)       && (buff + Utf8CharLen(SCC_SHIP)  < last)) buff += Utf8Encode(buff, SCC_SHIP);
    01441   if ((x & FACIL_AIRPORT)    && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
    01442   *buff = '\0';
    01443   return buff;
    01444 }
    01445
    01446 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
    01447 {
    01448   return GenerateTownNameString(buff, last, ind, seed);
    01449 }
    01450
    01451 static const char * const _silly_company_names[] = {
    01452   "Bloggs Brothers",
    01453   "Tiny Transport Ltd.",
    01454   "Express Travel",
    01455   "Comfy-Coach & Co.",
    01456   "Crush & Bump Ltd.",
    01457   "Broken & Late Ltd.",
    01458   "Sam Speedy & Son",
    01459   "Supersonic Travel",
    01460   "Mike's Motors",
    01461   "Lightning International",
    01462   "Pannik & Loozit Ltd.",
    01463   "Inter-City Transport",
    01464   "Getout & Pushit Ltd."
    01465 };
    01466
    01467 static const char * const _surname_list[] = {
    01468   "Adams",
    01469   "Allan",
    01470   "Baker",
    01471   "Bigwig",
    01472   "Black",
    01473   "Bloggs",
    01474   "Brown",
    01475   "Campbell",
    01476   "Gordon",
    01477   "Hamilton",
    01478   "Hawthorn",
    01479   "Higgins",
    01480   "Green",
    01481   "Gribble",
    01482   "Jones",
    01483   "McAlpine",
    01484   "MacDonald",
    01485   "McIntosh",
    01486   "Muir",
    01487   "Murphy",
    01488   "Nelson",
    01489   "O'Donnell",
    01490   "Parker",
    01491   "Phillips",
    01492   "Pilkington",
    01493   "Quigley",
    01494   "Sharkey",
    01495   "Thomson",
    01496   "Watkins"
    01497 };
    01498
    01499 static const char * const _silly_surname_list[] = {
    01500   "Grumpy",
    01501   "Dozy",
    01502   "Speedy",
    01503   "Nosey",
    01504   "Dribble",
    01505   "Mushroom",
    01506   "Cabbage",
    01507   "Sniffle",
    01508   "Fishy",
    01509   "Swindle",
    01510   "Sneaky",
    01511   "Nutkins"
    01512 };
    01513
    01514 static const char _initial_name_letters[] = {
    01515   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
    01516   'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
    01517 };
    01518
    01519 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
    01520 {
    01521   const char * const *base;
    01522   uint num;
    01523
    01524   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
    01525     base = _silly_surname_list;
    01526     num  = lengthof(_silly_surname_list);
    01527   } else {
    01528     base = _surname_list;
    01529     num  = lengthof(_surname_list);
    01530   }
    01531
    01532   buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
    01533   buff = strecpy(buff, " & Co.", last);
    01534
    01535   return buff;
    01536 }
    01537
    01538 static char *GenPresidentName(char *buff, uint32 x, const char *last)
    01539 {
    01540   char initial[] = "?. ";
    01541   const char * const *base;
    01542   uint num;
    01543   uint i;
    01544
    01545   initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
    01546   buff = strecpy(buff, initial, last);
    01547
    01548   i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
    01549   if (i < sizeof(_initial_name_letters)) {
    01550     initial[0] = _initial_name_letters[i];
    01551     buff = strecpy(buff, initial, last);
    01552   }
    01553
    01554   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
    01555     base = _silly_surname_list;
    01556     num  = lengthof(_silly_surname_list);
    01557   } else {
    01558     base = _surname_list;
    01559     num  = lengthof(_surname_list);
    01560   }
    01561
    01562   buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
    01563
    01564   return buff;
    01565 }
    01566
    01567 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
    01568 {
    01569   switch (ind) {
    01570     case 1: // not used
    01571       return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
    01572
    01573     case 2: // used for Foobar & Co company names
    01574       return GenAndCoName(buff, args->GetInt32(), last);
    01575
    01576     case 3: // President name
    01577       return GenPresidentName(buff, args->GetInt32(), last);
    01578   }
    01579
    01580   /* town name? */
    01581   if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
    01582     buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
    01583     return strecpy(buff, " Transport", last);
    01584   }
    01585
    01586   /* language name? */
    01587   if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
    01588     int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
    01589     return strecpy(buff,
    01590       &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
    01591   }
    01592
    01593   /* resolution size? */
    01594   if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
    01595     int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
    01596     buff += seprintf(
    01597       buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
    01598     );
    01599     return buff;
    01600   }
    01601
    01602   /* screenshot format name? */
    01603   if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
    01604     int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
    01605     return strecpy(buff, GetScreenshotFormatDesc(i), last);
    01606   }
    01607
    01608   NOT_REACHED();
    01609 }
    01610
    01611 #ifdef ENABLE_NETWORK
    01612 extern void SortNetworkLanguages();
    01613 #else /* ENABLE_NETWORK */
    01614 static inline void SortNetworkLanguages() {}
    01615 #endif /* ENABLE_NETWORK */
    01616
    01621 bool LanguagePackHeader::IsValid() const
    01622 {
    01623   return this->ident        == TO_LE32(LanguagePackHeader::IDENT) &&
    01624          this->version      == TO_LE32(LANGUAGE_PACK_VERSION) &&
    01625          this->plural_form  <  LANGUAGE_MAX_PLURAL &&
    01626          this->text_dir     <= 1 &&
    01627          this->newgrflangid < MAX_LANG &&
    01628          this->num_genders  < MAX_NUM_GENDERS &&
    01629          this->num_cases    < MAX_NUM_CASES &&
    01630          StrValid(this->name,                           lastof(this->name)) &&
    01631          StrValid(this->own_name,                       lastof(this->own_name)) &&
    01632          StrValid(this->isocode,                        lastof(this->isocode)) &&
    01633          StrValid(this->digit_group_separator,          lastof(this->digit_group_separator)) &&
    01634          StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
    01635          StrValid(this->digit_decimal_separator,        lastof(this->digit_decimal_separator));
    01636 }
    01637
    01643 bool ReadLanguagePack(const LanguageMetadata *lang)
    01644 {
    01645   /* Current language pack */
    01646   size_t len;
    01647   LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
    01648   if (lang_pack == NULL) return false;
    01649
    01650   /* End of read data (+ terminating zero added in ReadFileToMem()) */
    01651   const char *end = (char *)lang_pack + len + 1;
    01652
    01653   /* We need at least one byte of lang_pack->data */
    01654   if (end <= lang_pack->data || !lang_pack->IsValid()) {
    01655     free(lang_pack);
    01656     return false;
    01657   }
    01658
    01659 #if TTD_ENDIAN == TTD_BIG_ENDIAN
    01660   for (uint i = 0; i < TAB_COUNT; i++) {
    01661     lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
    01662   }
    01663 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
    01664
    01665   uint count = 0;
    01666   for (uint i = 0; i < TAB_COUNT; i++) {
    01667     uint num = lang_pack->offsets[i];
    01668     _langtab_start[i] = count;
    01669     _langtab_num[i] = num;
    01670     count += num;
    01671   }
    01672
    01673   /* Allocate offsets */
    01674   char **langpack_offs = MallocT<char *>(count);
    01675
    01676   /* Fill offsets */
    01677   char *s = lang_pack->data;
    01678   len = (byte)*s++;
    01679   for (uint i = 0; i < count; i++) {
    01680     if (s + len >= end) {
    01681       free(lang_pack);
    01682       free(langpack_offs);
    01683       return false;
    01684     }
    01685     if (len >= 0xC0) {
    01686       len = ((len & 0x3F) << 8) + (byte)*s++;
    01687       if (s + len >= end) {
    01688         free(lang_pack);
    01689         free(langpack_offs);
    01690         return false;
    01691       }
    01692     }
    01693     langpack_offs[i] = s;
    01694     s += len;
    01695     len = (byte)*s;
    01696     *s++ = '\0'; // zero terminate the string
    01697   }
    01698
    01699   free(_langpack);
    01700   _langpack = lang_pack;
    01701
    01702   free(_langpack_offs);
    01703   _langpack_offs = langpack_offs;
    01704
    01705   _current_language = lang;
    01706   _current_text_dir = (TextDirection)_current_language->text_dir;
    01707   const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
    01708   strecpy(_config_language_file, c_file, lastof(_config_language_file));
    01709   SetCurrentGrfLangID(_current_language->newgrflangid);
    01710
    01711 #ifdef WITH_ICU
    01712   /* Delete previous collator. */
    01713   if (_current_collator != NULL) {
    01714     delete _current_collator;
    01715     _current_collator = NULL;
    01716   }
    01717
    01718   /* Create a collator instance for our current locale. */
    01719   UErrorCode status = U_ZERO_ERROR;
    01720   _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
    01721   /* Sort number substrings by their numerical value. */
    01722   if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
    01723   /* Avoid using the collator if it is not correctly set. */
    01724   if (U_FAILURE(status)) {
    01725     delete _current_collator;
    01726     _current_collator = NULL;
    01727   }
    01728 #endif /* WITH_ICU */
    01729
    01730   /* Some lists need to be sorted again after a language change. */
    01731   ReconsiderGameScriptLanguage();
    01732   InitializeSortedCargoSpecs();
    01733   SortIndustryTypes();
    01734   BuildIndustriesLegend();
    01735   SortNetworkLanguages();
    01736   InvalidateWindowClassesData(WC_BUILD_VEHICLE);      // Build vehicle window.
    01737   InvalidateWindowClassesData(WC_TRAINS_LIST);        // Train group window.
    01738   InvalidateWindowClassesData(WC_ROADVEH_LIST);       // Road vehicle group window.
    01739   InvalidateWindowClassesData(WC_SHIPS_LIST);         // Ship group window.
    01740   InvalidateWindowClassesData(WC_AIRCRAFT_LIST);      // Aircraft group window.
    01741   InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
    01742   InvalidateWindowClassesData(WC_STATION_LIST);       // Station list window.
    01743
    01744   return true;
    01745 }
    01746
    01747 /* Win32 implementation in win32.cpp.
    01748  * OS X implementation in os/macosx/macos.mm. */
    01749 #if !(defined(WIN32) || defined(__APPLE__))
    01750
    01758 const char *GetCurrentLocale(const char *param)
    01759 {
    01760   const char *env;
    01761
    01762   env = getenv("LANGUAGE");
    01763   if (env != NULL) return env;
    01764
    01765   env = getenv("LC_ALL");
    01766   if (env != NULL) return env;
    01767
    01768   if (param != NULL) {
    01769     env = getenv(param);
    01770     if (env != NULL) return env;
    01771   }
    01772
    01773   return getenv("LANG");
    01774 }
    01775 #else
    01776 const char *GetCurrentLocale(const char *param);
    01777 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
    01778
    01779 int CDECL StringIDSorter(const StringID *a, const StringID *b)
    01780 {
    01781   char stra[512];
    01782   char strb[512];
    01783   GetString(stra, *a, lastof(stra));
    01784   GetString(strb, *b, lastof(strb));
    01785
    01786   return strcmp(stra, strb);
    01787 }
    01788
    01794 const LanguageMetadata *GetLanguage(byte newgrflangid)
    01795 {
    01796   for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
    01797     if (newgrflangid == lang->newgrflangid) return lang;
    01798   }
    01799
    01800   return NULL;
    01801 }
    01802
    01809 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
    01810 {
    01811   FILE *f = fopen(file, "rb");
    01812   if (f == NULL) return false;
    01813
    01814   size_t read = fread(hdr, sizeof(*hdr), 1, f);
    01815   fclose(f);
    01816
    01817   bool ret = read == 1 && hdr->IsValid();
    01818
    01819   /* Convert endianness for the windows language ID */
    01820   if (ret) {
    01821     hdr->missing = FROM_LE16(hdr->missing);
    01822     hdr->winlangid = FROM_LE16(hdr->winlangid);
    01823   }
    01824   return ret;
    01825 }
    01826
    01831 static void GetLanguageList(const char *path)
    01832 {
    01833   DIR *dir = ttd_opendir(path);
    01834   if (dir != NULL) {
    01835     struct dirent *dirent;
    01836     while ((dirent = readdir(dir)) != NULL) {
    01837       const char *d_name    = FS2OTTD(dirent->d_name);
    01838       const char *extension = strrchr(d_name, '.');
    01839
    01840       /* Not a language file */
    01841       if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
    01842
    01843       LanguageMetadata lmd;
    01844       seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
    01845
    01846       /* Check whether the file is of the correct version */
    01847       if (!GetLanguageFileHeader(lmd.file, &lmd)) {
    01848         DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
    01849       } else if (GetLanguage(lmd.newgrflangid) != NULL) {
    01850         DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
    01851       } else {
    01852         *_languages.Append() = lmd;
    01853       }
    01854     }
    01855     closedir(dir);
    01856   }
    01857 }
    01858
    01863 void InitializeLanguagePacks()
    01864 {
    01865   Searchpath sp;
    01866
    01867   FOR_ALL_SEARCHPATHS(sp) {
    01868     char path[MAX_PATH];
    01869     FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
    01870     GetLanguageList(path);
    01871   }
    01872   if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
    01873
    01874   /* Acquire the locale of the current system */
    01875   const char *lang = GetCurrentLocale("LC_MESSAGES");
    01876   if (lang == NULL) lang = "en_GB";
    01877
    01878   const LanguageMetadata *chosen_language   = NULL;
    01879   const LanguageMetadata *language_fallback = NULL;
    01880   const LanguageMetadata *en_GB_fallback    = _languages.Begin();
    01881
    01882   /* Find a proper language. */
    01883   for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
    01884     /* We are trying to find a default language. The priority is by
    01885      * configuration file, local environment and last, if nothing found,
    01886      * English. */
    01887     const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
    01888     if (strcmp(lang_file, _config_language_file) == 0) {
    01889       chosen_language = lng;
    01890       break;
    01891     }
    01892
    01893     if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback    = lng;
    01894     if (strncmp(lng->isocode, lang, 5) == 0) chosen_language   = lng;
    01895     if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
    01896   }
    01897
    01898   /* We haven't found the language in the config nor the one in the locale.
    01899    * Now we set it to one of the fallback languages */
    01900   if (chosen_language == NULL) {
    01901     chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
    01902   }
    01903
    01904   if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
    01905 }
    01906
    01911 const char *GetCurrentLanguageIsoCode()
    01912 {
    01913   return _langpack->isocode;
    01914 }
    01915
    01922 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
    01923 {
    01924   InitFreeType(this->Monospace());
    01925   const Sprite *question_mark[FS_END];
    01926
    01927   for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
    01928     question_mark[size] = GetGlyph(size, '?');
    01929   }
    01930
    01931   this->Reset();
    01932   for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
    01933     FontSize size = this->DefaultSize();
    01934     if (str != NULL) *str = text;
    01935     for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
    01936       if (c == SCC_SETX) {
    01937         /* SetX is, together with SetXY as special character that
    01938           * uses the next (two) characters as data points. We have
    01939           * to skip those, otherwise the UTF8 reading will go haywire. */
    01940         text++;
    01941       } else if (c == SCC_SETXY) {
    01942         text += 2;
    01943       } else if (c == SCC_TINYFONT) {
    01944         size = FS_SMALL;
    01945       } else if (c == SCC_BIGFONT) {
    01946         size = FS_LARGE;
    01947       } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
    01948         /* The character is printable, but not in the normal font. This is the case we were testing for. */
    01949         return true;
    01950       }
    01951     }
    01952   }
    01953   return false;
    01954 }
    01955
    01957 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
    01958   uint i;
    01959   uint j;
    01960
    01961   /* virtual */ void Reset()
    01962   {
    01963     this->i = 0;
    01964     this->j = 0;
    01965   }
    01966
    01967   /* virtual */ FontSize DefaultSize()
    01968   {
    01969     return FS_NORMAL;
    01970   }
    01971
    01972   /* virtual */ const char *NextString()
    01973   {
    01974     if (this->i >= TAB_COUNT) return NULL;
    01975
    01976     const char *ret = _langpack_offs[_langtab_start[i] + j];
    01977
    01978     this->j++;
    01979     while (this->j >= _langtab_num[this->i] && this->i < TAB_COUNT) {
    01980       i++;
    01981       j = 0;
    01982     }
    01983
    01984     return ret;
    01985   }
    01986
    01987   /* virtual */ bool Monospace()
    01988   {
    01989     return false;
    01990   }
    01991
    01992   /* virtual */ void SetFontNames(FreeTypeSettings *settings, const char *font_name)
    01993   {
    01994 #ifdef WITH_FREETYPE
    01995     strecpy(settings->small_font,  font_name, lastof(settings->small_font));
    01996     strecpy(settings->medium_font, font_name, lastof(settings->medium_font));
    01997     strecpy(settings->large_font,  font_name, lastof(settings->large_font));
    01998 #endif /* WITH_FREETYPE */
    01999   }
    02000 };
    02001
    02015 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
    02016 {
    02017   static LanguagePackGlyphSearcher pack_searcher;
    02018   if (searcher == NULL) searcher = &pack_searcher;
    02019   bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
    02020 #ifdef WITH_FREETYPE
    02021   if (bad_font) {
    02022     /* We found an unprintable character... lets try whether we can find
    02023      * a fallback font that can print the characters in the current language. */
    02024     FreeTypeSettings backup;
    02025     memcpy(&backup, &_freetype, sizeof(backup));
    02026
    02027     bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
    02028
    02029     memcpy(&_freetype, &backup, sizeof(backup));
    02030
    02031     if (bad_font && base_font) {
    02032       /* Our fallback font does miss characters too, so keep the
    02033        * user chosen font as that is more likely to be any good than
    02034        * the wild guess we made */
    02035       InitFreeType(searcher->Monospace());
    02036     }
    02037   }
    02038 #endif
    02039
    02040   if (bad_font) {
    02041     /* All attempts have failed. Display an error. As we do not want the string to be translated by
    02042      * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
    02043      * properly we have to set the colour of the string, otherwise we end up with a lot of artefacts.
    02044      * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
    02045      * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
    02046     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.");
    02047     Utf8Encode(err_str, SCC_YELLOW);
    02048     SetDParamStr(0, err_str);
    02049     ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
    02050
    02051     /* Reset the font width */
    02052     LoadStringWidthTable(searcher->Monospace());
    02053     return;
    02054   }
    02055
    02056   /* Update the font with cache */
    02057   LoadStringWidthTable(searcher->Monospace());
    02058
    02059 #if !defined(WITH_ICU)
    02060   /*
    02061    * For right-to-left languages we need the ICU library. If
    02062    * we do not have support for that library we warn the user
    02063    * about it with a message. As we do not want the string to
    02064    * be translated by the translators, we 'force' it into the
    02065    * binary and 'load' it via a BindCString. To do this
    02066    * properly we have to set the colour of the string,
    02067    * otherwise we end up with a lot of artefacts. The colour
    02068    * 'character' might change in the future, so for safety
    02069    * we just Utf8 Encode it into the string, which takes
    02070    * exactly three characters, so it replaces the "XXX" with
    02071    * the colour marker.
    02072    */
    02073   if (_current_text_dir != TD_LTR) {
    02074     static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
    02075     Utf8Encode(err_str, SCC_YELLOW);
    02076     SetDParamStr(0, err_str);
    02077     ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
    02078   }
    02079 #endif
    02080 }