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 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
00624 {
00625
00626 uint n = (byte)*b++;
00627 uint pos, i, mypos = 0;
00628
00629 for (i = pos = 0; i != n; i++) {
00630 uint len = (byte)*b++;
00631 if (i == form) mypos = pos;
00632 pos += len;
00633 }
00634
00635 *dst += seprintf(*dst, last, "%s", b + mypos);
00636 return b + pos;
00637 }
00638
00640 struct UnitConversion {
00641 int multiplier;
00642 int shift;
00643
00650 int64 ToDisplay(int64 input, bool round = true) const
00651 {
00652 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
00653 }
00654
00662 int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const
00663 {
00664 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
00665 }
00666 };
00667
00668 struct Units {
00669 UnitConversion c_velocity;
00670 StringID velocity;
00671 UnitConversion c_power;
00672 StringID power;
00673 UnitConversion c_weight;
00674 StringID s_weight;
00675 StringID l_weight;
00676 UnitConversion c_volume;
00677 StringID s_volume;
00678 StringID l_volume;
00679 UnitConversion c_force;
00680 StringID force;
00681 UnitConversion c_height;
00682 StringID height;
00683 };
00684
00685
00686 static const Units _units[] = {
00687 {
00688 { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL,
00689 { 1, 0}, STR_UNITS_POWER_IMPERIAL,
00690 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00691 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00692 { 1, 0}, STR_UNITS_FORCE_SI,
00693 { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL,
00694 },
00695 {
00696 { 103, 6}, STR_UNITS_VELOCITY_METRIC,
00697 {4153, 12}, STR_UNITS_POWER_METRIC,
00698 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00699 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00700 { 1, 0}, STR_UNITS_FORCE_SI,
00701 { 1, 0}, STR_UNITS_HEIGHT_SI,
00702 },
00703 {
00704 {1831, 12}, STR_UNITS_VELOCITY_SI,
00705 {6109, 13}, STR_UNITS_POWER_SI,
00706 {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00707 { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00708 { 1, 0}, STR_UNITS_FORCE_SI,
00709 { 1, 0}, STR_UNITS_HEIGHT_SI,
00710 },
00711 };
00712
00718 uint ConvertSpeedToDisplaySpeed(uint speed)
00719 {
00720
00721
00722
00723 return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed, false);
00724 }
00725
00731 uint ConvertDisplaySpeedToSpeed(uint speed)
00732 {
00733 return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed);
00734 }
00735
00741 uint ConvertKmhishSpeedToDisplaySpeed(uint speed)
00742 {
00743 return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed * 10, false) / 16;
00744 }
00745
00751 uint ConvertDisplaySpeedToKmhishSpeed(uint speed)
00752 {
00753 return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed * 16, true, 10);
00754 }
00764 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
00765 {
00766 uint orig_offset = args->offset;
00767
00768
00769 if (args->HasTypeInformation() && !dry_run) {
00770 if (UsingNewGRFTextStack()) {
00771
00772
00773
00774
00775
00776
00777 struct TextRefStack *backup = CreateTextRefStackBackup();
00778 FormatString(buff, str_arg, args, last, case_index, game_script, true);
00779 RestoreTextRefStackBackup(backup);
00780 } else {
00781 FormatString(buff, str_arg, args, last, case_index, game_script, true);
00782 }
00783
00784 args->offset = orig_offset;
00785 }
00786 WChar b;
00787 uint next_substr_case_index = 0;
00788 char *buf_start = buff;
00789 std::stack<const char *> str_stack;
00790 str_stack.push(str_arg);
00791
00792 for (;;) {
00793 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
00794 str_stack.pop();
00795 }
00796 if (str_stack.empty()) break;
00797 const char *&str = str_stack.top();
00798
00799 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00800
00801
00802 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), dry_run);
00803 if (b == 0) continue;
00804 }
00805
00806 switch (b) {
00807 case SCC_ENCODED: {
00808 uint64 sub_args_data[20];
00809 WChar sub_args_type[20];
00810 bool sub_args_need_free[20];
00811 StringParameters sub_args(sub_args_data, 20, sub_args_type);
00812
00813 sub_args.ClearTypeInformation();
00814 memset(sub_args_need_free, 0, sizeof(sub_args_need_free));
00815
00816 uint16 stringid;
00817 const char *s = str;
00818 char *p;
00819 stringid = strtol(str, &p, 16);
00820 if (*p != ':' && *p != '\0') {
00821 while (*p != '\0') p++;
00822 str = p;
00823 buff = strecat(buff, "(invalid SCC_ENCODED)", last);
00824 break;
00825 }
00826 if (stringid >= TAB_SIZE) {
00827 while (*p != '\0') p++;
00828 str = p;
00829 buff = strecat(buff, "(invalid StringID)", last);
00830 break;
00831 }
00832
00833 int i = 0;
00834 while (*p != '\0' && i < 20) {
00835 uint64 param;
00836 s = ++p;
00837
00838
00839 bool instring = false;
00840 bool escape = false;
00841 for (;; p++) {
00842 if (*p == '\\') {
00843 escape = true;
00844 continue;
00845 }
00846 if (*p == '"' && escape) {
00847 escape = false;
00848 continue;
00849 }
00850 escape = false;
00851
00852 if (*p == '"') {
00853 instring = !instring;
00854 continue;
00855 }
00856 if (instring) {
00857 continue;
00858 }
00859
00860 if (*p == ':') break;
00861 if (*p == '\0') break;
00862 }
00863
00864 if (*s != '"') {
00865
00866 WChar l;
00867 size_t len = Utf8Decode(&l, s);
00868 bool lookup = (l == SCC_ENCODED);
00869 if (lookup) s += len;
00870
00871 param = (int32)strtoul(s, &p, 16);
00872
00873 if (lookup) {
00874 if (param >= TAB_SIZE) {
00875 while (*p != '\0') p++;
00876 str = p;
00877 buff = strecat(buff, "(invalid sub-StringID)", last);
00878 break;
00879 }
00880 param = (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + param;
00881 }
00882
00883 sub_args.SetParam(i++, param);
00884 } else {
00885 char *g = strdup(s);
00886 g[p - s] = '\0';
00887
00888 sub_args_need_free[i] = true;
00889 sub_args.SetParam(i++, (uint64)(size_t)g);
00890 }
00891 }
00892
00893 if (*str != '\0') {
00894 str = p;
00895 buff = GetStringWithArgs(buff, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, last, true);
00896 }
00897
00898 for (int i = 0; i < 20; i++) {
00899 if (sub_args_need_free[i]) free((void *)sub_args.GetParam(i));
00900 }
00901 break;
00902 }
00903
00904 case SCC_NEWGRF_STRINL: {
00905 StringID substr = Utf8Consume(&str);
00906 str_stack.push(GetStringPtr(substr));
00907 break;
00908 }
00909
00910 case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
00911 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_WORD_STRING_ID);
00912 str_stack.push(GetStringPtr(substr));
00913 case_index = next_substr_case_index;
00914 next_substr_case_index = 0;
00915 break;
00916 }
00917
00918
00919 case SCC_GENDER_LIST: {
00920
00921 uint offset = orig_offset + (byte)*str++;
00922 int gender = 0;
00923 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
00924
00925
00926
00927 char input[4 + 1];
00928 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
00929 *p = '\0';
00930
00931
00932 char buf[256];
00933 bool old_sgd = _scan_for_gender_data;
00934 _scan_for_gender_data = true;
00935 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
00936 p = FormatString(buf, input, &tmp_params, lastof(buf));
00937 _scan_for_gender_data = old_sgd;
00938 *p = '\0';
00939
00940
00941 const char *s = buf;
00942 WChar c = Utf8Consume(&s);
00943
00944 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00945 }
00946 str = ParseStringChoice(str, gender, &buff, last);
00947 break;
00948 }
00949
00950
00951
00952 case SCC_GENDER_INDEX:
00953 if (_scan_for_gender_data) {
00954 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
00955 *buff++ = *str++;
00956 } else {
00957 str++;
00958 }
00959 break;
00960
00961 case SCC_PLURAL_LIST: {
00962 int plural_form = *str++;
00963 uint offset = orig_offset + (byte)*str++;
00964 int64 v = *args->GetPointerToOffset(offset);
00965 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
00966 break;
00967 }
00968
00969 case SCC_ARG_INDEX: {
00970 args->offset = orig_offset + (byte)*str++;
00971 break;
00972 }
00973
00974 case SCC_SET_CASE: {
00975
00976
00977 next_substr_case_index = (byte)*str++;
00978 break;
00979 }
00980
00981 case SCC_SWITCH_CASE: {
00982
00983
00984 uint num = (byte)*str++;
00985 while (num) {
00986 if ((byte)str[0] == case_index) {
00987
00988 str += 3;
00989 break;
00990 }
00991
00992 str += 3 + (str[1] << 8) + str[2];
00993 num--;
00994 }
00995 break;
00996 }
00997
00998 case SCC_SETX:
00999 if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
01000 buff += Utf8Encode(buff, SCC_SETX);
01001 *buff++ = *str++;
01002 }
01003 break;
01004
01005 case SCC_SETXY:
01006 if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
01007 buff += Utf8Encode(buff, SCC_SETXY);
01008 *buff++ = *str++;
01009 *buff++ = *str++;
01010 }
01011 break;
01012
01013 case SCC_REVISION:
01014 buff = strecpy(buff, _openttd_revision, last);
01015 break;
01016
01017 case SCC_STRING_ID:
01018 if (game_script) break;
01019 buff = GetStringWithArgs(buff, Utf8Consume(&str), args, last);
01020 break;
01021
01022 case SCC_RAW_STRING_POINTER: {
01023 if (game_script) break;
01024 const char *str = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
01025 buff = FormatString(buff, str, args, last);
01026 break;
01027 }
01028
01029 case SCC_STRING: {
01030 StringID str = args->GetInt32(SCC_STRING);
01031 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
01032
01033
01034
01035 StringParameters tmp_params(args->GetDataPointer(), args->num_param - args->offset, NULL);
01036 buff = GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
01037 next_substr_case_index = 0;
01038 break;
01039 }
01040
01041 case SCC_STRING1:
01042 case SCC_STRING2:
01043 case SCC_STRING3:
01044 case SCC_STRING4:
01045 case SCC_STRING5:
01046 case SCC_STRING6:
01047 case SCC_STRING7: {
01048
01049 StringID str = args->GetInt32(b);
01050 if (game_script && GB(str, TAB_COUNT_OFFSET, TAB_COUNT_BITS) != GAME_TEXT_TAB) break;
01051 uint size = b - SCC_STRING1 + 1;
01052 if (game_script && size > args->num_param - args->offset) {
01053 buff = strecat(buff, "(too many parameters)", last);
01054 } else {
01055 StringParameters sub_args(*args, size);
01056 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
01057 }
01058 next_substr_case_index = 0;
01059 break;
01060 }
01061
01062 case SCC_COMMA:
01063 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
01064 break;
01065
01066 case SCC_DECIMAL: {
01067 int64 number = args->GetInt64(SCC_DECIMAL);
01068 int digits = args->GetInt32(SCC_DECIMAL);
01069 buff = FormatCommaNumber(buff, number, last, digits);
01070 break;
01071 }
01072
01073 case SCC_NUM:
01074 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
01075 break;
01076
01077 case SCC_ZEROFILL_NUM: {
01078 int64 num = args->GetInt64();
01079 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
01080 break;
01081 }
01082
01083 case SCC_HEX:
01084 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
01085 break;
01086
01087 case SCC_BYTES:
01088 buff = FormatBytes(buff, args->GetInt64(), last);
01089 break;
01090
01091 case SCC_CARGO_TINY: {
01092
01093
01094
01095 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
01096 if (cargo >= CargoSpec::GetArraySize()) break;
01097
01098 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
01099 int64 amount = 0;
01100 switch (cargo_str) {
01101 case STR_TONS:
01102 amount = _units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64());
01103 break;
01104
01105 case STR_LITERS:
01106 amount = _units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64());
01107 break;
01108
01109 default: {
01110 amount = args->GetInt64();
01111 break;
01112 }
01113 }
01114
01115 buff = FormatCommaNumber(buff, amount, last);
01116 break;
01117 }
01118
01119 case SCC_CARGO_SHORT: {
01120
01121
01122
01123 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
01124 if (cargo >= CargoSpec::GetArraySize()) break;
01125
01126 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
01127 switch (cargo_str) {
01128 case STR_TONS: {
01129 assert(_settings_game.locale.units < lengthof(_units));
01130 int64 args_array[] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01131 StringParameters tmp_params(args_array);
01132 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
01133 break;
01134 }
01135
01136 case STR_LITERS: {
01137 assert(_settings_game.locale.units < lengthof(_units));
01138 int64 args_array[] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01139 StringParameters tmp_params(args_array);
01140 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
01141 break;
01142 }
01143
01144 default: {
01145 StringParameters tmp_params(*args, 1);
01146 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
01147 break;
01148 }
01149 }
01150 break;
01151 }
01152
01153 case SCC_CARGO_LONG: {
01154
01155 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
01156 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
01157
01158 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
01159 StringParameters tmp_args(*args, 1);
01160 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
01161 break;
01162 }
01163
01164 case SCC_CARGO_LIST: {
01165 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
01166 bool first = true;
01167
01168 const CargoSpec *cs;
01169 FOR_ALL_SORTED_CARGOSPECS(cs) {
01170 if (!HasBit(cmask, cs->Index())) continue;
01171
01172 if (buff >= last - 2) break;
01173
01174 if (first) {
01175 first = false;
01176 } else {
01177
01178 *buff++ = ',';
01179 *buff++ = ' ';
01180 }
01181
01182 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
01183 }
01184
01185
01186 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
01187
01188 *buff = '\0';
01189 next_substr_case_index = 0;
01190
01191
01192 assert(buff < last);
01193 break;
01194 }
01195
01196 case SCC_CURRENCY_SHORT:
01197 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
01198 break;
01199
01200 case SCC_CURRENCY_LONG:
01201 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
01202 break;
01203
01204 case SCC_DATE_TINY:
01205 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
01206 break;
01207
01208 case SCC_DATE_SHORT:
01209 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
01210 next_substr_case_index = 0;
01211 break;
01212
01213 case SCC_DATE_LONG:
01214 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
01215 next_substr_case_index = 0;
01216 break;
01217
01218 case SCC_DATE_ISO:
01219 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
01220 break;
01221
01222 case SCC_FORCE: {
01223 assert(_settings_game.locale.units < lengthof(_units));
01224 int64 args_array[1] = {_units[_settings_game.locale.units].c_force.ToDisplay(args->GetInt64())};
01225 StringParameters tmp_params(args_array);
01226 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].force), &tmp_params, last);
01227 break;
01228 }
01229
01230 case SCC_HEIGHT: {
01231 int64 args_array[] = {_units[_settings_game.locale.units].c_height.ToDisplay(args->GetInt64())};
01232 StringParameters tmp_params(args_array);
01233 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].height), &tmp_params, last);
01234 break;
01235 }
01236
01237 case SCC_POWER: {
01238 assert(_settings_game.locale.units < lengthof(_units));
01239 int64 args_array[1] = {_units[_settings_game.locale.units].c_power.ToDisplay(args->GetInt64())};
01240 StringParameters tmp_params(args_array);
01241 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].power), &tmp_params, last);
01242 break;
01243 }
01244
01245 case SCC_VELOCITY: {
01246 assert(_settings_game.locale.units < lengthof(_units));
01247 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
01248 StringParameters tmp_params(args_array);
01249 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].velocity), &tmp_params, last);
01250 break;
01251 }
01252
01253 case SCC_VOLUME_SHORT: {
01254 assert(_settings_game.locale.units < lengthof(_units));
01255 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01256 StringParameters tmp_params(args_array);
01257 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_volume), &tmp_params, last);
01258 break;
01259 }
01260
01261 case SCC_VOLUME_LONG: {
01262 assert(_settings_game.locale.units < lengthof(_units));
01263 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
01264 StringParameters tmp_params(args_array);
01265 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
01266 break;
01267 }
01268
01269 case SCC_WEIGHT_SHORT: {
01270 assert(_settings_game.locale.units < lengthof(_units));
01271 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01272 StringParameters tmp_params(args_array);
01273 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_weight), &tmp_params, last);
01274 break;
01275 }
01276
01277 case SCC_WEIGHT_LONG: {
01278 assert(_settings_game.locale.units < lengthof(_units));
01279 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
01280 StringParameters tmp_params(args_array);
01281 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
01282 break;
01283 }
01284
01285 case SCC_COMPANY_NAME: {
01286 const Company *c = Company::GetIfValid(args->GetInt32());
01287 if (c == NULL) break;
01288
01289 if (c->name != NULL) {
01290 int64 args_array[] = {(uint64)(size_t)c->name};
01291 StringParameters tmp_params(args_array);
01292 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01293 } else {
01294 int64 args_array[] = {c->name_2};
01295 StringParameters tmp_params(args_array);
01296 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
01297 }
01298 break;
01299 }
01300
01301 case SCC_COMPANY_NUM: {
01302 CompanyID company = (CompanyID)args->GetInt32();
01303
01304
01305 if (Company::IsValidHumanID(company)) {
01306 int64 args_array[] = {company + 1};
01307 StringParameters tmp_params(args_array);
01308 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
01309 }
01310 break;
01311 }
01312
01313 case SCC_DEPOT_NAME: {
01314 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
01315 if (vt == VEH_AIRCRAFT) {
01316 uint64 args_array[] = {args->GetInt32()};
01317 WChar types_array[] = {SCC_STATION_NAME};
01318 StringParameters tmp_params(args_array, 1, types_array);
01319 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
01320 break;
01321 }
01322
01323 const Depot *d = Depot::Get(args->GetInt32());
01324 if (d->name != NULL) {
01325 int64 args_array[] = {(uint64)(size_t)d->name};
01326 StringParameters tmp_params(args_array);
01327 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01328 } else {
01329 int64 args_array[] = {d->town->index, d->town_cn + 1};
01330 StringParameters tmp_params(args_array);
01331 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
01332 }
01333 break;
01334 }
01335
01336 case SCC_ENGINE_NAME: {
01337 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
01338 if (e == NULL) break;
01339
01340 if (e->name != NULL && e->IsEnabled()) {
01341 int64 args_array[] = {(uint64)(size_t)e->name};
01342 StringParameters tmp_params(args_array);
01343 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01344 } else {
01345 StringParameters tmp_params(NULL, 0, NULL);
01346 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
01347 }
01348 break;
01349 }
01350
01351 case SCC_GROUP_NAME: {
01352 const Group *g = Group::GetIfValid(args->GetInt32());
01353 if (g == NULL) break;
01354
01355 if (g->name != NULL) {
01356 int64 args_array[] = {(uint64)(size_t)g->name};
01357 StringParameters tmp_params(args_array);
01358 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01359 } else {
01360 int64 args_array[] = {g->index};
01361 StringParameters tmp_params(args_array);
01362
01363 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
01364 }
01365 break;
01366 }
01367
01368 case SCC_INDUSTRY_NAME: {
01369 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
01370 if (i == NULL) break;
01371
01372 if (_scan_for_gender_data) {
01373
01374
01375 StringParameters tmp_params(NULL, 0, NULL);
01376 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
01377 } else {
01378
01379 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
01380 StringParameters tmp_params(args_array);
01381
01382 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
01383 }
01384 next_substr_case_index = 0;
01385 break;
01386 }
01387
01388 case SCC_PRESIDENT_NAME: {
01389 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
01390 if (c == NULL) break;
01391
01392 if (c->president_name != NULL) {
01393 int64 args_array[] = {(uint64)(size_t)c->president_name};
01394 StringParameters tmp_params(args_array);
01395 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01396 } else {
01397 int64 args_array[] = {c->president_name_2};
01398 StringParameters tmp_params(args_array);
01399 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
01400 }
01401 break;
01402 }
01403
01404 case SCC_STATION_NAME: {
01405 StationID sid = args->GetInt32(SCC_STATION_NAME);
01406 const Station *st = Station::GetIfValid(sid);
01407
01408 if (st == NULL) {
01409
01410
01411
01412 StringParameters tmp_params(NULL, 0, NULL);
01413 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
01414 break;
01415 }
01416
01417 if (st->name != NULL) {
01418 int64 args_array[] = {(uint64)(size_t)st->name};
01419 StringParameters tmp_params(args_array);
01420 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01421 } else {
01422 StringID str = st->string_id;
01423 if (st->indtype != IT_INVALID) {
01424
01425 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01426
01427
01428
01429
01430 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
01431 str = indsp->station_name;
01432 }
01433 }
01434
01435 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
01436 StringParameters tmp_params(args_array);
01437 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01438 }
01439 break;
01440 }
01441
01442 case SCC_TOWN_NAME: {
01443 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
01444 if (t == NULL) break;
01445
01446 if (t->name != NULL) {
01447 int64 args_array[] = {(uint64)(size_t)t->name};
01448 StringParameters tmp_params(args_array);
01449 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01450 } else {
01451 buff = GetTownName(buff, t, last);
01452 }
01453 break;
01454 }
01455
01456 case SCC_WAYPOINT_NAME: {
01457 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
01458 if (wp == NULL) break;
01459
01460 if (wp->name != NULL) {
01461 int64 args_array[] = {(uint64)(size_t)wp->name};
01462 StringParameters tmp_params(args_array);
01463 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01464 } else {
01465 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
01466 StringParameters tmp_params(args_array);
01467 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
01468 if (wp->town_cn != 0) str++;
01469 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01470 }
01471 break;
01472 }
01473
01474 case SCC_VEHICLE_NAME: {
01475 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
01476 if (v == NULL) break;
01477
01478 if (v->name != NULL) {
01479 int64 args_array[] = {(uint64)(size_t)v->name};
01480 StringParameters tmp_params(args_array);
01481 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01482 } else {
01483 int64 args_array[] = {v->unitnumber};
01484 StringParameters tmp_params(args_array);
01485
01486 StringID str;
01487 switch (v->type) {
01488 default: NOT_REACHED();
01489 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
01490 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
01491 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
01492 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01493 }
01494
01495 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01496 }
01497 break;
01498 }
01499
01500 case SCC_SIGN_NAME: {
01501 const Sign *si = Sign::GetIfValid(args->GetInt32());
01502 if (si == NULL) break;
01503
01504 if (si->name != NULL) {
01505 int64 args_array[] = {(uint64)(size_t)si->name};
01506 StringParameters tmp_params(args_array);
01507 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01508 } else {
01509 StringParameters tmp_params(NULL, 0, NULL);
01510 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
01511 }
01512 break;
01513 }
01514
01515 case SCC_STATION_FEATURES: {
01516 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
01517 break;
01518 }
01519
01520 default:
01521 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01522 break;
01523 }
01524 }
01525 *buff = '\0';
01526 return buff;
01527 }
01528
01529
01530 static char *StationGetSpecialString(char *buff, int x, const char *last)
01531 {
01532 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01533 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01534 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01535 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01536 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01537 *buff = '\0';
01538 return buff;
01539 }
01540
01541 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01542 {
01543 return GenerateTownNameString(buff, last, ind, seed);
01544 }
01545
01546 static const char * const _silly_company_names[] = {
01547 "Bloggs Brothers",
01548 "Tiny Transport Ltd.",
01549 "Express Travel",
01550 "Comfy-Coach & Co.",
01551 "Crush & Bump Ltd.",
01552 "Broken & Late Ltd.",
01553 "Sam Speedy & Son",
01554 "Supersonic Travel",
01555 "Mike's Motors",
01556 "Lightning International",
01557 "Pannik & Loozit Ltd.",
01558 "Inter-City Transport",
01559 "Getout & Pushit Ltd."
01560 };
01561
01562 static const char * const _surname_list[] = {
01563 "Adams",
01564 "Allan",
01565 "Baker",
01566 "Bigwig",
01567 "Black",
01568 "Bloggs",
01569 "Brown",
01570 "Campbell",
01571 "Gordon",
01572 "Hamilton",
01573 "Hawthorn",
01574 "Higgins",
01575 "Green",
01576 "Gribble",
01577 "Jones",
01578 "McAlpine",
01579 "MacDonald",
01580 "McIntosh",
01581 "Muir",
01582 "Murphy",
01583 "Nelson",
01584 "O'Donnell",
01585 "Parker",
01586 "Phillips",
01587 "Pilkington",
01588 "Quigley",
01589 "Sharkey",
01590 "Thomson",
01591 "Watkins"
01592 };
01593
01594 static const char * const _silly_surname_list[] = {
01595 "Grumpy",
01596 "Dozy",
01597 "Speedy",
01598 "Nosey",
01599 "Dribble",
01600 "Mushroom",
01601 "Cabbage",
01602 "Sniffle",
01603 "Fishy",
01604 "Swindle",
01605 "Sneaky",
01606 "Nutkins"
01607 };
01608
01609 static const char _initial_name_letters[] = {
01610 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01611 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01612 };
01613
01614 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01615 {
01616 const char * const *base;
01617 uint num;
01618
01619 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01620 base = _silly_surname_list;
01621 num = lengthof(_silly_surname_list);
01622 } else {
01623 base = _surname_list;
01624 num = lengthof(_surname_list);
01625 }
01626
01627 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01628 buff = strecpy(buff, " & Co.", last);
01629
01630 return buff;
01631 }
01632
01633 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01634 {
01635 char initial[] = "?. ";
01636 const char * const *base;
01637 uint num;
01638 uint i;
01639
01640 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01641 buff = strecpy(buff, initial, last);
01642
01643 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01644 if (i < sizeof(_initial_name_letters)) {
01645 initial[0] = _initial_name_letters[i];
01646 buff = strecpy(buff, initial, last);
01647 }
01648
01649 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01650 base = _silly_surname_list;
01651 num = lengthof(_silly_surname_list);
01652 } else {
01653 base = _surname_list;
01654 num = lengthof(_surname_list);
01655 }
01656
01657 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01658
01659 return buff;
01660 }
01661
01662 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
01663 {
01664 switch (ind) {
01665 case 1:
01666 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
01667
01668 case 2:
01669 return GenAndCoName(buff, args->GetInt32(), last);
01670
01671 case 3:
01672 return GenPresidentName(buff, args->GetInt32(), last);
01673 }
01674
01675
01676 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01677 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
01678 return strecpy(buff, " Transport", last);
01679 }
01680
01681
01682 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01683 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01684 return strecpy(buff,
01685 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
01686 }
01687
01688
01689 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01690 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01691 buff += seprintf(
01692 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01693 );
01694 return buff;
01695 }
01696
01697
01698 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01699 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01700 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01701 }
01702
01703 NOT_REACHED();
01704 }
01705
01706 #ifdef ENABLE_NETWORK
01707 extern void SortNetworkLanguages();
01708 #else
01709 static inline void SortNetworkLanguages() {}
01710 #endif
01711
01716 bool LanguagePackHeader::IsValid() const
01717 {
01718 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
01719 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
01720 this->plural_form < LANGUAGE_MAX_PLURAL &&
01721 this->text_dir <= 1 &&
01722 this->newgrflangid < MAX_LANG &&
01723 this->num_genders < MAX_NUM_GENDERS &&
01724 this->num_cases < MAX_NUM_CASES &&
01725 StrValid(this->name, lastof(this->name)) &&
01726 StrValid(this->own_name, lastof(this->own_name)) &&
01727 StrValid(this->isocode, lastof(this->isocode)) &&
01728 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
01729 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
01730 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
01731 }
01732
01738 bool ReadLanguagePack(const LanguageMetadata *lang)
01739 {
01740
01741 size_t len;
01742 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
01743 if (lang_pack == NULL) return false;
01744
01745
01746 const char *end = (char *)lang_pack + len + 1;
01747
01748
01749 if (end <= lang_pack->data || !lang_pack->IsValid()) {
01750 free(lang_pack);
01751 return false;
01752 }
01753
01754 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01755 for (uint i = 0; i < TAB_COUNT; i++) {
01756 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01757 }
01758 #endif
01759
01760 uint count = 0;
01761 for (uint i = 0; i < TAB_COUNT; i++) {
01762 uint num = lang_pack->offsets[i];
01763 _langtab_start[i] = count;
01764 _langtab_num[i] = num;
01765 count += num;
01766 }
01767
01768
01769 char **langpack_offs = MallocT<char *>(count);
01770
01771
01772 char *s = lang_pack->data;
01773 len = (byte)*s++;
01774 for (uint i = 0; i < count; i++) {
01775 if (s + len >= end) {
01776 free(lang_pack);
01777 free(langpack_offs);
01778 return false;
01779 }
01780 if (len >= 0xC0) {
01781 len = ((len & 0x3F) << 8) + (byte)*s++;
01782 if (s + len >= end) {
01783 free(lang_pack);
01784 free(langpack_offs);
01785 return false;
01786 }
01787 }
01788 langpack_offs[i] = s;
01789 s += len;
01790 len = (byte)*s;
01791 *s++ = '\0';
01792 }
01793
01794 free(_langpack);
01795 _langpack = lang_pack;
01796
01797 free(_langpack_offs);
01798 _langpack_offs = langpack_offs;
01799
01800 _current_language = lang;
01801 _current_text_dir = (TextDirection)_current_language->text_dir;
01802 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
01803 strecpy(_config_language_file, c_file, lastof(_config_language_file));
01804 SetCurrentGrfLangID(_current_language->newgrflangid);
01805
01806 #ifdef WITH_ICU
01807
01808 if (_current_collator != NULL) {
01809 delete _current_collator;
01810 _current_collator = NULL;
01811 }
01812
01813
01814 UErrorCode status = U_ZERO_ERROR;
01815 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01816
01817 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01818
01819 if (U_FAILURE(status)) {
01820 delete _current_collator;
01821 _current_collator = NULL;
01822 }
01823 #endif
01824
01825
01826 ReconsiderGameScriptLanguage();
01827 InitializeSortedCargoSpecs();
01828 SortIndustryTypes();
01829 BuildIndustriesLegend();
01830 SortNetworkLanguages();
01831 InvalidateWindowClassesData(WC_BUILD_VEHICLE);
01832 InvalidateWindowClassesData(WC_TRAINS_LIST);
01833 InvalidateWindowClassesData(WC_ROADVEH_LIST);
01834 InvalidateWindowClassesData(WC_SHIPS_LIST);
01835 InvalidateWindowClassesData(WC_AIRCRAFT_LIST);
01836 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY);
01837 InvalidateWindowClassesData(WC_STATION_LIST);
01838
01839 return true;
01840 }
01841
01842
01843
01844 #if !(defined(WIN32) || defined(__APPLE__))
01845
01853 const char *GetCurrentLocale(const char *param)
01854 {
01855 const char *env;
01856
01857 env = getenv("LANGUAGE");
01858 if (env != NULL) return env;
01859
01860 env = getenv("LC_ALL");
01861 if (env != NULL) return env;
01862
01863 if (param != NULL) {
01864 env = getenv(param);
01865 if (env != NULL) return env;
01866 }
01867
01868 return getenv("LANG");
01869 }
01870 #else
01871 const char *GetCurrentLocale(const char *param);
01872 #endif
01873
01874 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01875 {
01876 char stra[512];
01877 char strb[512];
01878 GetString(stra, *a, lastof(stra));
01879 GetString(strb, *b, lastof(strb));
01880
01881 return strcmp(stra, strb);
01882 }
01883
01889 const LanguageMetadata *GetLanguage(byte newgrflangid)
01890 {
01891 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
01892 if (newgrflangid == lang->newgrflangid) return lang;
01893 }
01894
01895 return NULL;
01896 }
01897
01904 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
01905 {
01906 FILE *f = fopen(file, "rb");
01907 if (f == NULL) return false;
01908
01909 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01910 fclose(f);
01911
01912 bool ret = read == 1 && hdr->IsValid();
01913
01914
01915 if (ret) {
01916 hdr->missing = FROM_LE16(hdr->missing);
01917 hdr->winlangid = FROM_LE16(hdr->winlangid);
01918 }
01919 return ret;
01920 }
01921
01926 static void GetLanguageList(const char *path)
01927 {
01928 DIR *dir = ttd_opendir(path);
01929 if (dir != NULL) {
01930 struct dirent *dirent;
01931 while ((dirent = readdir(dir)) != NULL) {
01932 const char *d_name = FS2OTTD(dirent->d_name);
01933 const char *extension = strrchr(d_name, '.');
01934
01935
01936 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01937
01938 LanguageMetadata lmd;
01939 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
01940
01941
01942 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
01943 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
01944 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
01945 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
01946 } else {
01947 *_languages.Append() = lmd;
01948 }
01949 }
01950 closedir(dir);
01951 }
01952 }
01953
01958 void InitializeLanguagePacks()
01959 {
01960 Searchpath sp;
01961
01962 FOR_ALL_SEARCHPATHS(sp) {
01963 char path[MAX_PATH];
01964 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01965 GetLanguageList(path);
01966 }
01967 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
01968
01969
01970 const char *lang = GetCurrentLocale("LC_MESSAGES");
01971 if (lang == NULL) lang = "en_GB";
01972
01973 const LanguageMetadata *chosen_language = NULL;
01974 const LanguageMetadata *language_fallback = NULL;
01975 const LanguageMetadata *en_GB_fallback = _languages.Begin();
01976
01977
01978 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01979
01980
01981
01982 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
01983 if (strcmp(lang_file, _config_language_file) == 0) {
01984 chosen_language = lng;
01985 break;
01986 }
01987
01988 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
01989 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
01990 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
01991 }
01992
01993
01994
01995 if (chosen_language == NULL) {
01996 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
01997 }
01998
01999 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
02000 }
02001
02006 const char *GetCurrentLanguageIsoCode()
02007 {
02008 return _langpack->isocode;
02009 }
02010
02017 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
02018 {
02019 InitFreeType(this->Monospace());
02020 const Sprite *question_mark[FS_END];
02021
02022 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
02023 question_mark[size] = GetGlyph(size, '?');
02024 }
02025
02026 this->Reset();
02027 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
02028 FontSize size = this->DefaultSize();
02029 if (str != NULL) *str = text;
02030 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
02031 if (c == SCC_SETX) {
02032
02033
02034
02035 text++;
02036 } else if (c == SCC_SETXY) {
02037 text += 2;
02038 } else if (c == SCC_TINYFONT) {
02039 size = FS_SMALL;
02040 } else if (c == SCC_BIGFONT) {
02041 size = FS_LARGE;
02042 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
02043
02044 return true;
02045 }
02046 }
02047 }
02048 return false;
02049 }
02050
02052 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
02053 uint i;
02054 uint j;
02055
02056 void Reset()
02057 {
02058 this->i = 0;
02059 this->j = 0;
02060 }
02061
02062 FontSize DefaultSize()
02063 {
02064 return FS_NORMAL;
02065 }
02066
02067 const char *NextString()
02068 {
02069 if (this->i >= TAB_COUNT) return NULL;
02070
02071 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
02072
02073 this->j++;
02074 while (this->i < TAB_COUNT && this->j >= _langtab_num[this->i]) {
02075 this->i++;
02076 this->j = 0;
02077 }
02078
02079 return ret;
02080 }
02081
02082 bool Monospace()
02083 {
02084 return false;
02085 }
02086
02087 void SetFontNames(FreeTypeSettings *settings, const char *font_name)
02088 {
02089 #ifdef WITH_FREETYPE
02090 strecpy(settings->small_font, font_name, lastof(settings->small_font));
02091 strecpy(settings->medium_font, font_name, lastof(settings->medium_font));
02092 strecpy(settings->large_font, font_name, lastof(settings->large_font));
02093 #endif
02094 }
02095 };
02096
02110 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
02111 {
02112 static LanguagePackGlyphSearcher pack_searcher;
02113 if (searcher == NULL) searcher = &pack_searcher;
02114 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
02115 #ifdef WITH_FREETYPE
02116 if (bad_font) {
02117
02118
02119 FreeTypeSettings backup;
02120 memcpy(&backup, &_freetype, sizeof(backup));
02121
02122 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
02123
02124 memcpy(&_freetype, &backup, sizeof(backup));
02125
02126 if (bad_font && base_font) {
02127
02128
02129
02130 InitFreeType(searcher->Monospace());
02131 }
02132 }
02133 #endif
02134
02135 if (bad_font) {
02136
02137
02138
02139
02140
02141 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.");
02142 Utf8Encode(err_str, SCC_YELLOW);
02143 SetDParamStr(0, err_str);
02144 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
02145
02146
02147 LoadStringWidthTable(searcher->Monospace());
02148 return;
02149 }
02150
02151
02152 LoadStringWidthTable(searcher->Monospace());
02153
02154 #if !defined(WITH_ICU)
02155
02156
02157
02158
02159
02160
02161
02162
02163
02164
02165
02166
02167
02168 if (_current_text_dir != TD_LTR) {
02169 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
02170 Utf8Encode(err_str, SCC_YELLOW);
02171 SetDParamStr(0, err_str);
02172 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
02173 }
02174 #endif
02175 }