# strings.cpp

Go to the documentation of this file.

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