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