strings.cpp

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

Generated on Mon Sep 22 20:34:19 2008 for openttd by  doxygen 1.5.6