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 "fontcache.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
00099 void CopyInDParam(int offs, const uint64 *src, int num)
00100 {
00101 MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
00102 }
00103
00110 void CopyOutDParam(uint64 *dst, int offs, int num)
00111 {
00112 MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
00113 }
00114
00123 void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
00124 {
00125 char buf[DRAW_STRING_BUFFER];
00126 GetString(buf, string, lastof(buf));
00127
00128 MemCpyT(dst, _global_string_params.GetPointerToOffset(0), num);
00129 for (int i = 0; i < num; i++) {
00130 if (_global_string_params.HasTypeInformation() && _global_string_params.GetTypeAtOffset(i) == SCC_RAW_STRING_POINTER) {
00131 strings[i] = strdup((const char *)(size_t)_global_string_params.GetParam(i));
00132 dst[i] = (size_t)strings[i];
00133 } else {
00134 strings[i] = NULL;
00135 }
00136 }
00137 }
00138
00139 static char *StationGetSpecialString(char *buff, int x, const char *last);
00140 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
00141 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
00142
00143 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);
00144
00145 struct LanguagePack : public LanguagePackHeader {
00146 char data[];
00147 };
00148
00149 static char **_langpack_offs;
00150 static LanguagePack *_langpack;
00151 static uint _langtab_num[TAB_COUNT];
00152 static uint _langtab_start[TAB_COUNT];
00153 static bool _scan_for_gender_data = false;
00154
00155
00156 const char *GetStringPtr(StringID string)
00157 {
00158 switch (GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)) {
00159 case GAME_TEXT_TAB: return GetGameStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
00160
00161 case 26: return GetStringPtr(GetGRFStringID(0, 0xD000 + GB(string, TAB_SIZE_OFFSET, 10)));
00162 case 28: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS));
00163 case 29: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x0800);
00164 case 30: return GetGRFStringPtr(GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS) + 0x1000);
00165 default: return _langpack_offs[_langtab_start[GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS)] + GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS)];
00166 }
00167 }
00168
00179 char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, const char *last, uint case_index, bool game_script)
00180 {
00181 if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
00182
00183 uint index = GB(string, TAB_SIZE_OFFSET, TAB_SIZE_BITS);
00184 uint tab = GB(string, TAB_COUNT_OFFSET, TAB_COUNT_BITS);
00185
00186 switch (tab) {
00187 case 4:
00188 if (index >= 0xC0 && !game_script) {
00189 return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
00190 }
00191 break;
00192
00193 case 14:
00194 if (index >= 0xE4 && !game_script) {
00195 return GetSpecialNameString(buffr, index - 0xE4, args, last);
00196 }
00197 break;
00198
00199 case 15:
00200
00201 error("Incorrect conversion of custom name string.");
00202
00203 case GAME_TEXT_TAB:
00204 return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
00205
00206 case 26:
00207
00208 if (HasBit(index, 10)) {
00209 StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
00210 return GetStringWithArgs(buffr, string, args, last, case_index);
00211 }
00212 break;
00213
00214 case 28:
00215 return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
00216
00217 case 29:
00218 return FormatString(buffr, GetGRFStringPtr(index + 0x0800), args, last, case_index);
00219
00220 case 30:
00221 return FormatString(buffr, GetGRFStringPtr(index + 0x1000), args, last, case_index);
00222
00223 case 31:
00224 NOT_REACHED();
00225 }
00226
00227 if (index >= _langtab_num[tab]) {
00228 if (game_script) {
00229 return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
00230 }
00231 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
00232 }
00233
00234 return FormatString(buffr, GetStringPtr(string), args, last, case_index);
00235 }
00236
00237 char *GetString(char *buffr, StringID string, const char *last)
00238 {
00239 _global_string_params.ClearTypeInformation();
00240 _global_string_params.offset = 0;
00241 return GetStringWithArgs(buffr, string, &_global_string_params, last);
00242 }
00243
00244
00245 char *InlineString(char *buf, StringID string)
00246 {
00247 buf += Utf8Encode(buf, SCC_STRING_ID);
00248 buf += Utf8Encode(buf, string);
00249 return buf;
00250 }
00251
00252
00258 void SetDParamStr(uint n, const char *str)
00259 {
00260 SetDParam(n, (uint64)(size_t)str);
00261 }
00262
00267 void InjectDParam(uint amount)
00268 {
00269 _global_string_params.ShiftParameters(amount);
00270 }
00271
00283 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
00284 {
00285 static const int max_digits = 20;
00286 uint64 divisor = 10000000000000000000ULL;
00287 zerofill += fractional_digits;
00288 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
00289
00290 if (number < 0) {
00291 buff += seprintf(buff, last, "-");
00292 number = -number;
00293 }
00294
00295 uint64 num = number;
00296 uint64 tot = 0;
00297 for (int i = 0; i < max_digits; i++) {
00298 if (i == max_digits - fractional_digits) {
00299 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
00300 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
00301 buff += seprintf(buff, last, "%s", decimal_separator);
00302 }
00303
00304 uint64 quot = 0;
00305 if (num >= divisor) {
00306 quot = num / divisor;
00307 num = num % divisor;
00308 }
00309 if ((tot |= quot) || i >= max_digits - zerofill) {
00310 buff += seprintf(buff, last, "%i", (int)quot);
00311 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
00312 }
00313
00314 divisor /= 10;
00315 }
00316
00317 *buff = '\0';
00318
00319 return buff;
00320 }
00321
00322 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
00323 {
00324 const char *separator = _settings_game.locale.digit_group_separator;
00325 if (separator == NULL) separator = _langpack->digit_group_separator;
00326 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
00327 }
00328
00329 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
00330 {
00331 return FormatNumber(buff, number, last, "");
00332 }
00333
00334 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
00335 {
00336 return FormatNumber(buff, number, last, "", count);
00337 }
00338
00339 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
00340 {
00341 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
00342 }
00343
00351 static char *FormatBytes(char *buff, int64 number, const char *last)
00352 {
00353 assert(number >= 0);
00354
00355
00356 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
00357 uint id = 1;
00358 while (number >= 1024 * 1024) {
00359 number /= 1024;
00360 id++;
00361 }
00362
00363 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
00364 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
00365
00366 if (number < 1024) {
00367 id = 0;
00368 buff += seprintf(buff, last, "%i", (int)number);
00369 } else if (number < 1024 * 10) {
00370 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
00371 } else if (number < 1024 * 100) {
00372 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
00373 } else {
00374 assert(number < 1024 * 1024);
00375 buff += seprintf(buff, last, "%i", (int)number / 1024);
00376 }
00377
00378 assert(id < lengthof(iec_prefixes));
00379 buff += seprintf(buff, last, " %sB", iec_prefixes[id]);
00380
00381 return buff;
00382 }
00383
00384 static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index)
00385 {
00386 YearMonthDay ymd;
00387 ConvertDateToYMD(date, &ymd);
00388
00389 int64 args[] = {ymd.day + STR_ORDINAL_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
00390 StringParameters tmp_params(args);
00391 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
00392 }
00393
00394 static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index)
00395 {
00396 YearMonthDay ymd;
00397 ConvertDateToYMD(date, &ymd);
00398
00399 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
00400 StringParameters tmp_params(args);
00401 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
00402 }
00403
00404 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
00405 {
00406 YearMonthDay ymd;
00407 ConvertDateToYMD(date, &ymd);
00408
00409 char day[3];
00410 char month[3];
00411
00412 snprintf(day, lengthof(day), "%02i", ymd.day);
00413 snprintf(month, lengthof(month), "%02i", ymd.month + 1);
00414
00415 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
00416 StringParameters tmp_params(args);
00417 return FormatString(buff, GetStringPtr(str), &tmp_params, last);
00418 }
00419
00420 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
00421 {
00422
00423
00424 bool negative = number < 0;
00425 const char *multiplier = "";
00426
00427 number *= spec->rate;
00428
00429
00430 if (number < 0) {
00431 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
00432 buff += Utf8Encode(buff, SCC_RED);
00433 buff = strecpy(buff, "-", last);
00434 number = -number;
00435 }
00436
00437
00438
00439
00440 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
00441
00442
00443 if (compact) {
00444
00445
00446 if (number >= 1000000000 - 500) {
00447 number = (number + 500000) / 1000000;
00448 multiplier = "M";
00449 } else if (number >= 1000000) {
00450 number = (number + 500) / 1000;
00451 multiplier = "k";
00452 }
00453 }
00454
00455 const char *separator = _settings_game.locale.digit_group_separator_currency;
00456 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
00457 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
00458 buff = FormatNumber(buff, number, last, separator);
00459 buff = strecpy(buff, multiplier, last);
00460
00461
00462
00463
00464 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
00465
00466 if (negative) {
00467 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
00468 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
00469 *buff = '\0';
00470 }
00471
00472 return buff;
00473 }
00474
00481 static int DeterminePluralForm(int64 count, int plural_form)
00482 {
00483
00484 uint64 n = abs(count);
00485
00486 switch (plural_form) {
00487 default:
00488 NOT_REACHED();
00489
00490
00491
00492
00493
00494 case 0:
00495 return n != 1;
00496
00497
00498
00499
00500 case 1:
00501 return 0;
00502
00503
00504
00505
00506 case 2:
00507 return n > 1;
00508
00509
00510
00511
00512 case 3:
00513 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
00514
00515
00516
00517
00518 case 4:
00519 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
00520
00521
00522
00523
00524 case 5:
00525 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00526
00527
00528
00529
00530 case 6:
00531 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00532
00533
00534
00535
00536 case 7:
00537 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00538
00539
00540
00541
00542 case 8:
00543 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
00544
00545
00546
00547
00548 case 9:
00549 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
00550
00551
00552
00553
00554 case 10:
00555 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
00556
00557
00558
00559
00560
00561
00562
00563 case 11:
00564 switch (n % 10) {
00565 case 0:
00566 case 1:
00567 case 3:
00568 case 6:
00569 case 7:
00570 case 8:
00571 return 0;
00572
00573 case 2:
00574 case 4:
00575 case 5:
00576 case 9:
00577 return 1;
00578
00579 default:
00580 NOT_REACHED();
00581 }
00582
00583
00584
00585
00586 case 12:
00587 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
00588 }
00589 }
00590
00591 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
00592 {
00593
00594 uint n = (byte)*b++;
00595 uint pos, i, mypos = 0;
00596
00597 for (i = pos = 0; i != n; i++) {
00598 uint len = (byte)*b++;
00599 if (i == form) mypos = pos;
00600 pos += len;
00601 }
00602
00603 *dst += seprintf(*dst, last, "%s", b + mypos);
00604 return b + pos;
00605 }
00606
00608 struct UnitConversion {
00609 int multiplier;
00610 int shift;
00611
00618 int64 ToDisplay(int64 input, bool round = true) const
00619 {
00620 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
00621 }
00622
00630 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
00631 {
00632 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
00633 }
00634 };
00635
00636 struct Units {
00637 UnitConversion c_velocity;
00638 StringID velocity;
00639 UnitConversion c_power;
00640 StringID power;
00641 UnitConversion c_weight;
00642 StringID s_weight;
00643 StringID l_weight;
00644 UnitConversion c_volume;
00645 StringID s_volume;
00646 StringID l_volume;
00647 UnitConversion c_force;
00648 StringID force;
00649 UnitConversion c_height;
00650 StringID height;
00651 };
00652
00653
00654 static const Units _units[] = {
00655 {
00656 { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL,
00657 { 1, 0}, STR_UNITS_POWER_IMPERIAL,
00658 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00659 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00660 { 1, 0}, STR_UNITS_FORCE_SI,
00661 { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL,
00662 },
00663 {
00664 { 103, 6}, STR_UNITS_VELOCITY_METRIC,
00665 {4153, 12}, STR_UNITS_POWER_METRIC,
00666 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00667 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00668 { 1, 0}, STR_UNITS_FORCE_SI,
00669 { 1, 0}, STR_UNITS_HEIGHT_SI,
00670 },
00671 {
00672 {1831, 12}, STR_UNITS_VELOCITY_SI,
00673 {6109, 13}, STR_UNITS_POWER_SI,
00674 {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00675 { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00676 { 1, 0}, STR_UNITS_FORCE_SI,
00677 { 1, 0}, STR_UNITS_HEIGHT_SI,
00678 },
00679 };
00680
00686 uint ConvertSpeedToDisplaySpeed(uint speed)
00687 {
00688
00689
00690
00691 return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed, false);
00692 }
00693
00699 uint ConvertDisplaySpeedToSpeed(uint speed)
00700 {
00701 return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed);
00702 }
00703
00709 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
00710 {
00711 return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed * 10, false) / 16;
00712 }
00713
00719 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
00720 {
00721 return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed * 16, true, 10);
00722 }
00732 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
00733 {
00734 uint orig_offset = args->offset;
00735
00736
00737 if (args->HasTypeInformation() && !dry_run) {
00738 if (UsingNewGRFTextStack()) {
00739
00740
00741
00742
00743
00744
00745 struct TextRefStack *backup = CreateTextRefStackBackup();
00746 FormatString(buff, str_arg, args, last, case_index, game_script, true);
00747 RestoreTextRefStackBackup(backup);
00748 } else {
00749 FormatString(buff, str_arg, args, last, case_index, game_script, true);
00750 }
00751
00752 args->offset = orig_offset;
00753 }
00754 WChar b;
00755 uint next_substr_case_index = 0;
00756 char *buf_start = buff;
00757 std::stack<const char *> str_stack;
00758 str_stack.push(str_arg);
00759
00760 for (;;) {
00761 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
00762 str_stack.pop();
00763 }
00764 if (str_stack.empty()) break;
00765 const char *&str = str_stack.top();
00766
00767 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00768
00769
00770 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), dry_run);
00771 if (b == 0) continue;
00772 }
00773
00774 switch (b) {
00775 case SCC_ENCODED: {
00776 uint64 sub_args_data[20];
00777 WChar sub_args_type[20];
00778 bool sub_args_need_free[20];
00779 StringParameters sub_args(sub_args_data, 20, sub_args_type);
00780
00781 sub_args.ClearTypeInformation();
00782 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
00783
00784 uint16 stringid;
00785 const char *s = str;
00786 char *p;
00787 stringid = strtol(str, &p, 16);
00788 if (*p != ':' && *p != '\0') {
00789 while (*p != '\0') p++;
00790 str = p;
00791 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
00792 break;
00793 }
00794 if (stringid >= TAB_SIZE) {
00795 while (*p != '\0') p++;
00796 str = p;
00797 buff = strecat(buff, "(invalid StringID)", last);
00798 break;
00799 }
00800
00801 int i = 0;
00802 while (*p != '\0' && i < 20) {
00803 uint64 param;
00804 s = ++p;
00805
00806
00807 bool instring = false;
00808 bool escape = false;
00809 for (;; p++) {
00810 if (*p == '\\') {
00811 escape = true;
00812 continue;
00813 }
00814 if (*p == '"' && escape) {
00815 escape = false;
00816 continue;
00817 }
00818 escape = false;
00819
00820 if (*p == '"') {
00821 instring = !instring;
00822 continue;
00823 }
00824 if (instring) {
00825 continue;
00826 }
00827
00828 if (*p == ':') break;
00829 if (*p == '\0') break;
00830 }
00831
00832 if (*s != '"') {
00833
00834 WChar l;
00835 size_t len = Utf8Decode(&l, s);
00836 bool lookup = (l == SCC_ENCODED);
00837 if (lookup) s += len;
00838
00839 param = strtol(s, &p, 16);
00840
00841 if (lookup) {
00842 if (param >= TAB_SIZE) {
00843 while (*p != '\0') p++;
00844 str = p;
00845 buff = strecat(buff, "(invalid sub-StringID)", last);
00846 break;
00847 }
00848 param = (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + param;
00849 }
00850
00851 sub_args.SetParam(i++, param);
00852 } else {
00853 char *g = strdup(s);
00854 g[p - s] = '\0';
00855
00856 sub_args_need_free[i] = true;
00857 sub_args.SetParam(i++, (uint64)(size_t)g);
00858 }
00859 }
00860
00861 if (*str == '\0') break;
00862
00863 str = p;
00864 buff = GetStringWithArgs(buff, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, last, true);
00865
00866 for (int i = 0; i < 20; i++) {
00867 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
00868 }
00869 break;
00870 }
00871
00872 case SCC_NEWGRF_STRINL: {
00873 StringID substr = Utf8Consume(&str);
00874 str_stack.push(GetStringPtr(substr));
00875 break;
00876 }
00877
00878 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
00879 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
00880 str_stack.push(GetStringPtr(substr));
00881 case_index = next_substr_case_index;
00882 next_substr_case_index = 0;
00883 break;
00884 }
00885
00886
00887 case SCC_GENDER_LIST: {
00888
00889 uint offset = orig_offset + (byte)*str++;
00890 int gender = 0;
00891 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
00892
00893
00894
00895 char input[4 + 1];
00896 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
00897 *p = '\0';
00898
00899
00900 char buf[256];
00901 bool old_sgd = _scan_for_gender_data;
00902 _scan_for_gender_data = true;
00903 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
00904 p = FormatString(buf, input, &tmp_params, lastof(buf));
00905 _scan_for_gender_data = old_sgd;
00906 *p = '\0';
00907
00908
00909 const char *s = buf;
00910 WChar c = Utf8Consume(&s);
00911
00912 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00913 }
00914 str = ParseStringChoice(str, gender, &buff, last);
00915 break;
00916 }
00917
00918
00919
00920 case SCC_GENDER_INDEX:
00921 if (_scan_for_gender_data) {
00922 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
00923 *buff++ = *str++;
00924 } else {
00925 str++;
00926 }
00927 break;
00928
00929 case SCC_PLURAL_LIST: {
00930 int plural_form = *str++;
00931 uint offset = orig_offset + (byte)*str++;
00932 int64 v = *args->GetPointerToOffset(offset);
00933 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
00934 break;
00935 }
00936
00937 case SCC_ARG_INDEX: {
00938 args->offset = orig_offset + (byte)*str++;
00939 break;
00940 }
00941
00942 case SCC_SET_CASE: {
00943
00944
00945 next_substr_case_index = (byte)*str++;
00946 break;
00947 }
00948
00949 case SCC_SWITCH_CASE: {
00950
00951
00952 uint num = (byte)*str++;
00953 while (num) {
00954 if ((byte)str[0] == case_index) {
00955
00956 str += 3;
00957 break;
00958 }
00959
00960 str += 3 + (str[1] << 8) + str[2];
00961 num--;
00962 }
00963 break;
00964 }
00965
00966 case SCC_SETX:
00967 if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00968 buff += Utf8Encode(buff, SCC_SETX);
00969 *buff++ = *str++;
00970 }
00971 break;
00972
00973 case SCC_SETXY:
00974 if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00975 buff += Utf8Encode(buff, SCC_SETXY);
00976 *buff++ = *str++;
00977 *buff++ = *str++;
00978 }
00979 break;
00980
00981 case SCC_REVISION:
00982 buff = strecpy(buff, _openttd_revision, last);
00983 break;
00984
00985 case SCC_STRING_ID:
00986 if (game_script) break;
00987 buff = GetStringWithArgs(buff, Utf8Consume(&str), args, last);
00988 break;
00989
00990 case SCC_RAW_STRING_POINTER: {
00991 if (game_script) break;
00992 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
00993 buff = FormatString(buff, str, args, last);
00994 break;
00995 }
00996
00997 case SCC_STRING: {
00998 StringID str = args->GetInt32(SCC_STRING);
00999 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
01000
01001
01002
01003 StringParameters tmp_params(args->GetDataPointer(), args->num_param - args->offset, NULL);
01004 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
01005 next_substr_case_index = 0;
01006 break;
01007 }
01008
01009 case SCC_STRING1:
01010 case SCC_STRING2:
01011 case SCC_STRING3:
01012 case SCC_STRING4:
01013 case SCC_STRING5:
01014 case SCC_STRING6:
01015 case SCC_STRING7: {
01016
01017 StringID str = args->GetInt32(b);
01018 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
01019 StringParameters sub_args(*args, b - SCC_STRING1 + 1);
01020 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
01021 next_substr_case_index = 0;
01022 break;
01023 }
01024
01025 case SCC_COMMA:
01026 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
01027 break;
01028
01029 case SCC_DECIMAL: {
01030 int64 number = args->GetInt64(SCC_DECIMAL);
01031 int digits = args->GetInt32(SCC_DECIMAL);
01032 buff = FormatCommaNumber(buff, number, last, digits);
01033 break;
01034 }
01035
01036 case SCC_NUM:
01037 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
01038 break;
01039
01040 case SCC_ZEROFILL_NUM: {
01041 int64 num = args->GetInt64();
01042 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
01043 break;
01044 }
01045
01046 case SCC_HEX:
01047 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
01048 break;
01049
01050 case SCC_BYTES:
01051 buff = FormatBytes(buff, args->GetInt64(), last);
01052 break;
01053
01054 case SCC_CARGO_TINY: {
01055
01056
01057
01058 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
01059 if (cargo >= CargoSpec::GetArraySize()) break;
01060
01061 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
01062 int64 amount = 0;
01063 switch (cargo_str) {
01064 case STR_TONS:
01065 amount = _units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64());
01066 break;
01067
01068 case STR_LITERS:
01069 amount = _units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64());
01070 break;
01071
01072 default: {
01073 amount = args->GetInt64();
01074 break;
01075 }
01076 }
01077
01078 buff = FormatCommaNumber(buff, amount, last);
01079 break;
01080 }
01081
01082 case SCC_CARGO_SHORT: {
01083
01084
01085
01086 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
01087 if (cargo >= CargoSpec::GetArraySize()) break;
01088
01089 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
01090 switch (cargo_str) {
01091 case STR_TONS: {
01092 assert(_settings_game.locale.units < lengthof(_units));
01093 int64 args_array[] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01094 StringParameters tmp_params(args_array);
01095 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
01096 break;
01097 }
01098
01099 case STR_LITERS: {
01100 assert(_settings_game.locale.units < lengthof(_units));
01101 int64 args_array[] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01102 StringParameters tmp_params(args_array);
01103 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
01104 break;
01105 }
01106
01107 default: {
01108 StringParameters tmp_params(*args, 1);
01109 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
01110 break;
01111 }
01112 }
01113 break;
01114 }
01115
01116 case SCC_CARGO_LONG: {
01117
01118 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
01119 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
01120
01121 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
01122 StringParameters tmp_args(*args, 1);
01123 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
01124 break;
01125 }
01126
01127 case SCC_CARGO_LIST: {
01128 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
01129 bool first = true;
01130
01131 const CargoSpec *cs;
01132 FOR_ALL_SORTED_CARGOSPECS(cs) {
01133 if (!HasBit(cmask, cs->Index())) continue;
01134
01135 if (buff >= last - 2) break;
01136
01137 if (first) {
01138 first = false;
01139 } else {
01140
01141 *buff++ = ',';
01142 *buff++ = ' ';
01143 }
01144
01145 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
01146 }
01147
01148
01149 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
01150
01151 *buff = '\0';
01152 next_substr_case_index = 0;
01153
01154
01155 assert(buff < last);
01156 break;
01157 }
01158
01159 case SCC_CURRENCY_SHORT:
01160 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
01161 break;
01162
01163 case SCC_CURRENCY_LONG:
01164 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
01165 break;
01166
01167 case SCC_DATE_TINY:
01168 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
01169 break;
01170
01171 case SCC_DATE_SHORT:
01172 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
01173 next_substr_case_index = 0;
01174 break;
01175
01176 case SCC_DATE_LONG:
01177 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
01178 next_substr_case_index = 0;
01179 break;
01180
01181 case SCC_DATE_ISO:
01182 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
01183 break;
01184
01185 case SCC_FORCE: {
01186 assert(_settings_game.locale.units < lengthof(_units));
01187 int64 args_array[1] = {_units[_settings_game.locale.units].c_force.ToDisplay(args->GetInt64())};
01188 StringParameters tmp_params(args_array);
01189 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].force), &tmp_params, last);
01190 break;
01191 }
01192
01193 case SCC_HEIGHT: {
01194 int64 args_array[] = {_units[_settings_game.locale.units].c_height.ToDisplay(args->GetInt64())};
01195 StringParameters tmp_params(args_array);
01196 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].height), &tmp_params, last);
01197 break;
01198 }
01199
01200 case SCC_POWER: {
01201 assert(_settings_game.locale.units < lengthof(_units));
01202 int64 args_array[1] = {_units[_settings_game.locale.units].c_power.ToDisplay(args->GetInt64())};
01203 StringParameters tmp_params(args_array);
01204 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].power), &tmp_params, last);
01205 break;
01206 }
01207
01208 case SCC_VELOCITY: {
01209 assert(_settings_game.locale.units < lengthof(_units));
01210 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
01211 StringParameters tmp_params(args_array);
01212 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].velocity), &tmp_params, last);
01213 break;
01214 }
01215
01216 case SCC_VOLUME_SHORT: {
01217 assert(_settings_game.locale.units < lengthof(_units));
01218 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01219 StringParameters tmp_params(args_array);
01220 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_volume), &tmp_params, last);
01221 break;
01222 }
01223
01224 case SCC_VOLUME_LONG: {
01225 assert(_settings_game.locale.units < lengthof(_units));
01226 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
01227 StringParameters tmp_params(args_array);
01228 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
01229 break;
01230 }
01231
01232 case SCC_WEIGHT_SHORT: {
01233 assert(_settings_game.locale.units < lengthof(_units));
01234 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01235 StringParameters tmp_params(args_array);
01236 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_weight), &tmp_params, last);
01237 break;
01238 }
01239
01240 case SCC_WEIGHT_LONG: {
01241 assert(_settings_game.locale.units < lengthof(_units));
01242 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
01243 StringParameters tmp_params(args_array);
01244 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
01245 break;
01246 }
01247
01248 case SCC_COMPANY_NAME: {
01249 const Company *c = Company::GetIfValid(args->GetInt32());
01250 if (c == NULL) break;
01251
01252 if (c->name != NULL) {
01253 int64 args_array[] = {(uint64)(size_t)c->name};
01254 StringParameters tmp_params(args_array);
01255 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01256 } else {
01257 int64 args_array[] = {c->name_2};
01258 StringParameters tmp_params(args_array);
01259 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
01260 }
01261 break;
01262 }
01263
01264 case SCC_COMPANY_NUM: {
01265 CompanyID company = (CompanyID)args->GetInt32();
01266
01267
01268 if (Company::IsValidHumanID(company)) {
01269 int64 args_array[] = {company + 1};
01270 StringParameters tmp_params(args_array);
01271 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
01272 }
01273 break;
01274 }
01275
01276 case SCC_DEPOT_NAME: {
01277 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
01278 if (vt == VEH_AIRCRAFT) {
01279 uint64 args_array[] = {args->GetInt32()};
01280 WChar types_array[] = {SCC_STATION_NAME};
01281 StringParameters tmp_params(args_array, 1, types_array);
01282 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
01283 break;
01284 }
01285
01286 const Depot *d = Depot::Get(args->GetInt32());
01287 if (d->name != NULL) {
01288 int64 args_array[] = {(uint64)(size_t)d->name};
01289 StringParameters tmp_params(args_array);
01290 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01291 } else {
01292 int64 args_array[] = {d->town->index, d->town_cn + 1};
01293 StringParameters tmp_params(args_array);
01294 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
01295 }
01296 break;
01297 }
01298
01299 case SCC_ENGINE_NAME: {
01300 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
01301 if (e == NULL) break;
01302
01303 if (e->name != NULL && e->IsEnabled()) {
01304 int64 args_array[] = {(uint64)(size_t)e->name};
01305 StringParameters tmp_params(args_array);
01306 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01307 } else {
01308 StringParameters tmp_params(NULL, 0, NULL);
01309 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
01310 }
01311 break;
01312 }
01313
01314 case SCC_GROUP_NAME: {
01315 const Group *g = Group::GetIfValid(args->GetInt32());
01316 if (g == NULL) break;
01317
01318 if (g->name != NULL) {
01319 int64 args_array[] = {(uint64)(size_t)g->name};
01320 StringParameters tmp_params(args_array);
01321 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01322 } else {
01323 int64 args_array[] = {g->index};
01324 StringParameters tmp_params(args_array);
01325
01326 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
01327 }
01328 break;
01329 }
01330
01331 case SCC_INDUSTRY_NAME: {
01332 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
01333 if (i == NULL) break;
01334
01335 if (_scan_for_gender_data) {
01336
01337
01338 StringParameters tmp_params(NULL, 0, NULL);
01339 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
01340 } else {
01341
01342 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
01343 StringParameters tmp_params(args_array);
01344
01345 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
01346 }
01347 next_substr_case_index = 0;
01348 break;
01349 }
01350
01351 case SCC_PRESIDENT_NAME: {
01352 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
01353 if (c == NULL) break;
01354
01355 if (c->president_name != NULL) {
01356 int64 args_array[] = {(uint64)(size_t)c->president_name};
01357 StringParameters tmp_params(args_array);
01358 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01359 } else {
01360 int64 args_array[] = {c->president_name_2};
01361 StringParameters tmp_params(args_array);
01362 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
01363 }
01364 break;
01365 }
01366
01367 case SCC_STATION_NAME: {
01368 StationID sid = args->GetInt32(SCC_STATION_NAME);
01369 const Station *st = Station::GetIfValid(sid);
01370
01371 if (st == NULL) {
01372
01373
01374
01375 StringParameters tmp_params(NULL, 0, NULL);
01376 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
01377 break;
01378 }
01379
01380 if (st->name != NULL) {
01381 int64 args_array[] = {(uint64)(size_t)st->name};
01382 StringParameters tmp_params(args_array);
01383 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01384 } else {
01385 StringID str = st->string_id;
01386 if (st->indtype != IT_INVALID) {
01387
01388 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01389
01390
01391
01392
01393 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
01394 str = indsp->station_name;
01395 }
01396 }
01397
01398 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
01399 StringParameters tmp_params(args_array);
01400 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01401 }
01402 break;
01403 }
01404
01405 case SCC_TOWN_NAME: {
01406 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
01407 if (t == NULL) break;
01408
01409 if (t->name != NULL) {
01410 int64 args_array[] = {(uint64)(size_t)t->name};
01411 StringParameters tmp_params(args_array);
01412 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01413 } else {
01414 buff = GetTownName(buff, t, last);
01415 }
01416 break;
01417 }
01418
01419 case SCC_WAYPOINT_NAME: {
01420 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
01421 if (wp == NULL) break;
01422
01423 if (wp->name != NULL) {
01424 int64 args_array[] = {(uint64)(size_t)wp->name};
01425 StringParameters tmp_params(args_array);
01426 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01427 } else {
01428 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
01429 StringParameters tmp_params(args_array);
01430 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
01431 if (wp->town_cn != 0) str++;
01432 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01433 }
01434 break;
01435 }
01436
01437 case SCC_VEHICLE_NAME: {
01438 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
01439 if (v == NULL) break;
01440
01441 if (v->name != NULL) {
01442 int64 args_array[] = {(uint64)(size_t)v->name};
01443 StringParameters tmp_params(args_array);
01444 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01445 } else {
01446 int64 args_array[] = {v->unitnumber};
01447 StringParameters tmp_params(args_array);
01448
01449 StringID str;
01450 switch (v->type) {
01451 default: NOT_REACHED();
01452 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
01453 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
01454 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
01455 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01456 }
01457
01458 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01459 }
01460 break;
01461 }
01462
01463 case SCC_SIGN_NAME: {
01464 const Sign *si = Sign::GetIfValid(args->GetInt32());
01465 if (si == NULL) break;
01466
01467 if (si->name != NULL) {
01468 int64 args_array[] = {(uint64)(size_t)si->name};
01469 StringParameters tmp_params(args_array);
01470 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01471 } else {
01472 StringParameters tmp_params(NULL, 0, NULL);
01473 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
01474 }
01475 break;
01476 }
01477
01478 case SCC_STATION_FEATURES: {
01479 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
01480 break;
01481 }
01482
01483 default:
01484 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01485 break;
01486 }
01487 }
01488 *buff = '\0';
01489 return buff;
01490 }
01491
01492
01493 static char *StationGetSpecialString(char *buff, int x, const char *last)
01494 {
01495 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01496 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01497 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01498 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01499 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01500 *buff = '\0';
01501 return buff;
01502 }
01503
01504 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01505 {
01506 return GenerateTownNameString(buff, last, ind, seed);
01507 }
01508
01509 static const char * const _silly_company_names[] = {
01510 "Bloggs Brothers",
01511 "Tiny Transport Ltd.",
01512 "Express Travel",
01513 "Comfy-Coach & Co.",
01514 "Crush & Bump Ltd.",
01515 "Broken & Late Ltd.",
01516 "Sam Speedy & Son",
01517 "Supersonic Travel",
01518 "Mike's Motors",
01519 "Lightning International",
01520 "Pannik & Loozit Ltd.",
01521 "Inter-City Transport",
01522 "Getout & Pushit Ltd."
01523 };
01524
01525 static const char * const _surname_list[] = {
01526 "Adams",
01527 "Allan",
01528 "Baker",
01529 "Bigwig",
01530 "Black",
01531 "Bloggs",
01532 "Brown",
01533 "Campbell",
01534 "Gordon",
01535 "Hamilton",
01536 "Hawthorn",
01537 "Higgins",
01538 "Green",
01539 "Gribble",
01540 "Jones",
01541 "McAlpine",
01542 "MacDonald",
01543 "McIntosh",
01544 "Muir",
01545 "Murphy",
01546 "Nelson",
01547 "O'Donnell",
01548 "Parker",
01549 "Phillips",
01550 "Pilkington",
01551 "Quigley",
01552 "Sharkey",
01553 "Thomson",
01554 "Watkins"
01555 };
01556
01557 static const char * const _silly_surname_list[] = {
01558 "Grumpy",
01559 "Dozy",
01560 "Speedy",
01561 "Nosey",
01562 "Dribble",
01563 "Mushroom",
01564 "Cabbage",
01565 "Sniffle",
01566 "Fishy",
01567 "Swindle",
01568 "Sneaky",
01569 "Nutkins"
01570 };
01571
01572 static const char _initial_name_letters[] = {
01573 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01574 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01575 };
01576
01577 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01578 {
01579 const char * const *base;
01580 uint num;
01581
01582 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01583 base = _silly_surname_list;
01584 num = lengthof(_silly_surname_list);
01585 } else {
01586 base = _surname_list;
01587 num = lengthof(_surname_list);
01588 }
01589
01590 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01591 buff = strecpy(buff, " & Co.", last);
01592
01593 return buff;
01594 }
01595
01596 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01597 {
01598 char initial[] = "?. ";
01599 const char * const *base;
01600 uint num;
01601 uint i;
01602
01603 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01604 buff = strecpy(buff, initial, last);
01605
01606 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01607 if (i < sizeof(_initial_name_letters)) {
01608 initial[0] = _initial_name_letters[i];
01609 buff = strecpy(buff, initial, last);
01610 }
01611
01612 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01613 base = _silly_surname_list;
01614 num = lengthof(_silly_surname_list);
01615 } else {
01616 base = _surname_list;
01617 num = lengthof(_surname_list);
01618 }
01619
01620 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01621
01622 return buff;
01623 }
01624
01625 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
01626 {
01627 switch (ind) {
01628 case 1:
01629 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
01630
01631 case 2:
01632 return GenAndCoName(buff, args->GetInt32(), last);
01633
01634 case 3:
01635 return GenPresidentName(buff, args->GetInt32(), last);
01636 }
01637
01638
01639 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01640 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
01641 return strecpy(buff, " Transport", last);
01642 }
01643
01644
01645 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01646 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01647 return strecpy(buff,
01648 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
01649 }
01650
01651
01652 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01653 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01654 buff += seprintf(
01655 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01656 );
01657 return buff;
01658 }
01659
01660
01661 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01662 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01663 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01664 }
01665
01666 NOT_REACHED();
01667 }
01668
01669 #ifdef ENABLE_NETWORK
01670 extern void SortNetworkLanguages();
01671 #else
01672 static inline void SortNetworkLanguages() {}
01673 #endif
01674
01679 bool LanguagePackHeader::IsValid() const
01680 {
01681 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
01682 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
01683 this->plural_form < LANGUAGE_MAX_PLURAL &&
01684 this->text_dir <= 1 &&
01685 this->newgrflangid < MAX_LANG &&
01686 this->num_genders < MAX_NUM_GENDERS &&
01687 this->num_cases < MAX_NUM_CASES &&
01688 StrValid(this->name, lastof(this->name)) &&
01689 StrValid(this->own_name, lastof(this->own_name)) &&
01690 StrValid(this->isocode, lastof(this->isocode)) &&
01691 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
01692 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
01693 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
01694 }
01695
01701 bool ReadLanguagePack(const LanguageMetadata *lang)
01702 {
01703
01704 size_t len;
01705 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
01706 if (lang_pack == NULL) return false;
01707
01708
01709 const char *end = (char *)lang_pack + len + 1;
01710
01711
01712 if (end <= lang_pack->data || !lang_pack->IsValid()) {
01713 free(lang_pack);
01714 return false;
01715 }
01716
01717 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01718 for (uint i = 0; i < TAB_COUNT; i++) {
01719 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01720 }
01721 #endif
01722
01723 uint count = 0;
01724 for (uint i = 0; i < TAB_COUNT; i++) {
01725 uint num = lang_pack->offsets[i];
01726 _langtab_start[i] = count;
01727 _langtab_num[i] = num;
01728 count += num;
01729 }
01730
01731
01732 char **langpack_offs = MallocT<char *>(count);
01733
01734
01735 char *s = lang_pack->data;
01736 len = (byte)*s++;
01737 for (uint i = 0; i < count; i++) {
01738 if (s + len >= end) {
01739 free(lang_pack);
01740 free(langpack_offs);
01741 return false;
01742 }
01743 if (len >= 0xC0) {
01744 len = ((len & 0x3F) << 8) + (byte)*s++;
01745 if (s + len >= end) {
01746 free(lang_pack);
01747 free(langpack_offs);
01748 return false;
01749 }
01750 }
01751 langpack_offs[i] = s;
01752 s += len;
01753 len = (byte)*s;
01754 *s++ = '\0';
01755 }
01756
01757 free(_langpack);
01758 _langpack = lang_pack;
01759
01760 free(_langpack_offs);
01761 _langpack_offs = langpack_offs;
01762
01763 _current_language = lang;
01764 _current_text_dir = (TextDirection)_current_language->text_dir;
01765 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
01766 strecpy(_config_language_file, c_file, lastof(_config_language_file));
01767 SetCurrentGrfLangID(_current_language->newgrflangid);
01768
01769 #ifdef WITH_ICU
01770
01771 if (_current_collator != NULL) {
01772 delete _current_collator;
01773 _current_collator = NULL;
01774 }
01775
01776
01777 UErrorCode status = U_ZERO_ERROR;
01778 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01779
01780 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01781
01782 if (U_FAILURE(status)) {
01783 delete _current_collator;
01784 _current_collator = NULL;
01785 }
01786 #endif
01787
01788
01789 ReconsiderGameScriptLanguage();
01790 InitializeSortedCargoSpecs();
01791 SortIndustryTypes();
01792 BuildIndustriesLegend();
01793 SortNetworkLanguages();
01794 InvalidateWindowClassesData(WC_BUILD_VEHICLE);
01795 InvalidateWindowClassesData(WC_TRAINS_LIST);
01796 InvalidateWindowClassesData(WC_ROADVEH_LIST);
01797 InvalidateWindowClassesData(WC_SHIPS_LIST);
01798 InvalidateWindowClassesData(WC_AIRCRAFT_LIST);
01799 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY);
01800 InvalidateWindowClassesData(WC_STATION_LIST);
01801
01802 return true;
01803 }
01804
01805
01806
01807 #if !(defined(WIN32) || defined(__APPLE__))
01808
01816 const char *GetCurrentLocale(const char *param)
01817 {
01818 const char *env;
01819
01820 env = getenv("LANGUAGE");
01821 if (env != NULL) return env;
01822
01823 env = getenv("LC_ALL");
01824 if (env != NULL) return env;
01825
01826 if (param != NULL) {
01827 env = getenv(param);
01828 if (env != NULL) return env;
01829 }
01830
01831 return getenv("LANG");
01832 }
01833 #else
01834 const char *GetCurrentLocale(const char *param);
01835 #endif
01836
01837 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01838 {
01839 char stra[512];
01840 char strb[512];
01841 GetString(stra, *a, lastof(stra));
01842 GetString(strb, *b, lastof(strb));
01843
01844 return strcmp(stra, strb);
01845 }
01846
01852 const LanguageMetadata *GetLanguage(byte newgrflangid)
01853 {
01854 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
01855 if (newgrflangid == lang->newgrflangid) return lang;
01856 }
01857
01858 return NULL;
01859 }
01860
01867 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
01868 {
01869 FILE *f = fopen(file, "rb");
01870 if (f == NULL) return false;
01871
01872 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01873 fclose(f);
01874
01875 bool ret = read == 1 && hdr->IsValid();
01876
01877
01878 if (ret) {
01879 hdr->missing = FROM_LE16(hdr->missing);
01880 hdr->winlangid = FROM_LE16(hdr->winlangid);
01881 }
01882 return ret;
01883 }
01884
01889 static void GetLanguageList(const char *path)
01890 {
01891 DIR *dir = ttd_opendir(path);
01892 if (dir != NULL) {
01893 struct dirent *dirent;
01894 while ((dirent = readdir(dir)) != NULL) {
01895 const char *d_name = FS2OTTD(dirent->d_name);
01896 const char *extension = strrchr(d_name, '.');
01897
01898
01899 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01900
01901 LanguageMetadata lmd;
01902 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
01903
01904
01905 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
01906 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
01907 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
01908 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
01909 } else {
01910 *_languages.Append() = lmd;
01911 }
01912 }
01913 closedir(dir);
01914 }
01915 }
01916
01921 void InitializeLanguagePacks()
01922 {
01923 Searchpath sp;
01924
01925 FOR_ALL_SEARCHPATHS(sp) {
01926 char path[MAX_PATH];
01927 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01928 GetLanguageList(path);
01929 }
01930 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
01931
01932
01933 const char *lang = GetCurrentLocale("LC_MESSAGES");
01934 if (lang == NULL) lang = "en_GB";
01935
01936 const LanguageMetadata *chosen_language = NULL;
01937 const LanguageMetadata *language_fallback = NULL;
01938 const LanguageMetadata *en_GB_fallback = _languages.Begin();
01939
01940
01941 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01942
01943
01944
01945 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
01946 if (strcmp(lang_file, _config_language_file) == 0) {
01947 chosen_language = lng;
01948 break;
01949 }
01950
01951 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
01952 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
01953 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
01954 }
01955
01956
01957
01958 if (chosen_language == NULL) {
01959 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
01960 }
01961
01962 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
01963 }
01964
01969 const char *GetCurrentLanguageIsoCode()
01970 {
01971 return _langpack->isocode;
01972 }
01973
01980 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
01981 {
01982 InitFreeType(this->Monospace());
01983 const Sprite *question_mark[FS_END];
01984
01985 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
01986 question_mark[size] = GetGlyph(size, '?');
01987 }
01988
01989 this->Reset();
01990 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
01991 FontSize size = this->DefaultSize();
01992 if (str != NULL) *str = text;
01993 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
01994 if (c == SCC_SETX) {
01995
01996
01997
01998 text++;
01999 } else if (c == SCC_SETXY) {
02000 text += 2;
02001 } else if (c == SCC_TINYFONT) {
02002 size = FS_SMALL;
02003 } else if (c == SCC_BIGFONT) {
02004 size = FS_LARGE;
02005 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
02006
02007 return true;
02008 }
02009 }
02010 }
02011 return false;
02012 }
02013
02015 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
02016 uint i;
02017 uint j;
02018
02019 void Reset()
02020 {
02021 this->i = 0;
02022 this->j = 0;
02023 }
02024
02025 FontSize DefaultSize()
02026 {
02027 return FS_NORMAL;
02028 }
02029
02030 const char *NextString()
02031 {
02032 if (this->i >= TAB_COUNT) return NULL;
02033
02034 const char *ret = _langpack_offs[_langtab_start[i] + j];
02035
02036 this->j++;
02037 while (this->j >= _langtab_num[this->i] && this->i < TAB_COUNT) {
02038 i++;
02039 j = 0;
02040 }
02041
02042 return ret;
02043 }
02044
02045 bool Monospace()
02046 {
02047 return false;
02048 }
02049
02050 void SetFontNames(FreeTypeSettings *settings, const char *font_name)
02051 {
02052 #ifdef WITH_FREETYPE
02053 strecpy(settings->small_font, font_name, lastof(settings->small_font));
02054 strecpy(settings->medium_font, font_name, lastof(settings->medium_font));
02055 strecpy(settings->large_font, font_name, lastof(settings->large_font));
02056 #endif
02057 }
02058 };
02059
02073 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
02074 {
02075 static LanguagePackGlyphSearcher pack_searcher;
02076 if (searcher == NULL) searcher = &pack_searcher;
02077 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
02078 #ifdef WITH_FREETYPE
02079 if (bad_font) {
02080
02081
02082 FreeTypeSettings backup;
02083 memcpy(&backup, &_freetype, sizeof(backup));
02084
02085 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
02086
02087 memcpy(&_freetype, &backup, sizeof(backup));
02088
02089 if (bad_font && base_font) {
02090
02091
02092
02093 InitFreeType(searcher->Monospace());
02094 }
02095 }
02096 #endif
02097
02098 if (bad_font) {
02099
02100
02101
02102
02103
02104 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.");
02105 Utf8Encode(err_str, SCC_YELLOW);
02106 SetDParamStr(0, err_str);
02107 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
02108
02109
02110 LoadStringWidthTable(searcher->Monospace());
02111 return;
02112 }
02113
02114
02115 LoadStringWidthTable(searcher->Monospace());
02116
02117 #if !defined(WITH_ICU)
02118
02119
02120
02121
02122
02123
02124
02125
02126
02127
02128
02129
02130
02131 if (_current_text_dir != TD_LTR) {
02132 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
02133 Utf8Encode(err_str, SCC_YELLOW);
02134 SetDParamStr(0, err_str);
02135 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
02136 }
02137 #endif
02138 }