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 _keep_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
00545 case 11:
00546 switch (n % 10) {
00547 case 0:
00548 case 1:
00549 case 3:
00550 case 6:
00551 case 7:
00552 case 8:
00553 return 0;
00554
00555 case 2:
00556 case 4:
00557 case 5:
00558 case 9:
00559 return 1;
00560
00561 default:
00562 NOT_REACHED();
00563 }
00564
00565
00566
00567
00568 case 12:
00569 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
00570 }
00571 }
00572
00573 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
00574 {
00575
00576 uint n = (byte)*b++;
00577 uint pos, i, mypos = 0;
00578
00579 for (i = pos = 0; i != n; i++) {
00580 uint len = (byte)*b++;
00581 if (i == form) mypos = pos;
00582 pos += len;
00583 }
00584
00585 *dst += seprintf(*dst, last, "%s", b + mypos);
00586 return b + pos;
00587 }
00588
00590 struct UnitConversion {
00591 int multiplier;
00592 int shift;
00593
00600 int64 ToDisplay(int64 input, bool round = true) const
00601 {
00602 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
00603 }
00604
00611 int64 FromDisplay(int64 input, bool round = true) const
00612 {
00613 return ((input << this->shift) + (round ? this->multiplier / 2 : 0)) / this->multiplier;
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) / 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_kgd = _keep_gender_data;
00883 _keep_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 _keep_gender_data = old_kgd;
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 (_keep_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_CURRENCY_SHORT:
01109 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
01110 break;
01111
01112 case SCC_CURRENCY_LONG:
01113 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
01114 break;
01115
01116 case SCC_DATE_TINY:
01117 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
01118 break;
01119
01120 case SCC_DATE_SHORT:
01121 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
01122 next_substr_case_index = 0;
01123 break;
01124
01125 case SCC_DATE_LONG:
01126 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
01127 next_substr_case_index = 0;
01128 break;
01129
01130 case SCC_DATE_ISO:
01131 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
01132 break;
01133
01134 case SCC_FORCE: {
01135 assert(_settings_game.locale.units < lengthof(_units));
01136 int64 args_array[1] = {_units[_settings_game.locale.units].c_force.ToDisplay(args->GetInt64())};
01137 StringParameters tmp_params(args_array);
01138 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].force), &tmp_params, last);
01139 break;
01140 }
01141
01142 case SCC_HEIGHT: {
01143 int64 args_array[] = {_units[_settings_game.locale.units].c_height.ToDisplay(args->GetInt64())};
01144 StringParameters tmp_params(args_array);
01145 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].height), &tmp_params, last);
01146 break;
01147 }
01148
01149 case SCC_POWER: {
01150 assert(_settings_game.locale.units < lengthof(_units));
01151 int64 args_array[1] = {_units[_settings_game.locale.units].c_power.ToDisplay(args->GetInt64())};
01152 StringParameters tmp_params(args_array);
01153 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].power), &tmp_params, last);
01154 break;
01155 }
01156
01157 case SCC_VELOCITY: {
01158 assert(_settings_game.locale.units < lengthof(_units));
01159 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
01160 StringParameters tmp_params(args_array);
01161 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].velocity), &tmp_params, last);
01162 break;
01163 }
01164
01165 case SCC_VOLUME_SHORT: {
01166 assert(_settings_game.locale.units < lengthof(_units));
01167 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01168 StringParameters tmp_params(args_array);
01169 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_volume), &tmp_params, last);
01170 break;
01171 }
01172
01173 case SCC_VOLUME_LONG: {
01174 assert(_settings_game.locale.units < lengthof(_units));
01175 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
01176 StringParameters tmp_params(args_array);
01177 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
01178 break;
01179 }
01180
01181 case SCC_WEIGHT_SHORT: {
01182 assert(_settings_game.locale.units < lengthof(_units));
01183 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01184 StringParameters tmp_params(args_array);
01185 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_weight), &tmp_params, last);
01186 break;
01187 }
01188
01189 case SCC_WEIGHT_LONG: {
01190 assert(_settings_game.locale.units < lengthof(_units));
01191 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
01192 StringParameters tmp_params(args_array);
01193 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
01194 break;
01195 }
01196
01197 case SCC_COMPANY_NAME: {
01198 const Company *c = Company::GetIfValid(args->GetInt32());
01199 if (c == NULL) break;
01200
01201 if (c->name != NULL) {
01202 int64 args_array[] = {(uint64)(size_t)c->name};
01203 StringParameters tmp_params(args_array);
01204 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01205 } else {
01206 int64 args_array[] = {c->name_2};
01207 StringParameters tmp_params(args_array);
01208 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
01209 }
01210 break;
01211 }
01212
01213 case SCC_COMPANY_NUM: {
01214 CompanyID company = (CompanyID)args->GetInt32();
01215
01216
01217 if (Company::IsValidHumanID(company)) {
01218 int64 args_array[] = {company + 1};
01219 StringParameters tmp_params(args_array);
01220 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
01221 }
01222 break;
01223 }
01224
01225 case SCC_DEPOT_NAME: {
01226 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
01227 if (vt == VEH_AIRCRAFT) {
01228 uint64 args_array[] = {args->GetInt32()};
01229 WChar types_array[] = {SCC_STATION_NAME};
01230 StringParameters tmp_params(args_array, 1, types_array);
01231 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
01232 break;
01233 }
01234
01235 const Depot *d = Depot::Get(args->GetInt32());
01236 if (d->name != NULL) {
01237 int64 args_array[] = {(uint64)(size_t)d->name};
01238 StringParameters tmp_params(args_array);
01239 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01240 } else {
01241 int64 args_array[] = {d->town->index, d->town_cn + 1};
01242 StringParameters tmp_params(args_array);
01243 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
01244 }
01245 break;
01246 }
01247
01248 case SCC_ENGINE_NAME: {
01249 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
01250 if (e == NULL) break;
01251
01252 if (e->name != NULL && e->IsEnabled()) {
01253 int64 args_array[] = {(uint64)(size_t)e->name};
01254 StringParameters tmp_params(args_array);
01255 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01256 } else {
01257 StringParameters tmp_params(NULL, 0, NULL);
01258 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
01259 }
01260 break;
01261 }
01262
01263 case SCC_GROUP_NAME: {
01264 const Group *g = Group::GetIfValid(args->GetInt32());
01265 if (g == NULL) break;
01266
01267 if (g->name != NULL) {
01268 int64 args_array[] = {(uint64)(size_t)g->name};
01269 StringParameters tmp_params(args_array);
01270 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01271 } else {
01272 int64 args_array[] = {g->index};
01273 StringParameters tmp_params(args_array);
01274
01275 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
01276 }
01277 break;
01278 }
01279
01280 case SCC_INDUSTRY_NAME: {
01281 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
01282 if (i == NULL) break;
01283
01284
01285 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
01286 StringParameters tmp_params(args_array);
01287
01288 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
01289 next_substr_case_index = 0;
01290 break;
01291 }
01292
01293 case SCC_PRESIDENT_NAME: {
01294 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
01295 if (c == NULL) break;
01296
01297 if (c->president_name != NULL) {
01298 int64 args_array[] = {(uint64)(size_t)c->president_name};
01299 StringParameters tmp_params(args_array);
01300 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01301 } else {
01302 int64 args_array[] = {c->president_name_2};
01303 StringParameters tmp_params(args_array);
01304 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
01305 }
01306 break;
01307 }
01308
01309 case SCC_STATION_NAME: {
01310 StationID sid = args->GetInt32(SCC_STATION_NAME);
01311 const Station *st = Station::GetIfValid(sid);
01312
01313 if (st == NULL) {
01314
01315
01316
01317 StringParameters tmp_params(NULL, 0, NULL);
01318 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
01319 break;
01320 }
01321
01322 if (st->name != NULL) {
01323 int64 args_array[] = {(uint64)(size_t)st->name};
01324 StringParameters tmp_params(args_array);
01325 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01326 } else {
01327 StringID str = st->string_id;
01328 if (st->indtype != IT_INVALID) {
01329
01330 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01331
01332
01333
01334
01335 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
01336 str = indsp->station_name;
01337 }
01338 }
01339
01340 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
01341 StringParameters tmp_params(args_array);
01342 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01343 }
01344 break;
01345 }
01346
01347 case SCC_TOWN_NAME: {
01348 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
01349 if (t == NULL) break;
01350
01351 if (t->name != NULL) {
01352 int64 args_array[] = {(uint64)(size_t)t->name};
01353 StringParameters tmp_params(args_array);
01354 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01355 } else {
01356 buff = GetTownName(buff, t, last);
01357 }
01358 break;
01359 }
01360
01361 case SCC_WAYPOINT_NAME: {
01362 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
01363 if (wp == NULL) break;
01364
01365 if (wp->name != NULL) {
01366 int64 args_array[] = {(uint64)(size_t)wp->name};
01367 StringParameters tmp_params(args_array);
01368 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01369 } else {
01370 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
01371 StringParameters tmp_params(args_array);
01372 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
01373 if (wp->town_cn != 0) str++;
01374 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01375 }
01376 break;
01377 }
01378
01379 case SCC_VEHICLE_NAME: {
01380 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
01381 if (v == NULL) break;
01382
01383 if (v->name != NULL) {
01384 int64 args_array[] = {(uint64)(size_t)v->name};
01385 StringParameters tmp_params(args_array);
01386 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01387 } else {
01388 int64 args_array[] = {v->unitnumber};
01389 StringParameters tmp_params(args_array);
01390
01391 StringID str;
01392 switch (v->type) {
01393 default: NOT_REACHED();
01394 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
01395 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
01396 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
01397 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01398 }
01399
01400 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01401 }
01402 break;
01403 }
01404
01405 case SCC_SIGN_NAME: {
01406 const Sign *si = Sign::GetIfValid(args->GetInt32());
01407 if (si == NULL) break;
01408
01409 if (si->name != NULL) {
01410 int64 args_array[] = {(uint64)(size_t)si->name};
01411 StringParameters tmp_params(args_array);
01412 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01413 } else {
01414 StringParameters tmp_params(NULL, 0, NULL);
01415 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
01416 }
01417 break;
01418 }
01419
01420 case SCC_STATION_FEATURES: {
01421 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
01422 break;
01423 }
01424
01425 default:
01426 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01427 break;
01428 }
01429 }
01430 *buff = '\0';
01431 return buff;
01432 }
01433
01434
01435 static char *StationGetSpecialString(char *buff, int x, const char *last)
01436 {
01437 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01438 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01439 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01440 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01441 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01442 *buff = '\0';
01443 return buff;
01444 }
01445
01446 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01447 {
01448 return GenerateTownNameString(buff, last, ind, seed);
01449 }
01450
01451 static const char * const _silly_company_names[] = {
01452 "Bloggs Brothers",
01453 "Tiny Transport Ltd.",
01454 "Express Travel",
01455 "Comfy-Coach & Co.",
01456 "Crush & Bump Ltd.",
01457 "Broken & Late Ltd.",
01458 "Sam Speedy & Son",
01459 "Supersonic Travel",
01460 "Mike's Motors",
01461 "Lightning International",
01462 "Pannik & Loozit Ltd.",
01463 "Inter-City Transport",
01464 "Getout & Pushit Ltd."
01465 };
01466
01467 static const char * const _surname_list[] = {
01468 "Adams",
01469 "Allan",
01470 "Baker",
01471 "Bigwig",
01472 "Black",
01473 "Bloggs",
01474 "Brown",
01475 "Campbell",
01476 "Gordon",
01477 "Hamilton",
01478 "Hawthorn",
01479 "Higgins",
01480 "Green",
01481 "Gribble",
01482 "Jones",
01483 "McAlpine",
01484 "MacDonald",
01485 "McIntosh",
01486 "Muir",
01487 "Murphy",
01488 "Nelson",
01489 "O'Donnell",
01490 "Parker",
01491 "Phillips",
01492 "Pilkington",
01493 "Quigley",
01494 "Sharkey",
01495 "Thomson",
01496 "Watkins"
01497 };
01498
01499 static const char * const _silly_surname_list[] = {
01500 "Grumpy",
01501 "Dozy",
01502 "Speedy",
01503 "Nosey",
01504 "Dribble",
01505 "Mushroom",
01506 "Cabbage",
01507 "Sniffle",
01508 "Fishy",
01509 "Swindle",
01510 "Sneaky",
01511 "Nutkins"
01512 };
01513
01514 static const char _initial_name_letters[] = {
01515 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01516 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01517 };
01518
01519 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01520 {
01521 const char * const *base;
01522 uint num;
01523
01524 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01525 base = _silly_surname_list;
01526 num = lengthof(_silly_surname_list);
01527 } else {
01528 base = _surname_list;
01529 num = lengthof(_surname_list);
01530 }
01531
01532 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01533 buff = strecpy(buff, " & Co.", last);
01534
01535 return buff;
01536 }
01537
01538 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01539 {
01540 char initial[] = "?. ";
01541 const char * const *base;
01542 uint num;
01543 uint i;
01544
01545 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01546 buff = strecpy(buff, initial, last);
01547
01548 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01549 if (i < sizeof(_initial_name_letters)) {
01550 initial[0] = _initial_name_letters[i];
01551 buff = strecpy(buff, initial, last);
01552 }
01553
01554 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01555 base = _silly_surname_list;
01556 num = lengthof(_silly_surname_list);
01557 } else {
01558 base = _surname_list;
01559 num = lengthof(_surname_list);
01560 }
01561
01562 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01563
01564 return buff;
01565 }
01566
01567 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
01568 {
01569 switch (ind) {
01570 case 1:
01571 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
01572
01573 case 2:
01574 return GenAndCoName(buff, args->GetInt32(), last);
01575
01576 case 3:
01577 return GenPresidentName(buff, args->GetInt32(), last);
01578 }
01579
01580
01581 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01582 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
01583 return strecpy(buff, " Transport", last);
01584 }
01585
01586
01587 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01588 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01589 return strecpy(buff,
01590 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
01591 }
01592
01593
01594 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01595 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01596 buff += seprintf(
01597 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01598 );
01599 return buff;
01600 }
01601
01602
01603 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01604 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01605 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01606 }
01607
01608 NOT_REACHED();
01609 }
01610
01611 #ifdef ENABLE_NETWORK
01612 extern void SortNetworkLanguages();
01613 #else
01614 static inline void SortNetworkLanguages() {}
01615 #endif
01616
01621 bool LanguagePackHeader::IsValid() const
01622 {
01623 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
01624 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
01625 this->plural_form < LANGUAGE_MAX_PLURAL &&
01626 this->text_dir <= 1 &&
01627 this->newgrflangid < MAX_LANG &&
01628 this->num_genders < MAX_NUM_GENDERS &&
01629 this->num_cases < MAX_NUM_CASES &&
01630 StrValid(this->name, lastof(this->name)) &&
01631 StrValid(this->own_name, lastof(this->own_name)) &&
01632 StrValid(this->isocode, lastof(this->isocode)) &&
01633 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
01634 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
01635 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
01636 }
01637
01643 bool ReadLanguagePack(const LanguageMetadata *lang)
01644 {
01645
01646 size_t len;
01647 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
01648 if (lang_pack == NULL) return false;
01649
01650
01651 const char *end = (char *)lang_pack + len + 1;
01652
01653
01654 if (end <= lang_pack->data || !lang_pack->IsValid()) {
01655 free(lang_pack);
01656 return false;
01657 }
01658
01659 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01660 for (uint i = 0; i < TAB_COUNT; i++) {
01661 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01662 }
01663 #endif
01664
01665 uint count = 0;
01666 for (uint i = 0; i < TAB_COUNT; i++) {
01667 uint num = lang_pack->offsets[i];
01668 _langtab_start[i] = count;
01669 _langtab_num[i] = num;
01670 count += num;
01671 }
01672
01673
01674 char **langpack_offs = MallocT<char *>(count);
01675
01676
01677 char *s = lang_pack->data;
01678 len = (byte)*s++;
01679 for (uint i = 0; i < count; i++) {
01680 if (s + len >= end) {
01681 free(lang_pack);
01682 free(langpack_offs);
01683 return false;
01684 }
01685 if (len >= 0xC0) {
01686 len = ((len & 0x3F) << 8) + (byte)*s++;
01687 if (s + len >= end) {
01688 free(lang_pack);
01689 free(langpack_offs);
01690 return false;
01691 }
01692 }
01693 langpack_offs[i] = s;
01694 s += len;
01695 len = (byte)*s;
01696 *s++ = '\0';
01697 }
01698
01699 free(_langpack);
01700 _langpack = lang_pack;
01701
01702 free(_langpack_offs);
01703 _langpack_offs = langpack_offs;
01704
01705 _current_language = lang;
01706 _current_text_dir = (TextDirection)_current_language->text_dir;
01707 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
01708 strecpy(_config_language_file, c_file, lastof(_config_language_file));
01709 SetCurrentGrfLangID(_current_language->newgrflangid);
01710
01711 #ifdef WITH_ICU
01712
01713 if (_current_collator != NULL) {
01714 delete _current_collator;
01715 _current_collator = NULL;
01716 }
01717
01718
01719 UErrorCode status = U_ZERO_ERROR;
01720 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01721
01722 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01723
01724 if (U_FAILURE(status)) {
01725 delete _current_collator;
01726 _current_collator = NULL;
01727 }
01728 #endif
01729
01730
01731 ReconsiderGameScriptLanguage();
01732 InitializeSortedCargoSpecs();
01733 SortIndustryTypes();
01734 BuildIndustriesLegend();
01735 SortNetworkLanguages();
01736 InvalidateWindowClassesData(WC_BUILD_VEHICLE);
01737 InvalidateWindowClassesData(WC_TRAINS_LIST);
01738 InvalidateWindowClassesData(WC_ROADVEH_LIST);
01739 InvalidateWindowClassesData(WC_SHIPS_LIST);
01740 InvalidateWindowClassesData(WC_AIRCRAFT_LIST);
01741 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY);
01742 InvalidateWindowClassesData(WC_STATION_LIST);
01743
01744 return true;
01745 }
01746
01747
01748
01749 #if !(defined(WIN32) || defined(__APPLE__))
01750
01758 const char *GetCurrentLocale(const char *param)
01759 {
01760 const char *env;
01761
01762 env = getenv("LANGUAGE");
01763 if (env != NULL) return env;
01764
01765 env = getenv("LC_ALL");
01766 if (env != NULL) return env;
01767
01768 if (param != NULL) {
01769 env = getenv(param);
01770 if (env != NULL) return env;
01771 }
01772
01773 return getenv("LANG");
01774 }
01775 #else
01776 const char *GetCurrentLocale(const char *param);
01777 #endif
01778
01779 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01780 {
01781 char stra[512];
01782 char strb[512];
01783 GetString(stra, *a, lastof(stra));
01784 GetString(strb, *b, lastof(strb));
01785
01786 return strcmp(stra, strb);
01787 }
01788
01794 const LanguageMetadata *GetLanguage(byte newgrflangid)
01795 {
01796 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
01797 if (newgrflangid == lang->newgrflangid) return lang;
01798 }
01799
01800 return NULL;
01801 }
01802
01809 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
01810 {
01811 FILE *f = fopen(file, "rb");
01812 if (f == NULL) return false;
01813
01814 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01815 fclose(f);
01816
01817 bool ret = read == 1 && hdr->IsValid();
01818
01819
01820 if (ret) {
01821 hdr->missing = FROM_LE16(hdr->missing);
01822 hdr->winlangid = FROM_LE16(hdr->winlangid);
01823 }
01824 return ret;
01825 }
01826
01831 static void GetLanguageList(const char *path)
01832 {
01833 DIR *dir = ttd_opendir(path);
01834 if (dir != NULL) {
01835 struct dirent *dirent;
01836 while ((dirent = readdir(dir)) != NULL) {
01837 const char *d_name = FS2OTTD(dirent->d_name);
01838 const char *extension = strrchr(d_name, '.');
01839
01840
01841 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01842
01843 LanguageMetadata lmd;
01844 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
01845
01846
01847 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
01848 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
01849 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
01850 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
01851 } else {
01852 *_languages.Append() = lmd;
01853 }
01854 }
01855 closedir(dir);
01856 }
01857 }
01858
01863 void InitializeLanguagePacks()
01864 {
01865 Searchpath sp;
01866
01867 FOR_ALL_SEARCHPATHS(sp) {
01868 char path[MAX_PATH];
01869 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01870 GetLanguageList(path);
01871 }
01872 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
01873
01874
01875 const char *lang = GetCurrentLocale("LC_MESSAGES");
01876 if (lang == NULL) lang = "en_GB";
01877
01878 const LanguageMetadata *chosen_language = NULL;
01879 const LanguageMetadata *language_fallback = NULL;
01880 const LanguageMetadata *en_GB_fallback = _languages.Begin();
01881
01882
01883 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01884
01885
01886
01887 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
01888 if (strcmp(lang_file, _config_language_file) == 0) {
01889 chosen_language = lng;
01890 break;
01891 }
01892
01893 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
01894 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
01895 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
01896 }
01897
01898
01899
01900 if (chosen_language == NULL) {
01901 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
01902 }
01903
01904 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
01905 }
01906
01911 const char *GetCurrentLanguageIsoCode()
01912 {
01913 return _langpack->isocode;
01914 }
01915
01922 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
01923 {
01924 InitFreeType(this->Monospace());
01925 const Sprite *question_mark[FS_END];
01926
01927 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
01928 question_mark[size] = GetGlyph(size, '?');
01929 }
01930
01931 this->Reset();
01932 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
01933 FontSize size = this->DefaultSize();
01934 if (str != NULL) *str = text;
01935 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
01936 if (c == SCC_SETX) {
01937
01938
01939
01940 text++;
01941 } else if (c == SCC_SETXY) {
01942 text += 2;
01943 } else if (c == SCC_TINYFONT) {
01944 size = FS_SMALL;
01945 } else if (c == SCC_BIGFONT) {
01946 size = FS_LARGE;
01947 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
01948
01949 return true;
01950 }
01951 }
01952 }
01953 return false;
01954 }
01955
01957 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
01958 uint i;
01959 uint j;
01960
01961 void Reset()
01962 {
01963 this->i = 0;
01964 this->j = 0;
01965 }
01966
01967 FontSize DefaultSize()
01968 {
01969 return FS_NORMAL;
01970 }
01971
01972 const char *NextString()
01973 {
01974 if (this->i >= TAB_COUNT) return NULL;
01975
01976 const char *ret = _langpack_offs[_langtab_start[i] + j];
01977
01978 this->j++;
01979 while (this->j >= _langtab_num[this->i] && this->i < TAB_COUNT) {
01980 i++;
01981 j = 0;
01982 }
01983
01984 return ret;
01985 }
01986
01987 bool Monospace()
01988 {
01989 return false;
01990 }
01991
01992 void SetFontNames(FreeTypeSettings *settings, const char *font_name)
01993 {
01994 #ifdef WITH_FREETYPE
01995 strecpy(settings->small_font, font_name, lastof(settings->small_font));
01996 strecpy(settings->medium_font, font_name, lastof(settings->medium_font));
01997 strecpy(settings->large_font, font_name, lastof(settings->large_font));
01998 #endif
01999 }
02000 };
02001
02015 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
02016 {
02017 static LanguagePackGlyphSearcher pack_searcher;
02018 if (searcher == NULL) searcher = &pack_searcher;
02019 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
02020 #ifdef WITH_FREETYPE
02021 if (bad_font) {
02022
02023
02024 FreeTypeSettings backup;
02025 memcpy(&backup, &_freetype, sizeof(backup));
02026
02027 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
02028
02029 memcpy(&_freetype, &backup, sizeof(backup));
02030
02031 if (bad_font && base_font) {
02032
02033
02034
02035 InitFreeType(searcher->Monospace());
02036 }
02037 }
02038 #endif
02039
02040 if (bad_font) {
02041
02042
02043
02044
02045
02046 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.");
02047 Utf8Encode(err_str, SCC_YELLOW);
02048 SetDParamStr(0, err_str);
02049 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
02050
02051
02052 LoadStringWidthTable(searcher->Monospace());
02053 return;
02054 }
02055
02056
02057 LoadStringWidthTable(searcher->Monospace());
02058
02059 #if !defined(WITH_ICU)
02060
02061
02062
02063
02064
02065
02066
02067
02068
02069
02070
02071
02072
02073 if (_current_text_dir != TD_LTR) {
02074 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
02075 Utf8Encode(err_str, SCC_YELLOW);
02076 SetDParamStr(0, err_str);
02077 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
02078 }
02079 #endif
02080 }