# strings.cpp

Go to the documentation of this file.

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