# strings.cpp

Go to the documentation of this file.

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