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