strings.cpp

Go to the documentation of this file.
00001 /* $Id: strings.cpp 16830 2009-07-14 20:38:45Z 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, 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     /* Three forms, special cases for one and 2, 3, or 4
00465      * Used in:
00466      *   Czech */
00467     case 10:
00468       return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
00469 
00470     /* Two forms, special 'hack' for Korean; singular for numbers ending
00471      *   in a consonant and plural for numbers ending in a vowel.
00472      * Korean doesn't have the concept of plural, but depending on how a
00473      * number is pronounced it needs another version of a particle.
00474      * As such the plural system is misused to give this distinction.
00475      */
00476     case 11:
00477       switch (n % 10) {
00478         case 0: // yeong
00479         case 1: // il
00480         case 3: // sam
00481         case 6: // yuk
00482         case 7: // chil
00483         case 8: // pal
00484           return 0;
00485 
00486         case 2: // i
00487         case 4: // sa
00488         case 5: // o
00489         case 9: // gu
00490           return 1;
00491 
00492         default:
00493           NOT_REACHED();
00494       }
00495   }
00496 }
00497 
00498 static const char *ParseStringChoice(const char *b, uint form, char *dst, int *dstlen)
00499 {
00500   /* <NUM> {Length of each string} {each string} */
00501   uint n = (byte)*b++;
00502   uint pos, i, mylen = 0, mypos = 0;
00503 
00504   for (i = pos = 0; i != n; i++) {
00505     uint len = (byte)*b++;
00506     if (i == form) {
00507       mypos = pos;
00508       mylen = len;
00509     }
00510     pos += len;
00511   }
00512   *dstlen = mylen;
00513   memcpy(dst, b + mypos, mylen);
00514   return b + pos;
00515 }
00516 
00517 struct Units {
00518   int s_m;           
00519   int s_s;           
00520   StringID velocity; 
00521   int p_m;           
00522   int p_s;           
00523   StringID power;    
00524   int w_m;           
00525   int w_s;           
00526   StringID s_weight; 
00527   StringID l_weight; 
00528   int v_m;           
00529   int v_s;           
00530   StringID s_volume; 
00531   StringID l_volume; 
00532   int f_m;           
00533   int f_s;           
00534   StringID force;    
00535 };
00536 
00537 /* Unit conversions */
00538 static const Units units[] = {
00539   { // Imperial (Original, mph, hp, metric ton, litre, kN)
00540        1,  0, STR_UNITS_VELOCITY_IMPERIAL,
00541        1,  0, STR_UNITS_POWER_IMPERIAL,
00542        1,  0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00543     1000,  0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00544        1,  0, STR_UNITS_FORCE_SI,
00545   },
00546   { // Metric (km/h, hp, metric ton, litre, kN)
00547      103,  6, STR_UNITS_VELOCITY_METRIC,
00548        1,  0, STR_UNITS_POWER_METRIC,
00549        1,  0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00550     1000,  0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00551        1,  0, STR_UNITS_FORCE_SI,
00552   },
00553   { // SI (m/s, kilowatt, kilogram, cubic metres, kilonewton)
00554     1831, 12, STR_UNITS_VELOCITY_SI,
00555      764, 10, STR_UNITS_POWER_SI,
00556     1000,  0, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00557        1,  0, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00558        1,  0, STR_UNITS_FORCE_SI,
00559   },
00560 };
00561 
00567 uint ConvertSpeedToDisplaySpeed(uint speed)
00568 {
00569  return (speed * units[_settings_game.locale.units].s_m) >> units[_settings_game.locale.units].s_s;
00570 }
00571 
00577 uint ConvertDisplaySpeedToSpeed(uint speed)
00578 {
00579   return ((speed << units[_settings_game.locale.units].s_s) + units[_settings_game.locale.units].s_m / 2) / units[_settings_game.locale.units].s_m;
00580 }
00581 
00582 static char *FormatString(char *buff, const char *str, const int64 *argv, uint casei, const char *last)
00583 {
00584   WChar b;
00585   const int64 *argv_orig = argv;
00586   uint modifier = 0;
00587 
00588   while ((b = Utf8Consume(&str)) != '\0') {
00589     if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00590       /* We need to pass some stuff as it might be modified; oh boy. */
00591       b = RemapNewGRFStringControlCode(b, &buff, &str, (int64*)argv);
00592       if (b == 0) continue;
00593     }
00594 
00595     switch (b) {
00596       case SCC_SETX: // {SETX}
00597         if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00598           buff += Utf8Encode(buff, SCC_SETX);
00599           *buff++ = *str++;
00600         }
00601         break;
00602 
00603       case SCC_SETXY: // {SETXY}
00604         if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00605           buff += Utf8Encode(buff, SCC_SETXY);
00606           *buff++ = *str++;
00607           *buff++ = *str++;
00608         }
00609         break;
00610 
00611       case SCC_STRING_ID: // {STRINL}
00612         buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, last);
00613         break;
00614 
00615       case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
00616         const char *str = (const char*)(size_t)GetInt64(&argv);
00617         buff = FormatString(buff, str, argv, casei, last);
00618         break;
00619       }
00620 
00621       case SCC_DATE_LONG: // {DATE_LONG}
00622         buff = FormatYmdString(buff, GetInt32(&argv), last);
00623         break;
00624 
00625       case SCC_DATE_SHORT: // {DATE_SHORT}
00626         buff = FormatMonthAndYear(buff, GetInt32(&argv), last);
00627         break;
00628 
00629       case SCC_VELOCITY: {// {VELOCITY}
00630         int64 args[1];
00631         assert(_settings_game.locale.units < lengthof(units));
00632         args[0] = ConvertSpeedToDisplaySpeed(GetInt32(&argv) * 10 / 16);
00633         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].velocity), args, modifier >> 24, last);
00634         modifier = 0;
00635         break;
00636       }
00637 
00638       case SCC_CURRENCY_COMPACT: // {CURRCOMPACT}
00639         buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), true, last);
00640         break;
00641 
00642       case SCC_REVISION: // {REV}
00643         buff = strecpy(buff, _openttd_revision, last);
00644         break;
00645 
00646       case SCC_CARGO_SHORT: { // {SHORTCARGO}
00647         /* Short description of cargotypes. Layout:
00648          * 8-bit = cargo type
00649          * 16-bit = cargo count */
00650         StringID cargo_str = GetCargo(GetInt32(&argv))->units_volume;
00651         switch (cargo_str) {
00652           case STR_TONS: {
00653             int64 args[1];
00654             assert(_settings_game.locale.units < lengthof(units));
00655             args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00656             buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00657             modifier = 0;
00658             break;
00659           }
00660 
00661           case STR_LITERS: {
00662             int64 args[1];
00663             assert(_settings_game.locale.units < lengthof(units));
00664             args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00665             buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, modifier >> 24, last);
00666             modifier = 0;
00667             break;
00668           }
00669 
00670           default:
00671             if (cargo_str >= 0xE000 && cargo_str < 0xF800) {
00672               /* NewGRF strings from Action 4 use a different format here,
00673                * of e.g. "x tonnes of coal", so process accordingly. */
00674               buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00675             } else {
00676               buff = FormatCommaNumber(buff, GetInt32(&argv), last);
00677               buff = strecpy(buff, " ", last);
00678               buff = strecpy(buff, GetStringPtr(cargo_str), last);
00679             }
00680             break;
00681         }
00682       } break;
00683 
00684       case SCC_STRING1: { // {STRING1}
00685         /* String that consumes ONE argument */
00686         uint str = modifier + GetInt32(&argv);
00687         buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 1), last);
00688         modifier = 0;
00689         break;
00690       }
00691 
00692       case SCC_STRING2: { // {STRING2}
00693         /* String that consumes TWO arguments */
00694         uint str = modifier + GetInt32(&argv);
00695         buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 2), last);
00696         modifier = 0;
00697         break;
00698       }
00699 
00700       case SCC_STRING3: { // {STRING3}
00701         /* String that consumes THREE arguments */
00702         uint str = modifier + GetInt32(&argv);
00703         buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 3), last);
00704         modifier = 0;
00705         break;
00706       }
00707 
00708       case SCC_STRING4: { // {STRING4}
00709         /* String that consumes FOUR arguments */
00710         uint str = modifier + GetInt32(&argv);
00711         buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 4), last);
00712         modifier = 0;
00713         break;
00714       }
00715 
00716       case SCC_STRING5: { // {STRING5}
00717         /* String that consumes FIVE arguments */
00718         uint str = modifier + GetInt32(&argv);
00719         buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 5), last);
00720         modifier = 0;
00721         break;
00722       }
00723 
00724       case SCC_STATION_FEATURES: { // {STATIONFEATURES}
00725         buff = StationGetSpecialString(buff, GetInt32(&argv), last);
00726         break;
00727       }
00728 
00729       case SCC_INDUSTRY_NAME: { // {INDUSTRY}
00730         const Industry *i = GetIndustry(GetInt32(&argv));
00731         int64 args[2];
00732 
00733         /* industry not valid anymore? */
00734         if (!i->IsValid()) break;
00735 
00736         /* First print the town name and the industry type name. */
00737         args[0] = i->town->index;
00738         args[1] = GetIndustrySpec(i->type)->name;
00739         buff = FormatString(buff, GetStringPtr(STR_INDUSTRY_FORMAT), args, modifier >> 24, last);
00740         modifier = 0;
00741         break;
00742       }
00743 
00744       case SCC_VOLUME: { // {VOLUME}
00745         int64 args[1];
00746         assert(_settings_game.locale.units < lengthof(units));
00747         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00748         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, modifier >> 24, last);
00749         modifier = 0;
00750         break;
00751       }
00752 
00753       case SCC_GENDER_LIST: { // {G 0 Der Die Das}
00754         const char *s = GetStringPtr(argv_orig[(byte)*str++]); // contains the string that determines gender.
00755         int len;
00756         int gender = 0;
00757         if (s != NULL) {
00758           wchar_t c = Utf8Consume(&s);
00759           /* Switch case is always put before genders, so remove those bits */
00760           if (c == SCC_SWITCH_CASE) {
00761             /* Skip to the last (i.e. default) case */
00762             for (uint num = (byte)*s++; num != 0; num--) s += 3 + (s[1] << 8) + s[2];
00763 
00764             c = Utf8Consume(&s);
00765           }
00766           /* Does this string have a gender, if so, set it */
00767           if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00768         }
00769         str = ParseStringChoice(str, gender, buff, &len);
00770         buff += len;
00771         break;
00772       }
00773 
00774       case SCC_DATE_TINY: { // {DATE_TINY}
00775         buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_DATE_TINY, last);
00776         break;
00777       }
00778 
00779       case SCC_DATE_ISO: { // {DATE_ISO}
00780         buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_DATE_ISO, last);
00781         break;
00782       }
00783 
00784       case SCC_CARGO: { // {CARGO}
00785         /* Layout now is:
00786          *   8bit   - cargo type
00787          *   16-bit - cargo count */
00788         CargoID cargo = GetInt32(&argv);
00789         StringID cargo_str = (cargo == CT_INVALID) ? STR_8838_N_A : GetCargo(cargo)->quantifier;
00790         buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00791         break;
00792       }
00793 
00794       case SCC_POWER: { // {POWER}
00795         int64 args[1];
00796         assert(_settings_game.locale.units < lengthof(units));
00797         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].p_m >> units[_settings_game.locale.units].p_s;
00798         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].power), args, modifier >> 24, last);
00799         modifier = 0;
00800         break;
00801       }
00802 
00803       case SCC_VOLUME_SHORT: { // {VOLUME_S}
00804         int64 args[1];
00805         assert(_settings_game.locale.units < lengthof(units));
00806         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00807         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_volume), args, modifier >> 24, last);
00808         modifier = 0;
00809         break;
00810       }
00811 
00812       case SCC_WEIGHT: { // {WEIGHT}
00813         int64 args[1];
00814         assert(_settings_game.locale.units < lengthof(units));
00815         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00816         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00817         modifier = 0;
00818         break;
00819       }
00820 
00821       case SCC_WEIGHT_SHORT: { // {WEIGHT_S}
00822         int64 args[1];
00823         assert(_settings_game.locale.units < lengthof(units));
00824         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00825         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_weight), args, modifier >> 24, last);
00826         modifier = 0;
00827         break;
00828       }
00829 
00830       case SCC_FORCE: { // {FORCE}
00831         int64 args[1];
00832         assert(_settings_game.locale.units < lengthof(units));
00833         args[0] = GetInt32(&argv) * units[_settings_game.locale.units].f_m >> units[_settings_game.locale.units].f_s;
00834         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].force), args, modifier >> 24, last);
00835         modifier = 0;
00836         break;
00837       }
00838 
00839       case SCC_SKIP: // {SKIP}
00840         argv++;
00841         break;
00842 
00843       /* This sets up the gender for the string.
00844        * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
00845       case SCC_GENDER_INDEX: // {GENDER 0}
00846         str++;
00847         break;
00848 
00849       case SCC_STRING: {// {STRING}
00850         uint str = modifier + GetInt32(&argv);
00851         /* WARNING. It's prohibited for the included string to consume any arguments.
00852          * For included strings that consume argument, you should use STRING1, STRING2 etc.
00853          * To debug stuff you can set argv to NULL and it will tell you */
00854         buff = GetStringWithArgs(buff, str, argv, last);
00855         modifier = 0;
00856         break;
00857       }
00858 
00859       case SCC_COMMA: // {COMMA}
00860         buff = FormatCommaNumber(buff, GetInt64(&argv), last);
00861         break;
00862 
00863       case SCC_ARG_INDEX: // Move argument pointer
00864         argv = argv_orig + (byte)*str++;
00865         break;
00866 
00867       case SCC_PLURAL_LIST: { // {P}
00868         int64 v = argv_orig[(byte)*str++]; // contains the number that determines plural
00869         int len;
00870         str = ParseStringChoice(str, DeterminePluralForm(v), buff, &len);
00871         buff += len;
00872         break;
00873       }
00874 
00875       case SCC_NUM: // {NUM}
00876         buff = FormatNoCommaNumber(buff, GetInt64(&argv), last);
00877         break;
00878 
00879       case SCC_HEX: // {HEX}
00880         buff = FormatHexNumber(buff, GetInt64(&argv), last);
00881         break;
00882 
00883       case SCC_BYTES: // {BYTES}
00884         buff = FormatBytes(buff, GetInt64(&argv), last);
00885         break;
00886 
00887       case SCC_CURRENCY: // {CURRENCY}
00888         buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
00889         break;
00890 
00891       case SCC_WAYPOINT_NAME: { // {WAYPOINT}
00892         Waypoint *wp = GetWaypoint(GetInt32(&argv));
00893 
00894         assert(wp->IsValid());
00895 
00896         if (wp->name != NULL) {
00897           buff = strecpy(buff, wp->name, last);
00898         } else {
00899           int64 temp[2];
00900           temp[0] = wp->town_index;
00901           temp[1] = wp->town_cn + 1;
00902           StringID str = wp->town_cn == 0 ? STR_WAYPOINTNAME_CITY : STR_WAYPOINTNAME_CITY_SERIAL;
00903 
00904           buff = GetStringWithArgs(buff, str, temp, last);
00905         }
00906         break;
00907       }
00908 
00909       case SCC_STATION_NAME: { // {STATION}
00910         StationID sid = GetInt32(&argv);
00911 
00912         if (!IsValidStationID(sid)) {
00913           /* The station doesn't exist anymore. The only place where we might
00914            * be "drawing" an invalid station is in the case of cargo that is
00915            * in transit. */
00916           buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, NULL, last);
00917           break;
00918         }
00919 
00920         const Station *st = GetStation(sid);
00921         if (st->name != NULL) {
00922           buff = strecpy(buff, st->name, last);
00923         } else {
00924           StringID str = st->string_id;
00925           if (st->indtype != IT_INVALID) {
00926             /* Special case where the industry provides the name for the station */
00927             const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
00928 
00929             /* Industry GRFs can change which might remove the station name and
00930              * thus cause very strange things. Here we check for that before we
00931              * actually set the station name. */
00932             if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
00933               str = indsp->station_name;
00934             }
00935           }
00936 
00937           int64 temp[3];
00938           temp[0] = STR_TOWN;
00939           temp[1] = st->town->index;
00940           temp[2] = st->index;
00941           buff = GetStringWithArgs(buff, str, temp, last);
00942         }
00943         break;
00944       }
00945 
00946       case SCC_TOWN_NAME: { // {TOWN}
00947         const Town *t = GetTown(GetInt32(&argv));
00948         int64 temp[1];
00949 
00950         assert(t->IsValid());
00951 
00952         temp[0] = t->townnameparts;
00953         uint32 grfid = t->townnamegrfid;
00954 
00955         if (t->name != NULL) {
00956           buff = strecpy(buff, t->name, last);
00957         } else if (grfid == 0) {
00958           /* Original town name */
00959           buff = GetStringWithArgs(buff, t->townnametype, temp, last);
00960         } else {
00961           /* Newgrf town name */
00962           if (GetGRFTownName(grfid) != NULL) {
00963             /* The grf is loaded */
00964             buff = GRFTownNameGenerate(buff, t->townnamegrfid, t->townnametype, t->townnameparts, last);
00965           } else {
00966             /* Fallback to english original */
00967             buff = GetStringWithArgs(buff, SPECSTR_TOWNNAME_ENGLISH, temp, last);
00968           }
00969         }
00970         break;
00971       }
00972 
00973       case SCC_GROUP_NAME: { // {GROUP}
00974         const Group *g = GetGroup(GetInt32(&argv));
00975 
00976         assert(g->IsValid());
00977 
00978         if (g->name != NULL) {
00979           buff = strecpy(buff, g->name, last);
00980         } else {
00981           int64 args[1];
00982 
00983           args[0] = g->index;
00984           buff = GetStringWithArgs(buff, STR_GROUP_NAME_FORMAT, args, last);
00985         }
00986         break;
00987       }
00988 
00989       case SCC_ENGINE_NAME: { // {ENGINE}
00990         EngineID engine = (EngineID)GetInt32(&argv);
00991         const Engine *e = GetEngine(engine);
00992 
00993         if (e->name != NULL) {
00994           buff = strecpy(buff, e->name, last);
00995         } else {
00996           buff = GetStringWithArgs(buff, e->info.string_id, NULL, last);
00997         }
00998         break;
00999       }
01000 
01001       case SCC_VEHICLE_NAME: { // {VEHICLE}
01002         const Vehicle *v = GetVehicle(GetInt32(&argv));
01003 
01004         if (v->name != NULL) {
01005           buff = strecpy(buff, v->name, last);
01006         } else {
01007           int64 args[1];
01008           args[0] = v->unitnumber;
01009 
01010           StringID str;
01011           switch (v->type) {
01012             default: NOT_REACHED();
01013             case VEH_TRAIN:    str = STR_SV_TRAIN_NAME; break;
01014             case VEH_ROAD:     str = STR_SV_ROADVEH_NAME; break;
01015             case VEH_SHIP:     str = STR_SV_SHIP_NAME; break;
01016             case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01017           }
01018 
01019           buff = GetStringWithArgs(buff, str, args, last);
01020         }
01021         break;
01022       }
01023 
01024       case SCC_SIGN_NAME: { // {SIGN}
01025         const Sign *si = GetSign(GetInt32(&argv));
01026         if (si->name != NULL) {
01027           buff = strecpy(buff, si->name, last);
01028         } else {
01029           buff = GetStringWithArgs(buff, STR_280A_SIGN, NULL, last);
01030         }
01031         break;
01032       }
01033 
01034       case SCC_COMPANY_NAME: { // {COMPANY}
01035         const Company *c = GetCompany((CompanyID)GetInt32(&argv));
01036 
01037         if (c->name != NULL) {
01038           buff = strecpy(buff, c->name, last);
01039         } else {
01040           int64 args[1];
01041           args[0] = c->name_2;
01042           buff = GetStringWithArgs(buff, c->name_1, args, last);
01043         }
01044         break;
01045       }
01046 
01047       case SCC_COMPANY_NUM: { // {COMPANYNUM}
01048         CompanyID company = (CompanyID)GetInt32(&argv);
01049 
01050         /* Nothing is added for AI or inactive companies */
01051         if (IsValidCompanyID(company) && IsHumanCompany(company)) {
01052           int64 args[1];
01053           args[0] = company + 1;
01054           buff = GetStringWithArgs(buff, STR_7002_COMPANY, args, last);
01055         }
01056         break;
01057       }
01058 
01059       case SCC_PRESIDENT_NAME: { // {PRESIDENTNAME}
01060         const Company *c = GetCompany((CompanyID)GetInt32(&argv));
01061 
01062         if (c->president_name != NULL) {
01063           buff = strecpy(buff, c->president_name, last);
01064         } else {
01065           int64 args[1];
01066           args[0] = c->president_name_2;
01067           buff = GetStringWithArgs(buff, c->president_name_1, args, last);
01068         }
01069         break;
01070       }
01071 
01072       case SCC_SETCASE: { // {SETCASE}
01073         /* This is a pseudo command, it's outputted when someone does {STRING.ack}
01074          * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
01075         modifier = (byte)*str++ << 24;
01076         break;
01077       }
01078 
01079       case SCC_SWITCH_CASE: { // {Used to implement case switching}
01080         /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
01081          * Each LEN is printed using 2 bytes in big endian order. */
01082         uint num = (byte)*str++;
01083         while (num) {
01084           if ((byte)str[0] == casei) {
01085             /* Found the case, adjust str pointer and continue */
01086             str += 3;
01087             break;
01088           }
01089           /* Otherwise skip to the next case */
01090           str += 3 + (str[1] << 8) + str[2];
01091           num--;
01092         }
01093         break;
01094       }
01095 
01096       default:
01097         if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01098         break;
01099     }
01100   }
01101   *buff = '\0';
01102   return buff;
01103 }
01104 
01105 
01106 static char *StationGetSpecialString(char *buff, int x, const char *last)
01107 {
01108   if ((x & FACIL_TRAIN)      && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01109   if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01110   if ((x & FACIL_BUS_STOP)   && (buff + Utf8CharLen(SCC_BUS)   < last)) buff += Utf8Encode(buff, SCC_BUS);
01111   if ((x & FACIL_AIRPORT)    && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01112   if ((x & FACIL_DOCK)       && (buff + Utf8CharLen(SCC_SHIP)  < last)) buff += Utf8Encode(buff, SCC_SHIP);
01113   *buff = '\0';
01114   return buff;
01115 }
01116 
01117 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01118 {
01119   char name[512];
01120 
01121   _town_name_generators[ind](name, seed, lastof(name));
01122   return strecpy(buff, name, last);
01123 }
01124 
01125 static const char * const _silly_company_names[] = {
01126   "Bloggs Brothers",
01127   "Tiny Transport Ltd.",
01128   "Express Travel",
01129   "Comfy-Coach & Co.",
01130   "Crush & Bump Ltd.",
01131   "Broken & Late Ltd.",
01132   "Sam Speedy & Son",
01133   "Supersonic Travel",
01134   "Mike's Motors",
01135   "Lightning International",
01136   "Pannik & Loozit Ltd.",
01137   "Inter-City Transport",
01138   "Getout & Pushit Ltd."
01139 };
01140 
01141 static const char * const _surname_list[] = {
01142   "Adams",
01143   "Allan",
01144   "Baker",
01145   "Bigwig",
01146   "Black",
01147   "Bloggs",
01148   "Brown",
01149   "Campbell",
01150   "Gordon",
01151   "Hamilton",
01152   "Hawthorn",
01153   "Higgins",
01154   "Green",
01155   "Gribble",
01156   "Jones",
01157   "McAlpine",
01158   "MacDonald",
01159   "McIntosh",
01160   "Muir",
01161   "Murphy",
01162   "Nelson",
01163   "O'Donnell",
01164   "Parker",
01165   "Phillips",
01166   "Pilkington",
01167   "Quigley",
01168   "Sharkey",
01169   "Thomson",
01170   "Watkins"
01171 };
01172 
01173 static const char * const _silly_surname_list[] = {
01174   "Grumpy",
01175   "Dozy",
01176   "Speedy",
01177   "Nosey",
01178   "Dribble",
01179   "Mushroom",
01180   "Cabbage",
01181   "Sniffle",
01182   "Fishy",
01183   "Swindle",
01184   "Sneaky",
01185   "Nutkins"
01186 };
01187 
01188 static const char _initial_name_letters[] = {
01189   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01190   'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01191 };
01192 
01193 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01194 {
01195   const char * const *base;
01196   uint num;
01197 
01198   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01199     base = _silly_surname_list;
01200     num  = lengthof(_silly_surname_list);
01201   } else {
01202     base = _surname_list;
01203     num  = lengthof(_surname_list);
01204   }
01205 
01206   buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01207   buff = strecpy(buff, " & Co.", last);
01208 
01209   return buff;
01210 }
01211 
01212 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01213 {
01214   char initial[] = "?. ";
01215   const char * const *base;
01216   uint num;
01217   uint i;
01218 
01219   initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01220   buff = strecpy(buff, initial, last);
01221 
01222   i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01223   if (i < sizeof(_initial_name_letters)) {
01224     initial[0] = _initial_name_letters[i];
01225     buff = strecpy(buff, initial, last);
01226   }
01227 
01228   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01229     base = _silly_surname_list;
01230     num  = lengthof(_silly_surname_list);
01231   } else {
01232     base = _surname_list;
01233     num  = lengthof(_surname_list);
01234   }
01235 
01236   buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01237 
01238   return buff;
01239 }
01240 
01241 static char *GetSpecialNameString(char *buff, int ind, const int64 *argv, const char *last)
01242 {
01243   switch (ind) {
01244     case 1: // not used
01245       return strecpy(buff, _silly_company_names[GetInt32(&argv) & 0xFFFF], last);
01246 
01247     case 2: // used for Foobar & Co company names
01248       return GenAndCoName(buff, GetInt32(&argv), last);
01249 
01250     case 3: // President name
01251       return GenPresidentName(buff, GetInt32(&argv), last);
01252 
01253     case 4: // song names
01254       return strecpy(buff, _origin_songs_specs[GetInt32(&argv) - 1].song_name, last);
01255   }
01256 
01257   /* town name? */
01258   if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01259     buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv), last);
01260     return strecpy(buff, " Transport", last);
01261   }
01262 
01263   /* language name? */
01264   if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01265     int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01266     return strecpy(buff,
01267       i == _dynlang.curr ? _langpack->own_name : _dynlang.ent[i].name, last);
01268   }
01269 
01270   /* resolution size? */
01271   if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01272     int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01273     buff += seprintf(
01274       buff, last, "%dx%d", _resolutions[i].width, _resolutions[i].height
01275     );
01276     return buff;
01277   }
01278 
01279   /* screenshot format name? */
01280   if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01281     int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01282     return strecpy(buff, GetScreenshotFormatDesc(i), last);
01283   }
01284 
01285   assert(0);
01286   return NULL;
01287 }
01288 
01289 #ifdef ENABLE_NETWORK
01290 extern void SortNetworkLanguages();
01291 #else /* ENABLE_NETWORK */
01292 static inline void SortNetworkLanguages() {}
01293 #endif /* ENABLE_NETWORK */
01294 
01295 bool ReadLanguagePack(int lang_index)
01296 {
01297   int tot_count, i;
01298   size_t len;
01299   char **langpack_offs;
01300   char *s;
01301 
01302   LanguagePack *lang_pack = (LanguagePack*)ReadFileToMem(_dynlang.ent[lang_index].file, &len, 200000);
01303 
01304   if (lang_pack == NULL) return false;
01305   if (len < sizeof(LanguagePack) ||
01306       lang_pack->ident != TO_LE32(LANGUAGE_PACK_IDENT) ||
01307       lang_pack->version != TO_LE32(LANGUAGE_PACK_VERSION)) {
01308     free(lang_pack);
01309     return false;
01310   }
01311 
01312 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01313   for (i = 0; i != 32; i++) {
01314     lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01315   }
01316 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
01317 
01318   tot_count = 0;
01319   for (i = 0; i != 32; i++) {
01320     uint num = lang_pack->offsets[i];
01321     _langtab_start[i] = tot_count;
01322     _langtab_num[i] = num;
01323     tot_count += num;
01324   }
01325 
01326   /* Allocate offsets */
01327   langpack_offs = MallocT<char*>(tot_count);
01328 
01329   /* Fill offsets */
01330   s = lang_pack->data;
01331   for (i = 0; i != tot_count; i++) {
01332     len = (byte)*s;
01333     *s++ = '\0'; // zero terminate the string before.
01334     if (len >= 0xC0) len = ((len & 0x3F) << 8) + (byte)*s++;
01335     langpack_offs[i] = s;
01336     s += len;
01337   }
01338 
01339   free(_langpack);
01340   _langpack = lang_pack;
01341 
01342   free(_langpack_offs);
01343   _langpack_offs = langpack_offs;
01344 
01345   const char *c_file = strrchr(_dynlang.ent[lang_index].file, PATHSEPCHAR) + 1;
01346   strecpy(_dynlang.curr_file, c_file, lastof(_dynlang.curr_file));
01347 
01348   _dynlang.curr = lang_index;
01349   _dynlang.text_dir = (TextDirection)lang_pack->text_dir;
01350   SetCurrentGrfLangID(_langpack->newgrflangid);
01351   SortNetworkLanguages();
01352   return true;
01353 }
01354 
01355 /* Win32 implementation in win32.cpp.
01356  * OS X implementation in os/macosx/macos.mm. */
01357 #if !(defined(WIN32) || defined(__APPLE__))
01358 
01364 const char *GetCurrentLocale(const char *param)
01365 {
01366   const char *env;
01367 
01368   env = getenv("LANGUAGE");
01369   if (env != NULL) return env;
01370 
01371   env = getenv("LC_ALL");
01372   if (env != NULL) return env;
01373 
01374   if (param != NULL) {
01375     env = getenv(param);
01376     if (env != NULL) return env;
01377   }
01378 
01379   return getenv("LANG");
01380 }
01381 #else
01382 const char *GetCurrentLocale(const char *param);
01383 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
01384 
01385 int CDECL StringIDSorter(const void *a, const void *b)
01386 {
01387   const StringID va = *(const StringID*)a;
01388   const StringID vb = *(const StringID*)b;
01389   char stra[512];
01390   char strb[512];
01391   GetString(stra, va, lastof(stra));
01392   GetString(strb, vb, lastof(strb));
01393 
01394   return strcmp(stra, strb);
01395 }
01396 
01404 static bool UniqueLanguageFile(const Language *langs, uint max, const char *language)
01405 {
01406   for (uint i = 0; i < max; i++) {
01407     const char *f_name = strrchr(langs[i].file, PATHSEPCHAR) + 1;
01408     if (strcmp(f_name, language) == 0) return false; // duplicates
01409   }
01410 
01411   return true;
01412 }
01413 
01420 static bool GetLanguageFileHeader(const char *file, LanguagePack *hdr)
01421 {
01422   FILE *f = fopen(file, "rb");
01423   if (f == NULL) return false;
01424 
01425   size_t read = fread(hdr, sizeof(*hdr), 1, f);
01426   fclose(f);
01427 
01428   bool ret = read == 1 &&
01429       hdr->ident == TO_LE32(LANGUAGE_PACK_IDENT) &&
01430       hdr->version == TO_LE32(LANGUAGE_PACK_VERSION);
01431 
01432   /* Convert endianness for the windows language ID */
01433   if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01434   return ret;
01435 }
01436 
01445 static int GetLanguageList(Language *langs, int start, int max, const char *path)
01446 {
01447   int i = start;
01448 
01449   DIR *dir = ttd_opendir(path);
01450   if (dir != NULL) {
01451     struct dirent *dirent;
01452     while ((dirent = readdir(dir)) != NULL && i < max) {
01453       const char *d_name    = FS2OTTD(dirent->d_name);
01454       const char *extension = strrchr(d_name, '.');
01455 
01456       /* Not a language file */
01457       if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01458 
01459       /* Filter any duplicate language-files, first-come first-serve */
01460       if (!UniqueLanguageFile(langs, i, d_name)) continue;
01461 
01462       langs[i].file = str_fmt("%s%s", path, d_name);
01463 
01464       /* Check whether the file is of the correct version */
01465       LanguagePack hdr;
01466       if (!GetLanguageFileHeader(langs[i].file, &hdr)) {
01467         free(langs[i].file);
01468         continue;
01469       }
01470 
01471       i++;
01472     }
01473     closedir(dir);
01474   }
01475   return i - start;
01476 }
01477 
01482 void InitializeLanguagePacks()
01483 {
01484   Searchpath sp;
01485   Language files[MAX_LANG];
01486   uint language_count = 0;
01487 
01488   FOR_ALL_SEARCHPATHS(sp) {
01489     char path[MAX_PATH];
01490     FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01491     language_count += GetLanguageList(files, language_count, lengthof(files), path);
01492   }
01493   if (language_count == 0) usererror("No available language packs (invalid versions?)");
01494 
01495   /* Acquire the locale of the current system */
01496   const char *lang = GetCurrentLocale("LC_MESSAGES");
01497   if (lang == NULL) lang = "en_GB";
01498 
01499   int chosen_language   = -1; 
01500   int language_fallback = -1; 
01501   int en_GB_fallback    =  0; 
01502 
01503   DynamicLanguages *dl = &_dynlang;
01504   dl->num = 0;
01505   /* Fill the dynamic languages structures */
01506   for (uint i = 0; i < language_count; i++) {
01507     /* File read the language header */
01508     LanguagePack hdr;
01509     if (!GetLanguageFileHeader(files[i].file, &hdr)) continue;
01510 
01511     dl->ent[dl->num].file = files[i].file;
01512     dl->ent[dl->num].name = strdup(hdr.name);
01513 
01514     /* We are trying to find a default language. The priority is by
01515      * configuration file, local environment and last, if nothing found,
01516      * english. If def equals -1, we have not picked a default language */
01517     const char *lang_file = strrchr(dl->ent[dl->num].file, PATHSEPCHAR) + 1;
01518     if (strcmp(lang_file, dl->curr_file) == 0) chosen_language = dl->num;
01519 
01520     if (chosen_language == -1) {
01521       if (strcmp (hdr.isocode, "en_GB") == 0) en_GB_fallback    = dl->num;
01522       if (strncmp(hdr.isocode, lang, 5) == 0) chosen_language   = dl->num;
01523       if (strncmp(hdr.isocode, lang, 2) == 0) language_fallback = dl->num;
01524     }
01525 
01526     dl->num++;
01527   }
01528 
01529   if (dl->num == 0) usererror("Invalid version of language packs");
01530 
01531   /* We haven't found the language in the config nor the one in the locale.
01532    * Now we set it to one of the fallback languages */
01533   if (chosen_language == -1) {
01534     chosen_language = (language_fallback != -1) ? language_fallback : en_GB_fallback;
01535   }
01536 
01537   if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", dl->ent[chosen_language].file);
01538 }
01539 
01550 void CheckForMissingGlyphsInLoadedLanguagePack()
01551 {
01552 #ifdef WITH_FREETYPE
01553   /* Reset to the original state; switching languages might cause us to
01554    * automatically choose another font. This resets that choice. */
01555   UninitFreeType();
01556   InitFreeType();
01557   bool retry = false;
01558 #endif
01559 
01560   for (;;) {
01561     const Sprite *question_mark = GetGlyph(FS_NORMAL, '?');
01562 
01563     for (uint i = 0; i != 32; i++) {
01564       for (uint j = 0; j < _langtab_num[i]; j++) {
01565         const char *string = _langpack_offs[_langtab_start[i] + j];
01566         WChar c;
01567         while ((c = Utf8Consume(&string)) != '\0') {
01568           if (c == SCC_SETX) {
01569             /*
01570              * SetX is, together with SetXY as special character that
01571              * uses the next (two) characters as data points. We have
01572              * to skip those, otherwise the UTF8 reading will go
01573              * haywire.
01574              */
01575             string++;
01576           } else if (c == SCC_SETXY) {
01577             string += 2;
01578           } else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) {
01579 #ifdef WITH_FREETYPE
01580             if (!retry) {
01581               /* We found an unprintable character... lets try whether we can
01582                * find a fallback font that can print the characters in the
01583                * current language. */
01584               retry = true;
01585 
01586               FreeTypeSettings backup;
01587               memcpy(&backup, &_freetype, sizeof(backup));
01588 
01589               bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid);
01590               if (success) {
01591                 UninitFreeType();
01592                 InitFreeType();
01593               }
01594 
01595               memcpy(&_freetype, &backup, sizeof(backup));
01596 
01597               if (success) continue;
01598             } else {
01599               /* Our fallback font does miss characters too, so keep the
01600                * user chosen font as that is more likely to be any good than
01601                * the wild guess we made */
01602               UninitFreeType();
01603               InitFreeType();
01604             }
01605 #endif
01606             /*
01607              * The character is printable, but not in the normal font.
01608              * This is the case we were testing for. In this case we
01609              * have to show the error. 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             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.");
01620             Utf8Encode(err_str, SCC_YELLOW);
01621             SetDParamStr(0, err_str);
01622             ShowErrorMessage(INVALID_STRING_ID, STR_JUST_RAW_STRING, 0, 0);
01623 
01624             /* Reset the font width */
01625             LoadStringWidthTable();
01626             return;
01627           }
01628         }
01629       }
01630     }
01631     break;
01632   }
01633 
01634   /* Update the font with cache */
01635   LoadStringWidthTable();
01636 
01637 #if !defined(WITH_ICU)
01638   /*
01639    * For right-to-left languages we need the ICU library. If
01640    * we do not have support for that library we warn the user
01641    * about it with a message. As we do not want the string to
01642    * be translated by the translators, we 'force' it into the
01643    * binary and 'load' it via a BindCString. To do this
01644    * properly we have to set the colour of the string,
01645    * otherwise we end up with a lot of artefacts. The colour
01646    * 'character' might change in the future, so for safety
01647    * we just Utf8 Encode it into the string, which takes
01648    * exactly three characters, so it replaces the "XXX" with
01649    * the colour marker.
01650    */
01651   if (_dynlang.text_dir != TD_LTR) {
01652     static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01653     Utf8Encode(err_str, SCC_YELLOW);
01654     SetDParamStr(0, err_str);
01655     ShowErrorMessage(INVALID_STRING_ID, STR_JUST_RAW_STRING, 0, 0);
01656   }
01657 #endif
01658 }

Generated on Tue Jul 21 18:48:27 2009 for OpenTTD by  doxygen 1.5.6