strings.cpp

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

Generated on Mon Mar 23 00:25:23 2009 for OpenTTD by  doxygen 1.5.6