# strings.cpp

Go to the documentation of this file.

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