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