strings.cpp

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

Generated on Sun Nov 14 14:41:57 2010 for OpenTTD by  doxygen 1.6.1