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