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
00612 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
00613 {
00614 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
00615 }
00616 };
00617
00618 struct Units {
00619 UnitConversion c_velocity;
00620 StringID velocity;
00621 UnitConversion c_power;
00622 StringID power;
00623 UnitConversion c_weight;
00624 StringID s_weight;
00625 StringID l_weight;
00626 UnitConversion c_volume;
00627 StringID s_volume;
00628 StringID l_volume;
00629 UnitConversion c_force;
00630 StringID force;
00631 UnitConversion c_height;
00632 StringID height;
00633 };
00634
00635
00636 static const Units _units[] = {
00637 {
00638 { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL,
00639 { 1, 0}, STR_UNITS_POWER_IMPERIAL,
00640 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00641 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00642 { 1, 0}, STR_UNITS_FORCE_SI,
00643 { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL,
00644 },
00645 {
00646 { 103, 6}, STR_UNITS_VELOCITY_METRIC,
00647 {4153, 12}, STR_UNITS_POWER_METRIC,
00648 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00649 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00650 { 1, 0}, STR_UNITS_FORCE_SI,
00651 { 1, 0}, STR_UNITS_HEIGHT_SI,
00652 },
00653 {
00654 {1831, 12}, STR_UNITS_VELOCITY_SI,
00655 {6109, 13}, STR_UNITS_POWER_SI,
00656 {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00657 { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00658 { 1, 0}, STR_UNITS_FORCE_SI,
00659 { 1, 0}, STR_UNITS_HEIGHT_SI,
00660 },
00661 };
00662
00668 uint ConvertSpeedToDisplaySpeed(uint speed)
00669 {
00670
00671
00672
00673 return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed, false);
00674 }
00675
00681 uint ConvertDisplaySpeedToSpeed(uint speed)
00682 {
00683 return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed);
00684 }
00685
00691 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
00692 {
00693 return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed * 10, false) / 16;
00694 }
00695
00701 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
00702 {
00703 return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed * 16, true, 10);
00704 }
00714 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
00715 {
00716 uint orig_offset = args->offset;
00717
00718
00719 if (args->HasTypeInformation() && !dry_run) {
00720 if (UsingNewGRFTextStack()) {
00721
00722
00723
00724
00725
00726
00727 struct TextRefStack *backup = CreateTextRefStackBackup();
00728 FormatString(buff, str_arg, args, last, case_index, game_script, true);
00729 RestoreTextRefStackBackup(backup);
00730 } else {
00731 FormatString(buff, str_arg, args, last, case_index, game_script, true);
00732 }
00733
00734 args->offset = orig_offset;
00735 }
00736 WChar b;
00737 uint next_substr_case_index = 0;
00738 char *buf_start = buff;
00739 std::stack<const char *> str_stack;
00740 str_stack.push(str_arg);
00741
00742 for (;;) {
00743 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
00744 str_stack.pop();
00745 }
00746 if (str_stack.empty()) break;
00747 const char *&str = str_stack.top();
00748
00749 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00750
00751
00752 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), dry_run);
00753 if (b == 0) continue;
00754 }
00755
00756 switch (b) {
00757 case SCC_ENCODED: {
00758 uint64 sub_args_data[20];
00759 WChar sub_args_type[20];
00760 bool sub_args_need_free[20];
00761 StringParameters sub_args(sub_args_data, 20, sub_args_type);
00762
00763 sub_args.ClearTypeInformation();
00764 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
00765
00766 uint16 stringid;
00767 const char *s = str;
00768 char *p;
00769 stringid = strtol(str, &p, 16);
00770 if (*p != ':' && *p != '\0') {
00771 while (*p != '\0') p++;
00772 str = p;
00773 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
00774 break;
00775 }
00776 if (stringid >= TAB_SIZE) {
00777 while (*p != '\0') p++;
00778 str = p;
00779 buff = strecat(buff, "(invalid StringID)", last);
00780 break;
00781 }
00782
00783 int i = 0;
00784 while (*p != '\0') {
00785 uint64 param;
00786 s = ++p;
00787
00788
00789 bool instring = false;
00790 bool escape = false;
00791 for (;; p++) {
00792 if (*p == '\\') {
00793 escape = true;
00794 continue;
00795 }
00796 if (*p == '"' && escape) {
00797 escape = false;
00798 continue;
00799 }
00800 escape = false;
00801
00802 if (*p == '"') {
00803 instring = !instring;
00804 continue;
00805 }
00806 if (instring) {
00807 continue;
00808 }
00809
00810 if (*p == ':') break;
00811 if (*p == '\0') break;
00812 }
00813
00814 if (*s != '"') {
00815
00816 WChar l;
00817 size_t len = Utf8Decode(&l, s);
00818 bool lookup = (l == SCC_ENCODED);
00819 if (lookup) s += len;
00820
00821 param = strtol(s, &p, 16);
00822
00823 if (lookup) {
00824 if (param >= TAB_SIZE) {
00825 while (*p != '\0') p++;
00826 str = p;
00827 buff = strecat(buff, "(invalid sub-StringID)", last);
00828 break;
00829 }
00830 param = (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + param;
00831 }
00832
00833 sub_args.SetParam(i++, param);
00834 } else {
00835 char *g = strdup(s);
00836 g[p - s] = '\0';
00837
00838 sub_args_need_free[i] = true;
00839 sub_args.SetParam(i++, (uint64)(size_t)g);
00840 }
00841 }
00842
00843 if (*str == '\0') break;
00844
00845 str = p;
00846 buff = GetStringWithArgs(buff, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, last, true);
00847
00848 for (int i = 0; i < 20; i++) {
00849 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
00850 }
00851 break;
00852 }
00853
00854 case SCC_NEWGRF_STRINL: {
00855 StringID substr = Utf8Consume(&str);
00856 str_stack.push(GetStringPtr(substr));
00857 break;
00858 }
00859
00860 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
00861 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
00862 str_stack.push(GetStringPtr(substr));
00863 case_index = next_substr_case_index;
00864 next_substr_case_index = 0;
00865 break;
00866 }
00867
00868
00869 case SCC_GENDER_LIST: {
00870
00871 uint offset = orig_offset + (byte)*str++;
00872 int gender = 0;
00873 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
00874
00875
00876
00877 char input[4 + 1];
00878 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
00879 *p = '\0';
00880
00881
00882 char buf[256];
00883 bool old_kgd = _keep_gender_data;
00884 _keep_gender_data = true;
00885 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
00886 p = FormatString(buf, input, &tmp_params, lastof(buf));
00887 _keep_gender_data = old_kgd;
00888 *p = '\0';
00889
00890
00891 const char *s = buf;
00892 WChar c = Utf8Consume(&s);
00893
00894 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00895 }
00896 str = ParseStringChoice(str, gender, &buff, last);
00897 break;
00898 }
00899
00900
00901
00902 case SCC_GENDER_INDEX:
00903 if (_keep_gender_data) {
00904 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
00905 *buff++ = *str++;
00906 } else {
00907 str++;
00908 }
00909 break;
00910
00911 case SCC_PLURAL_LIST: {
00912 int plural_form = *str++;
00913 uint offset = orig_offset + (byte)*str++;
00914 int64 v = *args->GetPointerToOffset(offset);
00915 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
00916 break;
00917 }
00918
00919 case SCC_ARG_INDEX: {
00920 args->offset = orig_offset + (byte)*str++;
00921 break;
00922 }
00923
00924 case SCC_SET_CASE: {
00925
00926
00927 next_substr_case_index = (byte)*str++;
00928 break;
00929 }
00930
00931 case SCC_SWITCH_CASE: {
00932
00933
00934 uint num = (byte)*str++;
00935 while (num) {
00936 if ((byte)str[0] == case_index) {
00937
00938 str += 3;
00939 break;
00940 }
00941
00942 str += 3 + (str[1] << 8) + str[2];
00943 num--;
00944 }
00945 break;
00946 }
00947
00948 case SCC_SETX:
00949 if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00950 buff += Utf8Encode(buff, SCC_SETX);
00951 *buff++ = *str++;
00952 }
00953 break;
00954
00955 case SCC_SETXY:
00956 if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00957 buff += Utf8Encode(buff, SCC_SETXY);
00958 *buff++ = *str++;
00959 *buff++ = *str++;
00960 }
00961 break;
00962
00963 case SCC_REVISION:
00964 buff = strecpy(buff, _openttd_revision, last);
00965 break;
00966
00967 case SCC_STRING_ID:
00968 if (game_script) break;
00969 buff = GetStringWithArgs(buff, Utf8Consume(&str), args, last);
00970 break;
00971
00972 case SCC_RAW_STRING_POINTER: {
00973 if (game_script) break;
00974 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
00975 buff = FormatString(buff, str, args, last);
00976 break;
00977 }
00978
00979 case SCC_STRING: {
00980 StringID str = args->GetInt32(SCC_STRING);
00981 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
00982
00983
00984
00985 StringParameters tmp_params(args->GetDataPointer(), args->num_param - args->offset, NULL);
00986 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
00987 next_substr_case_index = 0;
00988 break;
00989 }
00990
00991 case SCC_STRING1:
00992 case SCC_STRING2:
00993 case SCC_STRING3:
00994 case SCC_STRING4:
00995 case SCC_STRING5:
00996 case SCC_STRING6:
00997 case SCC_STRING7: {
00998
00999 StringID str = args->GetInt32(b);
01000 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
01001 StringParameters sub_args(*args, b - SCC_STRING1 + 1);
01002 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
01003 next_substr_case_index = 0;
01004 break;
01005 }
01006
01007 case SCC_COMMA:
01008 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
01009 break;
01010
01011 case SCC_DECIMAL: {
01012 int64 number = args->GetInt64(SCC_DECIMAL);
01013 int digits = args->GetInt32(SCC_DECIMAL);
01014 buff = FormatCommaNumber(buff, number, last, digits);
01015 break;
01016 }
01017
01018 case SCC_NUM:
01019 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
01020 break;
01021
01022 case SCC_ZEROFILL_NUM: {
01023 int64 num = args->GetInt64();
01024 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
01025 break;
01026 }
01027
01028 case SCC_HEX:
01029 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
01030 break;
01031
01032 case SCC_BYTES:
01033 buff = FormatBytes(buff, args->GetInt64(), last);
01034 break;
01035
01036 case SCC_CARGO_TINY: {
01037
01038
01039
01040 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
01041 if (cargo >= CargoSpec::GetArraySize()) break;
01042
01043 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
01044 int64 amount = 0;
01045 switch (cargo_str) {
01046 case STR_TONS:
01047 amount = _units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64());
01048 break;
01049
01050 case STR_LITERS:
01051 amount = _units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64());
01052 break;
01053
01054 default: {
01055 amount = args->GetInt64();
01056 break;
01057 }
01058 }
01059
01060 buff = FormatCommaNumber(buff, amount, last);
01061 break;
01062 }
01063
01064 case SCC_CARGO_SHORT: {
01065
01066
01067
01068 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
01069 if (cargo >= CargoSpec::GetArraySize()) break;
01070
01071 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
01072 switch (cargo_str) {
01073 case STR_TONS: {
01074 assert(_settings_game.locale.units < lengthof(_units));
01075 int64 args_array[] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01076 StringParameters tmp_params(args_array);
01077 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
01078 break;
01079 }
01080
01081 case STR_LITERS: {
01082 assert(_settings_game.locale.units < lengthof(_units));
01083 int64 args_array[] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01084 StringParameters tmp_params(args_array);
01085 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
01086 break;
01087 }
01088
01089 default: {
01090 StringParameters tmp_params(*args, 1);
01091 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
01092 break;
01093 }
01094 }
01095 break;
01096 }
01097
01098 case SCC_CARGO_LONG: {
01099
01100 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
01101 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
01102
01103 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
01104 StringParameters tmp_args(*args, 1);
01105 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
01106 break;
01107 }
01108
01109 case SCC_CURRENCY_SHORT:
01110 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
01111 break;
01112
01113 case SCC_CURRENCY_LONG:
01114 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
01115 break;
01116
01117 case SCC_DATE_TINY:
01118 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
01119 break;
01120
01121 case SCC_DATE_SHORT:
01122 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
01123 next_substr_case_index = 0;
01124 break;
01125
01126 case SCC_DATE_LONG:
01127 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
01128 next_substr_case_index = 0;
01129 break;
01130
01131 case SCC_DATE_ISO:
01132 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
01133 break;
01134
01135 case SCC_FORCE: {
01136 assert(_settings_game.locale.units < lengthof(_units));
01137 int64 args_array[1] = {_units[_settings_game.locale.units].c_force.ToDisplay(args->GetInt64())};
01138 StringParameters tmp_params(args_array);
01139 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].force), &tmp_params, last);
01140 break;
01141 }
01142
01143 case SCC_HEIGHT: {
01144 int64 args_array[] = {_units[_settings_game.locale.units].c_height.ToDisplay(args->GetInt64())};
01145 StringParameters tmp_params(args_array);
01146 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].height), &tmp_params, last);
01147 break;
01148 }
01149
01150 case SCC_POWER: {
01151 assert(_settings_game.locale.units < lengthof(_units));
01152 int64 args_array[1] = {_units[_settings_game.locale.units].c_power.ToDisplay(args->GetInt64())};
01153 StringParameters tmp_params(args_array);
01154 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].power), &tmp_params, last);
01155 break;
01156 }
01157
01158 case SCC_VELOCITY: {
01159 assert(_settings_game.locale.units < lengthof(_units));
01160 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
01161 StringParameters tmp_params(args_array);
01162 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].velocity), &tmp_params, last);
01163 break;
01164 }
01165
01166 case SCC_VOLUME_SHORT: {
01167 assert(_settings_game.locale.units < lengthof(_units));
01168 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01169 StringParameters tmp_params(args_array);
01170 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_volume), &tmp_params, last);
01171 break;
01172 }
01173
01174 case SCC_VOLUME_LONG: {
01175 assert(_settings_game.locale.units < lengthof(_units));
01176 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
01177 StringParameters tmp_params(args_array);
01178 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
01179 break;
01180 }
01181
01182 case SCC_WEIGHT_SHORT: {
01183 assert(_settings_game.locale.units < lengthof(_units));
01184 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01185 StringParameters tmp_params(args_array);
01186 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_weight), &tmp_params, last);
01187 break;
01188 }
01189
01190 case SCC_WEIGHT_LONG: {
01191 assert(_settings_game.locale.units < lengthof(_units));
01192 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
01193 StringParameters tmp_params(args_array);
01194 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
01195 break;
01196 }
01197
01198 case SCC_COMPANY_NAME: {
01199 const Company *c = Company::GetIfValid(args->GetInt32());
01200 if (c == NULL) break;
01201
01202 if (c->name != NULL) {
01203 int64 args_array[] = {(uint64)(size_t)c->name};
01204 StringParameters tmp_params(args_array);
01205 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01206 } else {
01207 int64 args_array[] = {c->name_2};
01208 StringParameters tmp_params(args_array);
01209 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
01210 }
01211 break;
01212 }
01213
01214 case SCC_COMPANY_NUM: {
01215 CompanyID company = (CompanyID)args->GetInt32();
01216
01217
01218 if (Company::IsValidHumanID(company)) {
01219 int64 args_array[] = {company + 1};
01220 StringParameters tmp_params(args_array);
01221 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
01222 }
01223 break;
01224 }
01225
01226 case SCC_DEPOT_NAME: {
01227 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
01228 if (vt == VEH_AIRCRAFT) {
01229 uint64 args_array[] = {args->GetInt32()};
01230 WChar types_array[] = {SCC_STATION_NAME};
01231 StringParameters tmp_params(args_array, 1, types_array);
01232 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
01233 break;
01234 }
01235
01236 const Depot *d = Depot::Get(args->GetInt32());
01237 if (d->name != NULL) {
01238 int64 args_array[] = {(uint64)(size_t)d->name};
01239 StringParameters tmp_params(args_array);
01240 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01241 } else {
01242 int64 args_array[] = {d->town->index, d->town_cn + 1};
01243 StringParameters tmp_params(args_array);
01244 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
01245 }
01246 break;
01247 }
01248
01249 case SCC_ENGINE_NAME: {
01250 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
01251 if (e == NULL) break;
01252
01253 if (e->name != NULL && e->IsEnabled()) {
01254 int64 args_array[] = {(uint64)(size_t)e->name};
01255 StringParameters tmp_params(args_array);
01256 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01257 } else {
01258 StringParameters tmp_params(NULL, 0, NULL);
01259 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
01260 }
01261 break;
01262 }
01263
01264 case SCC_GROUP_NAME: {
01265 const Group *g = Group::GetIfValid(args->GetInt32());
01266 if (g == NULL) break;
01267
01268 if (g->name != NULL) {
01269 int64 args_array[] = {(uint64)(size_t)g->name};
01270 StringParameters tmp_params(args_array);
01271 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01272 } else {
01273 int64 args_array[] = {g->index};
01274 StringParameters tmp_params(args_array);
01275
01276 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
01277 }
01278 break;
01279 }
01280
01281 case SCC_INDUSTRY_NAME: {
01282 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
01283 if (i == NULL) break;
01284
01285
01286 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
01287 StringParameters tmp_params(args_array);
01288
01289 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
01290 next_substr_case_index = 0;
01291 break;
01292 }
01293
01294 case SCC_PRESIDENT_NAME: {
01295 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
01296 if (c == NULL) break;
01297
01298 if (c->president_name != NULL) {
01299 int64 args_array[] = {(uint64)(size_t)c->president_name};
01300 StringParameters tmp_params(args_array);
01301 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01302 } else {
01303 int64 args_array[] = {c->president_name_2};
01304 StringParameters tmp_params(args_array);
01305 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
01306 }
01307 break;
01308 }
01309
01310 case SCC_STATION_NAME: {
01311 StationID sid = args->GetInt32(SCC_STATION_NAME);
01312 const Station *st = Station::GetIfValid(sid);
01313
01314 if (st == NULL) {
01315
01316
01317
01318 StringParameters tmp_params(NULL, 0, NULL);
01319 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
01320 break;
01321 }
01322
01323 if (st->name != NULL) {
01324 int64 args_array[] = {(uint64)(size_t)st->name};
01325 StringParameters tmp_params(args_array);
01326 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01327 } else {
01328 StringID str = st->string_id;
01329 if (st->indtype != IT_INVALID) {
01330
01331 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01332
01333
01334
01335
01336 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
01337 str = indsp->station_name;
01338 }
01339 }
01340
01341 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
01342 StringParameters tmp_params(args_array);
01343 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01344 }
01345 break;
01346 }
01347
01348 case SCC_TOWN_NAME: {
01349 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
01350 if (t == NULL) break;
01351
01352 if (t->name != NULL) {
01353 int64 args_array[] = {(uint64)(size_t)t->name};
01354 StringParameters tmp_params(args_array);
01355 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01356 } else {
01357 buff = GetTownName(buff, t, last);
01358 }
01359 break;
01360 }
01361
01362 case SCC_WAYPOINT_NAME: {
01363 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
01364 if (wp == NULL) break;
01365
01366 if (wp->name != NULL) {
01367 int64 args_array[] = {(uint64)(size_t)wp->name};
01368 StringParameters tmp_params(args_array);
01369 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01370 } else {
01371 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
01372 StringParameters tmp_params(args_array);
01373 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
01374 if (wp->town_cn != 0) str++;
01375 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01376 }
01377 break;
01378 }
01379
01380 case SCC_VEHICLE_NAME: {
01381 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
01382 if (v == NULL) break;
01383
01384 if (v->name != NULL) {
01385 int64 args_array[] = {(uint64)(size_t)v->name};
01386 StringParameters tmp_params(args_array);
01387 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01388 } else {
01389 int64 args_array[] = {v->unitnumber};
01390 StringParameters tmp_params(args_array);
01391
01392 StringID str;
01393 switch (v->type) {
01394 default: NOT_REACHED();
01395 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
01396 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
01397 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
01398 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01399 }
01400
01401 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01402 }
01403 break;
01404 }
01405
01406 case SCC_SIGN_NAME: {
01407 const Sign *si = Sign::GetIfValid(args->GetInt32());
01408 if (si == NULL) break;
01409
01410 if (si->name != NULL) {
01411 int64 args_array[] = {(uint64)(size_t)si->name};
01412 StringParameters tmp_params(args_array);
01413 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01414 } else {
01415 StringParameters tmp_params(NULL, 0, NULL);
01416 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
01417 }
01418 break;
01419 }
01420
01421 case SCC_STATION_FEATURES: {
01422 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
01423 break;
01424 }
01425
01426 default:
01427 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01428 break;
01429 }
01430 }
01431 *buff = '\0';
01432 return buff;
01433 }
01434
01435
01436 static char *StationGetSpecialString(char *buff, int x, const char *last)
01437 {
01438 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01439 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01440 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01441 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01442 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01443 *buff = '\0';
01444 return buff;
01445 }
01446
01447 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01448 {
01449 return GenerateTownNameString(buff, last, ind, seed);
01450 }
01451
01452 static const char * const _silly_company_names[] = {
01453 "Bloggs Brothers",
01454 "Tiny Transport Ltd.",
01455 "Express Travel",
01456 "Comfy-Coach & Co.",
01457 "Crush & Bump Ltd.",
01458 "Broken & Late Ltd.",
01459 "Sam Speedy & Son",
01460 "Supersonic Travel",
01461 "Mike's Motors",
01462 "Lightning International",
01463 "Pannik & Loozit Ltd.",
01464 "Inter-City Transport",
01465 "Getout & Pushit Ltd."
01466 };
01467
01468 static const char * const _surname_list[] = {
01469 "Adams",
01470 "Allan",
01471 "Baker",
01472 "Bigwig",
01473 "Black",
01474 "Bloggs",
01475 "Brown",
01476 "Campbell",
01477 "Gordon",
01478 "Hamilton",
01479 "Hawthorn",
01480 "Higgins",
01481 "Green",
01482 "Gribble",
01483 "Jones",
01484 "McAlpine",
01485 "MacDonald",
01486 "McIntosh",
01487 "Muir",
01488 "Murphy",
01489 "Nelson",
01490 "O'Donnell",
01491 "Parker",
01492 "Phillips",
01493 "Pilkington",
01494 "Quigley",
01495 "Sharkey",
01496 "Thomson",
01497 "Watkins"
01498 };
01499
01500 static const char * const _silly_surname_list[] = {
01501 "Grumpy",
01502 "Dozy",
01503 "Speedy",
01504 "Nosey",
01505 "Dribble",
01506 "Mushroom",
01507 "Cabbage",
01508 "Sniffle",
01509 "Fishy",
01510 "Swindle",
01511 "Sneaky",
01512 "Nutkins"
01513 };
01514
01515 static const char _initial_name_letters[] = {
01516 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01517 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01518 };
01519
01520 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01521 {
01522 const char * const *base;
01523 uint num;
01524
01525 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01526 base = _silly_surname_list;
01527 num = lengthof(_silly_surname_list);
01528 } else {
01529 base = _surname_list;
01530 num = lengthof(_surname_list);
01531 }
01532
01533 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01534 buff = strecpy(buff, " & Co.", last);
01535
01536 return buff;
01537 }
01538
01539 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01540 {
01541 char initial[] = "?. ";
01542 const char * const *base;
01543 uint num;
01544 uint i;
01545
01546 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01547 buff = strecpy(buff, initial, last);
01548
01549 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01550 if (i < sizeof(_initial_name_letters)) {
01551 initial[0] = _initial_name_letters[i];
01552 buff = strecpy(buff, initial, last);
01553 }
01554
01555 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01556 base = _silly_surname_list;
01557 num = lengthof(_silly_surname_list);
01558 } else {
01559 base = _surname_list;
01560 num = lengthof(_surname_list);
01561 }
01562
01563 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01564
01565 return buff;
01566 }
01567
01568 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
01569 {
01570 switch (ind) {
01571 case 1:
01572 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
01573
01574 case 2:
01575 return GenAndCoName(buff, args->GetInt32(), last);
01576
01577 case 3:
01578 return GenPresidentName(buff, args->GetInt32(), last);
01579 }
01580
01581
01582 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01583 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
01584 return strecpy(buff, " Transport", last);
01585 }
01586
01587
01588 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01589 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01590 return strecpy(buff,
01591 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
01592 }
01593
01594
01595 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01596 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01597 buff += seprintf(
01598 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01599 );
01600 return buff;
01601 }
01602
01603
01604 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01605 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01606 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01607 }
01608
01609 NOT_REACHED();
01610 }
01611
01612 #ifdef ENABLE_NETWORK
01613 extern void SortNetworkLanguages();
01614 #else
01615 static inline void SortNetworkLanguages() {}
01616 #endif
01617
01622 bool LanguagePackHeader::IsValid() const
01623 {
01624 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
01625 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
01626 this->plural_form < LANGUAGE_MAX_PLURAL &&
01627 this->text_dir <= 1 &&
01628 this->newgrflangid < MAX_LANG &&
01629 this->num_genders < MAX_NUM_GENDERS &&
01630 this->num_cases < MAX_NUM_CASES &&
01631 StrValid(this->name, lastof(this->name)) &&
01632 StrValid(this->own_name, lastof(this->own_name)) &&
01633 StrValid(this->isocode, lastof(this->isocode)) &&
01634 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
01635 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
01636 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
01637 }
01638
01644 bool ReadLanguagePack(const LanguageMetadata *lang)
01645 {
01646
01647 size_t len;
01648 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
01649 if (lang_pack == NULL) return false;
01650
01651
01652 const char *end = (char *)lang_pack + len + 1;
01653
01654
01655 if (end <= lang_pack->data || !lang_pack->IsValid()) {
01656 free(lang_pack);
01657 return false;
01658 }
01659
01660 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01661 for (uint i = 0; i < TAB_COUNT; i++) {
01662 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01663 }
01664 #endif
01665
01666 uint count = 0;
01667 for (uint i = 0; i < TAB_COUNT; i++) {
01668 uint num = lang_pack->offsets[i];
01669 _langtab_start[i] = count;
01670 _langtab_num[i] = num;
01671 count += num;
01672 }
01673
01674
01675 char **langpack_offs = MallocT<char *>(count);
01676
01677
01678 char *s = lang_pack->data;
01679 len = (byte)*s++;
01680 for (uint i = 0; i < count; i++) {
01681 if (s + len >= end) {
01682 free(lang_pack);
01683 free(langpack_offs);
01684 return false;
01685 }
01686 if (len >= 0xC0) {
01687 len = ((len & 0x3F) << 8) + (byte)*s++;
01688 if (s + len >= end) {
01689 free(lang_pack);
01690 free(langpack_offs);
01691 return false;
01692 }
01693 }
01694 langpack_offs[i] = s;
01695 s += len;
01696 len = (byte)*s;
01697 *s++ = '\0';
01698 }
01699
01700 free(_langpack);
01701 _langpack = lang_pack;
01702
01703 free(_langpack_offs);
01704 _langpack_offs = langpack_offs;
01705
01706 _current_language = lang;
01707 _current_text_dir = (TextDirection)_current_language->text_dir;
01708 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
01709 strecpy(_config_language_file, c_file, lastof(_config_language_file));
01710 SetCurrentGrfLangID(_current_language->newgrflangid);
01711
01712 #ifdef WITH_ICU
01713
01714 if (_current_collator != NULL) {
01715 delete _current_collator;
01716 _current_collator = NULL;
01717 }
01718
01719
01720 UErrorCode status = U_ZERO_ERROR;
01721 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01722
01723 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01724
01725 if (U_FAILURE(status)) {
01726 delete _current_collator;
01727 _current_collator = NULL;
01728 }
01729 #endif
01730
01731
01732 ReconsiderGameScriptLanguage();
01733 InitializeSortedCargoSpecs();
01734 SortIndustryTypes();
01735 BuildIndustriesLegend();
01736 SortNetworkLanguages();
01737 InvalidateWindowClassesData(WC_BUILD_VEHICLE);
01738 InvalidateWindowClassesData(WC_TRAINS_LIST);
01739 InvalidateWindowClassesData(WC_ROADVEH_LIST);
01740 InvalidateWindowClassesData(WC_SHIPS_LIST);
01741 InvalidateWindowClassesData(WC_AIRCRAFT_LIST);
01742 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY);
01743 InvalidateWindowClassesData(WC_STATION_LIST);
01744
01745 return true;
01746 }
01747
01748
01749
01750 #if !(defined(WIN32) || defined(__APPLE__))
01751
01759 const char *GetCurrentLocale(const char *param)
01760 {
01761 const char *env;
01762
01763 env = getenv("LANGUAGE");
01764 if (env != NULL) return env;
01765
01766 env = getenv("LC_ALL");
01767 if (env != NULL) return env;
01768
01769 if (param != NULL) {
01770 env = getenv(param);
01771 if (env != NULL) return env;
01772 }
01773
01774 return getenv("LANG");
01775 }
01776 #else
01777 const char *GetCurrentLocale(const char *param);
01778 #endif
01779
01780 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01781 {
01782 char stra[512];
01783 char strb[512];
01784 GetString(stra, *a, lastof(stra));
01785 GetString(strb, *b, lastof(strb));
01786
01787 return strcmp(stra, strb);
01788 }
01789
01795 const LanguageMetadata *GetLanguage(byte newgrflangid)
01796 {
01797 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
01798 if (newgrflangid == lang->newgrflangid) return lang;
01799 }
01800
01801 return NULL;
01802 }
01803
01810 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
01811 {
01812 FILE *f = fopen(file, "rb");
01813 if (f == NULL) return false;
01814
01815 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01816 fclose(f);
01817
01818 bool ret = read == 1 && hdr->IsValid();
01819
01820
01821 if (ret) {
01822 hdr->missing = FROM_LE16(hdr->missing);
01823 hdr->winlangid = FROM_LE16(hdr->winlangid);
01824 }
01825 return ret;
01826 }
01827
01832 static void GetLanguageList(const char *path)
01833 {
01834 DIR *dir = ttd_opendir(path);
01835 if (dir != NULL) {
01836 struct dirent *dirent;
01837 while ((dirent = readdir(dir)) != NULL) {
01838 const char *d_name = FS2OTTD(dirent->d_name);
01839 const char *extension = strrchr(d_name, '.');
01840
01841
01842 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01843
01844 LanguageMetadata lmd;
01845 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
01846
01847
01848 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
01849 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
01850 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
01851 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
01852 } else {
01853 *_languages.Append() = lmd;
01854 }
01855 }
01856 closedir(dir);
01857 }
01858 }
01859
01864 void InitializeLanguagePacks()
01865 {
01866 Searchpath sp;
01867
01868 FOR_ALL_SEARCHPATHS(sp) {
01869 char path[MAX_PATH];
01870 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01871 GetLanguageList(path);
01872 }
01873 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
01874
01875
01876 const char *lang = GetCurrentLocale("LC_MESSAGES");
01877 if (lang == NULL) lang = "en_GB";
01878
01879 const LanguageMetadata *chosen_language = NULL;
01880 const LanguageMetadata *language_fallback = NULL;
01881 const LanguageMetadata *en_GB_fallback = _languages.Begin();
01882
01883
01884 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01885
01886
01887
01888 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
01889 if (strcmp(lang_file, _config_language_file) == 0) {
01890 chosen_language = lng;
01891 break;
01892 }
01893
01894 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
01895 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
01896 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
01897 }
01898
01899
01900
01901 if (chosen_language == NULL) {
01902 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
01903 }
01904
01905 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
01906 }
01907
01912 const char *GetCurrentLanguageIsoCode()
01913 {
01914 return _langpack->isocode;
01915 }
01916
01923 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
01924 {
01925 InitFreeType(this->Monospace());
01926 const Sprite *question_mark[FS_END];
01927
01928 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
01929 question_mark[size] = GetGlyph(size, '?');
01930 }
01931
01932 this->Reset();
01933 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
01934 FontSize size = this->DefaultSize();
01935 if (str != NULL) *str = text;
01936 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
01937 if (c == SCC_SETX) {
01938
01939
01940
01941 text++;
01942 } else if (c == SCC_SETXY) {
01943 text += 2;
01944 } else if (c == SCC_TINYFONT) {
01945 size = FS_SMALL;
01946 } else if (c == SCC_BIGFONT) {
01947 size = FS_LARGE;
01948 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
01949
01950 return true;
01951 }
01952 }
01953 }
01954 return false;
01955 }
01956
01958 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
01959 uint i;
01960 uint j;
01961
01962 void Reset()
01963 {
01964 this->i = 0;
01965 this->j = 0;
01966 }
01967
01968 FontSize DefaultSize()
01969 {
01970 return FS_NORMAL;
01971 }
01972
01973 const char *NextString()
01974 {
01975 if (this->i >= TAB_COUNT) return NULL;
01976
01977 const char *ret = _langpack_offs[_langtab_start[i] + j];
01978
01979 this->j++;
01980 while (this->j >= _langtab_num[this->i] && this->i < TAB_COUNT) {
01981 i++;
01982 j = 0;
01983 }
01984
01985 return ret;
01986 }
01987
01988 bool Monospace()
01989 {
01990 return false;
01991 }
01992
01993 void SetFontNames(FreeTypeSettings *settings, const char *font_name)
01994 {
01995 #ifdef WITH_FREETYPE
01996 strecpy(settings->small_font, font_name, lastof(settings->small_font));
01997 strecpy(settings->medium_font, font_name, lastof(settings->medium_font));
01998 strecpy(settings->large_font, font_name, lastof(settings->large_font));
01999 #endif
02000 }
02001 };
02002
02016 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
02017 {
02018 static LanguagePackGlyphSearcher pack_searcher;
02019 if (searcher == NULL) searcher = &pack_searcher;
02020 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
02021 #ifdef WITH_FREETYPE
02022 if (bad_font) {
02023
02024
02025 FreeTypeSettings backup;
02026 memcpy(&backup, &_freetype, sizeof(backup));
02027
02028 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
02029
02030 memcpy(&_freetype, &backup, sizeof(backup));
02031
02032 if (bad_font && base_font) {
02033
02034
02035
02036 InitFreeType(searcher->Monospace());
02037 }
02038 }
02039 #endif
02040
02041 if (bad_font) {
02042
02043
02044
02045
02046
02047 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.");
02048 Utf8Encode(err_str, SCC_YELLOW);
02049 SetDParamStr(0, err_str);
02050 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
02051
02052
02053 LoadStringWidthTable(searcher->Monospace());
02054 return;
02055 }
02056
02057
02058 LoadStringWidthTable(searcher->Monospace());
02059
02060 #if !defined(WITH_ICU)
02061
02062
02063
02064
02065
02066
02067
02068
02069
02070
02071
02072
02073
02074 if (_current_text_dir != TD_LTR) {
02075 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
02076 Utf8Encode(err_str, SCC_YELLOW);
02077 SetDParamStr(0, err_str);
02078 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
02079 }
02080 #endif
02081 }