strings.cpp

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

Generated on Wed Feb 17 23:06:52 2010 for OpenTTD by  doxygen 1.6.1