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