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