strings.cpp

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

Generated on Thu Sep 24 19:35:06 2009 for OpenTTD by  doxygen 1.5.6