strings.cpp

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

Generated on Mon May 11 15:48:07 2009 for OpenTTD by  doxygen 1.5.6