strings.cpp

Go to the documentation of this file.
00001 /* $Id: strings.cpp 15474 2009-02-14 01:29:18Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "currency.h"
00008 #include "spritecache.h"
00009 #include "namegen_func.h"
00010 #include "station_base.h"
00011 #include "town.h"
00012 #include "screenshot.h"
00013 #include "waypoint.h"
00014 #include "industry.h"
00015 #include "newgrf_text.h"
00016 #include "music.h"
00017 #include "fileio_func.h"
00018 #include "group.h"
00019 #include "newgrf_townname.h"
00020 #include "signs_base.h"
00021 #include "cargotype.h"
00022 #include "fontcache.h"
00023 #include "gui.h"
00024 #include "strings_func.h"
00025 #include "rev.h"
00026 #include "core/endian_func.hpp"
00027 #include "date_func.h"
00028 #include "vehicle_base.h"
00029 #include "company_func.h"
00030 #include "video/video_driver.hpp"
00031 #include "engine_base.h"
00032 #include "strgen/strgen.h"
00033 
00034 #include "table/strings.h"
00035 #include "table/control_codes.h"
00036 
00037 DynamicLanguages _dynlang;
00038 uint64 _decode_parameters[20];
00039 
00040 static char *StationGetSpecialString(char *buff, int x, const char *last);
00041 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
00042 static char *GetSpecialNameString(char *buff, int ind, const int64 *argv, const char *last);
00043 
00044 static char *FormatString(char *buff, const char *str, const int64 *argv, uint casei, const char *last);
00045 
00046 struct LanguagePack : public LanguagePackHeader {
00047   char data[VARARRAY_SIZE]; // list of strings
00048 };
00049 
00050 static char **_langpack_offs;
00051 static LanguagePack *_langpack;
00052 static uint _langtab_num[32];   // Offset into langpack offs
00053 static uint _langtab_start[32]; // Offset into langpack offs
00054 
00055 
00057 static inline int64 GetInt64(const int64 **argv)
00058 {
00059   assert(argv);
00060   return *(*argv)++;
00061 }
00062 
00064 static inline int32 GetInt32(const int64 **argv)
00065 {
00066   return (int32)GetInt64(argv);
00067 }
00068 
00070 static inline const int64 *GetArgvPtr(const int64 **argv, int n)
00071 {
00072   const int64 *result;
00073   assert(*argv);
00074   result = *argv;
00075   (*argv) += n;
00076   return result;
00077 }
00078 
00079 
00080 const char *GetStringPtr(StringID string)
00081 {
00082   switch (GB(string, 11, 5)) {
00083     case 28: return GetGRFStringPtr(GB(string, 0, 11));
00084     case 29: return GetGRFStringPtr(GB(string, 0, 11) + 0x0800);
00085     case 30: return GetGRFStringPtr(GB(string, 0, 11) + 0x1000);
00086     default: return _langpack_offs[_langtab_start[string >> 11] + (string & 0x7FF)];
00087   }
00088 }
00089 
00100 static char *GetStringWithArgs(char *buffr, uint string, const int64 *argv, const char *last)
00101 {
00102   if (GB(string, 0, 16) == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, argv, last);
00103 
00104   uint index = GB(string,  0, 11);
00105   uint tab   = GB(string, 11,  5);
00106 
00107   switch (tab) {
00108     case 4:
00109       if (index >= 0xC0)
00110         return GetSpecialTownNameString(buffr, index - 0xC0, GetInt32(&argv), last);
00111       break;
00112 
00113     case 14:
00114       if (index >= 0xE4)
00115         return GetSpecialNameString(buffr, index - 0xE4, argv, last);
00116       break;
00117 
00118     case 15:
00119       /* Old table for custom names. This is no longer used */
00120       error("Incorrect conversion of custom name string.");
00121 
00122     case 26:
00123       /* Include string within newgrf text (format code 81) */
00124       if (HasBit(index, 10)) {
00125         StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
00126         return GetStringWithArgs(buffr, string, argv, last);
00127       }
00128       break;
00129 
00130     case 28:
00131       return FormatString(buffr, GetGRFStringPtr(index), argv, 0, last);
00132 
00133     case 29:
00134       return FormatString(buffr, GetGRFStringPtr(index + 0x0800), argv, 0, last);
00135 
00136     case 30:
00137       return FormatString(buffr, GetGRFStringPtr(index + 0x1000), argv, 0, last);
00138 
00139     case 31:
00140       NOT_REACHED();
00141   }
00142 
00143   if (index >= _langtab_num[tab]) {
00144     error(
00145       "String 0x%X is invalid. "
00146       "Probably because an old version of the .lng file.\n", string
00147     );
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 
00176 void InjectDParam(uint amount)
00177 {
00178   assert((uint)amount < lengthof(_decode_parameters));
00179   memmove(_decode_parameters + amount, _decode_parameters, sizeof(_decode_parameters) - amount * sizeof(uint64));
00180 }
00181 
00182 // TODO
00183 static char *FormatCommaNumber(char *buff, int64 number, const char *last)
00184 {
00185   uint64 divisor = 10000000000000000000ULL;
00186   uint64 quot;
00187   int i;
00188   uint64 tot;
00189   uint64 num;
00190 
00191   if (number < 0) {
00192     *buff++ = '-';
00193     number = -number;
00194   }
00195 
00196   num = number;
00197 
00198   tot = 0;
00199   for (i = 0; i < 20; i++) {
00200     quot = 0;
00201     if (num >= divisor) {
00202       quot = num / divisor;
00203       num = num % divisor;
00204     }
00205     if (tot |= quot || i == 19) {
00206       *buff++ = '0' + quot;
00207       if ((i % 3) == 1 && i != 19) *buff++ = ',';
00208     }
00209 
00210     divisor /= 10;
00211   }
00212 
00213   *buff = '\0';
00214 
00215   return buff;
00216 }
00217 
00218 // TODO
00219 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
00220 {
00221   uint64 divisor = 10000000000000000000ULL;
00222   uint64 quot;
00223   int i;
00224   uint64 tot;
00225   uint64 num;
00226 
00227   if (number < 0) {
00228     buff = strecpy(buff, "-", last);
00229     number = -number;
00230   }
00231 
00232   num = number;
00233 
00234   tot = 0;
00235   for (i = 0; i < 20; i++) {
00236     quot = 0;
00237     if (num >= divisor) {
00238       quot = num / divisor;
00239       num = num % divisor;
00240     }
00241     if (tot |= quot || i == 19) {
00242       *buff++ = '0' + quot;
00243     }
00244 
00245     divisor /= 10;
00246   }
00247 
00248   *buff = '\0';
00249 
00250   return buff;
00251 }
00252 
00253 static char *FormatHexNumber(char *buff, int64 number, const char *last)
00254 {
00255   return buff + seprintf(buff, last, "0x%x", (uint32)number);
00256 }
00257 
00265 static char *FormatBytes(char *buff, int64 number, const char *last)
00266 {
00267   assert(number >= 0);
00268 
00269   /*                         0    2^10   2^20   2^30   2^40   2^50   2^60 */
00270   const char *siUnits[] = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" };
00271   uint id = 1;
00272   while (number >= 1024 * 1024) {
00273     number /= 1024;
00274     id++;
00275   }
00276 
00277   if (number < 1024) {
00278     id = 0;
00279     buff += seprintf(buff, last, "%i", (int)number);
00280   } else if (number < 1024 * 10) {
00281     buff += seprintf(buff, last, "%i.%02i", (int)number / 1024, (int)(number % 1024) * 100 / 1024);
00282   } else if (number < 1024 * 100) {
00283     buff += seprintf(buff, last, "%i.%01i", (int)number / 1024, (int)(number % 1024) * 10 / 1024);
00284   } else {
00285     assert(number < 1024 * 1024);
00286     buff += seprintf(buff, last, "%i", (int)number / 1024);
00287   }
00288 
00289   assert(id < lengthof(siUnits));
00290   buff += seprintf(buff, last, " %s", siUnits[id]);
00291 
00292   return buff;
00293 }
00294 
00295 static char *FormatYmdString(char *buff, Date date, const char *last)
00296 {
00297   YearMonthDay ymd;
00298   ConvertDateToYMD(date, &ymd);
00299 
00300   int64 args[3] = { ymd.day + STR_01AC_1ST - 1, STR_0162_JAN + ymd.month, ymd.year };
00301   return FormatString(buff, GetStringPtr(STR_DATE_LONG), args, 0, last);
00302 }
00303 
00304 static char *FormatMonthAndYear(char *buff, Date date, const char *last)
00305 {
00306   YearMonthDay ymd;
00307   ConvertDateToYMD(date, &ymd);
00308 
00309   int64 args[2] = { STR_MONTH_JAN + ymd.month, ymd.year };
00310   return FormatString(buff, GetStringPtr(STR_DATE_SHORT), args, 0, last);
00311 }
00312 
00313 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
00314 {
00315   YearMonthDay ymd;
00316   ConvertDateToYMD(date, &ymd);
00317 
00318   char day[3];
00319   char month[3];
00320   /* We want to zero-pad the days and months */
00321   snprintf(day,   lengthof(day),   "%02i", ymd.day);
00322   snprintf(month, lengthof(month), "%02i", ymd.month + 1);
00323 
00324   int64 args[3] = { (int64)(size_t)day, (int64)(size_t)month, ymd.year };
00325   return FormatString(buff, GetStringPtr(str), args, 0, last);
00326 }
00327 
00328 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
00329 {
00330   /* We are going to make number absolute for printing, so
00331    * keep this piece of data as we need it later on */
00332   bool negative = number < 0;
00333   const char *multiplier = "";
00334   char buf[40];
00335   char *p;
00336   int j;
00337 
00338   number *= spec->rate;
00339 
00340   /* convert from negative */
00341   if (number < 0) {
00342     if (buff + Utf8CharLen(SCC_RED) > last) return buff;
00343     buff += Utf8Encode(buff, SCC_RED);
00344     buff = strecpy(buff, "-", last);
00345     number = -number;
00346   }
00347 
00348   /* Add prefix part, folowing symbol_pos specification.
00349    * Here, it can can be either 0 (prefix) or 2 (both prefix anf suffix).
00350    * The only remaining value is 1 (suffix), so everything that is not 1 */
00351   if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
00352 
00353   /* for huge numbers, compact the number into k or M */
00354   if (compact) {
00355     if (number >= 1000000000) {
00356       number = (number + 500000) / 1000000;
00357       multiplier = "M";
00358     } else if (number >= 1000000) {
00359       number = (number + 500) / 1000;
00360       multiplier = "k";
00361     }
00362   }
00363 
00364   /* convert to ascii number and add commas */
00365   p = endof(buf);
00366   *--p = '\0';
00367   j = 4;
00368   do {
00369     if (--j == 0) {
00370       *--p = spec->separator;
00371       j = 3;
00372     }
00373     *--p = '0' + (char)(number % 10);
00374   } while ((number /= 10) != 0);
00375   buff = strecpy(buff, p, last);
00376 
00377   buff = strecpy(buff, multiplier, last);
00378 
00379   /* Add suffix part, folowing symbol_pos specification.
00380    * Here, it can can be either 1 (suffix) or 2 (both prefix anf suffix).
00381    * The only remaining value is 1 (prefix), so everything that is not 0 */
00382   if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
00383 
00384   if (negative) {
00385     if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
00386     buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
00387     *buff = '\0';
00388   }
00389 
00390   return buff;
00391 }
00392 
00393 static int DeterminePluralForm(int64 count)
00394 {
00395   /* The absolute value determines plurality */
00396   uint64 n = abs(count);
00397 
00398   switch (_langpack->plural_form) {
00399     default:
00400       NOT_REACHED();
00401 
00402     /* Two forms, singular used for one only
00403      * Used in:
00404      *   Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
00405      *   Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
00406     case 0:
00407       return n != 1;
00408 
00409     /* Only one form
00410      * Used in:
00411      *   Hungarian, Japanese, Korean, Turkish */
00412     case 1:
00413       return 0;
00414 
00415     /* Two forms, singular used for zero and one
00416      * Used in:
00417      *   French, Brazilian Portuguese */
00418     case 2:
00419       return n > 1;
00420 
00421     /* Three forms, special case for zero
00422      * Used in:
00423      *   Latvian */
00424     case 3:
00425       return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
00426 
00427     /* Three forms, special case for one and two
00428      * Used in:
00429      *   Gaelige (Irish) */
00430     case 4:
00431       return n == 1 ? 0 : n == 2 ? 1 : 2;
00432 
00433     /* Three forms, special case for numbers ending in 1[2-9]
00434      * Used in:
00435      *   Lithuanian */
00436     case 5:
00437       return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00438 
00439     /* Three forms, special cases for numbers ending in 1 and 2, 3, 4, except those ending in 1[1-4]
00440      * Used in:
00441      *   Croatian, Czech, Russian, Slovak, Ukrainian */
00442     case 6:
00443       return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00444 
00445     /* Three forms, special case for one and some numbers ending in 2, 3, or 4
00446      * Used in:
00447      *   Polish */
00448     case 7:
00449       return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00450 
00451     /* Four forms, special case for one and all numbers ending in 02, 03, or 04
00452      * Used in:
00453      *   Slovenian */
00454     case 8:
00455       return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
00456 
00457     /* Two forms; singular used for everything ending in 1 but not in 11.
00458      * Used in:
00459      *   Icelandic */
00460     case 9:
00461       return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
00462   }
00463 }
00464 
00465 static const char *ParseStringChoice(const char *b, uint form, char *dst, int *dstlen)
00466 {
00467   //<NUM> {Length of each string} {each string}
00468   uint n = (byte)*b++;
00469   uint pos, i, mylen = 0, mypos = 0;
00470 
00471   for (i = pos = 0; i != n; i++) {
00472     uint len = (byte)*b++;
00473     if (i == form) {
00474       mypos = pos;
00475       mylen = len;
00476     }
00477     pos += len;
00478   }
00479   *dstlen = mylen;
00480   memcpy(dst, b + mypos, mylen);
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, const int64 *argv, uint casei, const char *last)
00550 {
00551   WChar b;
00552   const 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, (int64*)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 = GetCargo(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 = GetIndustry(GetInt32(&argv));
00698         int64 args[2];
00699 
00700         /* industry not valid anymore? */
00701         if (!i->IsValid()) break;
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_INDUSTRY_FORMAT), 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 len;
00723         int gender = 0;
00724         if (s != NULL) {
00725           wchar_t 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, &len);
00737         buff += len;
00738         break;
00739       }
00740 
00741       case SCC_DATE_TINY: { // {DATE_TINY}
00742         buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_DATE_TINY, last);
00743         break;
00744       }
00745 
00746       case SCC_DATE_ISO: { // {DATE_ISO}
00747         buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_DATE_ISO, last);
00748         break;
00749       }
00750 
00751       case SCC_CARGO: { // {CARGO}
00752         /* Layout now is:
00753          *   8bit   - cargo type
00754          *   16-bit - cargo count */
00755         CargoID cargo = GetInt32(&argv);
00756         StringID cargo_str = (cargo == CT_INVALID) ? STR_8838_N_A : GetCargo(cargo)->quantifier;
00757         buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00758         break;
00759       }
00760 
00761       case SCC_POWER: { // {POWER}
00762         int64 args[1];
00763         assert(_settings_game.locale.units < lengthof(units));
00764         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].p_m >> units[_settings_game.locale.units].p_s;
00765         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].power), args, modifier >> 24, last);
00766         modifier = 0;
00767         break;
00768       }
00769 
00770       case SCC_VOLUME_SHORT: { // {VOLUME_S}
00771         int64 args[1];
00772         assert(_settings_game.locale.units < lengthof(units));
00773         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00774         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_volume), args, modifier >> 24, last);
00775         modifier = 0;
00776         break;
00777       }
00778 
00779       case SCC_WEIGHT: { // {WEIGHT}
00780         int64 args[1];
00781         assert(_settings_game.locale.units < lengthof(units));
00782         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00783         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00784         modifier = 0;
00785         break;
00786       }
00787 
00788       case SCC_WEIGHT_SHORT: { // {WEIGHT_S}
00789         int64 args[1];
00790         assert(_settings_game.locale.units < lengthof(units));
00791         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00792         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_weight), args, modifier >> 24, last);
00793         modifier = 0;
00794         break;
00795       }
00796 
00797       case SCC_FORCE: { // {FORCE}
00798         int64 args[1];
00799         assert(_settings_game.locale.units < lengthof(units));
00800         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].f_m >> units[_settings_game.locale.units].f_s;
00801         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].force), args, modifier >> 24, last);
00802         modifier = 0;
00803         break;
00804       }
00805 
00806       case SCC_SKIP: // {SKIP}
00807         argv++;
00808         break;
00809 
00810       /* This sets up the gender for the string.
00811        * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
00812       case SCC_GENDER_INDEX: // {GENDER 0}
00813         str++;
00814         break;
00815 
00816       case SCC_STRING: {// {STRING}
00817         uint str = modifier + GetInt32(&argv);
00818         /* WARNING. It's prohibited for the included string to consume any arguments.
00819          * For included strings that consume argument, you should use STRING1, STRING2 etc.
00820          * To debug stuff you can set argv to NULL and it will tell you */
00821         buff = GetStringWithArgs(buff, str, argv, last);
00822         modifier = 0;
00823         break;
00824       }
00825 
00826       case SCC_COMMA: // {COMMA}
00827         buff = FormatCommaNumber(buff, GetInt64(&argv), last);
00828         break;
00829 
00830       case SCC_ARG_INDEX: // Move argument pointer
00831         argv = argv_orig + (byte)*str++;
00832         break;
00833 
00834       case SCC_PLURAL_LIST: { // {P}
00835         int64 v = argv_orig[(byte)*str++]; // contains the number that determines plural
00836         int len;
00837         str = ParseStringChoice(str, DeterminePluralForm(v), buff, &len);
00838         buff += len;
00839         break;
00840       }
00841 
00842       case SCC_NUM: // {NUM}
00843         buff = FormatNoCommaNumber(buff, GetInt64(&argv), last);
00844         break;
00845 
00846       case SCC_HEX: // {HEX}
00847         buff = FormatHexNumber(buff, GetInt64(&argv), last);
00848         break;
00849 
00850       case SCC_BYTES: // {BYTES}
00851         buff = FormatBytes(buff, GetInt64(&argv), last);
00852         break;
00853 
00854       case SCC_CURRENCY: // {CURRENCY}
00855         buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
00856         break;
00857 
00858       case SCC_WAYPOINT_NAME: { // {WAYPOINT}
00859         Waypoint *wp = GetWaypoint(GetInt32(&argv));
00860 
00861         assert(wp->IsValid());
00862 
00863         if (wp->name != NULL) {
00864           buff = strecpy(buff, wp->name, last);
00865         } else {
00866           int64 temp[2];
00867           temp[0] = wp->town_index;
00868           temp[1] = wp->town_cn + 1;
00869           StringID str = wp->town_cn == 0 ? STR_WAYPOINTNAME_CITY : STR_WAYPOINTNAME_CITY_SERIAL;
00870 
00871           buff = GetStringWithArgs(buff, str, temp, last);
00872         }
00873         break;
00874       }
00875 
00876       case SCC_STATION_NAME: { // {STATION}
00877         StationID sid = GetInt32(&argv);
00878 
00879         if (!IsValidStationID(sid)) {
00880           /* The station doesn't exist anymore. The only place where we might
00881            * be "drawing" an invalid station is in the case of cargo that is
00882            * in transit. */
00883           buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, NULL, last);
00884           break;
00885         }
00886 
00887         const Station *st = GetStation(sid);
00888         if (st->name != NULL) {
00889           buff = strecpy(buff, st->name, last);
00890         } else {
00891           StringID str = st->string_id;
00892           if (st->indtype != IT_INVALID) {
00893             /* Special case where the industry provides the name for the station */
00894             const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
00895 
00896             /* Industry GRFs can change which might remove the station name and
00897              * thus cause very strange things. Here we check for that before we
00898              * actually set the station name. */
00899             if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
00900               str = indsp->station_name;
00901             }
00902           }
00903 
00904           int64 temp[3];
00905           temp[0] = STR_TOWN;
00906           temp[1] = st->town->index;
00907           temp[2] = st->index;
00908           buff = GetStringWithArgs(buff, str, temp, last);
00909         }
00910         break;
00911       }
00912 
00913       case SCC_TOWN_NAME: { // {TOWN}
00914         const Town *t = GetTown(GetInt32(&argv));
00915         int64 temp[1];
00916 
00917         assert(t->IsValid());
00918 
00919         temp[0] = t->townnameparts;
00920         uint32 grfid = t->townnamegrfid;
00921 
00922         if (t->name != NULL) {
00923           buff = strecpy(buff, t->name, last);
00924         } else if (grfid == 0) {
00925           /* Original town name */
00926           buff = GetStringWithArgs(buff, t->townnametype, temp, last);
00927         } else {
00928           /* Newgrf town name */
00929           if (GetGRFTownName(grfid) != NULL) {
00930             /* The grf is loaded */
00931             buff = GRFTownNameGenerate(buff, t->townnamegrfid, t->townnametype, t->townnameparts, last);
00932           } else {
00933             /* Fallback to english original */
00934             buff = GetStringWithArgs(buff, SPECSTR_TOWNNAME_ENGLISH, temp, last);
00935           }
00936         }
00937         break;
00938       }
00939 
00940       case SCC_GROUP_NAME: { // {GROUP}
00941         const Group *g = GetGroup(GetInt32(&argv));
00942 
00943         assert(g->IsValid());
00944 
00945         if (g->name != NULL) {
00946           buff = strecpy(buff, g->name, last);
00947         } else {
00948           int64 args[1];
00949 
00950           args[0] = g->index;
00951           buff = GetStringWithArgs(buff, STR_GROUP_NAME_FORMAT, args, last);
00952         }
00953         break;
00954       }
00955 
00956       case SCC_ENGINE_NAME: { // {ENGINE}
00957         EngineID engine = (EngineID)GetInt32(&argv);
00958         const Engine *e = GetEngine(engine);
00959 
00960         if (e->name != NULL) {
00961           buff = strecpy(buff, e->name, last);
00962         } else {
00963           buff = GetStringWithArgs(buff, e->info.string_id, NULL, last);
00964         }
00965         break;
00966       }
00967 
00968       case SCC_VEHICLE_NAME: { // {VEHICLE}
00969         const Vehicle *v = GetVehicle(GetInt32(&argv));
00970 
00971         if (v->name != NULL) {
00972           buff = strecpy(buff, v->name, last);
00973         } else {
00974           int64 args[1];
00975           args[0] = v->unitnumber;
00976 
00977           StringID str;
00978           switch (v->type) {
00979             default: NOT_REACHED();
00980             case VEH_TRAIN:    str = STR_SV_TRAIN_NAME; break;
00981             case VEH_ROAD:     str = STR_SV_ROADVEH_NAME; break;
00982             case VEH_SHIP:     str = STR_SV_SHIP_NAME; break;
00983             case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
00984           }
00985 
00986           buff = GetStringWithArgs(buff, str, args, last);
00987         }
00988         break;
00989       }
00990 
00991       case SCC_SIGN_NAME: { // {SIGN}
00992         const Sign *si = GetSign(GetInt32(&argv));
00993         if (si->name != NULL) {
00994           buff = strecpy(buff, si->name, last);
00995         } else {
00996           buff = GetStringWithArgs(buff, STR_280A_SIGN, NULL, last);
00997         }
00998         break;
00999       }
01000 
01001       case SCC_COMPANY_NAME: { // {COMPANY}
01002         const Company *c = GetCompany((CompanyID)GetInt32(&argv));
01003 
01004         if (c->name != NULL) {
01005           buff = strecpy(buff, c->name, last);
01006         } else {
01007           int64 args[1];
01008           args[0] = c->name_2;
01009           buff = GetStringWithArgs(buff, c->name_1, args, last);
01010         }
01011         break;
01012       }
01013 
01014       case SCC_COMPANY_NUM: { // {COMPANYNUM}
01015         CompanyID company = (CompanyID)GetInt32(&argv);
01016 
01017         /* Nothing is added for AI or inactive companies */
01018         if (IsValidCompanyID(company) && IsHumanCompany(company)) {
01019           int64 args[1];
01020           args[0] = company + 1;
01021           buff = GetStringWithArgs(buff, STR_7002_COMPANY, args, last);
01022         }
01023         break;
01024       }
01025 
01026       case SCC_PRESIDENT_NAME: { // {PRESIDENTNAME}
01027         const Company *c = GetCompany((CompanyID)GetInt32(&argv));
01028 
01029         if (c->president_name != NULL) {
01030           buff = strecpy(buff, c->president_name, last);
01031         } else {
01032           int64 args[1];
01033           args[0] = c->president_name_2;
01034           buff = GetStringWithArgs(buff, c->president_name_1, args, last);
01035         }
01036         break;
01037       }
01038 
01039       case SCC_SETCASE: { // {SETCASE}
01040         /* This is a pseudo command, it's outputted when someone does {STRING.ack}
01041          * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
01042         modifier = (byte)*str++ << 24;
01043         break;
01044       }
01045 
01046       case SCC_SWITCH_CASE: { // {Used to implement case switching}
01047         /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
01048          * Each LEN is printed using 2 bytes in big endian order. */
01049         uint num = (byte)*str++;
01050         while (num) {
01051           if ((byte)str[0] == casei) {
01052             /* Found the case, adjust str pointer and continue */
01053             str += 3;
01054             break;
01055           }
01056           /* Otherwise skip to the next case */
01057           str += 3 + (str[1] << 8) + str[2];
01058           num--;
01059         }
01060         break;
01061       }
01062 
01063       default:
01064         if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01065         break;
01066     }
01067   }
01068   *buff = '\0';
01069   return buff;
01070 }
01071 
01072 
01073 static char *StationGetSpecialString(char *buff, int x, const char *last)
01074 {
01075   if ((x & FACIL_TRAIN)      && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01076   if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01077   if ((x & FACIL_BUS_STOP)   && (buff + Utf8CharLen(SCC_BUS)   < last)) buff += Utf8Encode(buff, SCC_BUS);
01078   if ((x & FACIL_AIRPORT)    && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01079   if ((x & FACIL_DOCK)       && (buff + Utf8CharLen(SCC_SHIP)  < last)) buff += Utf8Encode(buff, SCC_SHIP);
01080   *buff = '\0';
01081   return buff;
01082 }
01083 
01084 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01085 {
01086   char name[512];
01087 
01088   _town_name_generators[ind](name, seed, lastof(name));
01089   return strecpy(buff, name, last);
01090 }
01091 
01092 static const char * const _silly_company_names[] = {
01093   "Bloggs Brothers",
01094   "Tiny Transport Ltd.",
01095   "Express Travel",
01096   "Comfy-Coach & Co.",
01097   "Crush & Bump Ltd.",
01098   "Broken & Late Ltd.",
01099   "Sam Speedy & Son",
01100   "Supersonic Travel",
01101   "Mike's Motors",
01102   "Lightning International",
01103   "Pannik & Loozit Ltd.",
01104   "Inter-City Transport",
01105   "Getout & Pushit Ltd."
01106 };
01107 
01108 static const char * const _surname_list[] = {
01109   "Adams",
01110   "Allan",
01111   "Baker",
01112   "Bigwig",
01113   "Black",
01114   "Bloggs",
01115   "Brown",
01116   "Campbell",
01117   "Gordon",
01118   "Hamilton",
01119   "Hawthorn",
01120   "Higgins",
01121   "Green",
01122   "Gribble",
01123   "Jones",
01124   "McAlpine",
01125   "MacDonald",
01126   "McIntosh",
01127   "Muir",
01128   "Murphy",
01129   "Nelson",
01130   "O'Donnell",
01131   "Parker",
01132   "Phillips",
01133   "Pilkington",
01134   "Quigley",
01135   "Sharkey",
01136   "Thomson",
01137   "Watkins"
01138 };
01139 
01140 static const char * const _silly_surname_list[] = {
01141   "Grumpy",
01142   "Dozy",
01143   "Speedy",
01144   "Nosey",
01145   "Dribble",
01146   "Mushroom",
01147   "Cabbage",
01148   "Sniffle",
01149   "Fishy",
01150   "Swindle",
01151   "Sneaky",
01152   "Nutkins"
01153 };
01154 
01155 static const char _initial_name_letters[] = {
01156   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01157   'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01158 };
01159 
01160 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01161 {
01162   const char * const *base;
01163   uint num;
01164 
01165   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01166     base = _silly_surname_list;
01167     num  = lengthof(_silly_surname_list);
01168   } else {
01169     base = _surname_list;
01170     num  = lengthof(_surname_list);
01171   }
01172 
01173   buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01174   buff = strecpy(buff, " & Co.", last);
01175 
01176   return buff;
01177 }
01178 
01179 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01180 {
01181   char initial[] = "?. ";
01182   const char * const *base;
01183   uint num;
01184   uint i;
01185 
01186   initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01187   buff = strecpy(buff, initial, last);
01188 
01189   i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01190   if (i < sizeof(_initial_name_letters)) {
01191     initial[0] = _initial_name_letters[i];
01192     buff = strecpy(buff, initial, last);
01193   }
01194 
01195   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01196     base = _silly_surname_list;
01197     num  = lengthof(_silly_surname_list);
01198   } else {
01199     base = _surname_list;
01200     num  = lengthof(_surname_list);
01201   }
01202 
01203   buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01204 
01205   return buff;
01206 }
01207 
01208 static char *GetSpecialNameString(char *buff, int ind, const int64 *argv, const char *last)
01209 {
01210   switch (ind) {
01211     case 1: // not used
01212       return strecpy(buff, _silly_company_names[GetInt32(&argv) & 0xFFFF], last);
01213 
01214     case 2: // used for Foobar & Co company names
01215       return GenAndCoName(buff, GetInt32(&argv), last);
01216 
01217     case 3: // President name
01218       return GenPresidentName(buff, GetInt32(&argv), last);
01219 
01220     case 4: // song names
01221       return strecpy(buff, _origin_songs_specs[GetInt32(&argv) - 1].song_name, last);
01222   }
01223 
01224   /* town name? */
01225   if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01226     buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv), last);
01227     return strecpy(buff, " Transport", last);
01228   }
01229 
01230   /* language name? */
01231   if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01232     int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01233     return strecpy(buff,
01234       i == _dynlang.curr ? _langpack->own_name : _dynlang.ent[i].name, last);
01235   }
01236 
01237   /* resolution size? */
01238   if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01239     int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01240     buff += seprintf(
01241       buff, last, "%dx%d", _resolutions[i].width, _resolutions[i].height
01242     );
01243     return buff;
01244   }
01245 
01246   /* screenshot format name? */
01247   if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01248     int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01249     return strecpy(buff, GetScreenshotFormatDesc(i), last);
01250   }
01251 
01252   assert(0);
01253   return NULL;
01254 }
01255 
01256 #ifdef ENABLE_NETWORK
01257 extern void SortNetworkLanguages();
01258 #else /* ENABLE_NETWORK */
01259 static inline void SortNetworkLanguages() {}
01260 #endif /* ENABLE_NETWORK */
01261 
01262 bool ReadLanguagePack(int lang_index)
01263 {
01264   int tot_count, i;
01265   size_t len;
01266   char **langpack_offs;
01267   char *s;
01268 
01269   LanguagePack *lang_pack = (LanguagePack*)ReadFileToMem(_dynlang.ent[lang_index].file, &len, 200000);
01270 
01271   if (lang_pack == NULL) return false;
01272   if (len < sizeof(LanguagePack) ||
01273       lang_pack->ident != TO_LE32(LANGUAGE_PACK_IDENT) ||
01274       lang_pack->version != TO_LE32(LANGUAGE_PACK_VERSION)) {
01275     free(lang_pack);
01276     return false;
01277   }
01278 
01279 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01280   for (i = 0; i != 32; i++) {
01281     lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01282   }
01283 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
01284 
01285   tot_count = 0;
01286   for (i = 0; i != 32; i++) {
01287     uint num = lang_pack->offsets[i];
01288     _langtab_start[i] = tot_count;
01289     _langtab_num[i] = num;
01290     tot_count += num;
01291   }
01292 
01293   /* Allocate offsets */
01294   langpack_offs = MallocT<char*>(tot_count);
01295 
01296   /* Fill offsets */
01297   s = lang_pack->data;
01298   for (i = 0; i != tot_count; i++) {
01299     len = (byte)*s;
01300     *s++ = '\0'; // zero terminate the string before.
01301     if (len >= 0xC0) len = ((len & 0x3F) << 8) + (byte)*s++;
01302     langpack_offs[i] = s;
01303     s += len;
01304   }
01305 
01306   free(_langpack);
01307   _langpack = lang_pack;
01308 
01309   free(_langpack_offs);
01310   _langpack_offs = langpack_offs;
01311 
01312   const char *c_file = strrchr(_dynlang.ent[lang_index].file, PATHSEPCHAR) + 1;
01313   strecpy(_dynlang.curr_file, c_file, lastof(_dynlang.curr_file));
01314 
01315   _dynlang.curr = lang_index;
01316   _dynlang.text_dir = (TextDirection)lang_pack->text_dir;
01317   SetCurrentGrfLangID(_langpack->isocode);
01318   SortNetworkLanguages();
01319   return true;
01320 }
01321 
01322 /* Win32 implementation in win32.cpp. */
01323 /* OS X implementation in os/macosx/macos.mm. */
01324 #if !(defined(WIN32) || defined(__APPLE__))
01325 
01331 const char *GetCurrentLocale(const char *param)
01332 {
01333   const char *env;
01334 
01335   env = getenv("LANGUAGE");
01336   if (env != NULL) return env;
01337 
01338   env = getenv("LC_ALL");
01339   if (env != NULL) return env;
01340 
01341   if (param != NULL) {
01342     env = getenv(param);
01343     if (env != NULL) return env;
01344   }
01345 
01346   return getenv("LANG");
01347 }
01348 #else
01349 const char *GetCurrentLocale(const char *param);
01350 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
01351 
01352 int CDECL StringIDSorter(const void *a, const void *b)
01353 {
01354   const StringID va = *(const StringID*)a;
01355   const StringID vb = *(const StringID*)b;
01356   char stra[512];
01357   char strb[512];
01358   GetString(stra, va, lastof(stra));
01359   GetString(strb, vb, lastof(strb));
01360 
01361   return strcmp(stra, strb);
01362 }
01363 
01371 static bool UniqueLanguageFile(const Language *langs, uint max, const char *language)
01372 {
01373   for (uint i = 0; i < max; i++) {
01374     const char *f_name = strrchr(langs[i].file, PATHSEPCHAR) + 1;
01375     if (strcmp(f_name, language) == 0) return false; // duplicates
01376   }
01377 
01378   return true;
01379 }
01380 
01387 static bool GetLanguageFileHeader(const char *file, LanguagePack *hdr)
01388 {
01389   FILE *f = fopen(file, "rb");
01390   if (f == NULL) return false;
01391 
01392   size_t read = fread(hdr, sizeof(*hdr), 1, f);
01393   fclose(f);
01394 
01395   bool ret = read == 1 &&
01396       hdr->ident == TO_LE32(LANGUAGE_PACK_IDENT) &&
01397       hdr->version == TO_LE32(LANGUAGE_PACK_VERSION);
01398 
01399   /* Convert endianness for the windows language ID */
01400   if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01401   return ret;
01402 }
01403 
01412 static int GetLanguageList(Language *langs, int start, int max, const char *path)
01413 {
01414   int i = start;
01415 
01416   DIR *dir = ttd_opendir(path);
01417   if (dir != NULL) {
01418     struct dirent *dirent;
01419     while ((dirent = readdir(dir)) != NULL && i < max) {
01420       const char *d_name    = FS2OTTD(dirent->d_name);
01421       const char *extension = strrchr(d_name, '.');
01422 
01423       /* Not a language file */
01424       if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01425 
01426       /* Filter any duplicate language-files, first-come first-serve */
01427       if (!UniqueLanguageFile(langs, i, d_name)) continue;
01428 
01429       langs[i].file = str_fmt("%s%s", path, d_name);
01430 
01431       /* Check whether the file is of the correct version */
01432       LanguagePack hdr;
01433       if (!GetLanguageFileHeader(langs[i].file, &hdr)) {
01434         free(langs[i].file);
01435         continue;
01436       }
01437 
01438       i++;
01439     }
01440     closedir(dir);
01441   }
01442   return i - start;
01443 }
01444 
01449 void InitializeLanguagePacks()
01450 {
01451   Searchpath sp;
01452   Language files[MAX_LANG];
01453   uint language_count = 0;
01454 
01455   FOR_ALL_SEARCHPATHS(sp) {
01456     char path[MAX_PATH];
01457     FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01458     language_count += GetLanguageList(files, language_count, lengthof(files), path);
01459   }
01460   if (language_count == 0) usererror("No available language packs (invalid versions?)");
01461 
01462   /* Acquire the locale of the current system */
01463   const char *lang = GetCurrentLocale("LC_MESSAGES");
01464   if (lang == NULL) lang = "en_GB";
01465 
01466   int chosen_language   = -1; 
01467   int language_fallback = -1; 
01468   int en_GB_fallback    =  0; 
01469 
01470   DynamicLanguages *dl = &_dynlang;
01471   dl->num = 0;
01472   /* Fill the dynamic languages structures */
01473   for (uint i = 0; i < language_count; i++) {
01474     /* File read the language header */
01475     LanguagePack hdr;
01476     if (!GetLanguageFileHeader(files[i].file, &hdr)) continue;
01477 
01478     dl->ent[dl->num].file = files[i].file;
01479     dl->ent[dl->num].name = strdup(hdr.name);
01480 
01481     /* We are trying to find a default language. The priority is by
01482      * configuration file, local environment and last, if nothing found,
01483      * english. If def equals -1, we have not picked a default language */
01484     const char *lang_file = strrchr(dl->ent[dl->num].file, PATHSEPCHAR) + 1;
01485     if (strcmp(lang_file, dl->curr_file) == 0) chosen_language = dl->num;
01486 
01487     if (chosen_language == -1) {
01488       if (strcmp (hdr.isocode, "en_GB") == 0) en_GB_fallback    = dl->num;
01489       if (strncmp(hdr.isocode, lang, 5) == 0) chosen_language   = dl->num;
01490       if (strncmp(hdr.isocode, lang, 2) == 0) language_fallback = dl->num;
01491     }
01492 
01493     dl->num++;
01494   }
01495 
01496   if (dl->num == 0) usererror("Invalid version of language packs");
01497 
01498   /* We haven't found the language in the config nor the one in the locale.
01499    * Now we set it to one of the fallback languages */
01500   if (chosen_language == -1) {
01501     chosen_language = (language_fallback != -1) ? language_fallback : en_GB_fallback;
01502   }
01503 
01504   if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", dl->ent[chosen_language].file);
01505 }
01506 
01517 void CheckForMissingGlyphsInLoadedLanguagePack()
01518 {
01519 #ifdef WITH_FREETYPE
01520   /* Reset to the original state; switching languages might cause us to
01521    * automatically choose another font. This resets that choice. */
01522   UninitFreeType();
01523   InitFreeType();
01524   bool retry = false;
01525 #endif
01526 
01527   for (;;) {
01528     const Sprite *question_mark = GetGlyph(FS_NORMAL, '?');
01529 
01530     for (uint i = 0; i != 32; i++) {
01531       for (uint j = 0; j < _langtab_num[i]; j++) {
01532         const char *string = _langpack_offs[_langtab_start[i] + j];
01533         WChar c;
01534         while ((c = Utf8Consume(&string)) != '\0') {
01535           if (c == SCC_SETX) {
01536             /*
01537              * SetX is, together with SetXY as special character that
01538              * uses the next (two) characters as data points. We have
01539              * to skip those, otherwise the UTF8 reading will go
01540              * haywire.
01541              */
01542             string++;
01543           } else if (c == SCC_SETXY) {
01544             string += 2;
01545           } else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) {
01546 #ifdef WITH_FREETYPE
01547             if (!retry) {
01548               /* We found an unprintable character... lets try whether we can
01549                * find a fallback font that can print the characters in the
01550                * current language. */
01551               retry = true;
01552 
01553               FreeTypeSettings backup;
01554               memcpy(&backup, &_freetype, sizeof(backup));
01555 
01556               bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid);
01557               if (success) {
01558                 UninitFreeType();
01559                 InitFreeType();
01560               }
01561 
01562               memcpy(&_freetype, &backup, sizeof(backup));
01563 
01564               if (success) continue;
01565             } else {
01566               /* Our fallback font does miss characters too, so keep the
01567                * user chosen font as that is more likely to be any good than
01568                * the wild guess we made */
01569               UninitFreeType();
01570               InitFreeType();
01571             }
01572 #endif
01573             /*
01574              * The character is printable, but not in the normal font.
01575              * This is the case we were testing for. In this case we
01576              * have to show the error. As we do not want the string to
01577              * be translated by the translators, we 'force' it into the
01578              * binary and 'load' it via a BindCString. To do this
01579              * properly we have to set the colour of the string,
01580              * otherwise we end up with a lot of artefacts. The colour
01581              * 'character' might change in the future, so for safety
01582              * we just Utf8 Encode it into the string, which takes
01583              * exactly three characters, so it replaces the "XXX" with
01584              * the colour marker.
01585              */
01586             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.");
01587             Utf8Encode(err_str, SCC_YELLOW);
01588             SetDParamStr(0, err_str);
01589             ShowErrorMessage(INVALID_STRING_ID, STR_JUST_RAW_STRING, 0, 0);
01590             return;
01591           }
01592         }
01593       }
01594     }
01595     break;
01596   }
01597 
01598 #if !defined(WITH_ICU)
01599   /*
01600    * For right-to-left languages we need the ICU library. If
01601    * we do not have support for that library we warn the user
01602    * about it with a message. As we do not want the string to
01603    * be translated by the translators, we 'force' it into the
01604    * binary and 'load' it via a BindCString. To do this
01605    * properly we have to set the colour of the string,
01606    * otherwise we end up with a lot of artefacts. The colour
01607    * 'character' might change in the future, so for safety
01608    * we just Utf8 Encode it into the string, which takes
01609    * exactly three characters, so it replaces the "XXX" with
01610    * the colour marker.
01611    */
01612   if (_dynlang.text_dir != TD_LTR) {
01613     static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01614     Utf8Encode(err_str, SCC_YELLOW);
01615     SetDParamStr(0, err_str);
01616     ShowErrorMessage(INVALID_STRING_ID, STR_JUST_RAW_STRING, 0, 0);
01617   }
01618 #endif
01619 }

Generated on Mon Feb 16 23:12:11 2009 for openttd by  doxygen 1.5.6