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 ? 1 : 0;
00528
00529
00530
00531
00532 case 1:
00533 return 0;
00534
00535
00536
00537
00538 case 2:
00539 return n > 1 ? 1 : 0;
00540
00541
00542
00543
00544
00545 case 3:
00546 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
00547
00548
00549
00550
00551 case 4:
00552 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
00553
00554
00555
00556
00557 case 5:
00558 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00559
00560
00561
00562
00563 case 6:
00564 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00565
00566
00567
00568
00569 case 7:
00570 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00571
00572
00573
00574
00575 case 8:
00576 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
00577
00578
00579
00580
00581 case 9:
00582 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
00583
00584
00585
00586
00587 case 10:
00588 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
00589
00590
00591
00592
00593
00594
00595 case 11:
00596 switch (n % 10) {
00597 case 0:
00598 case 1:
00599 case 3:
00600 case 6:
00601 case 7:
00602 case 8:
00603 return 0;
00604
00605 case 2:
00606 case 4:
00607 case 5:
00608 case 9:
00609 return 1;
00610
00611 default:
00612 NOT_REACHED();
00613 }
00614
00615
00616
00617
00618 case 12:
00619 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
00620
00621
00622
00623 case 13:
00624 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
00625 }
00626 }
00627
00628 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
00629 {
00630
00631 uint n = (byte)*b++;
00632 uint pos, i, mypos = 0;
00633
00634 for (i = pos = 0; i != n; i++) {
00635 uint len = (byte)*b++;
00636 if (i == form) mypos = pos;
00637 pos += len;
00638 }
00639
00640 *dst += seprintf(*dst, last, "%s", b + mypos);
00641 return b + pos;
00642 }
00643
00645 struct UnitConversion {
00646 int multiplier;
00647 int shift;
00648
00655 int64 ToDisplay(int64 input, bool round = true) const
00656 {
00657 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
00658 }
00659
00667 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
00668 {
00669 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
00670 }
00671 };
00672
00673 struct Units {
00674 UnitConversion c_velocity;
00675 StringID velocity;
00676 UnitConversion c_power;
00677 StringID power;
00678 UnitConversion c_weight;
00679 StringID s_weight;
00680 StringID l_weight;
00681 UnitConversion c_volume;
00682 StringID s_volume;
00683 StringID l_volume;
00684 UnitConversion c_force;
00685 StringID force;
00686 UnitConversion c_height;
00687 StringID height;
00688 };
00689
00690
00691 static const Units _units[] = {
00692 {
00693 { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL,
00694 { 1, 0}, STR_UNITS_POWER_IMPERIAL,
00695 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00696 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00697 { 1, 0}, STR_UNITS_FORCE_SI,
00698 { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL,
00699 },
00700 {
00701 { 103, 6}, STR_UNITS_VELOCITY_METRIC,
00702 {4153, 12}, STR_UNITS_POWER_METRIC,
00703 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00704 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00705 { 1, 0}, STR_UNITS_FORCE_SI,
00706 { 1, 0}, STR_UNITS_HEIGHT_SI,
00707 },
00708 {
00709 {1831, 12}, STR_UNITS_VELOCITY_SI,
00710 {6109, 13}, STR_UNITS_POWER_SI,
00711 {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00712 { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00713 { 1, 0}, STR_UNITS_FORCE_SI,
00714 { 1, 0}, STR_UNITS_HEIGHT_SI,
00715 },
00716 };
00717
00723 uint ConvertSpeedToDisplaySpeed(uint speed)
00724 {
00725
00726
00727
00728 return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed, false);
00729 }
00730
00736 uint ConvertDisplaySpeedToSpeed(uint speed)
00737 {
00738 return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed);
00739 }
00740
00746 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
00747 {
00748 return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed * 10, false) / 16;
00749 }
00750
00756 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
00757 {
00758 return _units[_settings_game.locale.units].c_velocity.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;
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(), 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_SETX:
01004 if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
01005 buff += Utf8Encode(buff, SCC_SETX);
01006 *buff++ = *str++;
01007 }
01008 break;
01009
01010 case SCC_SETXY:
01011 if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
01012 buff += Utf8Encode(buff, SCC_SETXY);
01013 *buff++ = *str++;
01014 *buff++ = *str++;
01015 }
01016 break;
01017
01018 case SCC_REVISION:
01019 buff = strecpy(buff, _openttd_revision, last);
01020 break;
01021
01022 case SCC_STRING_ID:
01023 if (game_script) break;
01024 buff = GetStringWithArgs(buff, Utf8Consume(&str), args, last);
01025 break;
01026
01027 case SCC_RAW_STRING_POINTER: {
01028 if (game_script) break;
01029 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
01030 buff = FormatString(buff, str, args, last);
01031 break;
01032 }
01033
01034 case SCC_STRING: {
01035 StringID str = args->GetInt32(SCC_STRING);
01036 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
01037
01038
01039
01040 StringParameters tmp_params(args->GetDataPointer(), args->num_param - args->offset, NULL);
01041 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
01042 next_substr_case_index = 0;
01043 break;
01044 }
01045
01046 case SCC_STRING1:
01047 case SCC_STRING2:
01048 case SCC_STRING3:
01049 case SCC_STRING4:
01050 case SCC_STRING5:
01051 case SCC_STRING6:
01052 case SCC_STRING7: {
01053
01054 StringID str = args->GetInt32(b);
01055 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
01056 uint size = b - SCC_STRING1 + 1;
01057 if (game_script && size > args->num_param - args->offset) {
01058 buff = strecat(buff, "(too many parameters)", last);
01059 } else {
01060 StringParameters sub_args(*args, size);
01061 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
01062 }
01063 next_substr_case_index = 0;
01064 break;
01065 }
01066
01067 case SCC_COMMA:
01068 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
01069 break;
01070
01071 case SCC_DECIMAL: {
01072 int64 number = args->GetInt64(SCC_DECIMAL);
01073 int digits = args->GetInt32(SCC_DECIMAL);
01074 buff = FormatCommaNumber(buff, number, last, digits);
01075 break;
01076 }
01077
01078 case SCC_NUM:
01079 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
01080 break;
01081
01082 case SCC_ZEROFILL_NUM: {
01083 int64 num = args->GetInt64();
01084 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
01085 break;
01086 }
01087
01088 case SCC_HEX:
01089 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
01090 break;
01091
01092 case SCC_BYTES:
01093 buff = FormatBytes(buff, args->GetInt64(), last);
01094 break;
01095
01096 case SCC_CARGO_TINY: {
01097
01098
01099
01100 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
01101 if (cargo >= CargoSpec::GetArraySize()) break;
01102
01103 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
01104 int64 amount = 0;
01105 switch (cargo_str) {
01106 case STR_TONS:
01107 amount = _units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64());
01108 break;
01109
01110 case STR_LITERS:
01111 amount = _units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64());
01112 break;
01113
01114 default: {
01115 amount = args->GetInt64();
01116 break;
01117 }
01118 }
01119
01120 buff = FormatCommaNumber(buff, amount, last);
01121 break;
01122 }
01123
01124 case SCC_CARGO_SHORT: {
01125
01126
01127
01128 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
01129 if (cargo >= CargoSpec::GetArraySize()) break;
01130
01131 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
01132 switch (cargo_str) {
01133 case STR_TONS: {
01134 assert(_settings_game.locale.units < lengthof(_units));
01135 int64 args_array[] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01136 StringParameters tmp_params(args_array);
01137 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
01138 break;
01139 }
01140
01141 case STR_LITERS: {
01142 assert(_settings_game.locale.units < lengthof(_units));
01143 int64 args_array[] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01144 StringParameters tmp_params(args_array);
01145 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
01146 break;
01147 }
01148
01149 default: {
01150 StringParameters tmp_params(*args, 1);
01151 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
01152 break;
01153 }
01154 }
01155 break;
01156 }
01157
01158 case SCC_CARGO_LONG: {
01159
01160 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
01161 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
01162
01163 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
01164 StringParameters tmp_args(*args, 1);
01165 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
01166 break;
01167 }
01168
01169 case SCC_CARGO_LIST: {
01170 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
01171 bool first = true;
01172
01173 const CargoSpec *cs;
01174 FOR_ALL_SORTED_CARGOSPECS(cs) {
01175 if (!HasBit(cmask, cs->Index())) continue;
01176
01177 if (buff >= last - 2) break;
01178
01179 if (first) {
01180 first = false;
01181 } else {
01182
01183 *buff++ = ',';
01184 *buff++ = ' ';
01185 }
01186
01187 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
01188 }
01189
01190
01191 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
01192
01193 *buff = '\0';
01194 next_substr_case_index = 0;
01195
01196
01197 assert(buff < last);
01198 break;
01199 }
01200
01201 case SCC_CURRENCY_SHORT:
01202 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
01203 break;
01204
01205 case SCC_CURRENCY_LONG:
01206 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
01207 break;
01208
01209 case SCC_DATE_TINY:
01210 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
01211 break;
01212
01213 case SCC_DATE_SHORT:
01214 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
01215 next_substr_case_index = 0;
01216 break;
01217
01218 case SCC_DATE_LONG:
01219 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
01220 next_substr_case_index = 0;
01221 break;
01222
01223 case SCC_DATE_ISO:
01224 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
01225 break;
01226
01227 case SCC_FORCE: {
01228 assert(_settings_game.locale.units < lengthof(_units));
01229 int64 args_array[1] = {_units[_settings_game.locale.units].c_force.ToDisplay(args->GetInt64())};
01230 StringParameters tmp_params(args_array);
01231 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].force), &tmp_params, last);
01232 break;
01233 }
01234
01235 case SCC_HEIGHT: {
01236 int64 args_array[] = {_units[_settings_game.locale.units].c_height.ToDisplay(args->GetInt64())};
01237 StringParameters tmp_params(args_array);
01238 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].height), &tmp_params, last);
01239 break;
01240 }
01241
01242 case SCC_POWER: {
01243 assert(_settings_game.locale.units < lengthof(_units));
01244 int64 args_array[1] = {_units[_settings_game.locale.units].c_power.ToDisplay(args->GetInt64())};
01245 StringParameters tmp_params(args_array);
01246 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].power), &tmp_params, last);
01247 break;
01248 }
01249
01250 case SCC_VELOCITY: {
01251 assert(_settings_game.locale.units < lengthof(_units));
01252 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
01253 StringParameters tmp_params(args_array);
01254 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].velocity), &tmp_params, last);
01255 break;
01256 }
01257
01258 case SCC_VOLUME_SHORT: {
01259 assert(_settings_game.locale.units < lengthof(_units));
01260 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01261 StringParameters tmp_params(args_array);
01262 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_volume), &tmp_params, last);
01263 break;
01264 }
01265
01266 case SCC_VOLUME_LONG: {
01267 assert(_settings_game.locale.units < lengthof(_units));
01268 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
01269 StringParameters tmp_params(args_array);
01270 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
01271 break;
01272 }
01273
01274 case SCC_WEIGHT_SHORT: {
01275 assert(_settings_game.locale.units < lengthof(_units));
01276 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01277 StringParameters tmp_params(args_array);
01278 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_weight), &tmp_params, last);
01279 break;
01280 }
01281
01282 case SCC_WEIGHT_LONG: {
01283 assert(_settings_game.locale.units < lengthof(_units));
01284 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
01285 StringParameters tmp_params(args_array);
01286 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
01287 break;
01288 }
01289
01290 case SCC_COMPANY_NAME: {
01291 const Company *c = Company::GetIfValid(args->GetInt32());
01292 if (c == NULL) break;
01293
01294 if (c->name != NULL) {
01295 int64 args_array[] = {(uint64)(size_t)c->name};
01296 StringParameters tmp_params(args_array);
01297 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01298 } else {
01299 int64 args_array[] = {c->name_2};
01300 StringParameters tmp_params(args_array);
01301 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
01302 }
01303 break;
01304 }
01305
01306 case SCC_COMPANY_NUM: {
01307 CompanyID company = (CompanyID)args->GetInt32();
01308
01309
01310 if (Company::IsValidHumanID(company)) {
01311 int64 args_array[] = {company + 1};
01312 StringParameters tmp_params(args_array);
01313 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
01314 }
01315 break;
01316 }
01317
01318 case SCC_DEPOT_NAME: {
01319 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
01320 if (vt == VEH_AIRCRAFT) {
01321 uint64 args_array[] = {args->GetInt32()};
01322 WChar types_array[] = {SCC_STATION_NAME};
01323 StringParameters tmp_params(args_array, 1, types_array);
01324 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
01325 break;
01326 }
01327
01328 const Depot *d = Depot::Get(args->GetInt32());
01329 if (d->name != NULL) {
01330 int64 args_array[] = {(uint64)(size_t)d->name};
01331 StringParameters tmp_params(args_array);
01332 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01333 } else {
01334 int64 args_array[] = {d->town->index, d->town_cn + 1};
01335 StringParameters tmp_params(args_array);
01336 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
01337 }
01338 break;
01339 }
01340
01341 case SCC_ENGINE_NAME: {
01342 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
01343 if (e == NULL) break;
01344
01345 if (e->name != NULL && e->IsEnabled()) {
01346 int64 args_array[] = {(uint64)(size_t)e->name};
01347 StringParameters tmp_params(args_array);
01348 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01349 } else {
01350 StringParameters tmp_params(NULL, 0, NULL);
01351 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
01352 }
01353 break;
01354 }
01355
01356 case SCC_GROUP_NAME: {
01357 const Group *g = Group::GetIfValid(args->GetInt32());
01358 if (g == NULL) break;
01359
01360 if (g->name != NULL) {
01361 int64 args_array[] = {(uint64)(size_t)g->name};
01362 StringParameters tmp_params(args_array);
01363 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01364 } else {
01365 int64 args_array[] = {g->index};
01366 StringParameters tmp_params(args_array);
01367
01368 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
01369 }
01370 break;
01371 }
01372
01373 case SCC_INDUSTRY_NAME: {
01374 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
01375 if (i == NULL) break;
01376
01377 if (_scan_for_gender_data) {
01378
01379
01380 StringParameters tmp_params(NULL, 0, NULL);
01381 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
01382 } else {
01383
01384 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
01385 StringParameters tmp_params(args_array);
01386
01387 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
01388 }
01389 next_substr_case_index = 0;
01390 break;
01391 }
01392
01393 case SCC_PRESIDENT_NAME: {
01394 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
01395 if (c == NULL) break;
01396
01397 if (c->president_name != NULL) {
01398 int64 args_array[] = {(uint64)(size_t)c->president_name};
01399 StringParameters tmp_params(args_array);
01400 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01401 } else {
01402 int64 args_array[] = {c->president_name_2};
01403 StringParameters tmp_params(args_array);
01404 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
01405 }
01406 break;
01407 }
01408
01409 case SCC_STATION_NAME: {
01410 StationID sid = args->GetInt32(SCC_STATION_NAME);
01411 const Station *st = Station::GetIfValid(sid);
01412
01413 if (st == NULL) {
01414
01415
01416
01417 StringParameters tmp_params(NULL, 0, NULL);
01418 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
01419 break;
01420 }
01421
01422 if (st->name != NULL) {
01423 int64 args_array[] = {(uint64)(size_t)st->name};
01424 StringParameters tmp_params(args_array);
01425 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01426 } else {
01427 StringID str = st->string_id;
01428 if (st->indtype != IT_INVALID) {
01429
01430 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01431
01432
01433
01434
01435 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
01436 str = indsp->station_name;
01437 }
01438 }
01439
01440 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
01441 StringParameters tmp_params(args_array);
01442 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01443 }
01444 break;
01445 }
01446
01447 case SCC_TOWN_NAME: {
01448 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
01449 if (t == NULL) break;
01450
01451 if (t->name != NULL) {
01452 int64 args_array[] = {(uint64)(size_t)t->name};
01453 StringParameters tmp_params(args_array);
01454 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01455 } else {
01456 buff = GetTownName(buff, t, last);
01457 }
01458 break;
01459 }
01460
01461 case SCC_WAYPOINT_NAME: {
01462 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
01463 if (wp == NULL) break;
01464
01465 if (wp->name != NULL) {
01466 int64 args_array[] = {(uint64)(size_t)wp->name};
01467 StringParameters tmp_params(args_array);
01468 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01469 } else {
01470 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
01471 StringParameters tmp_params(args_array);
01472 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
01473 if (wp->town_cn != 0) str++;
01474 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01475 }
01476 break;
01477 }
01478
01479 case SCC_VEHICLE_NAME: {
01480 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
01481 if (v == NULL) break;
01482
01483 if (v->name != NULL) {
01484 int64 args_array[] = {(uint64)(size_t)v->name};
01485 StringParameters tmp_params(args_array);
01486 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01487 } else {
01488 int64 args_array[] = {v->unitnumber};
01489 StringParameters tmp_params(args_array);
01490
01491 StringID str;
01492 switch (v->type) {
01493 default: NOT_REACHED();
01494 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
01495 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
01496 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
01497 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01498 }
01499
01500 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01501 }
01502 break;
01503 }
01504
01505 case SCC_SIGN_NAME: {
01506 const Sign *si = Sign::GetIfValid(args->GetInt32());
01507 if (si == NULL) break;
01508
01509 if (si->name != NULL) {
01510 int64 args_array[] = {(uint64)(size_t)si->name};
01511 StringParameters tmp_params(args_array);
01512 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01513 } else {
01514 StringParameters tmp_params(NULL, 0, NULL);
01515 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
01516 }
01517 break;
01518 }
01519
01520 case SCC_STATION_FEATURES: {
01521 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
01522 break;
01523 }
01524
01525 default:
01526 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01527 break;
01528 }
01529 }
01530 *buff = '\0';
01531 return buff;
01532 }
01533
01534
01535 static char *StationGetSpecialString(char *buff, int x, const char *last)
01536 {
01537 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01538 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01539 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01540 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01541 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01542 *buff = '\0';
01543 return buff;
01544 }
01545
01546 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01547 {
01548 return GenerateTownNameString(buff, last, ind, seed);
01549 }
01550
01551 static const char * const _silly_company_names[] = {
01552 "Bloggs Brothers",
01553 "Tiny Transport Ltd.",
01554 "Express Travel",
01555 "Comfy-Coach & Co.",
01556 "Crush & Bump Ltd.",
01557 "Broken & Late Ltd.",
01558 "Sam Speedy & Son",
01559 "Supersonic Travel",
01560 "Mike's Motors",
01561 "Lightning International",
01562 "Pannik & Loozit Ltd.",
01563 "Inter-City Transport",
01564 "Getout & Pushit Ltd."
01565 };
01566
01567 static const char * const _surname_list[] = {
01568 "Adams",
01569 "Allan",
01570 "Baker",
01571 "Bigwig",
01572 "Black",
01573 "Bloggs",
01574 "Brown",
01575 "Campbell",
01576 "Gordon",
01577 "Hamilton",
01578 "Hawthorn",
01579 "Higgins",
01580 "Green",
01581 "Gribble",
01582 "Jones",
01583 "McAlpine",
01584 "MacDonald",
01585 "McIntosh",
01586 "Muir",
01587 "Murphy",
01588 "Nelson",
01589 "O'Donnell",
01590 "Parker",
01591 "Phillips",
01592 "Pilkington",
01593 "Quigley",
01594 "Sharkey",
01595 "Thomson",
01596 "Watkins"
01597 };
01598
01599 static const char * const _silly_surname_list[] = {
01600 "Grumpy",
01601 "Dozy",
01602 "Speedy",
01603 "Nosey",
01604 "Dribble",
01605 "Mushroom",
01606 "Cabbage",
01607 "Sniffle",
01608 "Fishy",
01609 "Swindle",
01610 "Sneaky",
01611 "Nutkins"
01612 };
01613
01614 static const char _initial_name_letters[] = {
01615 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01616 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01617 };
01618
01619 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01620 {
01621 const char * const *base;
01622 uint num;
01623
01624 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01625 base = _silly_surname_list;
01626 num = lengthof(_silly_surname_list);
01627 } else {
01628 base = _surname_list;
01629 num = lengthof(_surname_list);
01630 }
01631
01632 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01633 buff = strecpy(buff, " & Co.", last);
01634
01635 return buff;
01636 }
01637
01638 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01639 {
01640 char initial[] = "?. ";
01641 const char * const *base;
01642 uint num;
01643 uint i;
01644
01645 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01646 buff = strecpy(buff, initial, last);
01647
01648 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01649 if (i < sizeof(_initial_name_letters)) {
01650 initial[0] = _initial_name_letters[i];
01651 buff = strecpy(buff, initial, last);
01652 }
01653
01654 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01655 base = _silly_surname_list;
01656 num = lengthof(_silly_surname_list);
01657 } else {
01658 base = _surname_list;
01659 num = lengthof(_surname_list);
01660 }
01661
01662 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01663
01664 return buff;
01665 }
01666
01667 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
01668 {
01669 switch (ind) {
01670 case 1:
01671 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
01672
01673 case 2:
01674 return GenAndCoName(buff, args->GetInt32(), last);
01675
01676 case 3:
01677 return GenPresidentName(buff, args->GetInt32(), last);
01678 }
01679
01680
01681 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01682 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
01683 return strecpy(buff, " Transport", last);
01684 }
01685
01686
01687 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01688 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01689 return strecpy(buff,
01690 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
01691 }
01692
01693
01694 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01695 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01696 buff += seprintf(
01697 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01698 );
01699 return buff;
01700 }
01701
01702
01703 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01704 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01705 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01706 }
01707
01708 NOT_REACHED();
01709 }
01710
01711 #ifdef ENABLE_NETWORK
01712 extern void SortNetworkLanguages();
01713 #else
01714 static inline void SortNetworkLanguages() {}
01715 #endif
01716
01721 bool LanguagePackHeader::IsValid() const
01722 {
01723 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
01724 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
01725 this->plural_form < LANGUAGE_MAX_PLURAL &&
01726 this->text_dir <= 1 &&
01727 this->newgrflangid < MAX_LANG &&
01728 this->num_genders < MAX_NUM_GENDERS &&
01729 this->num_cases < MAX_NUM_CASES &&
01730 StrValid(this->name, lastof(this->name)) &&
01731 StrValid(this->own_name, lastof(this->own_name)) &&
01732 StrValid(this->isocode, lastof(this->isocode)) &&
01733 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
01734 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
01735 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
01736 }
01737
01743 bool ReadLanguagePack(const LanguageMetadata *lang)
01744 {
01745
01746 size_t len;
01747 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
01748 if (lang_pack == NULL) return false;
01749
01750
01751 const char *end = (char *)lang_pack + len + 1;
01752
01753
01754 if (end <= lang_pack->data || !lang_pack->IsValid()) {
01755 free(lang_pack);
01756 return false;
01757 }
01758
01759 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01760 for (uint i = 0; i < TAB_COUNT; i++) {
01761 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01762 }
01763 #endif
01764
01765 uint count = 0;
01766 for (uint i = 0; i < TAB_COUNT; i++) {
01767 uint num = lang_pack->offsets[i];
01768 _langtab_start[i] = count;
01769 _langtab_num[i] = num;
01770 count += num;
01771 }
01772
01773
01774 char **langpack_offs = MallocT<char *>(count);
01775
01776
01777 char *s = lang_pack->data;
01778 len = (byte)*s++;
01779 for (uint i = 0; i < count; i++) {
01780 if (s + len >= end) {
01781 free(lang_pack);
01782 free(langpack_offs);
01783 return false;
01784 }
01785 if (len >= 0xC0) {
01786 len = ((len & 0x3F) << 8) + (byte)*s++;
01787 if (s + len >= end) {
01788 free(lang_pack);
01789 free(langpack_offs);
01790 return false;
01791 }
01792 }
01793 langpack_offs[i] = s;
01794 s += len;
01795 len = (byte)*s;
01796 *s++ = '\0';
01797 }
01798
01799 free(_langpack);
01800 _langpack = lang_pack;
01801
01802 free(_langpack_offs);
01803 _langpack_offs = langpack_offs;
01804
01805 _current_language = lang;
01806 _current_text_dir = (TextDirection)_current_language->text_dir;
01807 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
01808 strecpy(_config_language_file, c_file, lastof(_config_language_file));
01809 SetCurrentGrfLangID(_current_language->newgrflangid);
01810
01811 #ifdef WITH_ICU
01812
01813 if (_current_collator != NULL) {
01814 delete _current_collator;
01815 _current_collator = NULL;
01816 }
01817
01818
01819 UErrorCode status = U_ZERO_ERROR;
01820 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01821
01822 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01823
01824 if (U_FAILURE(status)) {
01825 delete _current_collator;
01826 _current_collator = NULL;
01827 }
01828 #endif
01829
01830
01831 ReconsiderGameScriptLanguage();
01832 InitializeSortedCargoSpecs();
01833 SortIndustryTypes();
01834 BuildIndustriesLegend();
01835 SortNetworkLanguages();
01836 InvalidateWindowClassesData(WC_BUILD_VEHICLE);
01837 InvalidateWindowClassesData(WC_TRAINS_LIST);
01838 InvalidateWindowClassesData(WC_ROADVEH_LIST);
01839 InvalidateWindowClassesData(WC_SHIPS_LIST);
01840 InvalidateWindowClassesData(WC_AIRCRAFT_LIST);
01841 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY);
01842 InvalidateWindowClassesData(WC_STATION_LIST);
01843
01844 return true;
01845 }
01846
01847
01848
01849 #if !(defined(WIN32) || defined(__APPLE__))
01850
01858 const char *GetCurrentLocale(const char *param)
01859 {
01860 const char *env;
01861
01862 env = getenv("LANGUAGE");
01863 if (env != NULL) return env;
01864
01865 env = getenv("LC_ALL");
01866 if (env != NULL) return env;
01867
01868 if (param != NULL) {
01869 env = getenv(param);
01870 if (env != NULL) return env;
01871 }
01872
01873 return getenv("LANG");
01874 }
01875 #else
01876 const char *GetCurrentLocale(const char *param);
01877 #endif
01878
01879 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01880 {
01881 char stra[512];
01882 char strb[512];
01883 GetString(stra, *a, lastof(stra));
01884 GetString(strb, *b, lastof(strb));
01885
01886 return strcmp(stra, strb);
01887 }
01888
01894 const LanguageMetadata *GetLanguage(byte newgrflangid)
01895 {
01896 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
01897 if (newgrflangid == lang->newgrflangid) return lang;
01898 }
01899
01900 return NULL;
01901 }
01902
01909 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
01910 {
01911 FILE *f = fopen(file, "rb");
01912 if (f == NULL) return false;
01913
01914 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01915 fclose(f);
01916
01917 bool ret = read == 1 && hdr->IsValid();
01918
01919
01920 if (ret) {
01921 hdr->missing = FROM_LE16(hdr->missing);
01922 hdr->winlangid = FROM_LE16(hdr->winlangid);
01923 }
01924 return ret;
01925 }
01926
01931 static void GetLanguageList(const char *path)
01932 {
01933 DIR *dir = ttd_opendir(path);
01934 if (dir != NULL) {
01935 struct dirent *dirent;
01936 while ((dirent = readdir(dir)) != NULL) {
01937 const char *d_name = FS2OTTD(dirent->d_name);
01938 const char *extension = strrchr(d_name, '.');
01939
01940
01941 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01942
01943 LanguageMetadata lmd;
01944 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
01945
01946
01947 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
01948 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
01949 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
01950 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
01951 } else {
01952 *_languages.Append() = lmd;
01953 }
01954 }
01955 closedir(dir);
01956 }
01957 }
01958
01963 void InitializeLanguagePacks()
01964 {
01965 Searchpath sp;
01966
01967 FOR_ALL_SEARCHPATHS(sp) {
01968 char path[MAX_PATH];
01969 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01970 GetLanguageList(path);
01971 }
01972 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
01973
01974
01975 const char *lang = GetCurrentLocale("LC_MESSAGES");
01976 if (lang == NULL) lang = "en_GB";
01977
01978 const LanguageMetadata *chosen_language = NULL;
01979 const LanguageMetadata *language_fallback = NULL;
01980 const LanguageMetadata *en_GB_fallback = _languages.Begin();
01981
01982
01983 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01984
01985
01986
01987 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
01988 if (strcmp(lang_file, _config_language_file) == 0) {
01989 chosen_language = lng;
01990 break;
01991 }
01992
01993 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
01994 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
01995 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
01996 }
01997
01998
01999
02000 if (chosen_language == NULL) {
02001 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
02002 }
02003
02004 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
02005 }
02006
02011 const char *GetCurrentLanguageIsoCode()
02012 {
02013 return _langpack->isocode;
02014 }
02015
02022 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
02023 {
02024 InitFreeType(this->Monospace());
02025 const Sprite *question_mark[FS_END];
02026
02027 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
02028 question_mark[size] = GetGlyph(size, '?');
02029 }
02030
02031 this->Reset();
02032 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
02033 FontSize size = this->DefaultSize();
02034 if (str != NULL) *str = text;
02035 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
02036 if (c == SCC_SETX) {
02037
02038
02039
02040 text++;
02041 } else if (c == SCC_SETXY) {
02042 text += 2;
02043 } else if (c == SCC_TINYFONT) {
02044 size = FS_SMALL;
02045 } else if (c == SCC_BIGFONT) {
02046 size = FS_LARGE;
02047 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
02048
02049 return true;
02050 }
02051 }
02052 }
02053 return false;
02054 }
02055
02057 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
02058 uint i;
02059 uint j;
02060
02061 void Reset()
02062 {
02063 this->i = 0;
02064 this->j = 0;
02065 }
02066
02067 FontSize DefaultSize()
02068 {
02069 return FS_NORMAL;
02070 }
02071
02072 const char *NextString()
02073 {
02074 if (this->i >= TAB_COUNT) return NULL;
02075
02076 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
02077
02078 this->j++;
02079 while (this->i < TAB_COUNT && this->j >= _langtab_num[this->i]) {
02080 this->i++;
02081 this->j = 0;
02082 }
02083
02084 return ret;
02085 }
02086
02087 bool Monospace()
02088 {
02089 return false;
02090 }
02091
02092 void SetFontNames(FreeTypeSettings *settings, const char *font_name)
02093 {
02094 #ifdef WITH_FREETYPE
02095 strecpy(settings->small_font, font_name, lastof(settings->small_font));
02096 strecpy(settings->medium_font, font_name, lastof(settings->medium_font));
02097 strecpy(settings->large_font, font_name, lastof(settings->large_font));
02098 #endif
02099 }
02100 };
02101
02115 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
02116 {
02117 static LanguagePackGlyphSearcher pack_searcher;
02118 if (searcher == NULL) searcher = &pack_searcher;
02119 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
02120 #ifdef WITH_FREETYPE
02121 if (bad_font) {
02122
02123
02124 FreeTypeSettings backup;
02125 memcpy(&backup, &_freetype, sizeof(backup));
02126
02127 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
02128
02129 memcpy(&_freetype, &backup, sizeof(backup));
02130
02131 if (bad_font && base_font) {
02132
02133
02134
02135 InitFreeType(searcher->Monospace());
02136 }
02137 }
02138 #endif
02139
02140 if (bad_font) {
02141
02142
02143
02144
02145
02146 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.");
02147 Utf8Encode(err_str, SCC_YELLOW);
02148 SetDParamStr(0, err_str);
02149 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
02150
02151
02152 LoadStringWidthTable(searcher->Monospace());
02153 return;
02154 }
02155
02156
02157 LoadStringWidthTable(searcher->Monospace());
02158
02159 #if !defined(WITH_ICU)
02160
02161
02162
02163
02164
02165
02166
02167
02168
02169
02170
02171
02172
02173 if (_current_text_dir != TD_LTR) {
02174 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
02175 Utf8Encode(err_str, SCC_YELLOW);
02176 SetDParamStr(0, err_str);
02177 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
02178 }
02179 #endif
02180 }