# strings.cpp

Go to the documentation of this file.

    00001 /* $Id: strings.cpp 26063 2013-11-23 13:24:31Z rubidium $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "currency.h"
    00014 #include "station_base.h"
    00015 #include "town.h"
    00016 #include "screenshot.h"
    00017 #include "waypoint_base.h"
    00018 #include "depot_base.h"
    00019 #include "industry.h"
    00020 #include "newgrf_text.h"
    00021 #include "fileio_func.h"
    00022 #include "signs_base.h"
    00023 #include "fontdetection.h"
    00024 #include "error.h"
    00025 #include "strings_func.h"
    00026 #include "rev.h"
    00027 #include "core/endian_func.hpp"
    00028 #include "date_func.h"
    00029 #include "vehicle_base.h"
    00030 #include "engine_base.h"
    00031 #include "language.h"
    00032 #include "townname_func.h"
    00033 #include "string_func.h"
    00034 #include "company_base.h"
    00035 #include "smallmap_gui.h"
    00036 #include "window_func.h"
    00037 #include "debug.h"
    00038 #include "game/game_text.hpp"
    00039 #include <stack>
    00040
    00041 #include "table/strings.h"
    00042 #include "table/control_codes.h"
    00043
    00044 char _config_language_file[MAX_PATH];
    00045 LanguageList _languages;
    00046 const LanguageMetadata *_current_language = NULL;
    00047
    00048 TextDirection _current_text_dir;
    00049
    00050 #ifdef WITH_ICU
    00051 Collator *_current_collator = NULL;
    00052 #endif /* WITH_ICU */
    00053
    00054 static uint64 _global_string_params_data[20];
    00055 static WChar _global_string_params_type[20];
    00056 StringParameters _global_string_params(_global_string_params_data, 20, _global_string_params_type);
    00057
    00059 void StringParameters::ClearTypeInformation()
    00060 {
    00061   assert(this->type != NULL);
    00062   MemSetT(this->type, 0, this->num_param);
    00063 }
    00064
    00065
    00070 int64 StringParameters::GetInt64(WChar type)
    00071 {
    00072   if (this->offset >= this->num_param) {
    00073     DEBUG(misc, 0, "Trying to read invalid string parameter");
    00074     return 0;
    00075   }
    00076   if (this->type != NULL) {
    00077     assert(this->type[this->offset] == 0 || this->type[this->offset] == type);
    00078     this->type[this->offset] = type;
    00079   }
    00080   return this->data[this->offset++];
    00081 }
    00082
    00087 void StringParameters::ShiftParameters(uint amount)
    00088 {
    00089   assert(amount <= this->num_param);
    00090   MemMoveT(this->data + amount, this->data, this->num_param - amount);
    00091 }
    00092
    00101 void SetDParamMaxValue(uint n, uint64 max_value, uint min_count, FontSize size)
    00102 {
    00103   uint num_digits = 1;
    00104   while (max_value >= 10) {
    00105     num_digits++;
    00106     max_value /= 10;
    00107   }
    00108   SetDParamMaxDigits(n, max(min_count, num_digits), size);
    00109 }
    00110
    00117 void SetDParamMaxDigits(uint n, uint count, FontSize size)
    00118 {
    00119   uint front, next;
    00120   GetBroadestDigit(&front, &next, size);
    00121   uint64 val = count > 1 ? front : next;
    00122   for (; count > 1; count--) {
    00123     val = 10 * val + next;
    00124   }
    00125   SetDParam(n, val);
    00126 }
    00127
    00134 void CopyInDParam(int offs, const uint64 *src, int num)
    00135 {
    00136   MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
    00137 }
    00138
    00145 void CopyOutDParam(uint64 *dst, int offs, int num)
    00146 {
    00147   MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
    00148 }
    00149
    00158 void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
    00159 {
    00160   char buf[DRAW_STRING_BUFFER];
    00161   GetString(buf, string, lastof(buf));
    00162
    00163   MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num);
    00164   for (int i = 0; i < num; i++) {
    00165     if (_global_string_params.HasTypeInformation() && _global_string_params.GetTypeAtOffset(i) == SCC_RAW_STRING_POINTER) {
    00166       strings[i] = strdup((const char *)(size_t)_global_string_params.GetParam(i));
    00167       dst[i] = (size_t)strings[i];
    00168     } else {
    00169       strings[i] = NULL;
    00170     }
    00171   }
    00172 }
    00173
    00174 static char *StationGetSpecialString(char *buff, int x, const char *last);
    00175 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
    00176 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
    00177
    00178 static char *FormatString(char *buff, const char *str, StringParameters *args, const char *last, uint case_index = 0, bool game_script = false, bool dry_run = false);
    00179
    00180 struct LanguagePack : public LanguagePackHeader {
    00181   char data[]; // list of strings
    00182 };
    00183
    00184 static char **_langpack_offs;
    00185 static LanguagePack *_langpack;
    00186 static uint _langtab_num[TAB_COUNT];
    00187 static uint _langtab_start[TAB_COUNT];
    00188 static bool _scan_for_gender_data = false;
    00189
    00190
    00191 const char *GetStringPtr(StringID string)
    00192 {
    00193   switch (GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)) {
    00194     case GAME_TEXT_TAB: return GetGameStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
    00195     /* GetGRFStringPtr doesn't handle 0xD4xx ids, we need to convert those to 0xD0xx. */
    00196     case 26: return GetStringPtr(GetGRFStringID(0, 0xD000 + GB(string, TAB_SIZE_OFFSET, 10)));
    00197     case 28: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
    00198     case 29: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x0800);
    00199     case 30: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x1000);
    00200     default: return _langpack_offs[_langtab_start[GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)] + GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS)];
    00201   }
    00202 }
    00203
    00214 char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
    00215 {
    00216   if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
    00217
    00218   uint index = GB(string, TAB_SIZE_OFFSET,  TAB_SIZE_BITS);
    00219   uint tab   = GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS);
    00220
    00221   switch (tab) {
    00222     case 4:
    00223       if (index >= 0xC0 && !game_script) {
    00224         return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
    00225       }
    00226       break;
    00227
    00228     case 14:
    00229       if (index >= 0xE4 && !game_script) {
    00230         return GetSpecialNameString(buffr, index - 0xE4, args, last);
    00231       }
    00232       break;
    00233
    00234     case 15:
    00235       /* Old table for custom names. This is no longer used */
    00236       if (!game_script) {
    00237         error("Incorrect conversion of custom name string.");
    00238       }
    00239       break;
    00240
    00241     case GAME_TEXT_TAB:
    00242       return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
    00243
    00244     case 26:
    00245       /* Include string within newgrf text (format code 81) */
    00246       if (HasBit(index, 10)) {
    00247         StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
    00248         return GetStringWithArgs(buffr, string, args, last, case_index);
    00249       }
    00250       break;
    00251
    00252     case 28:
    00253       return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
    00254
    00255     case 29:
    00256       return FormatString(buffr, GetGRFStringPtr(index + 0x0800), args, last, case_index);
    00257
    00258     case 30:
    00259       return FormatString(buffr, GetGRFStringPtr(index + 0x1000), args, last, case_index);
    00260   }
    00261
    00262   if (index >= _langtab_num[tab]) {
    00263     if (game_script) {
    00264       return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
    00265     }
    00266     error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
    00267   }
    00268
    00269   return FormatString(buffr, GetStringPtr(string), args, last, case_index);
    00270 }
    00271
    00272 char *GetString(char *buffr, StringID string, const char *last)
    00273 {
    00274   _global_string_params.ClearTypeInformation();
    00275   _global_string_params.offset = 0;
    00276   return GetStringWithArgs(buffr, string, &_global_string_params, last);
    00277 }
    00278
    00279
    00280 char *InlineString(char *buf, StringID string)
    00281 {
    00282   buf += Utf8Encode(buf, SCC_STRING_ID);
    00283   buf += Utf8Encode(buf, string);
    00284   return buf;
    00285 }
    00286
    00287
    00293 void SetDParamStr(uint n, const char *str)
    00294 {
    00295   SetDParam(n, (uint64)(size_t)str);
    00296 }
    00297
    00302 void InjectDParam(uint amount)
    00303 {
    00304   _global_string_params.ShiftParameters(amount);
    00305 }
    00306
    00318 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
    00319 {
    00320   static const int max_digits = 20;
    00321   uint64 divisor = 10000000000000000000ULL;
    00322   zerofill += fractional_digits;
    00323   int thousands_offset = (max_digits - fractional_digits - 1) % 3;
    00324
    00325   if (number < 0) {
    00326     buff += seprintf(buff, last, "-");
    00327     number = -number;
    00328   }
    00329
    00330   uint64 num = number;
    00331   uint64 tot = 0;
    00332   for (int i = 0; i < max_digits; i++) {
    00333     if (i == max_digits - fractional_digits) {
    00334       const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
    00335       if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
    00336       buff += seprintf(buff, last, "%s", decimal_separator);
    00337     }
    00338
    00339     uint64 quot = 0;
    00340     if (num >= divisor) {
    00341       quot = num / divisor;
    00342       num = num % divisor;
    00343     }
    00344     if ((tot |= quot) || i >= max_digits - zerofill) {
    00345       buff += seprintf(buff, last, "%i", (int)quot);
    00346       if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
    00347     }
    00348
    00349     divisor /= 10;
    00350   }
    00351
    00352   *buff = '\0';
    00353
    00354   return buff;
    00355 }
    00356
    00357 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
    00358 {
    00359   const char *separator = _settings_game.locale.digit_group_separator;
    00360   if (separator == NULL) separator = _langpack->digit_group_separator;
    00361   return FormatNumber(buff, number, last, separator, 1, fractional_digits);
    00362 }
    00363
    00364 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
    00365 {
    00366   return FormatNumber(buff, number, last, "");
    00367 }
    00368
    00369 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
    00370 {
    00371   return FormatNumber(buff, number, last, "", count);
    00372 }
    00373
    00374 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
    00375 {
    00376   return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
    00377 }
    00378
    00386 static char *FormatBytes(char *buff, int64 number, const char *last)
    00387 {
    00388   assert(number >= 0);
    00389
    00390   /*                                   1   2^10  2^20  2^30  2^40  2^50  2^60 */
    00391   const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
    00392   uint id = 1;
    00393   while (number >= 1024 * 1024) {
    00394     number /= 1024;
    00395     id++;
    00396   }
    00397
    00398   const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
    00399   if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
    00400
    00401   if (number < 1024) {
    00402     id = 0;
    00403     buff += seprintf(buff, last, "%i", (int)number);
    00404   } else if (number < 1024 * 10) {
    00405     buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
    00406   } else if (number < 1024 * 100) {
    00407     buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
    00408   } else {
    00409     assert(number < 1024 * 1024);
    00410     buff += seprintf(buff, last, "%i", (int)number / 1024);
    00411   }
    00412
    00413   assert(id < lengthof(iec_prefixes));
    00414   buff += seprintf(buff, last, " %sB", iec_prefixes[id]);
    00415
    00416   return buff;
    00417 }
    00418
    00419 static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
    00420 {
    00421   YearMonthDay ymd;
    00422   ConvertDateToYMD(date, &ymd);
    00423
    00424   int64 args[] = {ymd.day + STR_ORDINAL_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
    00425   StringParameters tmp_params(args);
    00426   return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
    00427 }
    00428
    00429 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
    00430 {
    00431   YearMonthDay ymd;
    00432   ConvertDateToYMD(date, &ymd);
    00433
    00434   int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
    00435   StringParameters tmp_params(args);
    00436   return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
    00437 }
    00438
    00439 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
    00440 {
    00441   YearMonthDay ymd;
    00442   ConvertDateToYMD(date, &ymd);
    00443
    00444   char day[3];
    00445   char month[3];
    00446   /* We want to zero-pad the days and months */
    00447   snprintf(day,   lengthof(day),   "%02i", ymd.day);
    00448   snprintf(month, lengthof(month), "%02i", ymd.month + 1);
    00449
    00450   int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
    00451   StringParameters tmp_params(args);
    00452   return FormatString(buff, GetStringPtr(str), &tmp_params, last);
    00453 }
    00454
    00455 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
    00456 {
    00457   /* We are going to make number absolute for printing, so
    00458    * keep this piece of data as we need it later on */
    00459   bool negative = number < 0;
    00460   const char *multiplier = "";
    00461
    00462   number *= spec->rate;
    00463
    00464   /* convert from negative */
    00465   if (number < 0) {
    00466     if (buff + Utf8CharLen(SCC_RED) > last) return buff;
    00467     buff += Utf8Encode(buff, SCC_RED);
    00468     buff = strecpy(buff, "-", last);
    00469     number = -number;
    00470   }
    00471
    00472   /* Add prefix part, following symbol_pos specification.
    00473    * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
    00474    * The only remaining value is 1 (suffix), so everything that is not 1 */
    00475   if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
    00476
    00477   /* for huge numbers, compact the number into k or M */
    00478   if (compact) {
    00479     /* Take care of the 'k' rounding. Having 1 000 000 k
    00480      * and 1 000 M is inconsistent, so always use 1 000 M. */
    00481     if (number >= 1000000000 - 500) {
    00482       number = (number + 500000) / 1000000;
    00483       multiplier = "M";
    00484     } else if (number >= 1000000) {
    00485       number = (number + 500) / 1000;
    00486       multiplier = "k";
    00487     }
    00488   }
    00489
    00490   const char *separator = _settings_game.locale.digit_group_separator_currency;
    00491   if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
    00492   if (separator == NULL) separator = _langpack->digit_group_separator_currency;
    00493   buff = FormatNumber(buff, number, last, separator);
    00494   buff = strecpy(buff, multiplier, last);
    00495
    00496   /* Add suffix part, following symbol_pos specification.
    00497    * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
    00498    * The only remaining value is 1 (prefix), so everything that is not 0 */
    00499   if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
    00500
    00501   if (negative) {
    00502     if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
    00503     buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
    00504     *buff = '\0';
    00505   }
    00506
    00507   return buff;
    00508 }
    00509
    00516 static int DeterminePluralForm(int64 count, int plural_form)
    00517 {
    00518   /* The absolute value determines plurality */
    00519   uint64 n = abs(count);
    00520
    00521   switch (plural_form) {
    00522     default:
    00523       NOT_REACHED();
    00524
    00525     /* Two forms: singular used for one only.
    00526      * Used in:
    00527      *   Danish, Dutch, English, German, Norwegian, Swedish, Estonian, Finnish,
    00528      *   Greek, Hebrew, Italian, Portuguese, Spanish, Esperanto */
    00529     case 0:
    00530       return n != 1 ? 1 : 0;
    00531
    00532     /* Only one form.
    00533      * Used in:
    00534      *   Hungarian, Japanese, Korean, Turkish */
    00535     case 1:
    00536       return 0;
    00537
    00538     /* Two forms: singular used for 0 and 1.
    00539      * Used in:
    00540      *   French, Brazilian Portuguese */
    00541     case 2:
    00542       return n > 1 ? 1 : 0;
    00543
    00544     /* Three forms: special cases for 0, and numbers ending in 1 except when ending in 11.
    00545      * Note: Cases are out of order for hysterical reasons. '0' is last.
    00546      * Used in:
    00547      *   Latvian */
    00548     case 3:
    00549       return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
    00550
    00551     /* Five forms: special cases for 1, 2, 3 to 6, and 7 to 10.
    00552      * Used in:
    00553      *   Gaelige (Irish) */
    00554     case 4:
    00555       return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
    00556
    00557     /* Three forms: special cases for numbers ending in 1 except when ending in 11, and 2 to 9 except when ending in 12 to 19.
    00558      * Used in:
    00559      *   Lithuanian */
    00560     case 5:
    00561       return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
    00562
    00563     /* Three forms: special cases for numbers ending in 1 except when ending in 11, and 2 to 4 except when ending in 12 to 14.
    00564      * Used in:
    00565      *   Croatian, Russian, Ukrainian */
    00566     case 6:
    00567       return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
    00568
    00569     /* Three forms: special cases for 1, and numbers ending in 2 to 4 except when ending in 12 to 14.
    00570      * Used in:
    00571      *   Polish */
    00572     case 7:
    00573       return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
    00574
    00575     /* Four forms: special cases for numbers ending in 01, 02, and 03 to 04.
    00576      * Used in:
    00577      *   Slovenian */
    00578     case 8:
    00579       return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
    00580
    00581     /* Two forms: singular used for numbers ending in 1 except when ending in 11.
    00582      * Used in:
    00583      *   Icelandic */
    00584     case 9:
    00585       return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
    00586
    00587     /* Three forms: special cases for 1, and 2 to 4
    00588      * Used in:
    00589      *   Czech, Slovak */
    00590     case 10:
    00591       return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
    00592
    00593     /* Two forms: cases for numbers ending with a consonant, and with a vowel.
    00594      * Korean doesn't have the concept of plural, but depending on how a
    00595      * number is pronounced it needs another version of a particle.
    00596      * As such the plural system is misused to give this distinction.
    00597      */
    00598     case 11:
    00599       switch (n % 10) {
    00600         case 0: // yeong
    00601         case 1: // il
    00602         case 3: // sam
    00603         case 6: // yuk
    00604         case 7: // chil
    00605         case 8: // pal
    00606           return 0;
    00607
    00608         case 2: // i
    00609         case 4: // sa
    00610         case 5: // o
    00611         case 9: // gu
    00612           return 1;
    00613
    00614         default:
    00615           NOT_REACHED();
    00616       }
    00617
    00618     /* Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.
    00619      * Used in:
    00620      *  Maltese */
    00621     case 12:
    00622       return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
    00623     /* Four forms: special cases for 1 and 11, 2 and 12, 3 .. 10 and 13 .. 19, other
    00624      * Used in:
    00625      *  Scottish Gaelic */
    00626     case 13:
    00627       return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
    00628   }
    00629 }
    00630
    00631 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
    00632 {
    00633   /* <NUM> {Length of each string} {each string} */
    00634   uint n = (byte)*b++;
    00635   uint pos, i, mypos = 0;
    00636
    00637   for (i = pos = 0; i != n; i++) {
    00638     uint len = (byte)*b++;
    00639     if (i == form) mypos = pos;
    00640     pos += len;
    00641   }
    00642
    00643   *dst += seprintf(*dst, last, "%s", b + mypos);
    00644   return b + pos;
    00645 }
    00646
    00648 struct UnitConversion {
    00649   int multiplier;
    00650   int shift;
    00651
    00658   int64 ToDisplay(int64 input, bool round = true) const
    00659   {
    00660     return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
    00661   }
    00662
    00670   int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
    00671   {
    00672     return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
    00673   }
    00674 };
    00675
    00677 struct Units {
    00678   UnitConversion c;
    00679   StringID s;
    00680 };
    00681
    00683 struct UnitsLong {
    00684   UnitConversion c;
    00685   StringID s;
    00686   StringID l;
    00687 };
    00688
    00690 static const Units _units_velocity[] = {
    00691   { {   1,  0}, STR_UNITS_VELOCITY_IMPERIAL },
    00692   { { 103,  6}, STR_UNITS_VELOCITY_METRIC   },
    00693   { {1831, 12}, STR_UNITS_VELOCITY_SI       },
    00694 };
    00695
    00697 static const Units _units_power[] = {
    00698   { {   1,  0}, STR_UNITS_POWER_IMPERIAL },
    00699   { {4153, 12}, STR_UNITS_POWER_METRIC   },
    00700   { {6109, 13}, STR_UNITS_POWER_SI       },
    00701 };
    00702
    00704 static const UnitsLong _units_weight[] = {
    00705   { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
    00706   { {   1,  0}, STR_UNITS_WEIGHT_SHORT_METRIC,   STR_UNITS_WEIGHT_LONG_METRIC   },
    00707   { {1000,  0}, STR_UNITS_WEIGHT_SHORT_SI,       STR_UNITS_WEIGHT_LONG_SI       },
    00708 };
    00709
    00711 static const UnitsLong _units_volume[] = {
    00712   { {4227,  4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
    00713   { {1000,  0}, STR_UNITS_VOLUME_SHORT_METRIC,   STR_UNITS_VOLUME_LONG_METRIC   },
    00714   { {   1,  0}, STR_UNITS_VOLUME_SHORT_SI,       STR_UNITS_VOLUME_LONG_SI       },
    00715 };
    00716
    00718 static const Units _units_force[] = {
    00719   { {3597,  4}, STR_UNITS_FORCE_IMPERIAL },
    00720   { {3263,  5}, STR_UNITS_FORCE_METRIC   },
    00721   { {   1,  0}, STR_UNITS_FORCE_SI       },
    00722 };
    00723
    00725 static const Units _units_height[] = {
    00726   { {   3,  0}, STR_UNITS_HEIGHT_IMPERIAL }, // "Wrong" conversion factor for more nicer GUI values
    00727   { {   1,  0}, STR_UNITS_HEIGHT_METRIC   },
    00728   { {   1,  0}, STR_UNITS_HEIGHT_SI       },
    00729 };
    00730
    00736 uint ConvertSpeedToDisplaySpeed(uint speed)
    00737 {
    00738   /* For historical reasons we don't want to mess with the
    00739    * conversion for speed. So, don't round it and keep the
    00740    * original conversion factors instead of the real ones. */
    00741   return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed, false);
    00742 }
    00743
    00749 uint ConvertDisplaySpeedToSpeed(uint speed)
    00750 {
    00751   return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed);
    00752 }
    00753
    00759 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
    00760 {
    00761   return _units_velocity[_settings_game.locale.units_velocity].c.ToDisplay(speed * 10, false) / 16;
    00762 }
    00763
    00769 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
    00770 {
    00771   return _units_velocity[_settings_game.locale.units_velocity].c.FromDisplay(speed * 16, true, 10);
    00772 }
    00782 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
    00783 {
    00784   uint orig_offset = args->offset;
    00785
    00786   /* When there is no array with types there is no need to do a dry run. */
    00787   if (args->HasTypeInformation() && !dry_run) {
    00788     if (UsingNewGRFTextStack()) {
    00789       /* Values from the NewGRF text stack are only copied to the normal
    00790        * argv array at the time they are encountered. That means that if
    00791        * another string command references a value later in the string it
    00792        * would fail. We solve that by running FormatString twice. The first
    00793        * pass makes sure the argv array is correctly filled and the second
    00794        * pass can reference later values without problems. */
    00795       struct TextRefStack *backup = CreateTextRefStackBackup();
    00796       FormatString(buff, str_arg, args, last, case_index, game_script, true);
    00797       RestoreTextRefStackBackup(backup);
    00798     } else {
    00799       FormatString(buff, str_arg, args, last, case_index, game_script, true);
    00800     }
    00801     /* We have to restore the original offset here to to read the correct values. */
    00802     args->offset = orig_offset;
    00803   }
    00804   WChar b = '\0';
    00805   uint next_substr_case_index = 0;
    00806   char *buf_start = buff;
    00807   std::stack<const char *> str_stack;
    00808   str_stack.push(str_arg);
    00809
    00810   for (;;) {
    00811     while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
    00812       str_stack.pop();
    00813     }
    00814     if (str_stack.empty()) break;
    00815     const char *&str = str_stack.top();
    00816
    00817     if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
    00818       /* We need to pass some stuff as it might be modified; oh boy. */
    00819       //todo: should argve be passed here too?
    00820       b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), dry_run);
    00821       if (b == 0) continue;
    00822     }
    00823
    00824     switch (b) {
    00825       case SCC_ENCODED: {
    00826         uint64 sub_args_data[20];
    00827         WChar sub_args_type[20];
    00828         bool sub_args_need_free[20];
    00829         StringParameters sub_args(sub_args_data, 20, sub_args_type);
    00830
    00831         sub_args.ClearTypeInformation();
    00832         memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
    00833
    00834         uint16 stringid;
    00835         const char *s = str;
    00836         char *p;
    00837         stringid = strtol(str, &p, 16);
    00838         if (*p != ':' && *p != '\0') {
    00839           while (*p != '\0') p++;
    00840           str = p;
    00841           buff = strecat(buff, "(invalid SCC_ENCODED)", last);
    00842           break;
    00843         }
    00844         if (stringid >= TAB_SIZE) {
    00845           while (*p != '\0') p++;
    00846           str = p;
    00847           buff = strecat(buff, "(invalid StringID)", last);
    00848           break;
    00849         }
    00850
    00851         int i = 0;
    00852         while (*p != '\0' && i < 20) {
    00853           uint64 param;
    00854           s = ++p;
    00855
    00856           /* Find the next value */
    00857           bool instring = false;
    00858           bool escape = false;
    00859           for (;; p++) {
    00860             if (*p == '\\') {
    00861               escape = true;
    00862               continue;
    00863             }
    00864             if (*p == '"' && escape) {
    00865               escape = false;
    00866               continue;
    00867             }
    00868             escape = false;
    00869
    00870             if (*p == '"') {
    00871               instring = !instring;
    00872               continue;
    00873             }
    00874             if (instring) {
    00875               continue;
    00876             }
    00877
    00878             if (*p == ':') break;
    00879             if (*p == '\0') break;
    00880           }
    00881
    00882           if (*s != '"') {
    00883             /* Check if we want to look up another string */
    00884             WChar l;
    00885             size_t len = Utf8Decode(&l, s);
    00886             bool lookup = (l == SCC_ENCODED);
    00887             if (lookup) s += len;
    00888
    00889             param = (int32)strtoul(s, &p, 16);
    00890
    00891             if (lookup) {
    00892               if (param >= TAB_SIZE) {
    00893                 while (*p != '\0') p++;
    00894                 str = p;
    00895                 buff = strecat(buff, "(invalid sub-StringID)", last);
    00896                 break;
    00897               }
    00898               param = (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + param;
    00899             }
    00900
    00901             sub_args.SetParam(i++, param);
    00902           } else {
    00903             char *g = strdup(s);
    00904             g[p - s] = '\0';
    00905
    00906             sub_args_need_free[i] = true;
    00907             sub_args.SetParam(i++, (uint64)(size_t)g);
    00908           }
    00909         }
    00910         /* If we didn't error out, we can actually print the string. */
    00911         if (*str != '\0') {
    00912           str = p;
    00913           buff = GetStringWithArgs(buff, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, last, true);
    00914         }
    00915
    00916         for (int i = 0; i < 20; i++) {
    00917           if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
    00918         }
    00919         break;
    00920       }
    00921
    00922       case SCC_NEWGRF_STRINL: {
    00923         StringID substr = Utf8Consume(&str);
    00924         str_stack.push(GetStringPtr(substr));
    00925         break;
    00926       }
    00927
    00928       case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
    00929         StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
    00930         str_stack.push(GetStringPtr(substr));
    00931         case_index = next_substr_case_index;
    00932         next_substr_case_index = 0;
    00933         break;
    00934       }
    00935
    00936
    00937       case SCC_GENDER_LIST: { // {G 0 Der Die Das}
    00938         /* First read the meta data from the language file. */
    00939         uint offset = orig_offset + (byte)*str++;
    00940         int gender = 0;
    00941         if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
    00942           /* Now we need to figure out what text to resolve, i.e.
    00943            * what do we need to draw? So get the actual raw string
    00944            * first using the control code to get said string. */
    00945           char input[4 + 1];
    00946           char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
    00947           *p = '\0';
    00948
    00949           /* Now do the string formatting. */
    00950           char buf[256];
    00951           bool old_sgd = _scan_for_gender_data;
    00952           _scan_for_gender_data = true;
    00953           StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
    00954           p = FormatString(buf, input, &tmp_params, lastof(buf));
    00955           _scan_for_gender_data = old_sgd;
    00956           *p = '\0';
    00957
    00958           /* And determine the string. */
    00959           const char *s = buf;
    00960           WChar c = Utf8Consume(&s);
    00961           /* Does this string have a gender, if so, set it */
    00962           if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
    00963         }
    00964         str = ParseStringChoice(str, gender, &buff, last);
    00965         break;
    00966       }
    00967
    00968       /* This sets up the gender for the string.
    00969        * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
    00970       case SCC_GENDER_INDEX: // {GENDER 0}
    00971         if (_scan_for_gender_data) {
    00972           buff += Utf8Encode(buff, SCC_GENDER_INDEX);
    00973           *buff++ = *str++;
    00974         } else {
    00975           str++;
    00976         }
    00977         break;
    00978
    00979       case SCC_PLURAL_LIST: { // {P}
    00980         int plural_form = *str++;          // contains the plural form for this string
    00981         uint offset = orig_offset + (byte)*str++;
    00982         int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
    00983         str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
    00984         break;
    00985       }
    00986
    00987       case SCC_ARG_INDEX: { // Move argument pointer
    00988         args->offset = orig_offset + (byte)*str++;
    00989         break;
    00990       }
    00991
    00992       case SCC_SET_CASE: { // {SET_CASE}
    00993         /* This is a pseudo command, it's outputted when someone does {STRING.ack}
    00994          * The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
    00995         next_substr_case_index = (byte)*str++;
    00996         break;
    00997       }
    00998
    00999       case SCC_SWITCH_CASE: { // {Used to implement case switching}
    01000         /* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
    01001          * Each LEN is printed using 2 bytes in big endian order. */
    01002         uint num = (byte)*str++;
    01003         while (num) {
    01004           if ((byte)str[0] == case_index) {
    01005             /* Found the case, adjust str pointer and continue */
    01006             str += 3;
    01007             break;
    01008           }
    01009           /* Otherwise skip to the next case */
    01010           str += 3 + (str[1] << 8) + str[2];
    01011           num--;
    01012         }
    01013         break;
    01014       }
    01015
    01016       case SCC_REVISION: // {REV}
    01017         buff = strecpy(buff, _openttd_revision, last);
    01018         break;
    01019
    01020       case SCC_STRING_ID: // {STRINL}
    01021         if (game_script) break;
    01022         buff = GetStringWithArgs(buff, Utf8Consume(&str), args, last);
    01023         break;
    01024
    01025       case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
    01026         if (game_script) break;
    01027         const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
    01028         buff = FormatString(buff, str, args, last);
    01029         break;
    01030       }
    01031
    01032       case SCC_STRING: {// {STRING}
    01033         StringID str = args->GetInt32(SCC_STRING);
    01034         if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
    01035         /* WARNING. It's prohibited for the included string to consume any arguments.
    01036          * For included strings that consume argument, you should use STRING1, STRING2 etc.
    01037          * To debug stuff you can set argv to NULL and it will tell you */
    01038         StringParameters tmp_params(args->GetDataPointer(), args->num_param - args->offset, NULL);
    01039         buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
    01040         next_substr_case_index = 0;
    01041         break;
    01042       }
    01043
    01044       case SCC_STRING1:
    01045       case SCC_STRING2:
    01046       case SCC_STRING3:
    01047       case SCC_STRING4:
    01048       case SCC_STRING5:
    01049       case SCC_STRING6:
    01050       case SCC_STRING7: { // {STRING1..7}
    01051         /* Strings that consume arguments */
    01052         StringID str = args->GetInt32(b);
    01053         if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
    01054         uint size = b - SCC_STRING1 + 1;
    01055         if (game_script && size > args->num_param - args->offset) {
    01056           buff = strecat(buff, "(too many parameters)", last);
    01057         } else {
    01058           StringParameters sub_args(*args, size);
    01059           buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
    01060         }
    01061         next_substr_case_index = 0;
    01062         break;
    01063       }
    01064
    01065       case SCC_COMMA: // {COMMA}
    01066         buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
    01067         break;
    01068
    01069       case SCC_DECIMAL: {// {DECIMAL}
    01070         int64 number = args->GetInt64(SCC_DECIMAL);
    01071         int digits = args->GetInt32(SCC_DECIMAL);
    01072         buff = FormatCommaNumber(buff, number, last, digits);
    01073         break;
    01074       }
    01075
    01076       case SCC_NUM: // {NUM}
    01077         buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
    01078         break;
    01079
    01080       case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
    01081         int64 num = args->GetInt64();
    01082         buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
    01083         break;
    01084       }
    01085
    01086       case SCC_HEX: // {HEX}
    01087         buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
    01088         break;
    01089
    01090       case SCC_BYTES: // {BYTES}
    01091         buff = FormatBytes(buff, args->GetInt64(), last);
    01092         break;
    01093
    01094       case SCC_CARGO_TINY: { // {CARGO_TINY}
    01095         /* Tiny description of cargotypes. Layout:
    01096          * param 1: cargo type
    01097          * param 2: cargo count */
    01098         CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
    01099         if (cargo >= CargoSpec::GetArraySize()) break;
    01100
    01101         StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
    01102         int64 amount = 0;
    01103         switch (cargo_str) {
    01104           case STR_TONS:
    01105             amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64());
    01106             break;
    01107
    01108           case STR_LITERS:
    01109             amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64());
    01110             break;
    01111
    01112           default: {
    01113             amount = args->GetInt64();
    01114             break;
    01115           }
    01116         }
    01117
    01118         buff = FormatCommaNumber(buff, amount, last);
    01119         break;
    01120       }
    01121
    01122       case SCC_CARGO_SHORT: { // {CARGO_SHORT}
    01123         /* Short description of cargotypes. Layout:
    01124          * param 1: cargo type
    01125          * param 2: cargo count */
    01126         CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
    01127         if (cargo >= CargoSpec::GetArraySize()) break;
    01128
    01129         StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
    01130         switch (cargo_str) {
    01131           case STR_TONS: {
    01132             assert(_settings_game.locale.units_weight < lengthof(_units_weight));
    01133             int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
    01134             StringParameters tmp_params(args_array);
    01135             buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
    01136             break;
    01137           }
    01138
    01139           case STR_LITERS: {
    01140             assert(_settings_game.locale.units_volume < lengthof(_units_volume));
    01141             int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
    01142             StringParameters tmp_params(args_array);
    01143             buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
    01144             break;
    01145           }
    01146
    01147           default: {
    01148             StringParameters tmp_params(*args, 1);
    01149             buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
    01150             break;
    01151           }
    01152         }
    01153         break;
    01154       }
    01155
    01156       case SCC_CARGO_LONG: { // {CARGO_LONG}
    01157         /* First parameter is cargo type, second parameter is cargo count */
    01158         CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
    01159         if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
    01160
    01161         StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
    01162         StringParameters tmp_args(*args, 1);
    01163         buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
    01164         break;
    01165       }
    01166
    01167       case SCC_CARGO_LIST: { // {CARGO_LIST}
    01168         uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
    01169         bool first = true;
    01170
    01171         const CargoSpec *cs;
    01172         FOR_ALL_SORTED_CARGOSPECS(cs) {
    01173           if (!HasBit(cmask, cs->Index())) continue;
    01174
    01175           if (buff >= last - 2) break; // ',' and ' '
    01176
    01177           if (first) {
    01178             first = false;
    01179           } else {
    01180             /* Add a comma if this is not the first item */
    01181             *buff++ = ',';
    01182             *buff++ = ' ';
    01183           }
    01184
    01185           buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
    01186         }
    01187
    01188         /* If first is still true then no cargo is accepted */
    01189         if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
    01190
    01191         *buff = '\0';
    01192         next_substr_case_index = 0;
    01193
    01194         /* Make sure we detect any buffer overflow */
    01195         assert(buff < last);
    01196         break;
    01197       }
    01198
    01199       case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
    01200         buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
    01201         break;
    01202
    01203       case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
    01204         buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
    01205         break;
    01206
    01207       case SCC_DATE_TINY: // {DATE_TINY}
    01208         buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
    01209         break;
    01210
    01211       case SCC_DATE_SHORT: // {DATE_SHORT}
    01212         buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
    01213         next_substr_case_index = 0;
    01214         break;
    01215
    01216       case SCC_DATE_LONG: // {DATE_LONG}
    01217         buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
    01218         next_substr_case_index = 0;
    01219         break;
    01220
    01221       case SCC_DATE_ISO: // {DATE_ISO}
    01222         buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
    01223         break;
    01224
    01225       case SCC_FORCE: { // {FORCE}
    01226         assert(_settings_game.locale.units_force < lengthof(_units_force));
    01227         int64 args_array[1] = {_units_force[_settings_game.locale.units_force].c.ToDisplay(args->GetInt64())};
    01228         StringParameters tmp_params(args_array);
    01229         buff = FormatString(buff, GetStringPtr(_units_force[_settings_game.locale.units_force].s), &tmp_params, last);
    01230         break;
    01231       }
    01232
    01233       case SCC_HEIGHT: { // {HEIGHT}
    01234         assert(_settings_game.locale.units_height < lengthof(_units_height));
    01235         int64 args_array[] = {_units_height[_settings_game.locale.units_height].c.ToDisplay(args->GetInt64())};
    01236         StringParameters tmp_params(args_array);
    01237         buff = FormatString(buff, GetStringPtr(_units_height[_settings_game.locale.units_height].s), &tmp_params, last);
    01238         break;
    01239       }
    01240
    01241       case SCC_POWER: { // {POWER}
    01242         assert(_settings_game.locale.units_power < lengthof(_units_power));
    01243         int64 args_array[1] = {_units_power[_settings_game.locale.units_power].c.ToDisplay(args->GetInt64())};
    01244         StringParameters tmp_params(args_array);
    01245         buff = FormatString(buff, GetStringPtr(_units_power[_settings_game.locale.units_power].s), &tmp_params, last);
    01246         break;
    01247       }
    01248
    01249       case SCC_VELOCITY: { // {VELOCITY}
    01250         assert(_settings_game.locale.units_velocity < lengthof(_units_velocity));
    01251         int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
    01252         StringParameters tmp_params(args_array);
    01253         buff = FormatString(buff, GetStringPtr(_units_velocity[_settings_game.locale.units_velocity].s), &tmp_params, last);
    01254         break;
    01255       }
    01256
    01257       case SCC_VOLUME_SHORT: { // {VOLUME_SHORT}
    01258         assert(_settings_game.locale.units_volume < lengthof(_units_volume));
    01259         int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
    01260         StringParameters tmp_params(args_array);
    01261         buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].s), &tmp_params, last);
    01262         break;
    01263       }
    01264
    01265       case SCC_VOLUME_LONG: { // {VOLUME_LONG}
    01266         assert(_settings_game.locale.units_volume < lengthof(_units_volume));
    01267         int64 args_array[1] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
    01268         StringParameters tmp_params(args_array);
    01269         buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
    01270         break;
    01271       }
    01272
    01273       case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT}
    01274         assert(_settings_game.locale.units_weight < lengthof(_units_weight));
    01275         int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
    01276         StringParameters tmp_params(args_array);
    01277         buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].s), &tmp_params, last);
    01278         break;
    01279       }
    01280
    01281       case SCC_WEIGHT_LONG: { // {WEIGHT_LONG}
    01282         assert(_settings_game.locale.units_weight < lengthof(_units_weight));
    01283         int64 args_array[1] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
    01284         StringParameters tmp_params(args_array);
    01285         buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
    01286         break;
    01287       }
    01288
    01289       case SCC_COMPANY_NAME: { // {COMPANY}
    01290         const Company *c = Company::GetIfValid(args->GetInt32());
    01291         if (c == NULL) break;
    01292
    01293         if (c->name != NULL) {
    01294           int64 args_array[] = {(uint64)(size_t)c->name};
    01295           StringParameters tmp_params(args_array);
    01296           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01297         } else {
    01298           int64 args_array[] = {c->name_2};
    01299           StringParameters tmp_params(args_array);
    01300           buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
    01301         }
    01302         break;
    01303       }
    01304
    01305       case SCC_COMPANY_NUM: { // {COMPANY_NUM}
    01306         CompanyID company = (CompanyID)args->GetInt32();
    01307
    01308         /* Nothing is added for AI or inactive companies */
    01309         if (Company::IsValidHumanID(company)) {
    01310           int64 args_array[] = {company + 1};
    01311           StringParameters tmp_params(args_array);
    01312           buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
    01313         }
    01314         break;
    01315       }
    01316
    01317       case SCC_DEPOT_NAME: { // {DEPOT}
    01318         VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
    01319         if (vt == VEH_AIRCRAFT) {
    01320           uint64 args_array[] = {args->GetInt32()};
    01321           WChar types_array[] = {SCC_STATION_NAME};
    01322           StringParameters tmp_params(args_array, 1, types_array);
    01323           buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
    01324           break;
    01325         }
    01326
    01327         const Depot *d = Depot::Get(args->GetInt32());
    01328         if (d->name != NULL) {
    01329           int64 args_array[] = {(uint64)(size_t)d->name};
    01330           StringParameters tmp_params(args_array);
    01331           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01332         } else {
    01333           int64 args_array[] = {d->town->index, d->town_cn + 1};
    01334           StringParameters tmp_params(args_array);
    01335           buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
    01336         }
    01337         break;
    01338       }
    01339
    01340       case SCC_ENGINE_NAME: { // {ENGINE}
    01341         const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
    01342         if (e == NULL) break;
    01343
    01344         if (e->name != NULL && e->IsEnabled()) {
    01345           int64 args_array[] = {(uint64)(size_t)e->name};
    01346           StringParameters tmp_params(args_array);
    01347           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01348         } else {
    01349           StringParameters tmp_params(NULL, 0, NULL);
    01350           buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
    01351         }
    01352         break;
    01353       }
    01354
    01355       case SCC_GROUP_NAME: { // {GROUP}
    01356         const Group *g = Group::GetIfValid(args->GetInt32());
    01357         if (g == NULL) break;
    01358
    01359         if (g->name != NULL) {
    01360           int64 args_array[] = {(uint64)(size_t)g->name};
    01361           StringParameters tmp_params(args_array);
    01362           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01363         } else {
    01364           int64 args_array[] = {g->index};
    01365           StringParameters tmp_params(args_array);
    01366
    01367           buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
    01368         }
    01369         break;
    01370       }
    01371
    01372       case SCC_INDUSTRY_NAME: { // {INDUSTRY}
    01373         const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
    01374         if (i == NULL) break;
    01375
    01376         if (_scan_for_gender_data) {
    01377           /* Gender is defined by the industry type.
    01378            * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
    01379           StringParameters tmp_params(NULL, 0, NULL);
    01380           buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
    01381         } else {
    01382           /* First print the town name and the industry type name. */
    01383           int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
    01384           StringParameters tmp_params(args_array);
    01385
    01386           buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
    01387         }
    01388         next_substr_case_index = 0;
    01389         break;
    01390       }
    01391
    01392       case SCC_PRESIDENT_NAME: { // {PRESIDENT_NAME}
    01393         const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
    01394         if (c == NULL) break;
    01395
    01396         if (c->president_name != NULL) {
    01397           int64 args_array[] = {(uint64)(size_t)c->president_name};
    01398           StringParameters tmp_params(args_array);
    01399           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01400         } else {
    01401           int64 args_array[] = {c->president_name_2};
    01402           StringParameters tmp_params(args_array);
    01403           buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
    01404         }
    01405         break;
    01406       }
    01407
    01408       case SCC_STATION_NAME: { // {STATION}
    01409         StationID sid = args->GetInt32(SCC_STATION_NAME);
    01410         const Station *st = Station::GetIfValid(sid);
    01411
    01412         if (st == NULL) {
    01413           /* The station doesn't exist anymore. The only place where we might
    01414            * be "drawing" an invalid station is in the case of cargo that is
    01415            * in transit. */
    01416           StringParameters tmp_params(NULL, 0, NULL);
    01417           buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
    01418           break;
    01419         }
    01420
    01421         if (st->name != NULL) {
    01422           int64 args_array[] = {(uint64)(size_t)st->name};
    01423           StringParameters tmp_params(args_array);
    01424           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01425         } else {
    01426           StringID str = st->string_id;
    01427           if (st->indtype != IT_INVALID) {
    01428             /* Special case where the industry provides the name for the station */
    01429             const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
    01430
    01431             /* Industry GRFs can change which might remove the station name and
    01432              * thus cause very strange things. Here we check for that before we
    01433              * actually set the station name. */
    01434             if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
    01435               str = indsp->station_name;
    01436             }
    01437           }
    01438
    01439           int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
    01440           StringParameters tmp_params(args_array);
    01441           buff = GetStringWithArgs(buff, str, &tmp_params, last);
    01442         }
    01443         break;
    01444       }
    01445
    01446       case SCC_TOWN_NAME: { // {TOWN}
    01447         const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
    01448         if (t == NULL) break;
    01449
    01450         if (t->name != NULL) {
    01451           int64 args_array[] = {(uint64)(size_t)t->name};
    01452           StringParameters tmp_params(args_array);
    01453           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01454         } else {
    01455           buff = GetTownName(buff, t, last);
    01456         }
    01457         break;
    01458       }
    01459
    01460       case SCC_WAYPOINT_NAME: { // {WAYPOINT}
    01461         Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
    01462         if (wp == NULL) break;
    01463
    01464         if (wp->name != NULL) {
    01465           int64 args_array[] = {(uint64)(size_t)wp->name};
    01466           StringParameters tmp_params(args_array);
    01467           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01468         } else {
    01469           int64 args_array[] = {wp->town->index, wp->town_cn + 1};
    01470           StringParameters tmp_params(args_array);
    01471           StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
    01472           if (wp->town_cn != 0) str++;
    01473           buff = GetStringWithArgs(buff, str, &tmp_params, last);
    01474         }
    01475         break;
    01476       }
    01477
    01478       case SCC_VEHICLE_NAME: { // {VEHICLE}
    01479         const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
    01480         if (v == NULL) break;
    01481
    01482         if (v->name != NULL) {
    01483           int64 args_array[] = {(uint64)(size_t)v->name};
    01484           StringParameters tmp_params(args_array);
    01485           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01486         } else {
    01487           int64 args_array[] = {v->unitnumber};
    01488           StringParameters tmp_params(args_array);
    01489
    01490           StringID str;
    01491           switch (v->type) {
    01492             default:           str = STR_INVALID_VEHICLE; break;
    01493             case VEH_TRAIN:    str = STR_SV_TRAIN_NAME; break;
    01494             case VEH_ROAD:     str = STR_SV_ROAD_VEHICLE_NAME; break;
    01495             case VEH_SHIP:     str = STR_SV_SHIP_NAME; break;
    01496             case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
    01497           }
    01498
    01499           buff = GetStringWithArgs(buff, str, &tmp_params, last);
    01500         }
    01501         break;
    01502       }
    01503
    01504       case SCC_SIGN_NAME: { // {SIGN}
    01505         const Sign *si = Sign::GetIfValid(args->GetInt32());
    01506         if (si == NULL) break;
    01507
    01508         if (si->name != NULL) {
    01509           int64 args_array[] = {(uint64)(size_t)si->name};
    01510           StringParameters tmp_params(args_array);
    01511           buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
    01512         } else {
    01513           StringParameters tmp_params(NULL, 0, NULL);
    01514           buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
    01515         }
    01516         break;
    01517       }
    01518
    01519       case SCC_STATION_FEATURES: { // {STATIONFEATURES}
    01520         buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
    01521         break;
    01522       }
    01523
    01524       default:
    01525         if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
    01526         break;
    01527     }
    01528   }
    01529   *buff = '\0';
    01530   return buff;
    01531 }
    01532
    01533
    01534 static char *StationGetSpecialString(char *buff, int x, const char *last)
    01535 {
    01536   if ((x & FACIL_TRAIN)      && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
    01537   if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
    01538   if ((x & FACIL_BUS_STOP)   && (buff + Utf8CharLen(SCC_BUS)   < last)) buff += Utf8Encode(buff, SCC_BUS);
    01539   if ((x & FACIL_DOCK)       && (buff + Utf8CharLen(SCC_SHIP)  < last)) buff += Utf8Encode(buff, SCC_SHIP);
    01540   if ((x & FACIL_AIRPORT)    && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
    01541   *buff = '\0';
    01542   return buff;
    01543 }
    01544
    01545 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
    01546 {
    01547   return GenerateTownNameString(buff, last, ind, seed);
    01548 }
    01549
    01550 static const char * const _silly_company_names[] = {
    01551   "Bloggs Brothers",
    01552   "Tiny Transport Ltd.",
    01553   "Express Travel",
    01554   "Comfy-Coach & Co.",
    01555   "Crush & Bump Ltd.",
    01556   "Broken & Late Ltd.",
    01557   "Sam Speedy & Son",
    01558   "Supersonic Travel",
    01559   "Mike's Motors",
    01560   "Lightning International",
    01561   "Pannik & Loozit Ltd.",
    01562   "Inter-City Transport",
    01563   "Getout & Pushit Ltd."
    01564 };
    01565
    01566 static const char * const _surname_list[] = {
    01567   "Adams",
    01568   "Allan",
    01569   "Baker",
    01570   "Bigwig",
    01571   "Black",
    01572   "Bloggs",
    01573   "Brown",
    01574   "Campbell",
    01575   "Gordon",
    01576   "Hamilton",
    01577   "Hawthorn",
    01578   "Higgins",
    01579   "Green",
    01580   "Gribble",
    01581   "Jones",
    01582   "McAlpine",
    01583   "MacDonald",
    01584   "McIntosh",
    01585   "Muir",
    01586   "Murphy",
    01587   "Nelson",
    01588   "O'Donnell",
    01589   "Parker",
    01590   "Phillips",
    01591   "Pilkington",
    01592   "Quigley",
    01593   "Sharkey",
    01594   "Thomson",
    01595   "Watkins"
    01596 };
    01597
    01598 static const char * const _silly_surname_list[] = {
    01599   "Grumpy",
    01600   "Dozy",
    01601   "Speedy",
    01602   "Nosey",
    01603   "Dribble",
    01604   "Mushroom",
    01605   "Cabbage",
    01606   "Sniffle",
    01607   "Fishy",
    01608   "Swindle",
    01609   "Sneaky",
    01610   "Nutkins"
    01611 };
    01612
    01613 static const char _initial_name_letters[] = {
    01614   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
    01615   'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
    01616 };
    01617
    01618 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
    01619 {
    01620   const char * const *base;
    01621   uint num;
    01622
    01623   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
    01624     base = _silly_surname_list;
    01625     num  = lengthof(_silly_surname_list);
    01626   } else {
    01627     base = _surname_list;
    01628     num  = lengthof(_surname_list);
    01629   }
    01630
    01631   buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
    01632   buff = strecpy(buff, " & Co.", last);
    01633
    01634   return buff;
    01635 }
    01636
    01637 static char *GenPresidentName(char *buff, uint32 x, const char *last)
    01638 {
    01639   char initial[] = "?. ";
    01640   const char * const *base;
    01641   uint num;
    01642   uint i;
    01643
    01644   initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
    01645   buff = strecpy(buff, initial, last);
    01646
    01647   i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
    01648   if (i < sizeof(_initial_name_letters)) {
    01649     initial[0] = _initial_name_letters[i];
    01650     buff = strecpy(buff, initial, last);
    01651   }
    01652
    01653   if (_settings_game.game_creation.landscape == LT_TOYLAND) {
    01654     base = _silly_surname_list;
    01655     num  = lengthof(_silly_surname_list);
    01656   } else {
    01657     base = _surname_list;
    01658     num  = lengthof(_surname_list);
    01659   }
    01660
    01661   buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
    01662
    01663   return buff;
    01664 }
    01665
    01666 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
    01667 {
    01668   switch (ind) {
    01669     case 1: // not used
    01670       return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
    01671
    01672     case 2: // used for Foobar & Co company names
    01673       return GenAndCoName(buff, args->GetInt32(), last);
    01674
    01675     case 3: // President name
    01676       return GenPresidentName(buff, args->GetInt32(), last);
    01677   }
    01678
    01679   /* town name? */
    01680   if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
    01681     buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
    01682     return strecpy(buff, " Transport", last);
    01683   }
    01684
    01685   /* language name? */
    01686   if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
    01687     int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
    01688     return strecpy(buff,
    01689       &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
    01690   }
    01691
    01692   /* resolution size? */
    01693   if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
    01694     int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
    01695     buff += seprintf(
    01696       buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
    01697     );
    01698     return buff;
    01699   }
    01700
    01701   /* screenshot format name? */
    01702   if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
    01703     int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
    01704     return strecpy(buff, GetScreenshotFormatDesc(i), last);
    01705   }
    01706
    01707   NOT_REACHED();
    01708 }
    01709
    01710 #ifdef ENABLE_NETWORK
    01711 extern void SortNetworkLanguages();
    01712 #else /* ENABLE_NETWORK */
    01713 static inline void SortNetworkLanguages() {}
    01714 #endif /* ENABLE_NETWORK */
    01715
    01720 bool LanguagePackHeader::IsValid() const
    01721 {
    01722   return this->ident        == TO_LE32(LanguagePackHeader::IDENT) &&
    01723          this->version      == TO_LE32(LANGUAGE_PACK_VERSION) &&
    01724          this->plural_form  <  LANGUAGE_MAX_PLURAL &&
    01725          this->text_dir     <= 1 &&
    01726          this->newgrflangid < MAX_LANG &&
    01727          this->num_genders  < MAX_NUM_GENDERS &&
    01728          this->num_cases    < MAX_NUM_CASES &&
    01729          StrValid(this->name,                           lastof(this->name)) &&
    01730          StrValid(this->own_name,                       lastof(this->own_name)) &&
    01731          StrValid(this->isocode,                        lastof(this->isocode)) &&
    01732          StrValid(this->digit_group_separator,          lastof(this->digit_group_separator)) &&
    01733          StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
    01734          StrValid(this->digit_decimal_separator,        lastof(this->digit_decimal_separator));
    01735 }
    01736
    01742 bool ReadLanguagePack(const LanguageMetadata *lang)
    01743 {
    01744   /* Current language pack */
    01745   size_t len;
    01746   LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
    01747   if (lang_pack == NULL) return false;
    01748
    01749   /* End of read data (+ terminating zero added in ReadFileToMem()) */
    01750   const char *end = (char *)lang_pack + len + 1;
    01751
    01752   /* We need at least one byte of lang_pack->data */
    01753   if (end <= lang_pack->data || !lang_pack->IsValid()) {
    01754     free(lang_pack);
    01755     return false;
    01756   }
    01757
    01758 #if TTD_ENDIAN == TTD_BIG_ENDIAN
    01759   for (uint i = 0; i < TAB_COUNT; i++) {
    01760     lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
    01761   }
    01762 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
    01763
    01764   uint count = 0;
    01765   for (uint i = 0; i < TAB_COUNT; i++) {
    01766     uint16 num = lang_pack->offsets[i];
    01767     if (num > TAB_SIZE) {
    01768       free(lang_pack);
    01769       return false;
    01770     }
    01771
    01772     _langtab_start[i] = count;
    01773     _langtab_num[i] = num;
    01774     count += num;
    01775   }
    01776
    01777   /* Allocate offsets */
    01778   char **langpack_offs = MallocT<char *>(count);
    01779
    01780   /* Fill offsets */
    01781   char *s = lang_pack->data;
    01782   len = (byte)*s++;
    01783   for (uint i = 0; i < count; i++) {
    01784     if (s + len >= end) {
    01785       free(lang_pack);
    01786       free(langpack_offs);
    01787       return false;
    01788     }
    01789     if (len >= 0xC0) {
    01790       len = ((len & 0x3F) << 8) + (byte)*s++;
    01791       if (s + len >= end) {
    01792         free(lang_pack);
    01793         free(langpack_offs);
    01794         return false;
    01795       }
    01796     }
    01797     langpack_offs[i] = s;
    01798     s += len;
    01799     len = (byte)*s;
    01800     *s++ = '\0'; // zero terminate the string
    01801   }
    01802
    01803   free(_langpack);
    01804   _langpack = lang_pack;
    01805
    01806   free(_langpack_offs);
    01807   _langpack_offs = langpack_offs;
    01808
    01809   _current_language = lang;
    01810   _current_text_dir = (TextDirection)_current_language->text_dir;
    01811   const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
    01812   strecpy(_config_language_file, c_file, lastof(_config_language_file));
    01813   SetCurrentGrfLangID(_current_language->newgrflangid);
    01814
    01815 #ifdef WITH_ICU
    01816   /* Delete previous collator. */
    01817   if (_current_collator != NULL) {
    01818     delete _current_collator;
    01819     _current_collator = NULL;
    01820   }
    01821
    01822   /* Create a collator instance for our current locale. */
    01823   UErrorCode status = U_ZERO_ERROR;
    01824   _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
    01825   /* Sort number substrings by their numerical value. */
    01826   if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
    01827   /* Avoid using the collator if it is not correctly set. */
    01828   if (U_FAILURE(status)) {
    01829     delete _current_collator;
    01830     _current_collator = NULL;
    01831   }
    01832 #endif /* WITH_ICU */
    01833
    01834   /* Some lists need to be sorted again after a language change. */
    01835   ReconsiderGameScriptLanguage();
    01836   InitializeSortedCargoSpecs();
    01837   SortIndustryTypes();
    01838   BuildIndustriesLegend();
    01839   SortNetworkLanguages();
    01840   InvalidateWindowClassesData(WC_BUILD_VEHICLE);      // Build vehicle window.
    01841   InvalidateWindowClassesData(WC_TRAINS_LIST);        // Train group window.
    01842   InvalidateWindowClassesData(WC_ROADVEH_LIST);       // Road vehicle group window.
    01843   InvalidateWindowClassesData(WC_SHIPS_LIST);         // Ship group window.
    01844   InvalidateWindowClassesData(WC_AIRCRAFT_LIST);      // Aircraft group window.
    01845   InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY); // Industry directory window.
    01846   InvalidateWindowClassesData(WC_STATION_LIST);       // Station list window.
    01847
    01848   return true;
    01849 }
    01850
    01851 /* Win32 implementation in win32.cpp.
    01852  * OS X implementation in os/macosx/macos.mm. */
    01853 #if !(defined(WIN32) || defined(__APPLE__))
    01854
    01862 const char *GetCurrentLocale(const char *param)
    01863 {
    01864   const char *env;
    01865
    01866   env = getenv("LANGUAGE");
    01867   if (env != NULL) return env;
    01868
    01869   env = getenv("LC_ALL");
    01870   if (env != NULL) return env;
    01871
    01872   if (param != NULL) {
    01873     env = getenv(param);
    01874     if (env != NULL) return env;
    01875   }
    01876
    01877   return getenv("LANG");
    01878 }
    01879 #else
    01880 const char *GetCurrentLocale(const char *param);
    01881 #endif /* !(defined(WIN32) || defined(__APPLE__)) */
    01882
    01883 int CDECL StringIDSorter(const StringID *a, const StringID *b)
    01884 {
    01885   char stra[512];
    01886   char strb[512];
    01887   GetString(stra, *a, lastof(stra));
    01888   GetString(strb, *b, lastof(strb));
    01889
    01890   return strcmp(stra, strb);
    01891 }
    01892
    01898 const LanguageMetadata *GetLanguage(byte newgrflangid)
    01899 {
    01900   for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
    01901     if (newgrflangid == lang->newgrflangid) return lang;
    01902   }
    01903
    01904   return NULL;
    01905 }
    01906
    01913 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
    01914 {
    01915   FILE *f = fopen(file, "rb");
    01916   if (f == NULL) return false;
    01917
    01918   size_t read = fread(hdr, sizeof(*hdr), 1, f);
    01919   fclose(f);
    01920
    01921   bool ret = read == 1 && hdr->IsValid();
    01922
    01923   /* Convert endianness for the windows language ID */
    01924   if (ret) {
    01925     hdr->missing = FROM_LE16(hdr->missing);
    01926     hdr->winlangid = FROM_LE16(hdr->winlangid);
    01927   }
    01928   return ret;
    01929 }
    01930
    01935 static void GetLanguageList(const char *path)
    01936 {
    01937   DIR *dir = ttd_opendir(path);
    01938   if (dir != NULL) {
    01939     struct dirent *dirent;
    01940     while ((dirent = readdir(dir)) != NULL) {
    01941       const char *d_name    = FS2OTTD(dirent->d_name);
    01942       const char *extension = strrchr(d_name, '.');
    01943
    01944       /* Not a language file */
    01945       if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
    01946
    01947       LanguageMetadata lmd;
    01948       seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
    01949
    01950       /* Check whether the file is of the correct version */
    01951       if (!GetLanguageFileHeader(lmd.file, &lmd)) {
    01952         DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
    01953       } else if (GetLanguage(lmd.newgrflangid) != NULL) {
    01954         DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
    01955       } else {
    01956         *_languages.Append() = lmd;
    01957       }
    01958     }
    01959     closedir(dir);
    01960   }
    01961 }
    01962
    01967 void InitializeLanguagePacks()
    01968 {
    01969   Searchpath sp;
    01970
    01971   FOR_ALL_SEARCHPATHS(sp) {
    01972     char path[MAX_PATH];
    01973     FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
    01974     GetLanguageList(path);
    01975   }
    01976   if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
    01977
    01978   /* Acquire the locale of the current system */
    01979   const char *lang = GetCurrentLocale("LC_MESSAGES");
    01980   if (lang == NULL) lang = "en_GB";
    01981
    01982   const LanguageMetadata *chosen_language   = NULL;
    01983   const LanguageMetadata *language_fallback = NULL;
    01984   const LanguageMetadata *en_GB_fallback    = _languages.Begin();
    01985
    01986   /* Find a proper language. */
    01987   for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
    01988     /* We are trying to find a default language. The priority is by
    01989      * configuration file, local environment and last, if nothing found,
    01990      * English. */
    01991     const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
    01992     if (strcmp(lang_file, _config_language_file) == 0) {
    01993       chosen_language = lng;
    01994       break;
    01995     }
    01996
    01997     if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback    = lng;
    01998     if (strncmp(lng->isocode, lang, 5) == 0) chosen_language   = lng;
    01999     if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
    02000   }
    02001
    02002   /* We haven't found the language in the config nor the one in the locale.
    02003    * Now we set it to one of the fallback languages */
    02004   if (chosen_language == NULL) {
    02005     chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
    02006   }
    02007
    02008   if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
    02009 }
    02010
    02015 const char *GetCurrentLanguageIsoCode()
    02016 {
    02017   return _langpack->isocode;
    02018 }
    02019
    02026 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
    02027 {
    02028   InitFreeType(this->Monospace());
    02029   const Sprite *question_mark[FS_END];
    02030
    02031   for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
    02032     question_mark[size] = GetGlyph(size, '?');
    02033   }
    02034
    02035   this->Reset();
    02036   for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
    02037     FontSize size = this->DefaultSize();
    02038     if (str != NULL) *str = text;
    02039     for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
    02040       if (c == SCC_TINYFONT) {
    02041         size = FS_SMALL;
    02042       } else if (c == SCC_BIGFONT) {
    02043         size = FS_LARGE;
    02044       } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
    02045         /* The character is printable, but not in the normal font. This is the case we were testing for. */
    02046         return true;
    02047       }
    02048     }
    02049   }
    02050   return false;
    02051 }
    02052
    02054 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
    02055   uint i;
    02056   uint j;
    02057
    02058   /* virtual */ void Reset()
    02059   {
    02060     this->i = 0;
    02061     this->j = 0;
    02062   }
    02063
    02064   /* virtual */ FontSize DefaultSize()
    02065   {
    02066     return FS_NORMAL;
    02067   }
    02068
    02069   /* virtual */ const char *NextString()
    02070   {
    02071     if (this->i >= TAB_COUNT) return NULL;
    02072
    02073     const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
    02074
    02075     this->j++;
    02076     while (this->i < TAB_COUNT && this->j >= _langtab_num[this->i]) {
    02077       this->i++;
    02078       this->j = 0;
    02079     }
    02080
    02081     return ret;
    02082   }
    02083
    02084   /* virtual */ bool Monospace()
    02085   {
    02086     return false;
    02087   }
    02088
    02089   /* virtual */ void SetFontNames(FreeTypeSettings *settings, const char *font_name)
    02090   {
    02091 #ifdef WITH_FREETYPE
    02092     strecpy(settings->small.font,  font_name, lastof(settings->small.font));
    02093     strecpy(settings->medium.font, font_name, lastof(settings->medium.font));
    02094     strecpy(settings->large.font,  font_name, lastof(settings->large.font));
    02095 #endif /* WITH_FREETYPE */
    02096   }
    02097 };
    02098
    02112 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
    02113 {
    02114   static LanguagePackGlyphSearcher pack_searcher;
    02115   if (searcher == NULL) searcher = &pack_searcher;
    02116   bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
    02117 #ifdef WITH_FREETYPE
    02118   if (bad_font) {
    02119     /* We found an unprintable character... lets try whether we can find
    02120      * a fallback font that can print the characters in the current language. */
    02121     FreeTypeSettings backup;
    02122     memcpy(&backup, &_freetype, sizeof(backup));
    02123
    02124     bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
    02125
    02126     memcpy(&_freetype, &backup, sizeof(backup));
    02127
    02128     if (bad_font && base_font) {
    02129       /* Our fallback font does miss characters too, so keep the
    02130        * user chosen font as that is more likely to be any good than
    02131        * the wild guess we made */
    02132       InitFreeType(searcher->Monospace());
    02133     }
    02134   }
    02135 #endif
    02136
    02137   if (bad_font) {
    02138     /* All attempts have failed. Display an error. As we do not want the string to be translated by
    02139      * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this
    02140      * properly we have to set the colour of the string, otherwise we end up with a lot of artifacts.
    02141      * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into
    02142      * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */
    02143     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.");
    02144     Utf8Encode(err_str, SCC_YELLOW);
    02145     SetDParamStr(0, err_str);
    02146     ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
    02147
    02148     /* Reset the font width */
    02149     LoadStringWidthTable(searcher->Monospace());
    02150     return;
    02151   }
    02152
    02153   /* Update the font with cache */
    02154   LoadStringWidthTable(searcher->Monospace());
    02155
    02156 #if !defined(WITH_ICU)
    02157   /*
    02158    * For right-to-left languages we need the ICU library. If
    02159    * we do not have support for that library we warn the user
    02160    * about it with a message. As we do not want the string to
    02161    * be translated by the translators, we 'force' it into the
    02162    * binary and 'load' it via a BindCString. To do this
    02163    * properly we have to set the colour of the string,
    02164    * otherwise we end up with a lot of artifacts. The colour
    02165    * 'character' might change in the future, so for safety
    02166    * we just Utf8 Encode it into the string, which takes
    02167    * exactly three characters, so it replaces the "XXX" with
    02168    * the colour marker.
    02169    */
    02170   if (_current_text_dir != TD_LTR) {
    02171     static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
    02172     Utf8Encode(err_str, SCC_YELLOW);
    02173     SetDParamStr(0, err_str);
    02174     ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
    02175   }
    02176 #endif
    02177 }