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