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