# strings.cpp

Go to the documentation of this file.

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