00001
00002
00003
00004
00005
00006
00007
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
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[];
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
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
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
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
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
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
00458
00459 bool negative = number < 0;
00460 const char *multiplier = "";
00461
00462 number *= spec->rate;
00463
00464
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
00473
00474
00475 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
00476
00477
00478 if (compact) {
00479
00480
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
00497
00498
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
00519 uint64 n = abs(count);
00520
00521 switch (plural_form) {
00522 default:
00523 NOT_REACHED();
00524
00525
00526
00527
00528
00529 case 0:
00530 return n != 1 ? 1 : 0;
00531
00532
00533
00534
00535 case 1:
00536 return 0;
00537
00538
00539
00540
00541 case 2:
00542 return n > 1 ? 1 : 0;
00543
00544
00545
00546
00547
00548 case 3:
00549 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
00550
00551
00552
00553
00554 case 4:
00555 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
00556
00557
00558
00559
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
00564
00565
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
00570
00571
00572 case 7:
00573 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00574
00575
00576
00577
00578 case 8:
00579 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
00580
00581
00582
00583
00584 case 9:
00585 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
00586
00587
00588
00589
00590 case 10:
00591 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
00592
00593
00594
00595
00596
00597
00598 case 11:
00599 switch (n % 10) {
00600 case 0:
00601 case 1:
00602 case 3:
00603 case 6:
00604 case 7:
00605 case 8:
00606 return 0;
00607
00608 case 2:
00609 case 4:
00610 case 5:
00611 case 9:
00612 return 1;
00613
00614 default:
00615 NOT_REACHED();
00616 }
00617
00618
00619
00620
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
00624
00625
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
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 },
00727 { { 1, 0}, STR_UNITS_HEIGHT_METRIC },
00728 { { 1, 0}, STR_UNITS_HEIGHT_SI },
00729 };
00730
00736 uint ConvertSpeedToDisplaySpeed(uint speed)
00737 {
00738
00739
00740
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
00787 if (args->HasTypeInformation() && !dry_run) {
00788 if (UsingNewGRFTextStack()) {
00789
00790
00791
00792
00793
00794
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
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
00819
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
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
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
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: {
00938
00939 uint offset = orig_offset + (byte)*str++;
00940 int gender = 0;
00941 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
00942
00943
00944
00945 char input[4 + 1];
00946 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
00947 *p = '\0';
00948
00949
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
00959 const char *s = buf;
00960 WChar c = Utf8Consume(&s);
00961
00962 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00963 }
00964 str = ParseStringChoice(str, gender, &buff, last);
00965 break;
00966 }
00967
00968
00969
00970 case SCC_GENDER_INDEX:
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: {
00980 int plural_form = *str++;
00981 uint offset = orig_offset + (byte)*str++;
00982 int64 v = *args->GetPointerToOffset(offset);
00983 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
00984 break;
00985 }
00986
00987 case SCC_ARG_INDEX: {
00988 args->offset = orig_offset + (byte)*str++;
00989 break;
00990 }
00991
00992 case SCC_SET_CASE: {
00993
00994
00995 next_substr_case_index = (byte)*str++;
00996 break;
00997 }
00998
00999 case SCC_SWITCH_CASE: {
01000
01001
01002 uint num = (byte)*str++;
01003 while (num) {
01004 if ((byte)str[0] == case_index) {
01005
01006 str += 3;
01007 break;
01008 }
01009
01010 str += 3 + (str[1] << 8) + str[2];
01011 num--;
01012 }
01013 break;
01014 }
01015
01016 case SCC_REVISION:
01017 buff = strecpy(buff, _openttd_revision, last);
01018 break;
01019
01020 case SCC_STRING_ID:
01021 if (game_script) break;
01022 buff = GetStringWithArgs(buff, Utf8Consume(&str), args, last);
01023 break;
01024
01025 case SCC_RAW_STRING_POINTER: {
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: {
01033 StringID str = args->GetInt32(SCC_STRING);
01034 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
01035
01036
01037
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: {
01051
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:
01066 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
01067 break;
01068
01069 case SCC_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:
01077 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
01078 break;
01079
01080 case SCC_ZEROFILL_NUM: {
01081 int64 num = args->GetInt64();
01082 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
01083 break;
01084 }
01085
01086 case SCC_HEX:
01087 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
01088 break;
01089
01090 case SCC_BYTES:
01091 buff = FormatBytes(buff, args->GetInt64(), last);
01092 break;
01093
01094 case SCC_CARGO_TINY: {
01095
01096
01097
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: {
01123
01124
01125
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: {
01157
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: {
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;
01176
01177 if (first) {
01178 first = false;
01179 } else {
01180
01181 *buff++ = ',';
01182 *buff++ = ' ';
01183 }
01184
01185 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
01186 }
01187
01188
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
01195 assert(buff < last);
01196 break;
01197 }
01198
01199 case SCC_CURRENCY_SHORT:
01200 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
01201 break;
01202
01203 case SCC_CURRENCY_LONG:
01204 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
01205 break;
01206
01207 case SCC_DATE_TINY:
01208 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
01209 break;
01210
01211 case SCC_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:
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:
01222 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
01223 break;
01224
01225 case SCC_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: {
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: {
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: {
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: {
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: {
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: {
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: {
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: {
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: {
01306 CompanyID company = (CompanyID)args->GetInt32();
01307
01308
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: {
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: {
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: {
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: {
01373 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
01374 if (i == NULL) break;
01375
01376 if (_scan_for_gender_data) {
01377
01378
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
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: {
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: {
01409 StationID sid = args->GetInt32(SCC_STATION_NAME);
01410 const Station *st = Station::GetIfValid(sid);
01411
01412 if (st == NULL) {
01413
01414
01415
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
01429 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01430
01431
01432
01433
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: {
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: {
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: {
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: {
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: {
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:
01670 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
01671
01672 case 2:
01673 return GenAndCoName(buff, args->GetInt32(), last);
01674
01675 case 3:
01676 return GenPresidentName(buff, args->GetInt32(), last);
01677 }
01678
01679
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
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
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
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
01713 static inline void SortNetworkLanguages() {}
01714 #endif
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
01745 size_t len;
01746 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
01747 if (lang_pack == NULL) return false;
01748
01749
01750 const char *end = (char *)lang_pack + len + 1;
01751
01752
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
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
01778 char **langpack_offs = MallocT<char *>(count);
01779
01780
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';
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
01817 if (_current_collator != NULL) {
01818 delete _current_collator;
01819 _current_collator = NULL;
01820 }
01821
01822
01823 UErrorCode status = U_ZERO_ERROR;
01824 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01825
01826 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01827
01828 if (U_FAILURE(status)) {
01829 delete _current_collator;
01830 _current_collator = NULL;
01831 }
01832 #endif
01833
01834
01835 ReconsiderGameScriptLanguage();
01836 InitializeSortedCargoSpecs();
01837 SortIndustryTypes();
01838 BuildIndustriesLegend();
01839 SortNetworkLanguages();
01840 InvalidateWindowClassesData(WC_BUILD_VEHICLE);
01841 InvalidateWindowClassesData(WC_TRAINS_LIST);
01842 InvalidateWindowClassesData(WC_ROADVEH_LIST);
01843 InvalidateWindowClassesData(WC_SHIPS_LIST);
01844 InvalidateWindowClassesData(WC_AIRCRAFT_LIST);
01845 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY);
01846 InvalidateWindowClassesData(WC_STATION_LIST);
01847
01848 return true;
01849 }
01850
01851
01852
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
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
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
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
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
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
01987 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01988
01989
01990
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
02003
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
02046 return true;
02047 }
02048 }
02049 }
02050 return false;
02051 }
02052
02054 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
02055 uint i;
02056 uint j;
02057
02058 void Reset()
02059 {
02060 this->i = 0;
02061 this->j = 0;
02062 }
02063
02064 FontSize DefaultSize()
02065 {
02066 return FS_NORMAL;
02067 }
02068
02069 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 bool Monospace()
02085 {
02086 return false;
02087 }
02088
02089 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
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
02120
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
02130
02131
02132 InitFreeType(searcher->Monospace());
02133 }
02134 }
02135 #endif
02136
02137 if (bad_font) {
02138
02139
02140
02141
02142
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
02149 LoadStringWidthTable(searcher->Monospace());
02150 return;
02151 }
02152
02153
02154 LoadStringWidthTable(searcher->Monospace());
02155
02156 #if !defined(WITH_ICU)
02157
02158
02159
02160
02161
02162
02163
02164
02165
02166
02167
02168
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 }