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