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 "openttd.h"
00014 #include "currency.h"
00015 #include "station_base.h"
00016 #include "town.h"
00017 #include "screenshot.h"
00018 #include "waypoint_base.h"
00019 #include "industry.h"
00020 #include "newgrf_text.h"
00021 #include "fileio_func.h"
00022 #include "group.h"
00023 #include "signs_base.h"
00024 #include "cargotype.h"
00025 #include "fontcache.h"
00026 #include "gui.h"
00027 #include "strings_func.h"
00028 #include "rev.h"
00029 #include "core/endian_func.hpp"
00030 #include "date_func.h"
00031 #include "vehicle_base.h"
00032 #include "video/video_driver.hpp"
00033 #include "engine_base.h"
00034 #include "strgen/strgen.h"
00035 #include "gfx_func.h"
00036 #include "townname_func.h"
00037 
00038 #include "table/strings.h"
00039 #include "table/control_codes.h"
00040 
00041 DynamicLanguages _dynlang;     
00042 uint64 _decode_parameters[20]; 
00043 
00044 static char *StationGetSpecialString(char *buff, int x, const char *last);
00045 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
00046 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const char *last);
00047 
00048 static char *FormatString(char *buff, const char *str, int64 *argv, uint casei, const char *last);
00049 
00050 struct LanguagePack : public LanguagePackHeader {
00051   char data[]; // list of strings
00052 };
00053 
00054 static char **_langpack_offs;
00055 static LanguagePack *_langpack;
00056 static uint _langtab_num[32];   // Offset into langpack offs
00057 static uint _langtab_start[32]; // Offset into langpack offs
00058 
00059 
00061 static inline int64 GetInt64(int64 **argv)
00062 {
00063   assert(argv);
00064   return *(*argv)++;
00065 }
00066 
00068 static inline int32 GetInt32(int64 **argv)
00069 {
00070   return (int32)GetInt64(argv);
00071 }
00072 
00074 static inline int64 *GetArgvPtr(int64 **argv, int n)
00075 {
00076   int64 *result;
00077   assert(*argv);
00078   result = *argv;
00079   (*argv) += n;
00080   return result;
00081 }
00082 
00083 
00084 const char *GetStringPtr(StringID string)
00085 {
00086   switch (GB(string, 11, 5)) {
00087     case 28: return GetGRFStringPtr(GB(string, 0, 11));
00088     case 29: return GetGRFStringPtr(GB(string, 0, 11) + 0x0800);
00089     case 30: return GetGRFStringPtr(GB(string, 0, 11) + 0x1000);
00090     default: return _langpack_offs[_langtab_start[string >> 11] + (string & 0x7FF)];
00091   }
00092 }
00093 
00104 char *GetStringWithArgs(char *buffr, uint string, int64 *argv, const char *last)
00105 {
00106   if (GB(string, 0, 16) == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, argv, last);
00107 
00108   uint index = GB(string,  0, 11);
00109   uint tab   = GB(string, 11,  5);
00110 
00111   switch (tab) {
00112     case 4:
00113       if (index >= 0xC0)
00114         return GetSpecialTownNameString(buffr, index - 0xC0, GetInt32(&argv), last);
00115       break;
00116 
00117     case 14:
00118       if (index >= 0xE4)
00119         return GetSpecialNameString(buffr, index - 0xE4, argv, last);
00120       break;
00121 
00122     case 15:
00123       /* Old table for custom names. This is no longer used */
00124       error("Incorrect conversion of custom name string.");
00125 
00126     case 26:
00127       /* Include string within newgrf text (format code 81) */
00128       if (HasBit(index, 10)) {
00129         StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
00130         return GetStringWithArgs(buffr, string, argv, last);
00131       }
00132       break;
00133 
00134     case 28:
00135       return FormatString(buffr, GetGRFStringPtr(index), argv, 0, last);
00136 
00137     case 29:
00138       return FormatString(buffr, GetGRFStringPtr(index + 0x0800), argv, 0, last);
00139 
00140     case 30:
00141       return FormatString(buffr, GetGRFStringPtr(index + 0x1000), argv, 0, last);
00142 
00143     case 31:
00144       NOT_REACHED();
00145   }
00146 
00147   if (index >= _langtab_num[tab]) {
00148     error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
00149   }
00150 
00151   return FormatString(buffr, GetStringPtr(GB(string, 0, 16)), argv, GB(string, 24, 8), last);
00152 }
00153 
00154 char *GetString(char *buffr, StringID string, const char *last)
00155 {
00156   return GetStringWithArgs(buffr, string, (int64*)_decode_parameters, last);
00157 }
00158 
00159 
00160 char *InlineString(char *buf, StringID string)
00161 {
00162   buf += Utf8Encode(buf, SCC_STRING_ID);
00163   buf += Utf8Encode(buf, string);
00164   return buf;
00165 }
00166 
00167 
00172 void SetDParamStr(uint n, const char *str)
00173 {
00174   SetDParam(n, (uint64)(size_t)str);
00175 }
00176 
00181 void InjectDParam(uint amount)
00182 {
00183   assert((uint)amount < lengthof(_decode_parameters));
00184   memmove(_decode_parameters + amount, _decode_parameters, sizeof(_decode_parameters) - amount * sizeof(uint64));
00185 }
00186 
00187 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill_from = 19)
00188 {
00189   uint64 divisor = 10000000000000000000ULL;
00190 
00191   if (number < 0) {
00192     buff += seprintf(buff, last, "-");
00193     number = -number;
00194   }
00195 
00196   uint64 num = number;
00197   uint64 tot = 0;
00198   for (int i = 0; i < 20; i++) {
00199     uint64 quot = 0;
00200     if (num >= divisor) {
00201       quot = num / divisor;
00202       num = num % divisor;
00203     }
00204     if (tot |= quot || i >= zerofill_from) {
00205       buff += seprintf(buff, last, "%i", (int)quot);
00206       if ((i % 3) == 1 && i != 19) buff = strecpy(buff, separator, last);
00207     }
00208 
00209     divisor /= 10;
00210   }
00211 
00212   *buff = '\0';
00213 
00214   return buff;
00215 }
00216 
00217 static char *FormatCommaNumber(char *buff, int64 number, const char *last)
00218 {
00219   const char *separator = _settings_game.locale.digit_group_separator;
00220   if (separator == NULL) separator = _langpack->digit_group_separator;
00221   return FormatNumber(buff, number, last, separator);
00222 }
00223 
00224 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
00225 {
00226   return FormatNumber(buff, number, last, "");
00227 }
00228 
00229 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
00230 {
00231   return FormatNumber(buff, number, last, "", 20 - count);
00232 }
00233 
00234 static char *FormatHexNumber(char *buff, int64 number, const char *last)
00235 {
00236   return buff + seprintf(buff, last, "0x%x", (uint32)number);
00237 }
00238 
00246 static char *FormatBytes(char *buff, int64 number, const char *last)
00247 {
00248   assert(number >= 0);
00249 
00250   /*                                    1   2^10  2^20  2^30  2^40  2^50  2^60 */
00251   const char * const iec_prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
00252   uint id = 1;
00253   while (number >= 1024 * 1024) {
00254     number /= 1024;
00255     id++;
00256   }
00257 
00258   const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
00259   if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
00260 
00261   if (number < 1024) {
00262     id = 0;
00263     buff += seprintf(buff, last, "%i", (int)number);
00264   } else if (number < 1024 * 10) {
00265     buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
00266   } else if (number < 1024 * 100) {
00267     buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
00268   } else {
00269     assert(number < 1024 * 1024);
00270     buff += seprintf(buff, last, "%i", (int)number / 1024);
00271   }
00272 
00273   assert(id < lengthof(iec_prefixes));
00274   buff += seprintf(buff, last, " %sB", iec_prefixes[id]);
00275 
00276   return buff;
00277 }
00278 
00279 static char *FormatYmdString(char *buff, Date date, const char *last)
00280 {
00281   YearMonthDay ymd;
00282   ConvertDateToYMD(date, &ymd);
00283 
00284   int64 args[3] = { ymd.day + STR_ORDINAL_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year };
00285   return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), args, 0, last);
00286 }
00287 
00288 static char *FormatMonthAndYear(char *buff, Date date, const char *last)
00289 {
00290   YearMonthDay ymd;
00291   ConvertDateToYMD(date, &ymd);
00292 
00293   int64 args[2] = { STR_MONTH_JAN + ymd.month, ymd.year };
00294   return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), args, 0, last);
00295 }
00296 
00297 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
00298 {
00299   YearMonthDay ymd;
00300   ConvertDateToYMD(date, &ymd);
00301 
00302   char day[3];
00303   char month[3];
00304   /* We want to zero-pad the days and months */
00305   snprintf(day,   lengthof(day),   "%02i", ymd.day);
00306   snprintf(month, lengthof(month), "%02i", ymd.month + 1);
00307 
00308   int64 args[3] = { (int64)(size_t)day, (int64)(size_t)month, ymd.year };
00309   return FormatString(buff, GetStringPtr(str), args, 0, last);
00310 }
00311 
00312 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
00313 {
00314   /* We are going to make number absolute for printing, so
00315    * keep this piece of data as we need it later on */
00316   bool negative = number < 0;
00317   const char *multiplier = "";
00318 
00319   number *= spec->rate;
00320 
00321   /* convert from negative */
00322   if (number < 0) {
00323     if (buff + Utf8CharLen(SCC_RED) > last) return buff;
00324     buff += Utf8Encode(buff, SCC_RED);
00325     buff = strecpy(buff, "-", last);
00326     number = -number;
00327   }
00328 
00329   /* Add prefix part, folowing symbol_pos specification.
00330    * Here, it can can be either 0 (prefix) or 2 (both prefix anf suffix).
00331    * The only remaining value is 1 (suffix), so everything that is not 1 */
00332   if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
00333 
00334   /* for huge numbers, compact the number into k or M */
00335   if (compact) {
00336     if (number >= 1000000000) {
00337       number = (number + 500000) / 1000000;
00338       multiplier = "M";
00339     } else if (number >= 1000000) {
00340       number = (number + 500) / 1000;
00341       multiplier = "k";
00342     }
00343   }
00344 
00345   const char *separator = _settings_game.locale.digit_group_separator_currency;
00346   if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
00347   if (separator == NULL) separator = _langpack->digit_group_separator_currency;
00348   buff = FormatNumber(buff, number, last, separator);
00349   buff = strecpy(buff, multiplier, last);
00350 
00351   /* Add suffix part, folowing symbol_pos specification.
00352    * Here, it can can be either 1 (suffix) or 2 (both prefix anf suffix).
00353    * The only remaining value is 1 (prefix), so everything that is not 0 */
00354   if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
00355 
00356   if (negative) {
00357     if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
00358     buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
00359     *buff = '\0';
00360   }
00361 
00362   return buff;
00363 }
00364 
00365 static int DeterminePluralForm(int64 count)
00366 {
00367   /* The absolute value determines plurality */
00368   uint64 n = abs(count);
00369 
00370   switch (_langpack->plural_form) {
00371     default:
00372       NOT_REACHED();
00373 
00374     /* Two forms, singular used for one only
00375      * Used in:
00376      *   Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
00377      *   Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
00378     case 0:
00379       return n != 1;
00380 
00381     /* Only one form
00382      * Used in:
00383      *   Hungarian, Japanese, Korean, Turkish */
00384     case 1:
00385       return 0;
00386 
00387     /* Two forms, singular used for zero and one
00388      * Used in:
00389      *   French, Brazilian Portuguese */
00390     case 2:
00391       return n > 1;
00392 
00393     /* Three forms, special case for zero
00394      * Used in:
00395      *   Latvian */
00396     case 3:
00397       return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
00398 
00399     /* Three forms, special case for one and two
00400      * Used in:
00401      *   Gaelige (Irish) */
00402     case 4:
00403       return n == 1 ? 0 : n == 2 ? 1 : 2;
00404 
00405     /* Three forms, special case for numbers ending in 1[2-9]
00406      * Used in:
00407      *   Lithuanian */
00408     case 5:
00409       return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00410 
00411     /* Three forms, special cases for numbers ending in 1 and 2, 3, 4, except those ending in 1[1-4]
00412      * Used in:
00413      *   Croatian, Russian, Slovak, Ukrainian */
00414     case 6:
00415       return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00416 
00417     /* Three forms, special case for one and some numbers ending in 2, 3, or 4
00418      * Used in:
00419      *   Polish */
00420     case 7:
00421       return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00422 
00423     /* Four forms, special case for one and all numbers ending in 02, 03, or 04
00424      * Used in:
00425      *   Slovenian */
00426     case 8:
00427       return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
00428 
00429     /* Two forms; singular used for everything ending in 1 but not in 11.
00430      * Used in:
00431      *   Icelandic */
00432     case 9:
00433       return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
00434 
00435     /* Three forms, special cases for one and 2, 3, or 4
00436      * Used in:
00437      *   Czech */
00438     case 10:
00439       return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
00440 
00441     /* Two forms, special 'hack' for Korean; singular for numbers ending
00442      *   in a consonant and plural for numbers ending in a vowel.
00443      * Korean doesn't have the concept of plural, but depending on how a
00444      * number is pronounced it needs another version of a particle.
00445      * As such the plural system is misused to give this distinction.
00446      */
00447     case 11:
00448       switch (n % 10) {
00449         case 0: // yeong
00450         case 1: // il
00451         case 3: // sam
00452         case 6: // yuk
00453         case 7: // chil
00454         case 8: // pal
00455           return 0;
00456 
00457         case 2: // i
00458         case 4: // sa
00459         case 5: // o
00460         case 9: // gu
00461           return 1;
00462 
00463         default:
00464           NOT_REACHED();
00465       }
00466   }
00467 }
00468 
00469 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
00470 {
00471   /* <NUM> {Length of each string} {each string} */
00472   uint n = (byte)*b++;
00473   uint pos, i, mypos = 0;
00474 
00475   for (i = pos = 0; i != n; i++) {
00476     uint len = (byte)*b++;
00477     if (i == form) mypos = pos;
00478     pos += len;
00479   }
00480 
00481   *dst += seprintf(*dst, last, "%s", b + mypos);
00482   return b + pos;
00483 }
00484 
00485 struct Units {
00486   int s_m;           
00487   int s_s;           
00488   StringID velocity; 
00489   int p_m;           
00490   int p_s;           
00491   StringID power;    
00492   int w_m;           
00493   int w_s;           
00494   StringID s_weight; 
00495   StringID l_weight; 
00496   int v_m;           
00497   int v_s;           
00498   StringID s_volume; 
00499   StringID l_volume; 
00500   int f_m;           
00501   int f_s;           
00502   StringID force;    
00503 };
00504 
00505 /* Unit conversions */
00506 static const Units units[] = {
00507   { // Imperial (Original, mph, hp, metric ton, litre, kN)
00508        1,  0, STR_UNITS_VELOCITY_IMPERIAL,
00509        1,  0, STR_UNITS_POWER_IMPERIAL,
00510        1,  0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00511     1000,  0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00512        1,  0, STR_UNITS_FORCE_SI,
00513   },
00514   { // Metric (km/h, hp, metric ton, litre, kN)
00515      103,  6, STR_UNITS_VELOCITY_METRIC,
00516        1,  0, STR_UNITS_POWER_METRIC,
00517        1,  0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00518     1000,  0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00519        1,  0, STR_UNITS_FORCE_SI,
00520   },
00521   { // SI (m/s, kilowatt, kilogram, cubic metres, kilonewton)
00522     1831, 12, STR_UNITS_VELOCITY_SI,
00523      764, 10, STR_UNITS_POWER_SI,
00524     1000,  0, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00525        1,  0, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00526        1,  0, STR_UNITS_FORCE_SI,
00527   },
00528 };
00529 
00535 uint ConvertSpeedToDisplaySpeed(uint speed)
00536 {
00537   return (speed * units[_settings_game.locale.units].s_m) >> units[_settings_game.locale.units].s_s;
00538 }
00539 
00545 uint ConvertDisplaySpeedToSpeed(uint speed)
00546 {
00547   return ((speed << units[_settings_game.locale.units].s_s) + units[_settings_game.locale.units].s_m / 2) / units[_settings_game.locale.units].s_m;
00548 }
00549 
00550 static char *FormatString(char *buff, const char *str, int64 *argv, uint casei, const char *last)
00551 {
00552   WChar b;
00553   int64 *argv_orig = argv;
00554   uint modifier = 0;
00555 
00556   while ((b = Utf8Consume(&str)) != '\0') {
00557     if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00558       /* We need to pass some stuff as it might be modified; oh boy. */
00559       b = RemapNewGRFStringControlCode(b, &buff, &str, argv);
00560       if (b == 0) continue;
00561     }
00562 
00563     switch (b) {
00564       case SCC_SETX: // {SETX}
00565         if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00566           buff += Utf8Encode(buff, SCC_SETX);
00567           *buff++ = *str++;
00568         }
00569         break;
00570 
00571       case SCC_SETXY: // {SETXY}
00572         if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00573           buff += Utf8Encode(buff, SCC_SETXY);
00574           *buff++ = *str++;
00575           *buff++ = *str++;
00576         }
00577         break;
00578 
00579       case SCC_STRING_ID: // {STRINL}
00580         buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, last);
00581         break;
00582 
00583       case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
00584         const char *str = (const char*)(size_t)GetInt64(&argv);
00585         buff = FormatString(buff, str, argv, casei, last);
00586         break;
00587       }
00588 
00589       case SCC_DATE_LONG: // {DATE_LONG}
00590         buff = FormatYmdString(buff, GetInt32(&argv), last);
00591         break;
00592 
00593       case SCC_DATE_SHORT: // {DATE_SHORT}
00594         buff = FormatMonthAndYear(buff, GetInt32(&argv), last);
00595         break;
00596 
00597       case SCC_VELOCITY: {// {VELOCITY}
00598         int64 args[1];
00599         assert(_settings_game.locale.units < lengthof(units));
00600         args[0] = ConvertSpeedToDisplaySpeed(GetInt32(&argv) * 10 / 16);
00601         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].velocity), args, modifier >> 24, last);
00602         modifier = 0;
00603         break;
00604       }
00605 
00606       case SCC_CURRENCY_COMPACT: // {CURRCOMPACT}
00607         buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), true, last);
00608         break;
00609 
00610       case SCC_REVISION: // {REV}
00611         buff = strecpy(buff, _openttd_revision, last);
00612         break;
00613 
00614       case SCC_CARGO_SHORT: { // {SHORTCARGO}
00615         /* Short description of cargotypes. Layout:
00616          * 8-bit = cargo type
00617          * 16-bit = cargo count */
00618         StringID cargo_str = CargoSpec::Get(GetInt32(&argv))->units_volume;
00619         switch (cargo_str) {
00620           case STR_TONS: {
00621             int64 args[1];
00622             assert(_settings_game.locale.units < lengthof(units));
00623             args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00624             buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00625             modifier = 0;
00626             break;
00627           }
00628 
00629           case STR_LITERS: {
00630             int64 args[1];
00631             assert(_settings_game.locale.units < lengthof(units));
00632             args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00633             buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, modifier >> 24, last);
00634             modifier = 0;
00635             break;
00636           }
00637 
00638           default:
00639             if (cargo_str >= 0xE000 && cargo_str < 0xF800) {
00640               /* NewGRF strings from Action 4 use a different format here,
00641                * of e.g. "x tonnes of coal", so process accordingly. */
00642               buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00643             } else {
00644               buff = FormatCommaNumber(buff, GetInt32(&argv), last);
00645               buff = strecpy(buff, " ", last);
00646               buff = strecpy(buff, GetStringPtr(cargo_str), last);
00647             }
00648             break;
00649         }
00650       } break;
00651 
00652       case SCC_STRING1: { // {STRING1}
00653         /* String that consumes ONE argument */
00654         uint str = modifier + GetInt32(&argv);
00655         buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 1), last);
00656         modifier = 0;
00657         break;
00658       }
00659 
00660       case SCC_STRING2: { // {STRING2}
00661         /* String that consumes TWO arguments */
00662         uint str = modifier + GetInt32(&argv);
00663         buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 2), last);
00664         modifier = 0;
00665         break;
00666       }
00667 
00668       case SCC_STRING3: { // {STRING3}
00669         /* String that consumes THREE arguments */
00670         uint str = modifier + GetInt32(&argv);
00671         buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 3), last);
00672         modifier = 0;
00673         break;
00674       }
00675 
00676       case SCC_STRING4: { // {STRING4}
00677         /* String that consumes FOUR arguments */
00678         uint str = modifier + GetInt32(&argv);
00679         buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 4), last);
00680         modifier = 0;
00681         break;
00682       }
00683 
00684       case SCC_STRING5: { // {STRING5}
00685         /* String that consumes FIVE arguments */
00686         uint str = modifier + GetInt32(&argv);
00687         buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 5), last);
00688         modifier = 0;
00689         break;
00690       }
00691 
00692       case SCC_STATION_FEATURES: { // {STATIONFEATURES}
00693         buff = StationGetSpecialString(buff, GetInt32(&argv), last);
00694         break;
00695       }
00696 
00697       case SCC_INDUSTRY_NAME: { // {INDUSTRY}
00698         const Industry *i = Industry::Get(GetInt32(&argv));
00699         int64 args[2];
00700 
00701         /* industry not valid anymore? */
00702         assert(i != NULL);
00703 
00704         /* First print the town name and the industry type name. */
00705         args[0] = i->town->index;
00706         args[1] = GetIndustrySpec(i->type)->name;
00707         buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), args, modifier >> 24, last);
00708         modifier = 0;
00709         break;
00710       }
00711 
00712       case SCC_VOLUME: { // {VOLUME}
00713         int64 args[1];
00714         assert(_settings_game.locale.units < lengthof(units));
00715         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00716         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, modifier >> 24, last);
00717         modifier = 0;
00718         break;
00719       }
00720 
00721       case SCC_GENDER_LIST: { // {G 0 Der Die Das}
00722         const char *s = GetStringPtr(argv_orig[(byte)*str++]); // contains the string that determines gender.
00723         int gender = 0;
00724         if (s != NULL) {
00725           WChar c = Utf8Consume(&s);
00726           /* Switch case is always put before genders, so remove those bits */
00727           if (c == SCC_SWITCH_CASE) {
00728             /* Skip to the last (i.e. default) case */
00729             for (uint num = (byte)*s++; num != 0; num--) s += 3 + (s[1] << 8) + s[2];
00730 
00731             c = Utf8Consume(&s);
00732           }
00733           /* Does this string have a gender, if so, set it */
00734           if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00735         }
00736         str = ParseStringChoice(str, gender, &buff, last);
00737         break;
00738       }
00739 
00740       case SCC_DATE_TINY: { // {DATE_TINY}
00741         buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_FORMAT_DATE_TINY, last);
00742         break;
00743       }
00744 
00745       case SCC_DATE_ISO: { // {DATE_ISO}
00746         buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_FORMAT_DATE_ISO, last);
00747         break;
00748       }
00749 
00750       case SCC_CARGO: { // {CARGO}
00751         /* First parameter is cargo type, second parameter is cargo count */
00752         CargoID cargo = GetInt32(&argv);
00753         StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
00754         buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00755         break;
00756       }
00757 
00758       case SCC_POWER: { // {POWER}
00759         int64 args[1];
00760         assert(_settings_game.locale.units < lengthof(units));
00761         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].p_m >> units[_settings_game.locale.units].p_s;
00762         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].power), args, modifier >> 24, last);
00763         modifier = 0;
00764         break;
00765       }
00766 
00767       case SCC_VOLUME_SHORT: { // {VOLUME_S}
00768         int64 args[1];
00769         assert(_settings_game.locale.units < lengthof(units));
00770         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00771         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_volume), args, modifier >> 24, last);
00772         modifier = 0;
00773         break;
00774       }
00775 
00776       case SCC_WEIGHT: { // {WEIGHT}
00777         int64 args[1];
00778         assert(_settings_game.locale.units < lengthof(units));
00779         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00780         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00781         modifier = 0;
00782         break;
00783       }
00784 
00785       case SCC_WEIGHT_SHORT: { // {WEIGHT_S}
00786         int64 args[1];
00787         assert(_settings_game.locale.units < lengthof(units));
00788         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00789         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_weight), args, modifier >> 24, last);
00790         modifier = 0;
00791         break;
00792       }
00793 
00794       case SCC_FORCE: { // {FORCE}
00795         int64 args[1];
00796         assert(_settings_game.locale.units < lengthof(units));
00797         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].f_m >> units[_settings_game.locale.units].f_s;
00798         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].force), args, modifier >> 24, last);
00799         modifier = 0;
00800         break;
00801       }
00802 
00803       /* This sets up the gender for the string.
00804        * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
00805       case SCC_GENDER_INDEX: // {GENDER 0}
00806         str++;
00807         break;
00808 
00809       case SCC_STRING: {// {STRING}
00810         uint str = modifier + GetInt32(&argv);
00811         /* WARNING. It's prohibited for the included string to consume any arguments.
00812          * For included strings that consume argument, you should use STRING1, STRING2 etc.
00813          * To debug stuff you can set argv to NULL and it will tell you */
00814         buff = GetStringWithArgs(buff, str, argv, last);
00815         modifier = 0;
00816         break;
00817       }
00818 
00819       case SCC_COMMA: // {COMMA}
00820         buff = FormatCommaNumber(buff, GetInt64(&argv), last);
00821         break;
00822 
00823       case SCC_ARG_INDEX: // Move argument pointer
00824         argv = argv_orig + (byte)*str++;
00825         break;
00826 
00827       case SCC_PLURAL_LIST: { // {P}
00828         int64 v = argv_orig[(byte)*str++]; // contains the number that determines plural
00829         str = ParseStringChoice(str, DeterminePluralForm(v), &buff, last);
00830         break;
00831       }
00832 
00833       case SCC_NUM: // {NUM}
00834         buff = FormatNoCommaNumber(buff, GetInt64(&argv), last);
00835         break;
00836 
00837       case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
00838         int64 num = GetInt64(&argv);
00839         buff = FormatZerofillNumber(buff, num, GetInt64(&argv), last);
00840       } break;
00841 
00842       case SCC_HEX: // {HEX}
00843         buff = FormatHexNumber(buff, GetInt64(&argv), last);
00844         break;
00845 
00846       case SCC_BYTES: // {BYTES}
00847         buff = FormatBytes(buff, GetInt64(&argv), last);
00848         break;
00849 
00850       case SCC_CURRENCY: // {CURRENCY}
00851         buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
00852         break;
00853 
00854       case SCC_WAYPOINT_NAME: { // {WAYPOINT}
00855         Waypoint *wp = Waypoint::Get(GetInt32(&argv));
00856 
00857         assert(wp != NULL);
00858 
00859         if (wp->name != NULL) {
00860           buff = strecpy(buff, wp->name, last);
00861         } else {
00862           int64 temp[2];
00863           temp[0] = wp->town->index;
00864           temp[1] = wp->town_cn + 1;
00865           StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
00866           if (wp->town_cn != 0) str++;
00867           buff = GetStringWithArgs(buff, str, temp, last);
00868         }
00869         break;
00870       }
00871 
00872       case SCC_STATION_NAME: { // {STATION}
00873         StationID sid = GetInt32(&argv);
00874         const Station *st = Station::GetIfValid(sid);
00875 
00876         if (st == NULL) {
00877           /* The station doesn't exist anymore. The only place where we might
00878            * be "drawing" an invalid station is in the case of cargo that is
00879            * in transit. */
00880           buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, NULL, last);
00881           break;
00882         }
00883 
00884         if (st->name != NULL) {
00885           buff = strecpy(buff, st->name, last);
00886         } else {
00887           StringID str = st->string_id;
00888           if (st->indtype != IT_INVALID) {
00889             /* Special case where the industry provides the name for the station */
00890             const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
00891 
00892             /* Industry GRFs can change which might remove the station name and
00893              * thus cause very strange things. Here we check for that before we
00894              * actually set the station name. */
00895             if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
00896               str = indsp->station_name;
00897             }
00898           }
00899 
00900           int64 temp[3];
00901           temp[0] = STR_TOWN_NAME;
00902           temp[1] = st->town->index;
00903           temp[2] = st->index;
00904           buff = GetStringWithArgs(buff, str, temp, last);
00905         }
00906         break;
00907       }
00908 
00909       case SCC_TOWN_NAME: { // {TOWN}
00910         const Town *t = Town::Get(GetInt32(&argv));
00911 
00912         assert(t != NULL);
00913 
00914         if (t->name != NULL) {
00915           buff = strecpy(buff, t->name, last);
00916         } else {
00917           buff = GetTownName(buff, t, last);
00918         }
00919         break;
00920       }
00921 
00922       case SCC_GROUP_NAME: { // {GROUP}
00923         const Group *g = Group::Get(GetInt32(&argv));
00924 
00925         assert(g != NULL);
00926 
00927         if (g->name != NULL) {
00928           buff = strecpy(buff, g->name, last);
00929         } else {
00930           int64 args[1];
00931 
00932           args[0] = g->index;
00933           buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, args, last);
00934         }
00935         break;
00936       }
00937 
00938       case SCC_ENGINE_NAME: { // {ENGINE}
00939         EngineID engine = (EngineID)GetInt32(&argv);
00940         const Engine *e = Engine::Get(engine);
00941 
00942         assert(e != NULL);
00943 
00944         if (e->name != NULL) {
00945           buff = strecpy(buff, e->name, last);
00946         } else {
00947           buff = GetStringWithArgs(buff, e->info.string_id, NULL, last);
00948         }
00949         break;
00950       }
00951 
00952       case SCC_VEHICLE_NAME: { // {VEHICLE}
00953         const Vehicle *v = Vehicle::Get(GetInt32(&argv));
00954 
00955         assert(v != NULL);
00956 
00957         if (v->name != NULL) {
00958           buff = strecpy(buff, v->name, last);
00959         } else {
00960           int64 args[1];
00961           args[0] = v->unitnumber;
00962 
00963           StringID str;
00964           switch (v->type) {
00965             default: NOT_REACHED();
00966             case VEH_TRAIN:    str = STR_SV_TRAIN_NAME; break;
00967             case VEH_ROAD:     str = STR_SV_ROAD_VEHICLE_NAME; break;
00968             case VEH_SHIP:     str = STR_SV_SHIP_NAME; break;
00969             case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
00970           }
00971 
00972           buff = GetStringWithArgs(buff, str, args, last);
00973         }
00974         break;
00975       }
00976 
00977       case SCC_SIGN_NAME: { // {SIGN}
00978         const Sign *si = Sign::Get(GetInt32(&argv));
00979         if (si->name != NULL) {
00980           buff = strecpy(buff, si->name, last);
00981         } else {
00982           buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, NULL, last);
00983         }
00984         break;
00985       }
00986 
00987       case SCC_COMPANY_NAME: { // {COMPANY}
00988         const Company *c = Company::Get((CompanyID)GetInt32(&argv));
00989 
00990         if (c->name != NULL) {
00991           buff = strecpy(buff, c->name, last);
00992         } else {
00993           int64 args[1];
00994           args[0] = c->name_2;
00995           buff = GetStringWithArgs(buff, c->name_1, args, last);
00996         }
00997         break;
00998       }
00999 
01000       case SCC_COMPANY_NUM: { // {COMPANYNUM}
01001         CompanyID company = (CompanyID)GetInt32(&argv);
01002 
01003         /* Nothing is added for AI or inactive companies */
01004         if (Company::IsValidHumanID(company)) {
01005           int64 args[1];
01006           args[0] = company + 1;
01007           buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, args, last);
01008         }
01009         break;
01010       }
01011 
01012       case SCC_PRESIDENT_NAME: { // {PRESIDENTNAME}
01013         const Company *c = Company::Get((CompanyID)GetInt32(&argv));
01014 
01015         if (c->president_name != NULL) {
01016           buff = strecpy(buff, c->president_name, last);
01017         } else {
01018           int64 args[1];
01019           args[0] = c->president_name_2;
01020           buff = GetStringWithArgs(buff, c->president_name_1, args, last);
01021         }
01022         break;
01023       }
01024 
01025       case SCC_SETCASE: { // {SETCASE}
01026         /* This is a pseudo command, it's outputted when someone does {STRING.ack}
01027          * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
01028         modifier = (byte)*str++ << 24;
01029         break;
01030       }
01031 
01032       case SCC_SWITCH_CASE: { // {Used to implement case switching}
01033         /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
01034          * Each LEN is printed using 2 bytes in big endian order. */
01035         uint num = (byte)*str++;
01036         while (num) {
01037           if ((byte)str[0] == casei) {
01038             /* Found the case, adjust str pointer and continue */
01039             str += 3;
01040             break;
01041           }
01042           /* Otherwise skip to the next case */
01043           str += 3 + (str[1] << 8) + str[2];
01044           num--;
01045         }
01046         break;
01047       }
01048 
01049       default:
01050         if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01051         break;
01052     }
01053   }
01054   *buff = '\0';
01055   return buff;
01056 }
01057 
01058 
01059 static char *StationGetSpecialString(char *buff, int x, const char *last)
01060 {
01061   if ((x & FACIL_TRAIN)      && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01062   if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01063   if ((x & FACIL_BUS_STOP)   && (buff + Utf8CharLen(SCC_BUS)   < last)) buff += Utf8Encode(buff, SCC_BUS);
01064   if ((x & FACIL_AIRPORT)    && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01065   if ((x & FACIL_DOCK)       && (buff + Utf8CharLen(SCC_SHIP)  < last)) buff += Utf8Encode(buff, SCC_SHIP);
01066   *buff = '\0';
01067   return buff;
01068 }
01069 
01070 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01071 {
01072   return GenerateTownNameString(buff, last, ind, seed);
01073 }
01074 
01075 static const char * const _silly_company_names[] = {
01076   "Bloggs Brothers",
01077   "Tiny Transport Ltd.",
01078   "Express Travel",
01079   "Comfy-Coach & Co.",
01080   "Crush & Bump Ltd.",
01081   "Broken & Late Ltd.",
01082   "Sam Speedy & Son",
01083   "Supersonic Travel",
01084   "Mike's Motors",
01085   "Lightning International",
01086   "Pannik & Loozit Ltd.",
01087   "Inter-City Transport",
01088   "Getout & Pushit Ltd."
01089 };
01090 
01091 static const char * const _surname_list[] = {
01092   "Adams",
01093   "Allan",
01094   "Baker",
01095   "Bigwig",
01096   "Black",
01097   "Bloggs",
01098   "Brown",
01099   "Campbell",
01100   "Gordon",
01101   "Hamilton",
01102   "Hawthorn",
01103   "Higgins",
01104   "Green",
01105   "Gribble",
01106   "Jones",
01107   "McAlpine",
01108   "MacDonald",
01109   "McIntosh",
01110   "Muir",
01111   "Murphy",
01112   "Nelson",
01113   "O'Donnell",
01114   "Parker",
01115   "Phillips",
01116   "Pilkington",
01117   "Quigley",
01118   "Sharkey",
01119   "Thomson",
01120   "Watkins"
01121 };
01122 
01123 static const char * const _silly_surname_list[] = {
01124   "Grumpy",
01125   "Dozy",
01126   "Speedy",
01127   "Nosey",
01128   "Dribble",
01129   "Mushroom",
01130   "Cabbage",
01131   "Sniffle",
01132   "Fishy",
01133   "Swindle",
01134   "Sneaky",
01135   "Nutkins"
01136 };
01137 
01138 static const char _initial_name_letters[] = {
01139   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01140   'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01141 };
01142 
01143 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01144 {
01145   const char * const *base;
01146   uint num;
01147 
01148   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01149     base = _silly_surname_list;
01150     num  = lengthof(_silly_surname_list);
01151   } else {
01152     base = _surname_list;
01153     num  = lengthof(_surname_list);
01154   }
01155 
01156   buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01157   buff = strecpy(buff, " & Co.", last);
01158 
01159   return buff;
01160 }
01161 
01162 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01163 {
01164   char initial[] = "?. ";
01165   const char * const *base;
01166   uint num;
01167   uint i;
01168 
01169   initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01170   buff = strecpy(buff, initial, last);
01171 
01172   i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01173   if (i < sizeof(_initial_name_letters)) {
01174     initial[0] = _initial_name_letters[i];
01175     buff = strecpy(buff, initial, last);
01176   }
01177 
01178   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01179     base = _silly_surname_list;
01180     num  = lengthof(_silly_surname_list);
01181   } else {
01182     base = _surname_list;
01183     num  = lengthof(_surname_list);
01184   }
01185 
01186   buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01187 
01188   return buff;
01189 }
01190 
01191 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const char *last)
01192 {
01193   switch (ind) {
01194     case 1: // not used
01195       return strecpy(buff, _silly_company_names[GetInt32(&argv) & 0xFFFF], last);
01196 
01197     case 2: // used for Foobar & Co company names
01198       return GenAndCoName(buff, GetInt32(&argv), last);
01199 
01200     case 3: // President name
01201       return GenPresidentName(buff, GetInt32(&argv), last);
01202   }
01203 
01204   /* town name? */
01205   if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01206     buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv), last);
01207     return strecpy(buff, " Transport", last);
01208   }
01209 
01210   /* language name? */
01211   if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01212     int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01213     return strecpy(buff,
01214       i == _dynlang.curr ? _langpack->own_name : _dynlang.ent[i].name, last);
01215   }
01216 
01217   /* resolution size? */
01218   if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01219     int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01220     buff += seprintf(
01221       buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01222     );
01223     return buff;
01224   }
01225 
01226   /* screenshot format name? */
01227   if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01228     int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01229     return strecpy(buff, GetScreenshotFormatDesc(i), last);
01230   }
01231 
01232   NOT_REACHED();
01233 }
01234 
01235 #ifdef ENABLE_NETWORK
01236 extern void SortNetworkLanguages();
01237 #else /* ENABLE_NETWORK */
01238 static inline void SortNetworkLanguages() {}
01239 #endif /* ENABLE_NETWORK */
01240 
01241 bool ReadLanguagePack(int lang_index)
01242 {
01243   int tot_count, i;
01244   size_t len;
01245   char **langpack_offs;
01246   char *s;
01247 
01248   LanguagePack *lang_pack = (LanguagePack*)ReadFileToMem(_dynlang.ent[lang_index].file, &len, 200000);
01249 
01250   if (lang_pack == NULL) return false;
01251   if (len < sizeof(LanguagePack) ||
01252       lang_pack->ident != TO_LE32(LANGUAGE_PACK_IDENT) ||
01253       lang_pack->version != TO_LE32(LANGUAGE_PACK_VERSION)) {
01254     free(lang_pack);
01255     return false;
01256   }
01257 
01258 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01259   for (i = 0; i != 32; i++) {
01260     lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01261   }
01262 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
01263 
01264   tot_count = 0;
01265   for (i = 0; i != 32; i++) {
01266     uint num = lang_pack->offsets[i];
01267     _langtab_start[i] = tot_count;
01268     _langtab_num[i] = num;
01269     tot_count += num;
01270   }
01271 
01272   /* Allocate offsets */
01273   langpack_offs = MallocT<char*>(tot_count);
01274 
01275   /* Fill offsets */
01276   s = lang_pack->data;
01277   for (i = 0; i != tot_count; i++) {
01278     len = (byte)*s;
01279     *s++ = '\0'; // zero terminate the string before.
01280     if (len >= 0xC0) len = ((len & 0x3F) << 8) + (byte)*s++;
01281     langpack_offs[i] = s;
01282     s += len;
01283   }
01284 
01285   free(_langpack);
01286   _langpack = lang_pack;
01287 
01288   free(_langpack_offs);
01289   _langpack_offs = langpack_offs;
01290 
01291   const char *c_file = strrchr(_dynlang.ent[lang_index].file, PATHSEPCHAR) + 1;
01292   strecpy(_dynlang.curr_file, c_file, lastof(_dynlang.curr_file));
01293 
01294   _dynlang.curr = lang_index;
01295   _dynlang.text_dir = (TextDirection)lang_pack->text_dir;
01296   SetCurrentGrfLangID(_langpack->newgrflangid);
01297   SortNetworkLanguages();
01298   return true;
01299 }
01300 
01301 /* Win32 implementation in win32.cpp.
01302  * OS X implementation in os/macosx/macos.mm. */
01303 #if !(defined(WIN32) || defined(__APPLE__))
01304 
01310 const char *GetCurrentLocale(const char *param)
01311 {
01312   const char *env;
01313 
01314   env = getenv("LANGUAGE");
01315   if (env != NULL) return env;
01316 
01317   env = getenv("LC_ALL");
01318   if (env != NULL) return env;
01319 
01320   if (param != NULL) {
01321     env = getenv(param);
01322     if (env != NULL) return env;
01323   }
01324 
01325   return getenv("LANG");
01326 }
01327 #else
01328 const char *GetCurrentLocale(const char *param);
01329 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
01330 
01331 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01332 {
01333   char stra[512];
01334   char strb[512];
01335   GetString(stra, *a, lastof(stra));
01336   GetString(strb, *b, lastof(strb));
01337 
01338   return strcmp(stra, strb);
01339 }
01340 
01348 static bool UniqueLanguageFile(const Language *langs, uint max, const char *language)
01349 {
01350   for (uint i = 0; i < max; i++) {
01351     const char *f_name = strrchr(langs[i].file, PATHSEPCHAR) + 1;
01352     if (strcmp(f_name, language) == 0) return false; // duplicates
01353   }
01354 
01355   return true;
01356 }
01357 
01364 static bool GetLanguageFileHeader(const char *file, LanguagePack *hdr)
01365 {
01366   FILE *f = fopen(file, "rb");
01367   if (f == NULL) return false;
01368 
01369   size_t read = fread(hdr, sizeof(*hdr), 1, f);
01370   fclose(f);
01371 
01372   bool ret = read == 1 &&
01373       hdr->ident == TO_LE32(LANGUAGE_PACK_IDENT) &&
01374       hdr->version == TO_LE32(LANGUAGE_PACK_VERSION);
01375 
01376   /* Convert endianness for the windows language ID */
01377   if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01378   return ret;
01379 }
01380 
01389 static int GetLanguageList(Language *langs, int start, int max, const char *path)
01390 {
01391   int i = start;
01392 
01393   DIR *dir = ttd_opendir(path);
01394   if (dir != NULL) {
01395     struct dirent *dirent;
01396     while ((dirent = readdir(dir)) != NULL && i < max) {
01397       const char *d_name    = FS2OTTD(dirent->d_name);
01398       const char *extension = strrchr(d_name, '.');
01399 
01400       /* Not a language file */
01401       if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01402 
01403       /* Filter any duplicate language-files, first-come first-serve */
01404       if (!UniqueLanguageFile(langs, i, d_name)) continue;
01405 
01406       langs[i].file = str_fmt("%s%s", path, d_name);
01407 
01408       /* Check whether the file is of the correct version */
01409       LanguagePack hdr;
01410       if (!GetLanguageFileHeader(langs[i].file, &hdr)) {
01411         free(langs[i].file);
01412         continue;
01413       }
01414 
01415       i++;
01416     }
01417     closedir(dir);
01418   }
01419   return i - start;
01420 }
01421 
01426 void InitializeLanguagePacks()
01427 {
01428   Searchpath sp;
01429   Language files[MAX_LANG];
01430   uint language_count = 0;
01431 
01432   FOR_ALL_SEARCHPATHS(sp) {
01433     char path[MAX_PATH];
01434     FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01435     language_count += GetLanguageList(files, language_count, lengthof(files), path);
01436   }
01437   if (language_count == 0) usererror("No available language packs (invalid versions?)");
01438 
01439   /* Acquire the locale of the current system */
01440   const char *lang = GetCurrentLocale("LC_MESSAGES");
01441   if (lang == NULL) lang = "en_GB";
01442 
01443   int chosen_language   = -1; 
01444   int language_fallback = -1; 
01445   int en_GB_fallback    =  0; 
01446 
01447   DynamicLanguages *dl = &_dynlang;
01448   dl->num = 0;
01449   /* Fill the dynamic languages structures */
01450   for (uint i = 0; i < language_count; i++) {
01451     /* File read the language header */
01452     LanguagePack hdr;
01453     if (!GetLanguageFileHeader(files[i].file, &hdr)) continue;
01454 
01455     dl->ent[dl->num].file = files[i].file;
01456     dl->ent[dl->num].name = strdup(hdr.name);
01457 
01458     /* We are trying to find a default language. The priority is by
01459      * configuration file, local environment and last, if nothing found,
01460      * english. If def equals -1, we have not picked a default language */
01461     const char *lang_file = strrchr(dl->ent[dl->num].file, PATHSEPCHAR) + 1;
01462     if (strcmp(lang_file, dl->curr_file) == 0) chosen_language = dl->num;
01463 
01464     if (chosen_language == -1) {
01465       if (strcmp (hdr.isocode, "en_GB") == 0) en_GB_fallback    = dl->num;
01466       if (strncmp(hdr.isocode, lang, 5) == 0) chosen_language   = dl->num;
01467       if (strncmp(hdr.isocode, lang, 2) == 0) language_fallback = dl->num;
01468     }
01469 
01470     dl->num++;
01471   }
01472 
01473   if (dl->num == 0) usererror("Invalid version of language packs");
01474 
01475   /* We haven't found the language in the config nor the one in the locale.
01476    * Now we set it to one of the fallback languages */
01477   if (chosen_language == -1) {
01478     chosen_language = (language_fallback != -1) ? language_fallback : en_GB_fallback;
01479   }
01480 
01481   if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", dl->ent[chosen_language].file);
01482 }
01483 
01488 const char *GetCurrentLanguageIsoCode()
01489 {
01490   return _langpack->isocode;
01491 }
01492 
01503 void CheckForMissingGlyphsInLoadedLanguagePack()
01504 {
01505 #ifdef WITH_FREETYPE
01506   /* Reset to the original state; switching languages might cause us to
01507    * automatically choose another font. This resets that choice. */
01508   UninitFreeType();
01509   InitFreeType();
01510   bool retry = false;
01511 #endif
01512 
01513   for (;;) {
01514     const Sprite *question_mark = GetGlyph(FS_NORMAL, '?');
01515 
01516     for (uint i = 0; i != 32; i++) {
01517       for (uint j = 0; j < _langtab_num[i]; j++) {
01518         const char *string = _langpack_offs[_langtab_start[i] + j];
01519         WChar c;
01520         while ((c = Utf8Consume(&string)) != '\0') {
01521           if (c == SCC_SETX) {
01522             /*
01523              * SetX is, together with SetXY as special character that
01524              * uses the next (two) characters as data points. We have
01525              * to skip those, otherwise the UTF8 reading will go
01526              * haywire.
01527              */
01528             string++;
01529           } else if (c == SCC_SETXY) {
01530             string += 2;
01531           } else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) {
01532 #ifdef WITH_FREETYPE
01533             if (!retry) {
01534               /* We found an unprintable character... lets try whether we can
01535                * find a fallback font that can print the characters in the
01536                * current language. */
01537               retry = true;
01538 
01539               FreeTypeSettings backup;
01540               memcpy(&backup, &_freetype, sizeof(backup));
01541 
01542               bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, string);
01543               if (success) {
01544                 UninitFreeType();
01545                 InitFreeType();
01546               }
01547 
01548               memcpy(&_freetype, &backup, sizeof(backup));
01549 
01550               if (success) continue;
01551             } else {
01552               /* Our fallback font does miss characters too, so keep the
01553                * user chosen font as that is more likely to be any good than
01554                * the wild guess we made */
01555               UninitFreeType();
01556               InitFreeType();
01557             }
01558 #endif
01559             /*
01560              * The character is printable, but not in the normal font.
01561              * This is the case we were testing for. In this case we
01562              * have to show the error. As we do not want the string to
01563              * be translated by the translators, we 'force' it into the
01564              * binary and 'load' it via a BindCString. To do this
01565              * properly we have to set the colour of the string,
01566              * otherwise we end up with a lot of artefacts. The colour
01567              * 'character' might change in the future, so for safety
01568              * we just Utf8 Encode it into the string, which takes
01569              * exactly three characters, so it replaces the "XXX" with
01570              * the colour marker.
01571              */
01572             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.");
01573             Utf8Encode(err_str, SCC_YELLOW);
01574             SetDParamStr(0, err_str);
01575             ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, 0, 0);
01576 
01577             /* Reset the font width */
01578             LoadStringWidthTable();
01579             return;
01580           }
01581         }
01582       }
01583     }
01584     break;
01585   }
01586 
01587   /* Update the font with cache */
01588   LoadStringWidthTable();
01589 
01590 #if !defined(WITH_ICU)
01591   /*
01592    * For right-to-left languages we need the ICU library. If
01593    * we do not have support for that library we warn the user
01594    * about it with a message. As we do not want the string to
01595    * be translated by the translators, we 'force' it into the
01596    * binary and 'load' it via a BindCString. To do this
01597    * properly we have to set the colour of the string,
01598    * otherwise we end up with a lot of artefacts. The colour
01599    * 'character' might change in the future, so for safety
01600    * we just Utf8 Encode it into the string, which takes
01601    * exactly three characters, so it replaces the "XXX" with
01602    * the colour marker.
01603    */
01604   if (_dynlang.text_dir != TD_LTR) {
01605     static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01606     Utf8Encode(err_str, SCC_YELLOW);
01607     SetDParamStr(0, err_str);
01608     ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, 0, 0);
01609   }
01610 #endif
01611 }

Generated on Wed Dec 30 20:40:07 2009 for OpenTTD by  doxygen 1.5.6