strings.cpp

Go to the documentation of this file.
00001 /* $Id: strings.cpp 21885 2011-01-21 23:10:02Z yexo $ */
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 
00546 struct Units {
00547   int s_m;           
00548   int s_s;           
00549   StringID velocity; 
00550   int p_m;           
00551   int p_s;           
00552   StringID power;    
00553   int w_m;           
00554   int w_s;           
00555   StringID s_weight; 
00556   StringID l_weight; 
00557   int v_m;           
00558   int v_s;           
00559   StringID s_volume; 
00560   StringID l_volume; 
00561   int f_m;           
00562   int f_s;           
00563   StringID force;    
00564   int h_m;           
00565   int h_s;           
00566   StringID height;   
00567 };
00568 
00569 /* Unit conversions */
00570 static const Units units[] = {
00571   { // Imperial (Original, mph, hp, metric ton, litre, kN, ft)
00572        1,  0, STR_UNITS_VELOCITY_IMPERIAL,
00573        1,  0, STR_UNITS_POWER_IMPERIAL,
00574        1,  0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00575     1000,  0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00576        1,  0, STR_UNITS_FORCE_SI,
00577        3,  0, STR_UNITS_HEIGHT_IMPERIAL,
00578   },
00579   { // Metric (km/h, hp, metric ton, litre, kN, metre)
00580      103,  6, STR_UNITS_VELOCITY_METRIC,
00581        1,  0, STR_UNITS_POWER_METRIC,
00582        1,  0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00583     1000,  0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00584        1,  0, STR_UNITS_FORCE_SI,
00585        1,  0, STR_UNITS_HEIGHT_SI,
00586   },
00587   { // SI (m/s, kilowatt, kilogram, cubic metre, kilonewton, metre)
00588     1831, 12, STR_UNITS_VELOCITY_SI,
00589      764, 10, STR_UNITS_POWER_SI,
00590     1000,  0, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00591        1,  0, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00592        1,  0, STR_UNITS_FORCE_SI,
00593        1,  0, STR_UNITS_HEIGHT_SI,
00594   },
00595 };
00596 
00602 uint ConvertSpeedToDisplaySpeed(uint speed)
00603 {
00604   return (speed * units[_settings_game.locale.units].s_m) >> units[_settings_game.locale.units].s_s;
00605 }
00606 
00612 uint ConvertDisplaySpeedToSpeed(uint speed)
00613 {
00614   return ((speed << units[_settings_game.locale.units].s_s) + units[_settings_game.locale.units].s_m / 2) / units[_settings_game.locale.units].s_m;
00615 }
00616 
00628 static char *FormatString(char *buff, const char *str_arg, int64 *argv, const int64 *argve, uint casei, const char *last, WChar *argt, bool dry_run)
00629 {
00630   /* When there is no array with types there is no need to do a dry run. */
00631   if (argt == NULL) dry_run = true;
00632   if (UsingNewGRFTextStack() && !dry_run) {
00633     /* Values from the NewGRF text stack are only copied to the normal
00634      * argv array at the time they are encountered. That means that if
00635      * another string command references a value later in the string it
00636      * would fail. We solve that by running FormatString twice. The first
00637      * pass makes sure the argv array is correctly filled and the second
00638      * pass can reference later values without problems. */
00639     struct TextRefStack *backup = CreateTextRefStackBackup();
00640     FormatString(buff, str_arg, argv, argve, casei, last, argt, true);
00641     RestoreTextRefStackBackup(backup);
00642   } else if (!dry_run) {
00643     FormatString(buff, str_arg, argv, argve, casei, last, argt, true);
00644   }
00645   WChar b;
00646   int64 *argv_orig = argv;
00647   WChar *argt_orig = argt;
00648   uint modifier = 0;
00649   char *buf_start = buff;
00650   std::stack<const char *> str_stack;
00651   str_stack.push(str_arg);
00652 
00653   while (true) {
00654     while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
00655       str_stack.pop();
00656     }
00657     if (str_stack.empty()) break;
00658     const char *&str = str_stack.top();
00659 
00660     if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00661       /* We need to pass some stuff as it might be modified; oh boy. */
00662       //todo: should argve be passed here too?
00663       b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, argv);
00664       if (b == 0) continue;
00665     }
00666 
00667     switch (b) {
00668       case SCC_NEWGRF_STRINL: {
00669         StringID substr = Utf8Consume(&str);
00670         str_stack.push(GetStringPtr(substr));
00671         break;
00672       }
00673 
00674       case SCC_NEWGRF_PRINT_STRING_ID: {
00675         StringID substr = GetInt32(&argv, argve, &argt, SCC_NEWGRF_PRINT_STRING_ID);
00676         str_stack.push(GetStringPtr(substr));
00677         break;
00678       }
00679 
00680 
00681       case SCC_SETX: // {SETX}
00682         if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00683           buff += Utf8Encode(buff, SCC_SETX);
00684           *buff++ = *str++;
00685         }
00686         break;
00687 
00688       case SCC_SETXY: // {SETXY}
00689         if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00690           buff += Utf8Encode(buff, SCC_SETXY);
00691           *buff++ = *str++;
00692           *buff++ = *str++;
00693         }
00694         break;
00695 
00696       case SCC_STRING_ID: // {STRINL}
00697         buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, argve, last, argt);
00698         break;
00699 
00700       case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
00701         const char *str = (const char*)(size_t)GetInt64(&argv, argve, &argt);
00702         buff = FormatString(buff, str, argv, argve, casei, last, argt);
00703         break;
00704       }
00705 
00706       case SCC_DATE_LONG: // {DATE_LONG}
00707         buff = FormatYmdString(buff, GetInt32(&argv, argve, &argt, SCC_DATE_LONG), modifier, last);
00708         break;
00709 
00710       case SCC_DATE_SHORT: // {DATE_SHORT}
00711         buff = FormatMonthAndYear(buff, GetInt32(&argv, argve, &argt, SCC_DATE_SHORT), modifier, last);
00712         break;
00713 
00714       case SCC_VELOCITY: { // {VELOCITY}
00715         int64 args[1];
00716         assert(_settings_game.locale.units < lengthof(units));
00717         args[0] = ConvertSpeedToDisplaySpeed(GetInt32(&argv, argve, &argt, SCC_VELOCITY) * 10 / 16);
00718         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].velocity), args, endof(args), modifier >> 24, last);
00719         modifier = 0;
00720         break;
00721       }
00722 
00723       case SCC_HEIGHT: { // {HEIGHT}
00724         int64 args[1] = {GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].h_m >> units[_settings_game.locale.units].h_s};
00725         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].height), args, endof(args), modifier >> 24, last);
00726         modifier = 0;
00727         break;
00728       }
00729 
00730       case SCC_CURRENCY_COMPACT: // {CURRCOMPACT}
00731         buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv, argve, &argt), true, last);
00732         break;
00733 
00734       case SCC_REVISION: // {REV}
00735         buff = strecpy(buff, _openttd_revision, last);
00736         break;
00737 
00738       case SCC_CARGO_SHORT: { // {SHORTCARGO}
00739         /* Short description of cargotypes. Layout:
00740          * 8-bit = cargo type
00741          * 16-bit = cargo count */
00742         StringID cargo_str = CargoSpec::Get(GetInt32(&argv, argve, &argt, SCC_CARGO_SHORT))->units_volume;
00743         switch (cargo_str) {
00744           case STR_TONS: {
00745             int64 args[1];
00746             assert(_settings_game.locale.units < lengthof(units));
00747             args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00748             buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, endof(args), modifier >> 24, last);
00749             modifier = 0;
00750             break;
00751           }
00752 
00753           case STR_LITERS: {
00754             int64 args[1];
00755             assert(_settings_game.locale.units < lengthof(units));
00756             args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00757             buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, endof(args), modifier >> 24, last);
00758             modifier = 0;
00759             break;
00760           }
00761 
00762           default:
00763             buff = GetStringWithArgs(buff, cargo_str, argv++, argve, last, argt++);
00764             break;
00765         }
00766         break;
00767       }
00768 
00769       case SCC_STRING1: { // {STRING1}
00770         /* String that consumes ONE argument */
00771         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING1);
00772         WChar *orig_argt = argt;
00773         int64 *args = GetArgvPtr(&argv, 1, argve, &argt);
00774         buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00775         modifier = 0;
00776         break;
00777       }
00778 
00779       case SCC_STRING2: { // {STRING2}
00780         /* String that consumes TWO arguments */
00781         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING2);
00782         WChar *orig_argt = argt;
00783         int64 *args = GetArgvPtr(&argv, 2, argve, &argt);
00784         buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00785         modifier = 0;
00786         break;
00787       }
00788 
00789       case SCC_STRING3: { // {STRING3}
00790         /* String that consumes THREE arguments */
00791         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING3);
00792         WChar *orig_argt = argt;
00793         int64 *args = GetArgvPtr(&argv, 3, argve, &argt);
00794         buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00795         modifier = 0;
00796         break;
00797       }
00798 
00799       case SCC_STRING4: { // {STRING4}
00800         /* String that consumes FOUR arguments */
00801         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING4);
00802         WChar *orig_argt = argt;
00803         int64 *args = GetArgvPtr(&argv, 4, argve, &argt);
00804         buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00805         modifier = 0;
00806         break;
00807       }
00808 
00809       case SCC_STRING5: { // {STRING5}
00810         /* String that consumes FIVE arguments */
00811         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING5);
00812         WChar *orig_argt = argt;
00813         int64 *args = GetArgvPtr(&argv, 5, argve, &argt);
00814         buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00815         modifier = 0;
00816         break;
00817       }
00818 
00819       case SCC_STATION_FEATURES: { // {STATIONFEATURES}
00820         buff = StationGetSpecialString(buff, GetInt32(&argv, argve, &argt, SCC_STATION_FEATURES), last);
00821         break;
00822       }
00823 
00824       case SCC_INDUSTRY_NAME: { // {INDUSTRY}
00825         const Industry *i = Industry::Get(GetInt32(&argv, argve, &argt, SCC_INDUSTRY_NAME));
00826         int64 args[2];
00827 
00828         /* industry not valid anymore? */
00829         assert(i != NULL);
00830 
00831         /* First print the town name and the industry type name. */
00832         args[0] = i->town->index;
00833         args[1] = GetIndustrySpec(i->type)->name;
00834         buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), args, endof(args), modifier >> 24, last);
00835         modifier = 0;
00836         break;
00837       }
00838 
00839       case SCC_VOLUME: { // {VOLUME}
00840         int64 args[1];
00841         assert(_settings_game.locale.units < lengthof(units));
00842         args[0] = GetInt32(&argv, argve, &argt, SCC_VOLUME) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00843         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, endof(args), modifier >> 24, last);
00844         modifier = 0;
00845         break;
00846       }
00847 
00848       case SCC_GENDER_LIST: { // {G 0 Der Die Das}
00849         /* First read the meta data from the language file. */
00850         byte offset = (byte)*str++;
00851         assert(argv_orig + offset < argve);
00852         int gender = 0;
00853         if (!dry_run && argt != NULL && argt_orig[offset] != 0) {
00854           /* Now we need to figure out what text to resolve, i.e.
00855            * what do we need to draw? So get the actual raw string
00856            * first using the control code to get said string. */
00857           char input[4 + 1];
00858           char *p = input + Utf8Encode(input, argt_orig[offset]);
00859           *p = '\0';
00860 
00861           /* Now do the string formatting. */
00862           char buf[256];
00863           bool old_kgd = _keep_gender_data;
00864           _keep_gender_data = true;
00865           p = FormatString(buf, input, argv_orig + offset, argve, 0, lastof(buf));
00866           _keep_gender_data = old_kgd;
00867           *p = '\0';
00868 
00869           /* And determine the string. */
00870           const char *s = buf;
00871           WChar c = Utf8Consume(&s);
00872           /* Does this string have a gender, if so, set it */
00873           if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00874         }
00875         str = ParseStringChoice(str, gender, &buff, last);
00876         break;
00877       }
00878 
00879       case SCC_DATE_TINY: { // {DATE_TINY}
00880         buff = FormatTinyOrISODate(buff, GetInt32(&argv, argve, &argt, SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
00881         break;
00882       }
00883 
00884       case SCC_DATE_ISO: { // {DATE_ISO}
00885         buff = FormatTinyOrISODate(buff, GetInt32(&argv, argve, &argt), STR_FORMAT_DATE_ISO, last);
00886         break;
00887       }
00888 
00889       case SCC_CARGO: { // {CARGO}
00890         /* First parameter is cargo type, second parameter is cargo count */
00891         CargoID cargo = GetInt32(&argv, argve, &argt, SCC_CARGO);
00892         StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
00893         buff = GetStringWithArgs(buff, cargo_str, argv++, argve, last);
00894         break;
00895       }
00896 
00897       case SCC_POWER: { // {POWER}
00898         int64 args[1];
00899         assert(_settings_game.locale.units < lengthof(units));
00900         args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].p_m >> units[_settings_game.locale.units].p_s;
00901         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].power), args, endof(args), modifier >> 24, last);
00902         modifier = 0;
00903         break;
00904       }
00905 
00906       case SCC_VOLUME_SHORT: { // {VOLUME_S}
00907         int64 args[1];
00908         assert(_settings_game.locale.units < lengthof(units));
00909         args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00910         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_volume), args, endof(args), modifier >> 24, last);
00911         modifier = 0;
00912         break;
00913       }
00914 
00915       case SCC_WEIGHT: { // {WEIGHT}
00916         int64 args[1];
00917         assert(_settings_game.locale.units < lengthof(units));
00918         args[0] = GetInt32(&argv, argve, &argt, SCC_WEIGHT) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00919         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, endof(args), modifier >> 24, last);
00920         modifier = 0;
00921         break;
00922       }
00923 
00924       case SCC_WEIGHT_SHORT: { // {WEIGHT_S}
00925         int64 args[1];
00926         assert(_settings_game.locale.units < lengthof(units));
00927         args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00928         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_weight), args, endof(args), modifier >> 24, last);
00929         modifier = 0;
00930         break;
00931       }
00932 
00933       case SCC_FORCE: { // {FORCE}
00934         int64 args[1];
00935         assert(_settings_game.locale.units < lengthof(units));
00936         args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].f_m >> units[_settings_game.locale.units].f_s;
00937         buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].force), args, endof(args), modifier >> 24, last);
00938         modifier = 0;
00939         break;
00940       }
00941 
00942       /* This sets up the gender for the string.
00943        * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
00944       case SCC_GENDER_INDEX: // {GENDER 0}
00945         if (_keep_gender_data) {
00946           buff += Utf8Encode(buff, SCC_GENDER_INDEX);
00947           *buff++ = *str++;
00948         } else {
00949           str++;
00950         }
00951         break;
00952 
00953       case SCC_STRING: {// {STRING}
00954         uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING);
00955         /* WARNING. It's prohibited for the included string to consume any arguments.
00956          * For included strings that consume argument, you should use STRING1, STRING2 etc.
00957          * To debug stuff you can set argv to NULL and it will tell you */
00958         buff = GetStringWithArgs(buff, str, argv, argve, last);
00959         modifier = 0;
00960         break;
00961       }
00962 
00963       case SCC_COMMA: // {COMMA}
00964         buff = FormatCommaNumber(buff, GetInt64(&argv, argve, &argt, SCC_COMMA), last);
00965         break;
00966 
00967       case SCC_ARG_INDEX: { // Move argument pointer
00968         byte offset = (byte)*str++;
00969         argv = argv_orig + offset;
00970         if (argt_orig != NULL) argt = argt_orig + offset;
00971         break;
00972       }
00973 
00974       case SCC_PLURAL_LIST: { // {P}
00975         int plural_form = *str++;          // contains the plural form for this string
00976         byte idx = *str++;
00977         assert(argv_orig + idx < argve);
00978         int64 v = argv_orig[idx]; // contains the number that determines plural
00979         str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
00980         break;
00981       }
00982 
00983       case SCC_NUM: // {NUM}
00984         buff = FormatNoCommaNumber(buff, GetInt64(&argv, argve, &argt, SCC_NUM), last);
00985         break;
00986 
00987       case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
00988         int64 num = GetInt64(&argv, argve, &argt);
00989         buff = FormatZerofillNumber(buff, num, GetInt64(&argv, argve, &argt), last);
00990         break;
00991       }
00992 
00993       case SCC_HEX: // {HEX}
00994         buff = FormatHexNumber(buff, (uint64)GetInt64(&argv, argve, &argt, SCC_HEX), last);
00995         break;
00996 
00997       case SCC_BYTES: // {BYTES}
00998         buff = FormatBytes(buff, GetInt64(&argv, argve, &argt), last);
00999         break;
01000 
01001       case SCC_CURRENCY: // {CURRENCY}
01002         buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv, argve, &argt, SCC_CURRENCY), false, last);
01003         break;
01004 
01005       case SCC_WAYPOINT_NAME: { // {WAYPOINT}
01006         Waypoint *wp = Waypoint::Get(GetInt32(&argv, argve, &argt, SCC_WAYPOINT_NAME));
01007 
01008         assert(wp != NULL);
01009 
01010         if (wp->name != NULL) {
01011           buff = strecpy(buff, wp->name, last);
01012         } else {
01013           int64 args[2];
01014           args[0] = wp->town->index;
01015           args[1] = wp->town_cn + 1;
01016           StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
01017           if (wp->town_cn != 0) str++;
01018           buff = GetStringWithArgs(buff, str, args, endof(args), last);
01019         }
01020         break;
01021       }
01022 
01023       case SCC_STATION_NAME: { // {STATION}
01024         StationID sid = GetInt32(&argv, argve, &argt, SCC_STATION_NAME);
01025         const Station *st = Station::GetIfValid(sid);
01026 
01027         if (st == NULL) {
01028           /* The station doesn't exist anymore. The only place where we might
01029            * be "drawing" an invalid station is in the case of cargo that is
01030            * in transit. */
01031           buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, NULL, NULL, last);
01032           break;
01033         }
01034 
01035         if (st->name != NULL) {
01036           buff = strecpy(buff, st->name, last);
01037         } else {
01038           StringID str = st->string_id;
01039           if (st->indtype != IT_INVALID) {
01040             /* Special case where the industry provides the name for the station */
01041             const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01042 
01043             /* Industry GRFs can change which might remove the station name and
01044              * thus cause very strange things. Here we check for that before we
01045              * actually set the station name. */
01046             if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
01047               str = indsp->station_name;
01048             }
01049           }
01050 
01051           int64 args[3];
01052           args[0] = STR_TOWN_NAME;
01053           args[1] = st->town->index;
01054           args[2] = st->index;
01055           buff = GetStringWithArgs(buff, str, args, endof(args), last);
01056         }
01057         break;
01058       }
01059 
01060       case SCC_DEPOT_NAME: { // {DEPOT}
01061         VehicleType vt = (VehicleType)GetInt32(&argv, argve, &argt, SCC_DEPOT_NAME);
01062         if (vt == VEH_AIRCRAFT) {
01063           int64 args[] = { GetInt32(&argv, argve, &argt) };
01064           buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, args, endof(args), last);
01065           break;
01066         }
01067 
01068         const Depot *d = Depot::Get(GetInt32(&argv, argve, &argt));
01069         if (d->name != NULL) {
01070           buff = strecpy(buff, d->name, last);
01071         } else {
01072           int64 args[] = { d->town->index, d->town_cn + 1 };
01073           buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), args, endof(args), last);
01074         }
01075         break;
01076       }
01077 
01078       case SCC_TOWN_NAME: { // {TOWN}
01079         const Town *t = Town::Get(GetInt32(&argv, argve, &argt, SCC_TOWN_NAME));
01080 
01081         assert(t != NULL);
01082 
01083         if (t->name != NULL) {
01084           buff = strecpy(buff, t->name, last);
01085         } else {
01086           buff = GetTownName(buff, t, last);
01087         }
01088         break;
01089       }
01090 
01091       case SCC_GROUP_NAME: { // {GROUP}
01092         const Group *g = Group::Get(GetInt32(&argv, argve, &argt));
01093 
01094         assert(g != NULL);
01095 
01096         if (g->name != NULL) {
01097           buff = strecpy(buff, g->name, last);
01098         } else {
01099           int64 args[1];
01100 
01101           args[0] = g->index;
01102           buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, args, endof(args), last);
01103         }
01104         break;
01105       }
01106 
01107       case SCC_ENGINE_NAME: { // {ENGINE}
01108         EngineID engine = (EngineID)GetInt32(&argv, argve, &argt, SCC_ENGINE_NAME);
01109         const Engine *e = Engine::Get(engine);
01110 
01111         assert(e != NULL);
01112 
01113         if (e->name != NULL && e->info.string_id != STR_NEWGRF_INVALID_ENGINE) {
01114           buff = strecpy(buff, e->name, last);
01115         } else {
01116           buff = GetStringWithArgs(buff, e->info.string_id, NULL, NULL, last);
01117         }
01118         break;
01119       }
01120 
01121       case SCC_VEHICLE_NAME: { // {VEHICLE}
01122         const Vehicle *v = Vehicle::Get(GetInt32(&argv, argve, &argt, SCC_VEHICLE_NAME));
01123 
01124         assert(v != NULL);
01125 
01126         if (v->name != NULL) {
01127           buff = strecpy(buff, v->name, last);
01128         } else {
01129           int64 args[1];
01130           args[0] = v->unitnumber;
01131 
01132           StringID str;
01133           switch (v->type) {
01134             default: NOT_REACHED();
01135             case VEH_TRAIN:    str = STR_SV_TRAIN_NAME; break;
01136             case VEH_ROAD:     str = STR_SV_ROAD_VEHICLE_NAME; break;
01137             case VEH_SHIP:     str = STR_SV_SHIP_NAME; break;
01138             case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01139           }
01140 
01141           buff = GetStringWithArgs(buff, str, args, endof(args), last);
01142         }
01143         break;
01144       }
01145 
01146       case SCC_SIGN_NAME: { // {SIGN}
01147         const Sign *si = Sign::Get(GetInt32(&argv, argve, &argt));
01148         if (si->name != NULL) {
01149           buff = strecpy(buff, si->name, last);
01150         } else {
01151           buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, NULL, NULL, last);
01152         }
01153         break;
01154       }
01155 
01156       case SCC_COMPANY_NAME: { // {COMPANY}
01157         const Company *c = Company::Get((CompanyID)GetInt32(&argv, argve, &argt));
01158 
01159         if (c->name != NULL) {
01160           buff = strecpy(buff, c->name, last);
01161         } else {
01162           int64 args[1];
01163           args[0] = c->name_2;
01164           buff = GetStringWithArgs(buff, c->name_1, args, endof(args), last);
01165         }
01166         break;
01167       }
01168 
01169       case SCC_COMPANY_NUM: { // {COMPANYNUM}
01170         CompanyID company = (CompanyID)GetInt32(&argv, argve, &argt);
01171 
01172         /* Nothing is added for AI or inactive companies */
01173         if (Company::IsValidHumanID(company)) {
01174           int64 args[1];
01175           args[0] = company + 1;
01176           buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, args, endof(args), last);
01177         }
01178         break;
01179       }
01180 
01181       case SCC_PRESIDENT_NAME: { // {PRESIDENTNAME}
01182         const Company *c = Company::Get((CompanyID)GetInt32(&argv, argve, &argt, SCC_PRESIDENT_NAME));
01183 
01184         if (c->president_name != NULL) {
01185           buff = strecpy(buff, c->president_name, last);
01186         } else {
01187           int64 args[1];
01188           args[0] = c->president_name_2;
01189           buff = GetStringWithArgs(buff, c->president_name_1, args, endof(args), last);
01190         }
01191         break;
01192       }
01193 
01194       case SCC_SETCASE: { // {SETCASE}
01195         /* This is a pseudo command, it's outputted when someone does {STRING.ack}
01196          * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
01197         modifier = (byte)*str++ << 24;
01198         break;
01199       }
01200 
01201       case SCC_SWITCH_CASE: { // {Used to implement case switching}
01202         /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
01203          * Each LEN is printed using 2 bytes in big endian order. */
01204         uint num = (byte)*str++;
01205         while (num) {
01206           if ((byte)str[0] == casei) {
01207             /* Found the case, adjust str pointer and continue */
01208             str += 3;
01209             break;
01210           }
01211           /* Otherwise skip to the next case */
01212           str += 3 + (str[1] << 8) + str[2];
01213           num--;
01214         }
01215         break;
01216       }
01217 
01218       default:
01219         if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01220         break;
01221     }
01222   }
01223   *buff = '\0';
01224   return buff;
01225 }
01226 
01227 
01228 static char *StationGetSpecialString(char *buff, int x, const char *last)
01229 {
01230   if ((x & FACIL_TRAIN)      && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01231   if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01232   if ((x & FACIL_BUS_STOP)   && (buff + Utf8CharLen(SCC_BUS)   < last)) buff += Utf8Encode(buff, SCC_BUS);
01233   if ((x & FACIL_DOCK)       && (buff + Utf8CharLen(SCC_SHIP)  < last)) buff += Utf8Encode(buff, SCC_SHIP);
01234   if ((x & FACIL_AIRPORT)    && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01235   *buff = '\0';
01236   return buff;
01237 }
01238 
01239 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01240 {
01241   return GenerateTownNameString(buff, last, ind, seed);
01242 }
01243 
01244 static const char * const _silly_company_names[] = {
01245   "Bloggs Brothers",
01246   "Tiny Transport Ltd.",
01247   "Express Travel",
01248   "Comfy-Coach & Co.",
01249   "Crush & Bump Ltd.",
01250   "Broken & Late Ltd.",
01251   "Sam Speedy & Son",
01252   "Supersonic Travel",
01253   "Mike's Motors",
01254   "Lightning International",
01255   "Pannik & Loozit Ltd.",
01256   "Inter-City Transport",
01257   "Getout & Pushit Ltd."
01258 };
01259 
01260 static const char * const _surname_list[] = {
01261   "Adams",
01262   "Allan",
01263   "Baker",
01264   "Bigwig",
01265   "Black",
01266   "Bloggs",
01267   "Brown",
01268   "Campbell",
01269   "Gordon",
01270   "Hamilton",
01271   "Hawthorn",
01272   "Higgins",
01273   "Green",
01274   "Gribble",
01275   "Jones",
01276   "McAlpine",
01277   "MacDonald",
01278   "McIntosh",
01279   "Muir",
01280   "Murphy",
01281   "Nelson",
01282   "O'Donnell",
01283   "Parker",
01284   "Phillips",
01285   "Pilkington",
01286   "Quigley",
01287   "Sharkey",
01288   "Thomson",
01289   "Watkins"
01290 };
01291 
01292 static const char * const _silly_surname_list[] = {
01293   "Grumpy",
01294   "Dozy",
01295   "Speedy",
01296   "Nosey",
01297   "Dribble",
01298   "Mushroom",
01299   "Cabbage",
01300   "Sniffle",
01301   "Fishy",
01302   "Swindle",
01303   "Sneaky",
01304   "Nutkins"
01305 };
01306 
01307 static const char _initial_name_letters[] = {
01308   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01309   'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01310 };
01311 
01312 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01313 {
01314   const char * const *base;
01315   uint num;
01316 
01317   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01318     base = _silly_surname_list;
01319     num  = lengthof(_silly_surname_list);
01320   } else {
01321     base = _surname_list;
01322     num  = lengthof(_surname_list);
01323   }
01324 
01325   buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01326   buff = strecpy(buff, " & Co.", last);
01327 
01328   return buff;
01329 }
01330 
01331 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01332 {
01333   char initial[] = "?. ";
01334   const char * const *base;
01335   uint num;
01336   uint i;
01337 
01338   initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01339   buff = strecpy(buff, initial, last);
01340 
01341   i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01342   if (i < sizeof(_initial_name_letters)) {
01343     initial[0] = _initial_name_letters[i];
01344     buff = strecpy(buff, initial, last);
01345   }
01346 
01347   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01348     base = _silly_surname_list;
01349     num  = lengthof(_silly_surname_list);
01350   } else {
01351     base = _surname_list;
01352     num  = lengthof(_surname_list);
01353   }
01354 
01355   buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01356 
01357   return buff;
01358 }
01359 
01360 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const int64 *argve, const char *last, WChar *argt)
01361 {
01362   switch (ind) {
01363     case 1: // not used
01364       return strecpy(buff, _silly_company_names[GetInt32(&argv, argve, &argt) & 0xFFFF], last);
01365 
01366     case 2: // used for Foobar & Co company names
01367       return GenAndCoName(buff, GetInt32(&argv, argve, &argt), last);
01368 
01369     case 3: // President name
01370       return GenPresidentName(buff, GetInt32(&argv, argve, &argt), last);
01371   }
01372 
01373   /* town name? */
01374   if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01375     buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv, argve, &argt), last);
01376     return strecpy(buff, " Transport", last);
01377   }
01378 
01379   /* language name? */
01380   if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01381     int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01382     return strecpy(buff,
01383       &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
01384   }
01385 
01386   /* resolution size? */
01387   if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01388     int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01389     buff += seprintf(
01390       buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01391     );
01392     return buff;
01393   }
01394 
01395   /* screenshot format name? */
01396   if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01397     int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01398     return strecpy(buff, GetScreenshotFormatDesc(i), last);
01399   }
01400 
01401   NOT_REACHED();
01402 }
01403 
01404 #ifdef ENABLE_NETWORK
01405 extern void SortNetworkLanguages();
01406 #else /* ENABLE_NETWORK */
01407 static inline void SortNetworkLanguages() {}
01408 #endif /* ENABLE_NETWORK */
01409 
01414 bool LanguagePackHeader::IsValid() const
01415 {
01416   return this->ident        == TO_LE32(LanguagePackHeader::IDENT) &&
01417          this->version      == TO_LE32(LANGUAGE_PACK_VERSION) &&
01418          this->plural_form  <  LANGUAGE_MAX_PLURAL &&
01419          this->text_dir     <= 1 &&
01420          this->newgrflangid < MAX_LANG &&
01421          this->num_genders  < MAX_NUM_GENDERS &&
01422          this->num_cases    < MAX_NUM_CASES &&
01423          StrValid(this->name,                           lastof(this->name)) &&
01424          StrValid(this->own_name,                       lastof(this->own_name)) &&
01425          StrValid(this->isocode,                        lastof(this->isocode)) &&
01426          StrValid(this->digit_group_separator,          lastof(this->digit_group_separator)) &&
01427          StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
01428          StrValid(this->digit_decimal_separator,        lastof(this->digit_decimal_separator));
01429 }
01430 
01431 bool ReadLanguagePack(const LanguageMetadata *lang)
01432 {
01433   /* Current language pack */
01434   size_t len;
01435   LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 200000);
01436   if (lang_pack == NULL) return false;
01437 
01438   /* End of read data (+ terminating zero added in ReadFileToMem()) */
01439   const char *end = (char *)lang_pack + len + 1;
01440 
01441   /* We need at least one byte of lang_pack->data */
01442   if (end <= lang_pack->data || !lang_pack->IsValid()) {
01443     free(lang_pack);
01444     return false;
01445   }
01446 
01447 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01448   for (uint i = 0; i < 32; i++) {
01449     lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01450   }
01451 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
01452 
01453   uint count = 0;
01454   for (uint i = 0; i < 32; i++) {
01455     uint num = lang_pack->offsets[i];
01456     _langtab_start[i] = count;
01457     _langtab_num[i] = num;
01458     count += num;
01459   }
01460 
01461   /* Allocate offsets */
01462   char **langpack_offs = MallocT<char *>(count);
01463 
01464   /* Fill offsets */
01465   char *s = lang_pack->data;
01466   len = (byte)*s++;
01467   for (uint i = 0; i < count; i++) {
01468     if (s + len >= end) {
01469       free(lang_pack);
01470       free(langpack_offs);
01471       return false;
01472     }
01473     if (len >= 0xC0) {
01474       len = ((len & 0x3F) << 8) + (byte)*s++;
01475       if (s + len >= end) {
01476         free(lang_pack);
01477         free(langpack_offs);
01478         return false;
01479       }
01480     }
01481     langpack_offs[i] = s;
01482     s += len;
01483     len = (byte)*s;
01484     *s++ = '\0'; // zero terminate the string
01485   }
01486 
01487   free(_langpack);
01488   _langpack = lang_pack;
01489 
01490   free(_langpack_offs);
01491   _langpack_offs = langpack_offs;
01492 
01493   _current_language = lang;
01494   _current_text_dir = (TextDirection)_current_language->text_dir;
01495   const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
01496   strecpy(_config_language_file, c_file, lastof(_config_language_file));
01497   SetCurrentGrfLangID(_current_language->newgrflangid);
01498 
01499 #ifdef WITH_ICU
01500   /* Delete previous collator. */
01501   if (_current_collator != NULL) {
01502     delete _current_collator;
01503     _current_collator = NULL;
01504   }
01505 
01506   /* Create a collator instance for our current locale. */
01507   UErrorCode status = U_ZERO_ERROR;
01508   _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01509   /* Sort number substrings by their numerical value. */
01510   if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01511   /* Avoid using the collator if it is not correctly set. */
01512   if (U_FAILURE(status)) {
01513     delete _current_collator;
01514     _current_collator = NULL;
01515   }
01516 #endif /* WITH_ICU */
01517 
01518   /* Some lists need to be sorted again after a language change. */
01519   InitializeSortedCargoSpecs();
01520   SortIndustryTypes();
01521   BuildIndustriesLegend();
01522   SortNetworkLanguages();
01523   InvalidateWindowClassesData(WC_BUILD_VEHICLE);      // Build vehicle window.
01524   InvalidateWindowClassesData(WC_TRAINS_LIST);        // Train group window.
01525   InvalidateWindowClassesData(WC_ROADVEH_LIST);       // Road vehicle group window.
01526   InvalidateWindowClassesData(WC_SHIPS_LIST);         // Ship group window.
01527   InvalidateWindowClassesData(WC_AIRCRAFT_LIST);      // Aircraft group window.
01528   InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
01529   InvalidateWindowClassesData(WC_STATION_LIST);       // Station list window.
01530 
01531   return true;
01532 }
01533 
01534 /* Win32 implementation in win32.cpp.
01535  * OS X implementation in os/macosx/macos.mm. */
01536 #if !(defined(WIN32) || defined(__APPLE__))
01537 
01545 const char *GetCurrentLocale(const char *param)
01546 {
01547   const char *env;
01548 
01549   env = getenv("LANGUAGE");
01550   if (env != NULL) return env;
01551 
01552   env = getenv("LC_ALL");
01553   if (env != NULL) return env;
01554 
01555   if (param != NULL) {
01556     env = getenv(param);
01557     if (env != NULL) return env;
01558   }
01559 
01560   return getenv("LANG");
01561 }
01562 #else
01563 const char *GetCurrentLocale(const char *param);
01564 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
01565 
01566 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01567 {
01568   char stra[512];
01569   char strb[512];
01570   GetString(stra, *a, lastof(stra));
01571   GetString(strb, *b, lastof(strb));
01572 
01573   return strcmp(stra, strb);
01574 }
01575 
01581 const LanguageMetadata *GetLanguage(byte newgrflangid)
01582 {
01583   for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
01584     if (newgrflangid == lang->newgrflangid) return lang;
01585   }
01586 
01587   return NULL;
01588 }
01589 
01596 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
01597 {
01598   FILE *f = fopen(file, "rb");
01599   if (f == NULL) return false;
01600 
01601   size_t read = fread(hdr, sizeof(*hdr), 1, f);
01602   fclose(f);
01603 
01604   bool ret = read == 1 && hdr->IsValid();
01605 
01606   /* Convert endianness for the windows language ID */
01607   if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01608   return ret;
01609 }
01610 
01615 static void GetLanguageList(const char *path)
01616 {
01617   DIR *dir = ttd_opendir(path);
01618   if (dir != NULL) {
01619     struct dirent *dirent;
01620     while ((dirent = readdir(dir)) != NULL) {
01621       const char *d_name    = FS2OTTD(dirent->d_name);
01622       const char *extension = strrchr(d_name, '.');
01623 
01624       /* Not a language file */
01625       if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01626 
01627       LanguageMetadata lmd;
01628       seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
01629 
01630       /* Check whether the file is of the correct version */
01631       if (!GetLanguageFileHeader(lmd.file, &lmd)) {
01632         DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
01633       } else if (GetLanguage(lmd.newgrflangid) != NULL) {
01634         DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
01635       } else {
01636         *_languages.Append() = lmd;
01637       }
01638     }
01639     closedir(dir);
01640   }
01641 }
01642 
01647 void InitializeLanguagePacks()
01648 {
01649   Searchpath sp;
01650 
01651   FOR_ALL_SEARCHPATHS(sp) {
01652     char path[MAX_PATH];
01653     FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01654     GetLanguageList(path);
01655   }
01656   if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
01657 
01658   /* Acquire the locale of the current system */
01659   const char *lang = GetCurrentLocale("LC_MESSAGES");
01660   if (lang == NULL) lang = "en_GB";
01661 
01662   const LanguageMetadata *chosen_language   = NULL; 
01663   const LanguageMetadata *language_fallback = NULL; 
01664   const LanguageMetadata *en_GB_fallback    = _languages.Begin(); 
01665 
01666   /* Find a proper language. */
01667   for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01668     /* We are trying to find a default language. The priority is by
01669      * configuration file, local environment and last, if nothing found,
01670      * english. */
01671     const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
01672     if (strcmp(lang_file, _config_language_file) == 0) {
01673       chosen_language = lng;
01674       break;
01675     }
01676 
01677     if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback    = lng;
01678     if (strncmp(lng->isocode, lang, 5) == 0) chosen_language   = lng;
01679     if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
01680   }
01681 
01682   /* We haven't found the language in the config nor the one in the locale.
01683    * Now we set it to one of the fallback languages */
01684   if (chosen_language == NULL) {
01685     chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
01686   }
01687 
01688   if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
01689 }
01690 
01695 const char *GetCurrentLanguageIsoCode()
01696 {
01697   return _langpack->isocode;
01698 }
01699 
01706 static bool FindMissingGlyphs(const char **str)
01707 {
01708 #ifdef WITH_FREETYPE
01709   UninitFreeType();
01710   InitFreeType();
01711 #endif
01712   const Sprite *question_mark[FS_END];
01713   FontSize size;
01714 
01715   for (size = FS_BEGIN; size < FS_END; size++) {
01716     question_mark[size] = GetGlyph(size, '?');
01717   }
01718 
01719   for (uint i = 0; i != 32; i++) {
01720     for (uint j = 0; j < _langtab_num[i]; j++) {
01721       size = FS_NORMAL;
01722       const char *text = _langpack_offs[_langtab_start[i] + j];
01723       if (str != NULL) *str = text;
01724       for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
01725         if (c == SCC_SETX) {
01726           /* SetX is, together with SetXY as special character that
01727            * uses the next (two) characters as data points. We have
01728            * to skip those, otherwise the UTF8 reading will go haywire. */
01729           text++;
01730         } else if (c == SCC_SETXY) {
01731           text += 2;
01732         } else if (c == SCC_TINYFONT) {
01733           size = FS_SMALL;
01734         } else if (c == SCC_BIGFONT) {
01735           size = FS_LARGE;
01736         } else if (IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
01737           /* The character is printable, but not in the normal font. This is the case we were testing for. */
01738           return true;
01739         }
01740       }
01741     }
01742   }
01743   return false;
01744 }
01745 
01756 void CheckForMissingGlyphsInLoadedLanguagePack()
01757 {
01758   bool bad_font = FindMissingGlyphs(NULL);
01759 #ifdef WITH_FREETYPE
01760   if (bad_font) {
01761     /* We found an unprintable character... lets try whether we can find
01762      * a fallback font that can print the characters in the current language. */
01763     FreeTypeSettings backup;
01764     memcpy(&backup, &_freetype, sizeof(backup));
01765 
01766     bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, &FindMissingGlyphs);
01767 
01768     memcpy(&_freetype, &backup, sizeof(backup));
01769 
01770     if (bad_font) {
01771       /* Our fallback font does miss characters too, so keep the
01772        * user chosen font as that is more likely to be any good than
01773        * the wild guess we made */
01774       UninitFreeType();
01775       InitFreeType();
01776     }
01777   }
01778 #endif
01779 
01780   if (bad_font) {
01781     /* All attempts have failed. Display an error. As we do not want the string to be translated by
01782      * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
01783      * properly we have to set the colour of the string, otherwise we end up with a lot of artefacts.
01784      * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
01785      * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
01786     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.");
01787     Utf8Encode(err_str, SCC_YELLOW);
01788     SetDParamStr(0, err_str);
01789     ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
01790 
01791     /* Reset the font width */
01792     LoadStringWidthTable();
01793     return;
01794   }
01795 
01796   /* Update the font with cache */
01797   LoadStringWidthTable();
01798 
01799 #if !defined(WITH_ICU)
01800   /*
01801    * For right-to-left languages we need the ICU library. If
01802    * we do not have support for that library we warn the user
01803    * about it with a message. As we do not want the string to
01804    * be translated by the translators, we 'force' it into the
01805    * binary and 'load' it via a BindCString. To do this
01806    * properly we have to set the colour of the string,
01807    * otherwise we end up with a lot of artefacts. The colour
01808    * 'character' might change in the future, so for safety
01809    * we just Utf8 Encode it into the string, which takes
01810    * exactly three characters, so it replaces the "XXX" with
01811    * the colour marker.
01812    */
01813   if (_current_text_dir != TD_LTR) {
01814     static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01815     Utf8Encode(err_str, SCC_YELLOW);
01816     SetDParamStr(0, err_str);
01817     ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
01818   }
01819 #endif
01820 }

Generated on Fri Feb 4 20:53:47 2011 for OpenTTD by  doxygen 1.6.1