strings.cpp

Go to the documentation of this file.
00001 /* $Id: strings.cpp 22166 2011-03-03 20:48:24Z 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 "depot_base.h"
00019 #include "industry.h"
00020 #include "newgrf_text.h"
00021 #include "fileio_func.h"
00022 #include "group.h"
00023 #include "signs_base.h"
00024 #include "cargotype.h"
00025 #include "fontcache.h"
00026 #include "gui.h"
00027 #include "strings_func.h"
00028 #include "rev.h"
00029 #include "core/endian_func.hpp"
00030 #include "date_func.h"
00031 #include "vehicle_base.h"
00032 #include "engine_base.h"
00033 #include "language.h"
00034 #include "townname_func.h"
00035 #include "string_func.h"
00036 #include "company_base.h"
00037 #include "smallmap_gui.h"
00038 #include "window_func.h"
00039 #include "debug.h"
00040 #include <stack>
00041 
00042 #include "table/strings.h"
00043 #include "table/control_codes.h"
00044 
00045 char _config_language_file[MAX_PATH];             
00046 LanguageList _languages;                          
00047 const LanguageMetadata *_current_language = NULL; 
00048 
00049 TextDirection _current_text_dir; 
00050 uint64 _decode_parameters[20];   
00051 WChar _parameter_type[20];       
00052 
00053 #ifdef WITH_ICU
00054 Collator *_current_collator = NULL;               
00055 #endif /* WITH_ICU */
00056 
00057 static char *StationGetSpecialString(char *buff, int x, const char *last);
00058 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
00059 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const int64 *argve, const char *last, WChar *argt = NULL);
00060 
00061 static char *FormatString(char *buff, const char *str, int64 *argv, const int64 *argve, uint casei, const char *last, WChar *argt = NULL, bool dry_run = false);
00062 
00063 struct LanguagePack : public LanguagePackHeader {
00064   char data[]; // list of strings
00065 };
00066 
00067 static char **_langpack_offs;
00068 static LanguagePack *_langpack;
00069 static uint _langtab_num[32];   
00070 static uint _langtab_start[32]; 
00071 static bool _keep_gender_data = false;  
00072 
00073 
00084 static inline int64 GetInt64(int64 **argv, const int64 *argve, WChar **argt, WChar type = 0)
00085 {
00086   assert(*argv != NULL);
00087   assert(*argv < argve);
00088   if (*argt != NULL) {
00089     assert(**argt == 0 || **argt == type);
00090     **argt = type;
00091     (*argt)++;
00092   }
00093   return *(*argv)++;
00094 }
00095 
00097 static inline int32 GetInt32(int64 **argv, const int64 *argve, WChar **argt, WChar type = 0)
00098 {
00099   return (int32)GetInt64(argv, argve, argt, type);
00100 }
00101 
00110 static inline int64 *GetArgvPtr(int64 **argv, int n, const int64 *argve, WChar **argt)
00111 {
00112   int64 *result;
00113   assert(*argv != NULL);
00114   assert((*argv + n) <= argve);
00115   result = *argv;
00116   (*argv) += n;
00117   if (*argt != NULL) (*argt) += n;
00118   return result;
00119 }
00120 
00121 
00122 const char *GetStringPtr(StringID string)
00123 {
00124   switch (GB(string, 11, 5)) {
00125     /* GetGRFStringPtr doesn't handle 0xD4xx ids, we need to convert those to 0xD0xx. */
00126     case 26: return GetStringPtr(GetGRFStringID(0, 0xD000 + GB(string, 0, 10)));
00127     case 28: return GetGRFStringPtr(GB(string, 0, 11));
00128     case 29: return GetGRFStringPtr(GB(string, 0, 11) + 0x0800);
00129     case 30: return GetGRFStringPtr(GB(string, 0, 11) + 0x1000);
00130     default: return _langpack_offs[_langtab_start[string >> 11] + (string & 0x7FF)];
00131   }
00132 }
00133 
00147 char *GetStringWithArgs(char *buffr, uint string, int64 *argv, const int64 *argve, const char *last, WChar *argt)
00148 {
00149   if (GB(string, 0, 16) == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, argv, argve, last, argt);
00150 
00151   uint index = GB(string,  0, 11);
00152   uint tab   = GB(string, 11,  5);
00153 
00154   switch (tab) {
00155     case 4:
00156       if (index >= 0xC0) {
00157         return GetSpecialTownNameString(buffr, index - 0xC0, GetInt32(&argv, argve, &argt), last);
00158       }
00159       break;
00160 
00161     case 14:
00162       if (index >= 0xE4) {
00163         return GetSpecialNameString(buffr, index - 0xE4, argv, argve, last, argt);
00164       }
00165       break;
00166 
00167     case 15:
00168       /* Old table for custom names. This is no longer used */
00169       error("Incorrect conversion of custom name string.");
00170 
00171     case 26:
00172       /* Include string within newgrf text (format code 81) */
00173       if (HasBit(index, 10)) {
00174         StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
00175         return GetStringWithArgs(buffr, string, argv, argve, last, argt);
00176       }
00177       break;
00178 
00179     case 28:
00180       return FormatString(buffr, GetGRFStringPtr(index), argv, argve, GB(string, 24, 8), last, argt);
00181 
00182     case 29:
00183       return FormatString(buffr, GetGRFStringPtr(index + 0x0800), argv, argve, GB(string, 24, 8), last, argt);
00184 
00185     case 30:
00186       return FormatString(buffr, GetGRFStringPtr(index + 0x1000), argv, argve, GB(string, 24, 8), last, argt);
00187 
00188     case 31:
00189       NOT_REACHED();
00190   }
00191 
00192   if (index >= _langtab_num[tab]) {
00193     error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
00194   }
00195 
00196   return FormatString(buffr, GetStringPtr(GB(string, 0, 16)), argv, argve, GB(string, 24, 8), last, argt);
00197 }
00198 
00199 char *GetString(char *buffr, StringID string, const char *last)
00200 {
00201   memset(_parameter_type, 0, sizeof(_parameter_type));
00202   return GetStringWithArgs(buffr, string, (int64*)_decode_parameters, (int64*)endof(_decode_parameters), last, _parameter_type);
00203 }
00204 
00205 
00206 char *InlineString(char *buf, StringID string)
00207 {
00208   buf += Utf8Encode(buf, SCC_STRING_ID);
00209   buf += Utf8Encode(buf, string);
00210   return buf;
00211 }
00212 
00213 
00219 void SetDParamStr(uint n, const char *str)
00220 {
00221   SetDParam(n, (uint64)(size_t)str);
00222 }
00223 
00228 void InjectDParam(uint amount)
00229 {
00230   assert((uint)amount < lengthof(_decode_parameters));
00231   memmove(_decode_parameters + amount, _decode_parameters, sizeof(_decode_parameters) - amount * sizeof(uint64));
00232 }
00233 
00234 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill_from = 19)
00235 {
00236   uint64 divisor = 10000000000000000000ULL;
00237 
00238   if (number < 0) {
00239     buff += seprintf(buff, last, "-");
00240     number = -number;
00241   }
00242 
00243   uint64 num = number;
00244   uint64 tot = 0;
00245   for (int i = 0; i < 20; i++) {
00246     uint64 quot = 0;
00247     if (num >= divisor) {
00248       quot = num / divisor;
00249       num = num % divisor;
00250     }
00251     if (tot |= quot || i >= zerofill_from) {
00252       buff += seprintf(buff, last, "%i", (int)quot);
00253       if ((i % 3) == 1 && i != 19) buff = strecpy(buff, separator, last);
00254     }
00255 
00256     divisor /= 10;
00257   }
00258 
00259   *buff = '\0';
00260 
00261   return buff;
00262 }
00263 
00264 static char *FormatCommaNumber(char *buff, int64 number, const char *last)
00265 {
00266   const char *separator = _settings_game.locale.digit_group_separator;
00267   if (separator == NULL) separator = _langpack->digit_group_separator;
00268   return FormatNumber(buff, number, last, separator);
00269 }
00270 
00271 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
00272 {
00273   return FormatNumber(buff, number, last, "");
00274 }
00275 
00276 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
00277 {
00278   return FormatNumber(buff, number, last, "", 20 - count);
00279 }
00280 
00281 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
00282 {
00283   return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
00284 }
00285 
00293 static char *FormatBytes(char *buff, int64 number, const char *last)
00294 {
00295   assert(number >= 0);
00296 
00297   /*                                    1   2^10  2^20  2^30  2^40  2^50  2^60 */
00298   const char * const iec_prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
00299   uint id = 1;
00300   while (number >= 1024 * 1024) {
00301     number /= 1024;
00302     id++;
00303   }
00304 
00305   const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
00306   if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
00307 
00308   if (number < 1024) {
00309     id = 0;
00310     buff += seprintf(buff, last, "%i", (int)number);
00311   } else if (number < 1024 * 10) {
00312     buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
00313   } else if (number < 1024 * 100) {
00314     buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
00315   } else {
00316     assert(number < 1024 * 1024);
00317     buff += seprintf(buff, last, "%i", (int)number / 1024);
00318   }
00319 
00320   assert(id < lengthof(iec_prefixes));
00321   buff += seprintf(buff, last, " %sB", iec_prefixes[id]);
00322 
00323   return buff;
00324 }
00325 
00326 static char *FormatYmdString(char *buff, Date date, uint modifier, const char *last)
00327 {
00328   YearMonthDay ymd;
00329   ConvertDateToYMD(date, &ymd);
00330 
00331   int64 args[3] = { ymd.day + STR_ORDINAL_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year };
00332   return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), args, endof(args), modifier >> 24, last);
00333 }
00334 
00335 static char *FormatMonthAndYear(char *buff, Date date, uint modifier, const char *last)
00336 {
00337   YearMonthDay ymd;
00338   ConvertDateToYMD(date, &ymd);
00339 
00340   int64 args[2] = { STR_MONTH_JAN + ymd.month, ymd.year };
00341   return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), args, endof(args), modifier >> 24, last);
00342 }
00343 
00344 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
00345 {
00346   YearMonthDay ymd;
00347   ConvertDateToYMD(date, &ymd);
00348 
00349   char day[3];
00350   char month[3];
00351   /* We want to zero-pad the days and months */
00352   snprintf(day,   lengthof(day),   "%02i", ymd.day);
00353   snprintf(month, lengthof(month), "%02i", ymd.month + 1);
00354 
00355   int64 args[3] = { (int64)(size_t)day, (int64)(size_t)month, ymd.year };
00356   return FormatString(buff, GetStringPtr(str), args, endof(args), 0, last);
00357 }
00358 
00359 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
00360 {
00361   /* We are going to make number absolute for printing, so
00362    * keep this piece of data as we need it later on */
00363   bool negative = number < 0;
00364   const char *multiplier = "";
00365 
00366   number *= spec->rate;
00367 
00368   /* convert from negative */
00369   if (number < 0) {
00370     if (buff + Utf8CharLen(SCC_RED) > last) return buff;
00371     buff += Utf8Encode(buff, SCC_RED);
00372     buff = strecpy(buff, "-", last);
00373     number = -number;
00374   }
00375 
00376   /* Add prefix part, folowing symbol_pos specification.
00377    * Here, it can can be either 0 (prefix) or 2 (both prefix anf suffix).
00378    * The only remaining value is 1 (suffix), so everything that is not 1 */
00379   if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
00380 
00381   /* for huge numbers, compact the number into k or M */
00382   if (compact) {
00383     /* Take care of the 'k' rounding. Having 1 000 000 k
00384      * and 1 000 M is inconsistent, so always use 1 000 M. */
00385     if (number >= 1000000000 - 500) {
00386       number = (number + 500000) / 1000000;
00387       multiplier = "M";
00388     } else if (number >= 1000000) {
00389       number = (number + 500) / 1000;
00390       multiplier = "k";
00391     }
00392   }
00393 
00394   const char *separator = _settings_game.locale.digit_group_separator_currency;
00395   if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
00396   if (separator == NULL) separator = _langpack->digit_group_separator_currency;
00397   buff = FormatNumber(buff, number, last, separator);
00398   buff = strecpy(buff, multiplier, last);
00399 
00400   /* Add suffix part, folowing symbol_pos specification.
00401    * Here, it can can be either 1 (suffix) or 2 (both prefix anf suffix).
00402    * The only remaining value is 1 (prefix), so everything that is not 0 */
00403   if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
00404 
00405   if (negative) {
00406     if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
00407     buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
00408     *buff = '\0';
00409   }
00410 
00411   return buff;
00412 }
00413 
00420 static int DeterminePluralForm(int64 count, int plural_form)
00421 {
00422   /* The absolute value determines plurality */
00423   uint64 n = abs(count);
00424 
00425   switch (plural_form) {
00426     default:
00427       NOT_REACHED();
00428 
00429     /* Two forms, singular used for one only
00430      * Used in:
00431      *   Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
00432      *   Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
00433     case 0:
00434       return n != 1;
00435 
00436     /* Only one form
00437      * Used in:
00438      *   Hungarian, Japanese, Korean, Turkish */
00439     case 1:
00440       return 0;
00441 
00442     /* Two forms, singular used for zero and one
00443      * Used in:
00444      *   French, Brazilian Portuguese */
00445     case 2:
00446       return n > 1;
00447 
00448     /* Three forms, special case for 0 and ending in 1, except those ending in 11
00449      * Used in:
00450      *   Latvian */
00451     case 3:
00452       return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
00453 
00454     /* Five forms, special case for one, two, 3 to 6 and 7 to 10
00455      * Used in:
00456      *   Gaelige (Irish) */
00457     case 4:
00458       return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
00459 
00460     /* Three forms, special case for numbers ending in 1[2-9]
00461      * Used in:
00462      *   Lithuanian */
00463     case 5:
00464       return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00465 
00466     /* Three forms, special cases for numbers ending in 1 and 2, 3, 4, except those ending in 1[1-4]
00467      * Used in:
00468      *   Croatian, Russian, Ukrainian */
00469     case 6:
00470       return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00471 
00472     /* Three forms, special case for one and some numbers ending in 2, 3, or 4
00473      * Used in:
00474      *   Polish */
00475     case 7:
00476       return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00477 
00478     /* Four forms, special case for one and all numbers ending in 02, 03, or 04
00479      * Used in:
00480      *   Slovenian */
00481     case 8:
00482       return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
00483 
00484     /* Two forms; singular used for everything ending in 1 but not in 11.
00485      * Used in:
00486      *   Icelandic */
00487     case 9:
00488       return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
00489 
00490     /* Three forms, special cases for one and 2, 3, or 4
00491      * Used in:
00492      *   Czech, Slovak */
00493     case 10:
00494       return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
00495 
00496     /* Two forms, special 'hack' for Korean; singular for numbers ending
00497      *   in a consonant and plural for numbers ending in a vowel.
00498      * Korean doesn't have the concept of plural, but depending on how a
00499      * number is pronounced it needs another version of a particle.
00500      * As such the plural system is misused to give this distinction.
00501      */
00502     case 11:
00503       switch (n % 10) {
00504         case 0: // yeong
00505         case 1: // il
00506         case 3: // sam
00507         case 6: // yuk
00508         case 7: // chil
00509         case 8: // pal
00510           return 0;
00511 
00512         case 2: // i
00513         case 4: // sa
00514         case 5: // o
00515         case 9: // gu
00516           return 1;
00517 
00518         default:
00519           NOT_REACHED();
00520       }
00521 
00522     /* Four forms: one, 0 and everything ending in 02..10, everything ending in 11..19.
00523      * Used in:
00524      *  Maltese */
00525     case 12:
00526       return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
00527   }
00528 }
00529 
00530 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
00531 {
00532   /* <NUM> {Length of each string} {each string} */
00533   uint n = (byte)*b++;
00534   uint pos, i, mypos = 0;
00535 
00536   for (i = pos = 0; i != n; i++) {
00537     uint len = (byte)*b++;
00538     if (i == form) mypos = pos;
00539     pos += len;
00540   }
00541 
00542   *dst += seprintf(*dst, last, "%s", b + mypos);
00543   return b + pos;
00544 }
00545 
00547 struct UnitConversion {
00548   int multiplier; 
00549   int shift;      
00550 
00557   int64 ToDisplay(int64 input, bool round = true) const
00558   {
00559     return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
00560   }
00561 
00568   int64 FromDisplay(int64 input, bool round = true) const
00569   {
00570     return ((input << this->shift) + (round ? this->multiplier / 2 : 0)) / this->multiplier;
00571   }
00572 };
00573 
00574 struct Units {
00575   UnitConversion c_velocity; 
00576   StringID velocity;         
00577   UnitConversion c_power;    
00578   StringID power;            
00579   UnitConversion c_weight;   
00580   StringID s_weight;         
00581   StringID l_weight;         
00582   UnitConversion c_volume;   
00583   StringID s_volume;         
00584   StringID l_volume;         
00585   UnitConversion c_force;    
00586   StringID force;            
00587   UnitConversion c_height;   
00588   StringID height;           
00589 };
00590 
00591 /* Unit conversions */
00592 static const Units _units[] = {
00593   { // Imperial (Original, mph, hp, metric ton, litre, kN, ft)
00594     {   1,  0}, STR_UNITS_VELOCITY_IMPERIAL,
00595     {   1,  0}, STR_UNITS_POWER_IMPERIAL,
00596     {   1,  0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00597     {1000,  0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00598     {   1,  0}, STR_UNITS_FORCE_SI,
00599     {   3,  0}, STR_UNITS_HEIGHT_IMPERIAL, // "Wrong" conversion factor for more nicer GUI values
00600   },
00601   { // Metric (km/h, hp, metric ton, litre, kN, metre)
00602     { 103,  6}, STR_UNITS_VELOCITY_METRIC,
00603     {4153, 12}, STR_UNITS_POWER_METRIC,
00604     {   1,  0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00605     {1000,  0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00606     {   1,  0}, STR_UNITS_FORCE_SI,
00607     {   1,  0}, STR_UNITS_HEIGHT_SI,
00608   },
00609   { // SI (m/s, kilowatt, kilogram, cubic metre, kilonewton, metre)
00610     {1831, 12}, STR_UNITS_VELOCITY_SI,
00611     {6109, 13}, STR_UNITS_POWER_SI,
00612     {1000,  0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00613     {   1,  0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00614     {   1,  0}, STR_UNITS_FORCE_SI,
00615     {   1,  0}, STR_UNITS_HEIGHT_SI,
00616   },
00617 };
00618 
00624 uint ConvertSpeedToDisplaySpeed(uint speed)
00625 {
00626   /* For historical reasons we don't want to mess with the
00627    * conversion for speed. So, don't round it and keep the
00628    * original conversion factors instead of the real ones. */
00629   return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed, false);
00630 }
00631 
00637 uint ConvertDisplaySpeedToSpeed(uint speed)
00638 {
00639   return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed);
00640 }
00641 
00653 static char *FormatString(char *buff, const char *str_arg, int64 *argv, const int64 *argve, uint casei, const char *last, WChar *argt, bool dry_run)
00654 {
00655   /* When there is no array with types there is no need to do a dry run. */
00656   if (argt == NULL) dry_run = true;
00657   if (UsingNewGRFTextStack() && !dry_run) {
00658     /* Values from the NewGRF text stack are only copied to the normal
00659      * argv array at the time they are encountered. That means that if
00660      * another string command references a value later in the string it
00661      * would fail. We solve that by running FormatString twice. The first
00662      * pass makes sure the argv array is correctly filled and the second
00663      * pass can reference later values without problems. */
00664     struct TextRefStack *backup = CreateTextRefStackBackup();
00665     FormatString(buff, str_arg, argv, argve, casei, last, argt, true);
00666     RestoreTextRefStackBackup(backup);
00667   } else if (!dry_run) {
00668     FormatString(buff, str_arg, argv, argve, casei, last, argt, true);
00669   }
00670   WChar b;
00671   int64 *argv_orig = argv;
00672   WChar *argt_orig = argt;
00673   uint modifier = 0;
00674   char *buf_start = buff;
00675   std::stack<const char *> str_stack;
00676   str_stack.push(str_arg);
00677 
00678   while (true) {
00679     while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
00680       str_stack.pop();
00681     }
00682     if (str_stack.empty()) break;
00683     const char *&str = str_stack.top();
00684 
00685     if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00686       /* We need to pass some stuff as it might be modified; oh boy. */
00687       //todo: should argve be passed here too?
00688       b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, argv);
00689       if (b == 0) continue;
00690     }
00691 
00692     switch (b) {
00693       case SCC_NEWGRF_STRINL: {
00694         StringID substr = Utf8Consume(&str);
00695         str_stack.push(GetStringPtr(substr));
00696         break;
00697       }
00698 
00699       case SCC_NEWGRF_PRINT_STRING_ID: {
00700         StringID substr = GetInt32(&argv, argve, &argt, SCC_NEWGRF_PRINT_STRING_ID);
00701         str_stack.push(GetStringPtr(substr));
00702         break;
00703       }
00704 
00705 
00706       case SCC_SETX: // {SETX}
00707         if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00708           buff += Utf8Encode(buff, SCC_SETX);
00709           *buff++ = *str++;
00710         }
00711         break;
00712 
00713       case SCC_SETXY: // {SETXY}
00714         if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00715           buff += Utf8Encode(buff, SCC_SETXY);
00716           *buff++ = *str++;
00717           *buff++ = *str++;
00718         }
00719         break;
00720 
00721       case SCC_STRING_ID: // {STRINL}
00722         buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, argve, last, argt);
00723         break;
00724 
00725       case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
00726         const char *str = (const char*)(size_t)GetInt64(&argv, argve, &argt);
00727         buff = FormatString(buff, str, argv, argve, casei, last, argt);
00728         break;
00729       }
00730 
00731       case SCC_DATE_LONG: // {DATE_LONG}
00732         buff = FormatYmdString(buff, GetInt32(&argv, argve, &argt, SCC_DATE_LONG), modifier, last);
00733         break;
00734 
00735       case SCC_DATE_SHORT: // {DATE_SHORT}
00736         buff = FormatMonthAndYear(buff, GetInt32(&argv, argve, &argt, SCC_DATE_SHORT), modifier, last);
00737         break;
00738 
00739       case SCC_VELOCITY: { // {VELOCITY}
00740         int64 args[1];
00741         assert(_settings_game.locale.units < lengthof(_units));
00742         args[0] = ConvertSpeedToDisplaySpeed(GetInt64(&argv, argve, &argt, SCC_VELOCITY) * 10 / 16);
00743         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].velocity), args, endof(args), modifier >> 24, last);
00744         modifier = 0;
00745         break;
00746       }
00747 
00748       case SCC_HEIGHT: { // {HEIGHT}
00749         int64 args[1] = {_units[_settings_game.locale.units].c_height.ToDisplay(GetInt64(&argv, argve, &argt))};
00750         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].height), args, endof(args), modifier >> 24, last);
00751         modifier = 0;
00752         break;
00753       }
00754 
00755       case SCC_CURRENCY_COMPACT: // {CURRCOMPACT}
00756         buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv, argve, &argt), true, last);
00757         break;
00758 
00759       case SCC_REVISION: // {REV}
00760         buff = strecpy(buff, _openttd_revision, last);
00761         break;
00762 
00763       case SCC_CARGO_SHORT: { // {SHORTCARGO}
00764         /* Short description of cargotypes. Layout:
00765          * 8-bit = cargo type
00766          * 16-bit = cargo count */
00767         StringID cargo_str = CargoSpec::Get(GetInt32(&argv, argve, &argt, SCC_CARGO_SHORT))->units_volume;
00768         switch (cargo_str) {
00769           case STR_TONS: {
00770             int64 args[1];
00771             assert(_settings_game.locale.units < lengthof(_units));
00772             args[0] = _units[_settings_game.locale.units].c_weight.ToDisplay(GetInt64(&argv, argve, &argt));
00773             buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), args, endof(args), modifier >> 24, last);
00774             modifier = 0;
00775             break;
00776           }
00777 
00778           case STR_LITERS: {
00779             int64 args[1];
00780             assert(_settings_game.locale.units < lengthof(_units));
00781             args[0] = _units[_settings_game.locale.units].c_volume.ToDisplay(GetInt64(&argv, argve, &argt));
00782             buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), args, endof(args), modifier >> 24, last);
00783             modifier = 0;
00784             break;
00785           }
00786 
00787           default:
00788             buff = GetStringWithArgs(buff, cargo_str, argv++, argve, last, argt++);
00789             break;
00790         }
00791         break;
00792       }
00793 
00794       case SCC_STRING1: { // {STRING1}
00795         /* String that consumes ONE argument */
00796         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING1);
00797         WChar *orig_argt = argt;
00798         int64 *args = GetArgvPtr(&argv, 1, argve, &argt);
00799         buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00800         modifier = 0;
00801         break;
00802       }
00803 
00804       case SCC_STRING2: { // {STRING2}
00805         /* String that consumes TWO arguments */
00806         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING2);
00807         WChar *orig_argt = argt;
00808         int64 *args = GetArgvPtr(&argv, 2, argve, &argt);
00809         buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00810         modifier = 0;
00811         break;
00812       }
00813 
00814       case SCC_STRING3: { // {STRING3}
00815         /* String that consumes THREE arguments */
00816         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING3);
00817         WChar *orig_argt = argt;
00818         int64 *args = GetArgvPtr(&argv, 3, argve, &argt);
00819         buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00820         modifier = 0;
00821         break;
00822       }
00823 
00824       case SCC_STRING4: { // {STRING4}
00825         /* String that consumes FOUR arguments */
00826         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING4);
00827         WChar *orig_argt = argt;
00828         int64 *args = GetArgvPtr(&argv, 4, argve, &argt);
00829         buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00830         modifier = 0;
00831         break;
00832       }
00833 
00834       case SCC_STRING5: { // {STRING5}
00835         /* String that consumes FIVE arguments */
00836         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING5);
00837         WChar *orig_argt = argt;
00838         int64 *args = GetArgvPtr(&argv, 5, argve, &argt);
00839         buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00840         modifier = 0;
00841         break;
00842       }
00843 
00844       case SCC_STATION_FEATURES: { // {STATIONFEATURES}
00845         buff = StationGetSpecialString(buff, GetInt32(&argv, argve, &argt, SCC_STATION_FEATURES), last);
00846         break;
00847       }
00848 
00849       case SCC_INDUSTRY_NAME: { // {INDUSTRY}
00850         const Industry *i = Industry::Get(GetInt32(&argv, argve, &argt, SCC_INDUSTRY_NAME));
00851         int64 args[2];
00852 
00853         /* industry not valid anymore? */
00854         assert(i != NULL);
00855 
00856         /* First print the town name and the industry type name. */
00857         args[0] = i->town->index;
00858         args[1] = GetIndustrySpec(i->type)->name;
00859         buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), args, endof(args), modifier >> 24, last);
00860         modifier = 0;
00861         break;
00862       }
00863 
00864       case SCC_VOLUME: { // {VOLUME}
00865         int64 args[1];
00866         assert(_settings_game.locale.units < lengthof(_units));
00867         args[0] = _units[_settings_game.locale.units].c_volume.ToDisplay(GetInt64(&argv, argve, &argt, SCC_VOLUME));
00868         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), args, endof(args), modifier >> 24, last);
00869         modifier = 0;
00870         break;
00871       }
00872 
00873       case SCC_GENDER_LIST: { // {G 0 Der Die Das}
00874         /* First read the meta data from the language file. */
00875         byte offset = (byte)*str++;
00876         assert(argv_orig + offset < argve);
00877         int gender = 0;
00878         if (!dry_run && argt != NULL && argt_orig[offset] != 0) {
00879           /* Now we need to figure out what text to resolve, i.e.
00880            * what do we need to draw? So get the actual raw string
00881            * first using the control code to get said string. */
00882           char input[4 + 1];
00883           char *p = input + Utf8Encode(input, argt_orig[offset]);
00884           *p = '\0';
00885 
00886           /* Now do the string formatting. */
00887           char buf[256];
00888           bool old_kgd = _keep_gender_data;
00889           _keep_gender_data = true;
00890           p = FormatString(buf, input, argv_orig + offset, argve, 0, lastof(buf));
00891           _keep_gender_data = old_kgd;
00892           *p = '\0';
00893 
00894           /* And determine the string. */
00895           const char *s = buf;
00896           WChar c = Utf8Consume(&s);
00897           /* Does this string have a gender, if so, set it */
00898           if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00899         }
00900         str = ParseStringChoice(str, gender, &buff, last);
00901         break;
00902       }
00903 
00904       case SCC_DATE_TINY: { // {DATE_TINY}
00905         buff = FormatTinyOrISODate(buff, GetInt32(&argv, argve, &argt, SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
00906         break;
00907       }
00908 
00909       case SCC_DATE_ISO: { // {DATE_ISO}
00910         buff = FormatTinyOrISODate(buff, GetInt32(&argv, argve, &argt), STR_FORMAT_DATE_ISO, last);
00911         break;
00912       }
00913 
00914       case SCC_CARGO: { // {CARGO}
00915         /* First parameter is cargo type, second parameter is cargo count */
00916         CargoID cargo = GetInt32(&argv, argve, &argt, SCC_CARGO);
00917         StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
00918         buff = GetStringWithArgs(buff, cargo_str, argv++, argve, last);
00919         break;
00920       }
00921 
00922       case SCC_POWER: { // {POWER}
00923         int64 args[1];
00924         assert(_settings_game.locale.units < lengthof(_units));
00925         args[0] = _units[_settings_game.locale.units].c_power.ToDisplay(GetInt64(&argv, argve, &argt));
00926         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].power), args, endof(args), modifier >> 24, last);
00927         modifier = 0;
00928         break;
00929       }
00930 
00931       case SCC_VOLUME_SHORT: { // {VOLUME_S}
00932         int64 args[1];
00933         assert(_settings_game.locale.units < lengthof(_units));
00934         args[0] = _units[_settings_game.locale.units].c_volume.ToDisplay(GetInt64(&argv, argve, &argt));
00935         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_volume), args, endof(args), modifier >> 24, last);
00936         modifier = 0;
00937         break;
00938       }
00939 
00940       case SCC_WEIGHT: { // {WEIGHT}
00941         int64 args[1];
00942         assert(_settings_game.locale.units < lengthof(_units));
00943         args[0] = _units[_settings_game.locale.units].c_weight.ToDisplay(GetInt64(&argv, argve, &argt, SCC_WEIGHT));
00944         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), args, endof(args), modifier >> 24, last);
00945         modifier = 0;
00946         break;
00947       }
00948 
00949       case SCC_WEIGHT_SHORT: { // {WEIGHT_S}
00950         int64 args[1];
00951         assert(_settings_game.locale.units < lengthof(_units));
00952         args[0] = _units[_settings_game.locale.units].c_weight.ToDisplay(GetInt64(&argv, argve, &argt));
00953         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_weight), args, endof(args), modifier >> 24, last);
00954         modifier = 0;
00955         break;
00956       }
00957 
00958       case SCC_FORCE: { // {FORCE}
00959         int64 args[1];
00960         assert(_settings_game.locale.units < lengthof(_units));
00961         args[0] = _units[_settings_game.locale.units].c_force.ToDisplay(GetInt64(&argv, argve, &argt));
00962         buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].force), args, endof(args), modifier >> 24, last);
00963         modifier = 0;
00964         break;
00965       }
00966 
00967       /* This sets up the gender for the string.
00968        * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
00969       case SCC_GENDER_INDEX: // {GENDER 0}
00970         if (_keep_gender_data) {
00971           buff += Utf8Encode(buff, SCC_GENDER_INDEX);
00972           *buff++ = *str++;
00973         } else {
00974           str++;
00975         }
00976         break;
00977 
00978       case SCC_STRING: {// {STRING}
00979         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING);
00980         /* WARNING. It's prohibited for the included string to consume any arguments.
00981          * For included strings that consume argument, you should use STRING1, STRING2 etc.
00982          * To debug stuff you can set argv to NULL and it will tell you */
00983         buff = GetStringWithArgs(buff, str, argv, argve, last);
00984         modifier = 0;
00985         break;
00986       }
00987 
00988       case SCC_COMMA: // {COMMA}
00989         buff = FormatCommaNumber(buff, GetInt64(&argv, argve, &argt, SCC_COMMA), last);
00990         break;
00991 
00992       case SCC_ARG_INDEX: { // Move argument pointer
00993         byte offset = (byte)*str++;
00994         argv = argv_orig + offset;
00995         if (argt_orig != NULL) argt = argt_orig + offset;
00996         break;
00997       }
00998 
00999       case SCC_PLURAL_LIST: { // {P}
01000         int plural_form = *str++;          // contains the plural form for this string
01001         byte idx = *str++;
01002         assert(argv_orig + idx < argve);
01003         int64 v = argv_orig[idx]; // contains the number that determines plural
01004         str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
01005         break;
01006       }
01007 
01008       case SCC_NUM: // {NUM}
01009         buff = FormatNoCommaNumber(buff, GetInt64(&argv, argve, &argt, SCC_NUM), last);
01010         break;
01011 
01012       case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
01013         int64 num = GetInt64(&argv, argve, &argt);
01014         buff = FormatZerofillNumber(buff, num, GetInt64(&argv, argve, &argt), last);
01015         break;
01016       }
01017 
01018       case SCC_HEX: // {HEX}
01019         buff = FormatHexNumber(buff, (uint64)GetInt64(&argv, argve, &argt, SCC_HEX), last);
01020         break;
01021 
01022       case SCC_BYTES: // {BYTES}
01023         buff = FormatBytes(buff, GetInt64(&argv, argve, &argt), last);
01024         break;
01025 
01026       case SCC_CURRENCY: // {CURRENCY}
01027         buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv, argve, &argt, SCC_CURRENCY), false, last);
01028         break;
01029 
01030       case SCC_WAYPOINT_NAME: { // {WAYPOINT}
01031         Waypoint *wp = Waypoint::Get(GetInt32(&argv, argve, &argt, SCC_WAYPOINT_NAME));
01032 
01033         assert(wp != NULL);
01034 
01035         if (wp->name != NULL) {
01036           buff = strecpy(buff, wp->name, last);
01037         } else {
01038           int64 args[2];
01039           args[0] = wp->town->index;
01040           args[1] = wp->town_cn + 1;
01041           StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
01042           if (wp->town_cn != 0) str++;
01043           buff = GetStringWithArgs(buff, str, args, endof(args), last);
01044         }
01045         break;
01046       }
01047 
01048       case SCC_STATION_NAME: { // {STATION}
01049         StationID sid = GetInt32(&argv, argve, &argt, SCC_STATION_NAME);
01050         const Station *st = Station::GetIfValid(sid);
01051 
01052         if (st == NULL) {
01053           /* The station doesn't exist anymore. The only place where we might
01054            * be "drawing" an invalid station is in the case of cargo that is
01055            * in transit. */
01056           buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, NULL, NULL, last);
01057           break;
01058         }
01059 
01060         if (st->name != NULL) {
01061           buff = strecpy(buff, st->name, last);
01062         } else {
01063           StringID str = st->string_id;
01064           if (st->indtype != IT_INVALID) {
01065             /* Special case where the industry provides the name for the station */
01066             const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01067 
01068             /* Industry GRFs can change which might remove the station name and
01069              * thus cause very strange things. Here we check for that before we
01070              * actually set the station name. */
01071             if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
01072               str = indsp->station_name;
01073             }
01074           }
01075 
01076           int64 args[3];
01077           args[0] = STR_TOWN_NAME;
01078           args[1] = st->town->index;
01079           args[2] = st->index;
01080           buff = GetStringWithArgs(buff, str, args, endof(args), last);
01081         }
01082         break;
01083       }
01084 
01085       case SCC_DEPOT_NAME: { // {DEPOT}
01086         VehicleType vt = (VehicleType)GetInt32(&argv, argve, &argt, SCC_DEPOT_NAME);
01087         if (vt == VEH_AIRCRAFT) {
01088           int64 args[] = { GetInt32(&argv, argve, &argt) };
01089           buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, args, endof(args), last);
01090           break;
01091         }
01092 
01093         const Depot *d = Depot::Get(GetInt32(&argv, argve, &argt));
01094         if (d->name != NULL) {
01095           buff = strecpy(buff, d->name, last);
01096         } else {
01097           int64 args[] = { d->town->index, d->town_cn + 1 };
01098           buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), args, endof(args), last);
01099         }
01100         break;
01101       }
01102 
01103       case SCC_TOWN_NAME: { // {TOWN}
01104         const Town *t = Town::Get(GetInt32(&argv, argve, &argt, SCC_TOWN_NAME));
01105 
01106         assert(t != NULL);
01107 
01108         if (t->name != NULL) {
01109           buff = strecpy(buff, t->name, last);
01110         } else {
01111           buff = GetTownName(buff, t, last);
01112         }
01113         break;
01114       }
01115 
01116       case SCC_GROUP_NAME: { // {GROUP}
01117         const Group *g = Group::Get(GetInt32(&argv, argve, &argt));
01118 
01119         assert(g != NULL);
01120 
01121         if (g->name != NULL) {
01122           buff = strecpy(buff, g->name, last);
01123         } else {
01124           int64 args[1];
01125 
01126           args[0] = g->index;
01127           buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, args, endof(args), last);
01128         }
01129         break;
01130       }
01131 
01132       case SCC_ENGINE_NAME: { // {ENGINE}
01133         EngineID engine = (EngineID)GetInt32(&argv, argve, &argt, SCC_ENGINE_NAME);
01134         const Engine *e = Engine::Get(engine);
01135 
01136         assert(e != NULL);
01137 
01138         if (e->name != NULL && e->IsEnabled()) {
01139           buff = strecpy(buff, e->name, last);
01140         } else {
01141           buff = GetStringWithArgs(buff, e->info.string_id, NULL, NULL, last);
01142         }
01143         break;
01144       }
01145 
01146       case SCC_VEHICLE_NAME: { // {VEHICLE}
01147         const Vehicle *v = Vehicle::Get(GetInt32(&argv, argve, &argt, SCC_VEHICLE_NAME));
01148 
01149         assert(v != NULL);
01150 
01151         if (v->name != NULL) {
01152           buff = strecpy(buff, v->name, last);
01153         } else {
01154           int64 args[1];
01155           args[0] = v->unitnumber;
01156 
01157           StringID str;
01158           switch (v->type) {
01159             default: NOT_REACHED();
01160             case VEH_TRAIN:    str = STR_SV_TRAIN_NAME; break;
01161             case VEH_ROAD:     str = STR_SV_ROAD_VEHICLE_NAME; break;
01162             case VEH_SHIP:     str = STR_SV_SHIP_NAME; break;
01163             case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01164           }
01165 
01166           buff = GetStringWithArgs(buff, str, args, endof(args), last);
01167         }
01168         break;
01169       }
01170 
01171       case SCC_SIGN_NAME: { // {SIGN}
01172         const Sign *si = Sign::Get(GetInt32(&argv, argve, &argt));
01173         if (si->name != NULL) {
01174           buff = strecpy(buff, si->name, last);
01175         } else {
01176           buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, NULL, NULL, last);
01177         }
01178         break;
01179       }
01180 
01181       case SCC_COMPANY_NAME: { // {COMPANY}
01182         const Company *c = Company::Get((CompanyID)GetInt32(&argv, argve, &argt));
01183 
01184         if (c->name != NULL) {
01185           buff = strecpy(buff, c->name, last);
01186         } else {
01187           int64 args[1];
01188           args[0] = c->name_2;
01189           buff = GetStringWithArgs(buff, c->name_1, args, endof(args), last);
01190         }
01191         break;
01192       }
01193 
01194       case SCC_COMPANY_NUM: { // {COMPANYNUM}
01195         CompanyID company = (CompanyID)GetInt32(&argv, argve, &argt);
01196 
01197         /* Nothing is added for AI or inactive companies */
01198         if (Company::IsValidHumanID(company)) {
01199           int64 args[1];
01200           args[0] = company + 1;
01201           buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, args, endof(args), last);
01202         }
01203         break;
01204       }
01205 
01206       case SCC_PRESIDENT_NAME: { // {PRESIDENTNAME}
01207         const Company *c = Company::Get((CompanyID)GetInt32(&argv, argve, &argt, SCC_PRESIDENT_NAME));
01208 
01209         if (c->president_name != NULL) {
01210           buff = strecpy(buff, c->president_name, last);
01211         } else {
01212           int64 args[1];
01213           args[0] = c->president_name_2;
01214           buff = GetStringWithArgs(buff, c->president_name_1, args, endof(args), last);
01215         }
01216         break;
01217       }
01218 
01219       case SCC_SETCASE: { // {SETCASE}
01220         /* This is a pseudo command, it's outputted when someone does {STRING.ack}
01221          * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
01222         modifier = (byte)*str++ << 24;
01223         break;
01224       }
01225 
01226       case SCC_SWITCH_CASE: { // {Used to implement case switching}
01227         /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
01228          * Each LEN is printed using 2 bytes in big endian order. */
01229         uint num = (byte)*str++;
01230         while (num) {
01231           if ((byte)str[0] == casei) {
01232             /* Found the case, adjust str pointer and continue */
01233             str += 3;
01234             break;
01235           }
01236           /* Otherwise skip to the next case */
01237           str += 3 + (str[1] << 8) + str[2];
01238           num--;
01239         }
01240         break;
01241       }
01242 
01243       default:
01244         if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01245         break;
01246     }
01247   }
01248   *buff = '\0';
01249   return buff;
01250 }
01251 
01252 
01253 static char *StationGetSpecialString(char *buff, int x, const char *last)
01254 {
01255   if ((x & FACIL_TRAIN)      && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01256   if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01257   if ((x & FACIL_BUS_STOP)   && (buff + Utf8CharLen(SCC_BUS)   < last)) buff += Utf8Encode(buff, SCC_BUS);
01258   if ((x & FACIL_DOCK)       && (buff + Utf8CharLen(SCC_SHIP)  < last)) buff += Utf8Encode(buff, SCC_SHIP);
01259   if ((x & FACIL_AIRPORT)    && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01260   *buff = '\0';
01261   return buff;
01262 }
01263 
01264 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01265 {
01266   return GenerateTownNameString(buff, last, ind, seed);
01267 }
01268 
01269 static const char * const _silly_company_names[] = {
01270   "Bloggs Brothers",
01271   "Tiny Transport Ltd.",
01272   "Express Travel",
01273   "Comfy-Coach & Co.",
01274   "Crush & Bump Ltd.",
01275   "Broken & Late Ltd.",
01276   "Sam Speedy & Son",
01277   "Supersonic Travel",
01278   "Mike's Motors",
01279   "Lightning International",
01280   "Pannik & Loozit Ltd.",
01281   "Inter-City Transport",
01282   "Getout & Pushit Ltd."
01283 };
01284 
01285 static const char * const _surname_list[] = {
01286   "Adams",
01287   "Allan",
01288   "Baker",
01289   "Bigwig",
01290   "Black",
01291   "Bloggs",
01292   "Brown",
01293   "Campbell",
01294   "Gordon",
01295   "Hamilton",
01296   "Hawthorn",
01297   "Higgins",
01298   "Green",
01299   "Gribble",
01300   "Jones",
01301   "McAlpine",
01302   "MacDonald",
01303   "McIntosh",
01304   "Muir",
01305   "Murphy",
01306   "Nelson",
01307   "O'Donnell",
01308   "Parker",
01309   "Phillips",
01310   "Pilkington",
01311   "Quigley",
01312   "Sharkey",
01313   "Thomson",
01314   "Watkins"
01315 };
01316 
01317 static const char * const _silly_surname_list[] = {
01318   "Grumpy",
01319   "Dozy",
01320   "Speedy",
01321   "Nosey",
01322   "Dribble",
01323   "Mushroom",
01324   "Cabbage",
01325   "Sniffle",
01326   "Fishy",
01327   "Swindle",
01328   "Sneaky",
01329   "Nutkins"
01330 };
01331 
01332 static const char _initial_name_letters[] = {
01333   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01334   'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01335 };
01336 
01337 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01338 {
01339   const char * const *base;
01340   uint num;
01341 
01342   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01343     base = _silly_surname_list;
01344     num  = lengthof(_silly_surname_list);
01345   } else {
01346     base = _surname_list;
01347     num  = lengthof(_surname_list);
01348   }
01349 
01350   buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01351   buff = strecpy(buff, " & Co.", last);
01352 
01353   return buff;
01354 }
01355 
01356 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01357 {
01358   char initial[] = "?. ";
01359   const char * const *base;
01360   uint num;
01361   uint i;
01362 
01363   initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01364   buff = strecpy(buff, initial, last);
01365 
01366   i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01367   if (i < sizeof(_initial_name_letters)) {
01368     initial[0] = _initial_name_letters[i];
01369     buff = strecpy(buff, initial, last);
01370   }
01371 
01372   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01373     base = _silly_surname_list;
01374     num  = lengthof(_silly_surname_list);
01375   } else {
01376     base = _surname_list;
01377     num  = lengthof(_surname_list);
01378   }
01379 
01380   buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01381 
01382   return buff;
01383 }
01384 
01385 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const int64 *argve, const char *last, WChar *argt)
01386 {
01387   switch (ind) {
01388     case 1: // not used
01389       return strecpy(buff, _silly_company_names[GetInt32(&argv, argve, &argt) & 0xFFFF], last);
01390 
01391     case 2: // used for Foobar & Co company names
01392       return GenAndCoName(buff, GetInt32(&argv, argve, &argt), last);
01393 
01394     case 3: // President name
01395       return GenPresidentName(buff, GetInt32(&argv, argve, &argt), last);
01396   }
01397 
01398   /* town name? */
01399   if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01400     buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv, argve, &argt), last);
01401     return strecpy(buff, " Transport", last);
01402   }
01403 
01404   /* language name? */
01405   if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01406     int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01407     return strecpy(buff,
01408       &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
01409   }
01410 
01411   /* resolution size? */
01412   if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01413     int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01414     buff += seprintf(
01415       buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01416     );
01417     return buff;
01418   }
01419 
01420   /* screenshot format name? */
01421   if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01422     int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01423     return strecpy(buff, GetScreenshotFormatDesc(i), last);
01424   }
01425 
01426   NOT_REACHED();
01427 }
01428 
01429 #ifdef ENABLE_NETWORK
01430 extern void SortNetworkLanguages();
01431 #else /* ENABLE_NETWORK */
01432 static inline void SortNetworkLanguages() {}
01433 #endif /* ENABLE_NETWORK */
01434 
01439 bool LanguagePackHeader::IsValid() const
01440 {
01441   return this->ident        == TO_LE32(LanguagePackHeader::IDENT) &&
01442          this->version      == TO_LE32(LANGUAGE_PACK_VERSION) &&
01443          this->plural_form  <  LANGUAGE_MAX_PLURAL &&
01444          this->text_dir     <= 1 &&
01445          this->newgrflangid < MAX_LANG &&
01446          this->num_genders  < MAX_NUM_GENDERS &&
01447          this->num_cases    < MAX_NUM_CASES &&
01448          StrValid(this->name,                           lastof(this->name)) &&
01449          StrValid(this->own_name,                       lastof(this->own_name)) &&
01450          StrValid(this->isocode,                        lastof(this->isocode)) &&
01451          StrValid(this->digit_group_separator,          lastof(this->digit_group_separator)) &&
01452          StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
01453          StrValid(this->digit_decimal_separator,        lastof(this->digit_decimal_separator));
01454 }
01455 
01456 bool ReadLanguagePack(const LanguageMetadata *lang)
01457 {
01458   /* Current language pack */
01459   size_t len;
01460   LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
01461   if (lang_pack == NULL) return false;
01462 
01463   /* End of read data (+ terminating zero added in ReadFileToMem()) */
01464   const char *end = (char *)lang_pack + len + 1;
01465 
01466   /* We need at least one byte of lang_pack->data */
01467   if (end <= lang_pack->data || !lang_pack->IsValid()) {
01468     free(lang_pack);
01469     return false;
01470   }
01471 
01472 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01473   for (uint i = 0; i < 32; i++) {
01474     lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01475   }
01476 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
01477 
01478   uint count = 0;
01479   for (uint i = 0; i < 32; i++) {
01480     uint num = lang_pack->offsets[i];
01481     _langtab_start[i] = count;
01482     _langtab_num[i] = num;
01483     count += num;
01484   }
01485 
01486   /* Allocate offsets */
01487   char **langpack_offs = MallocT<char *>(count);
01488 
01489   /* Fill offsets */
01490   char *s = lang_pack->data;
01491   len = (byte)*s++;
01492   for (uint i = 0; i < count; i++) {
01493     if (s + len >= end) {
01494       free(lang_pack);
01495       free(langpack_offs);
01496       return false;
01497     }
01498     if (len >= 0xC0) {
01499       len = ((len & 0x3F) << 8) + (byte)*s++;
01500       if (s + len >= end) {
01501         free(lang_pack);
01502         free(langpack_offs);
01503         return false;
01504       }
01505     }
01506     langpack_offs[i] = s;
01507     s += len;
01508     len = (byte)*s;
01509     *s++ = '\0'; // zero terminate the string
01510   }
01511 
01512   free(_langpack);
01513   _langpack = lang_pack;
01514 
01515   free(_langpack_offs);
01516   _langpack_offs = langpack_offs;
01517 
01518   _current_language = lang;
01519   _current_text_dir = (TextDirection)_current_language->text_dir;
01520   const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
01521   strecpy(_config_language_file, c_file, lastof(_config_language_file));
01522   SetCurrentGrfLangID(_current_language->newgrflangid);
01523 
01524 #ifdef WITH_ICU
01525   /* Delete previous collator. */
01526   if (_current_collator != NULL) {
01527     delete _current_collator;
01528     _current_collator = NULL;
01529   }
01530 
01531   /* Create a collator instance for our current locale. */
01532   UErrorCode status = U_ZERO_ERROR;
01533   _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01534   /* Sort number substrings by their numerical value. */
01535   if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01536   /* Avoid using the collator if it is not correctly set. */
01537   if (U_FAILURE(status)) {
01538     delete _current_collator;
01539     _current_collator = NULL;
01540   }
01541 #endif /* WITH_ICU */
01542 
01543   /* Some lists need to be sorted again after a language change. */
01544   InitializeSortedCargoSpecs();
01545   SortIndustryTypes();
01546   BuildIndustriesLegend();
01547   SortNetworkLanguages();
01548   InvalidateWindowClassesData(WC_BUILD_VEHICLE);      // Build vehicle window.
01549   InvalidateWindowClassesData(WC_TRAINS_LIST);        // Train group window.
01550   InvalidateWindowClassesData(WC_ROADVEH_LIST);       // Road vehicle group window.
01551   InvalidateWindowClassesData(WC_SHIPS_LIST);         // Ship group window.
01552   InvalidateWindowClassesData(WC_AIRCRAFT_LIST);      // Aircraft group window.
01553   InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
01554   InvalidateWindowClassesData(WC_STATION_LIST);       // Station list window.
01555 
01556   return true;
01557 }
01558 
01559 /* Win32 implementation in win32.cpp.
01560  * OS X implementation in os/macosx/macos.mm. */
01561 #if !(defined(WIN32) || defined(__APPLE__))
01562 
01570 const char *GetCurrentLocale(const char *param)
01571 {
01572   const char *env;
01573 
01574   env = getenv("LANGUAGE");
01575   if (env != NULL) return env;
01576 
01577   env = getenv("LC_ALL");
01578   if (env != NULL) return env;
01579 
01580   if (param != NULL) {
01581     env = getenv(param);
01582     if (env != NULL) return env;
01583   }
01584 
01585   return getenv("LANG");
01586 }
01587 #else
01588 const char *GetCurrentLocale(const char *param);
01589 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
01590 
01591 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01592 {
01593   char stra[512];
01594   char strb[512];
01595   GetString(stra, *a, lastof(stra));
01596   GetString(strb, *b, lastof(strb));
01597 
01598   return strcmp(stra, strb);
01599 }
01600 
01606 const LanguageMetadata *GetLanguage(byte newgrflangid)
01607 {
01608   for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
01609     if (newgrflangid == lang->newgrflangid) return lang;
01610   }
01611 
01612   return NULL;
01613 }
01614 
01621 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
01622 {
01623   FILE *f = fopen(file, "rb");
01624   if (f == NULL) return false;
01625 
01626   size_t read = fread(hdr, sizeof(*hdr), 1, f);
01627   fclose(f);
01628 
01629   bool ret = read == 1 && hdr->IsValid();
01630 
01631   /* Convert endianness for the windows language ID */
01632   if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01633   return ret;
01634 }
01635 
01640 static void GetLanguageList(const char *path)
01641 {
01642   DIR *dir = ttd_opendir(path);
01643   if (dir != NULL) {
01644     struct dirent *dirent;
01645     while ((dirent = readdir(dir)) != NULL) {
01646       const char *d_name    = FS2OTTD(dirent->d_name);
01647       const char *extension = strrchr(d_name, '.');
01648 
01649       /* Not a language file */
01650       if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01651 
01652       LanguageMetadata lmd;
01653       seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
01654 
01655       /* Check whether the file is of the correct version */
01656       if (!GetLanguageFileHeader(lmd.file, &lmd)) {
01657         DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
01658       } else if (GetLanguage(lmd.newgrflangid) != NULL) {
01659         DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
01660       } else {
01661         *_languages.Append() = lmd;
01662       }
01663     }
01664     closedir(dir);
01665   }
01666 }
01667 
01672 void InitializeLanguagePacks()
01673 {
01674   Searchpath sp;
01675 
01676   FOR_ALL_SEARCHPATHS(sp) {
01677     char path[MAX_PATH];
01678     FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01679     GetLanguageList(path);
01680   }
01681   if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
01682 
01683   /* Acquire the locale of the current system */
01684   const char *lang = GetCurrentLocale("LC_MESSAGES");
01685   if (lang == NULL) lang = "en_GB";
01686 
01687   const LanguageMetadata *chosen_language   = NULL; 
01688   const LanguageMetadata *language_fallback = NULL; 
01689   const LanguageMetadata *en_GB_fallback    = _languages.Begin(); 
01690 
01691   /* Find a proper language. */
01692   for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01693     /* We are trying to find a default language. The priority is by
01694      * configuration file, local environment and last, if nothing found,
01695      * english. */
01696     const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
01697     if (strcmp(lang_file, _config_language_file) == 0) {
01698       chosen_language = lng;
01699       break;
01700     }
01701 
01702     if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback    = lng;
01703     if (strncmp(lng->isocode, lang, 5) == 0) chosen_language   = lng;
01704     if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
01705   }
01706 
01707   /* We haven't found the language in the config nor the one in the locale.
01708    * Now we set it to one of the fallback languages */
01709   if (chosen_language == NULL) {
01710     chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
01711   }
01712 
01713   if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
01714 }
01715 
01720 const char *GetCurrentLanguageIsoCode()
01721 {
01722   return _langpack->isocode;
01723 }
01724 
01731 static bool FindMissingGlyphs(const char **str)
01732 {
01733 #ifdef WITH_FREETYPE
01734   UninitFreeType();
01735   InitFreeType();
01736 #endif
01737   const Sprite *question_mark[FS_END];
01738   FontSize size;
01739 
01740   for (size = FS_BEGIN; size < FS_END; size++) {
01741     question_mark[size] = GetGlyph(size, '?');
01742   }
01743 
01744   for (uint i = 0; i != 32; i++) {
01745     for (uint j = 0; j < _langtab_num[i]; j++) {
01746       size = FS_NORMAL;
01747       const char *text = _langpack_offs[_langtab_start[i] + j];
01748       if (str != NULL) *str = text;
01749       for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
01750         if (c == SCC_SETX) {
01751           /* SetX is, together with SetXY as special character that
01752            * uses the next (two) characters as data points. We have
01753            * to skip those, otherwise the UTF8 reading will go haywire. */
01754           text++;
01755         } else if (c == SCC_SETXY) {
01756           text += 2;
01757         } else if (c == SCC_TINYFONT) {
01758           size = FS_SMALL;
01759         } else if (c == SCC_BIGFONT) {
01760           size = FS_LARGE;
01761         } else if (IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
01762           /* The character is printable, but not in the normal font. This is the case we were testing for. */
01763           return true;
01764         }
01765       }
01766     }
01767   }
01768   return false;
01769 }
01770 
01781 void CheckForMissingGlyphsInLoadedLanguagePack()
01782 {
01783   bool bad_font = FindMissingGlyphs(NULL);
01784 #ifdef WITH_FREETYPE
01785   if (bad_font) {
01786     /* We found an unprintable character... lets try whether we can find
01787      * a fallback font that can print the characters in the current language. */
01788     FreeTypeSettings backup;
01789     memcpy(&backup, &_freetype, sizeof(backup));
01790 
01791     bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, &FindMissingGlyphs);
01792 
01793     memcpy(&_freetype, &backup, sizeof(backup));
01794 
01795     if (bad_font) {
01796       /* Our fallback font does miss characters too, so keep the
01797        * user chosen font as that is more likely to be any good than
01798        * the wild guess we made */
01799       UninitFreeType();
01800       InitFreeType();
01801     }
01802   }
01803 #endif
01804 
01805   if (bad_font) {
01806     /* All attempts have failed. Display an error. As we do not want the string to be translated by
01807      * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
01808      * properly we have to set the colour of the string, otherwise we end up with a lot of artefacts.
01809      * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
01810      * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
01811     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.");
01812     Utf8Encode(err_str, SCC_YELLOW);
01813     SetDParamStr(0, err_str);
01814     ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
01815 
01816     /* Reset the font width */
01817     LoadStringWidthTable();
01818     return;
01819   }
01820 
01821   /* Update the font with cache */
01822   LoadStringWidthTable();
01823 
01824 #if !defined(WITH_ICU)
01825   /*
01826    * For right-to-left languages we need the ICU library. If
01827    * we do not have support for that library we warn the user
01828    * about it with a message. As we do not want the string to
01829    * be translated by the translators, we 'force' it into the
01830    * binary and 'load' it via a BindCString. To do this
01831    * properly we have to set the colour of the string,
01832    * otherwise we end up with a lot of artefacts. The colour
01833    * 'character' might change in the future, so for safety
01834    * we just Utf8 Encode it into the string, which takes
01835    * exactly three characters, so it replaces the "XXX" with
01836    * the colour marker.
01837    */
01838   if (_current_text_dir != TD_LTR) {
01839     static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01840     Utf8Encode(err_str, SCC_YELLOW);
01841     SetDParamStr(0, err_str);
01842     ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
01843   }
01844 #endif
01845 }

Generated on Fri Mar 4 21:37:06 2011 for OpenTTD by  doxygen 1.6.1