strings.cpp

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

Generated on Sun May 8 07:30:20 2011 for OpenTTD by  doxygen 1.6.1