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') break;
00894
00895 str = p;
00896 buff = GetStringWithArgs(buff, (GAME_TEXT_TAB << TAB_COUNT_OFFSET) + stringid, &sub_args, last, true);
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 StringParameters sub_args(*args, b - SCC_STRING1 + 1);
01052 buff = GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
01053 next_substr_case_index = 0;
01054 break;
01055 }
01056
01057 case SCC_COMMA:
01058 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
01059 break;
01060
01061 case SCC_DECIMAL: {
01062 int64 number = args->GetInt64(SCC_DECIMAL);
01063 int digits = args->GetInt32(SCC_DECIMAL);
01064 buff = FormatCommaNumber(buff, number, last, digits);
01065 break;
01066 }
01067
01068 case SCC_NUM:
01069 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
01070 break;
01071
01072 case SCC_ZEROFILL_NUM: {
01073 int64 num = args->GetInt64();
01074 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
01075 break;
01076 }
01077
01078 case SCC_HEX:
01079 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
01080 break;
01081
01082 case SCC_BYTES:
01083 buff = FormatBytes(buff, args->GetInt64(), last);
01084 break;
01085
01086 case SCC_CARGO_TINY: {
01087
01088
01089
01090 CargoID cargo = args->GetInt32(SCC_CARGO_TINY);
01091 if (cargo >= CargoSpec::GetArraySize()) break;
01092
01093 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
01094 int64 amount = 0;
01095 switch (cargo_str) {
01096 case STR_TONS:
01097 amount = _units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64());
01098 break;
01099
01100 case STR_LITERS:
01101 amount = _units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64());
01102 break;
01103
01104 default: {
01105 amount = args->GetInt64();
01106 break;
01107 }
01108 }
01109
01110 buff = FormatCommaNumber(buff, amount, last);
01111 break;
01112 }
01113
01114 case SCC_CARGO_SHORT: {
01115
01116
01117
01118 CargoID cargo = args->GetInt32(SCC_CARGO_SHORT);
01119 if (cargo >= CargoSpec::GetArraySize()) break;
01120
01121 StringID cargo_str = CargoSpec::Get(cargo)->units_volume;
01122 switch (cargo_str) {
01123 case STR_TONS: {
01124 assert(_settings_game.locale.units < lengthof(_units));
01125 int64 args_array[] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01126 StringParameters tmp_params(args_array);
01127 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
01128 break;
01129 }
01130
01131 case STR_LITERS: {
01132 assert(_settings_game.locale.units < lengthof(_units));
01133 int64 args_array[] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01134 StringParameters tmp_params(args_array);
01135 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
01136 break;
01137 }
01138
01139 default: {
01140 StringParameters tmp_params(*args, 1);
01141 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
01142 break;
01143 }
01144 }
01145 break;
01146 }
01147
01148 case SCC_CARGO_LONG: {
01149
01150 CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
01151 if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
01152
01153 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
01154 StringParameters tmp_args(*args, 1);
01155 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
01156 break;
01157 }
01158
01159 case SCC_CARGO_LIST: {
01160 uint32 cmask = args->GetInt32(SCC_CARGO_LIST);
01161 bool first = true;
01162
01163 const CargoSpec *cs;
01164 FOR_ALL_SORTED_CARGOSPECS(cs) {
01165 if (!HasBit(cmask, cs->Index())) continue;
01166
01167 if (buff >= last - 2) break;
01168
01169 if (first) {
01170 first = false;
01171 } else {
01172
01173 *buff++ = ',';
01174 *buff++ = ' ';
01175 }
01176
01177 buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
01178 }
01179
01180
01181 if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
01182
01183 *buff = '\0';
01184 next_substr_case_index = 0;
01185
01186
01187 assert(buff < last);
01188 break;
01189 }
01190
01191 case SCC_CURRENCY_SHORT:
01192 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
01193 break;
01194
01195 case SCC_CURRENCY_LONG:
01196 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
01197 break;
01198
01199 case SCC_DATE_TINY:
01200 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
01201 break;
01202
01203 case SCC_DATE_SHORT:
01204 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
01205 next_substr_case_index = 0;
01206 break;
01207
01208 case SCC_DATE_LONG:
01209 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
01210 next_substr_case_index = 0;
01211 break;
01212
01213 case SCC_DATE_ISO:
01214 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
01215 break;
01216
01217 case SCC_FORCE: {
01218 assert(_settings_game.locale.units < lengthof(_units));
01219 int64 args_array[1] = {_units[_settings_game.locale.units].c_force.ToDisplay(args->GetInt64())};
01220 StringParameters tmp_params(args_array);
01221 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].force), &tmp_params, last);
01222 break;
01223 }
01224
01225 case SCC_HEIGHT: {
01226 int64 args_array[] = {_units[_settings_game.locale.units].c_height.ToDisplay(args->GetInt64())};
01227 StringParameters tmp_params(args_array);
01228 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].height), &tmp_params, last);
01229 break;
01230 }
01231
01232 case SCC_POWER: {
01233 assert(_settings_game.locale.units < lengthof(_units));
01234 int64 args_array[1] = {_units[_settings_game.locale.units].c_power.ToDisplay(args->GetInt64())};
01235 StringParameters tmp_params(args_array);
01236 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].power), &tmp_params, last);
01237 break;
01238 }
01239
01240 case SCC_VELOCITY: {
01241 assert(_settings_game.locale.units < lengthof(_units));
01242 int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY))};
01243 StringParameters tmp_params(args_array);
01244 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].velocity), &tmp_params, last);
01245 break;
01246 }
01247
01248 case SCC_VOLUME_SHORT: {
01249 assert(_settings_game.locale.units < lengthof(_units));
01250 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01251 StringParameters tmp_params(args_array);
01252 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_volume), &tmp_params, last);
01253 break;
01254 }
01255
01256 case SCC_VOLUME_LONG: {
01257 assert(_settings_game.locale.units < lengthof(_units));
01258 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64(SCC_VOLUME_LONG))};
01259 StringParameters tmp_params(args_array);
01260 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, last);
01261 break;
01262 }
01263
01264 case SCC_WEIGHT_SHORT: {
01265 assert(_settings_game.locale.units < lengthof(_units));
01266 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01267 StringParameters tmp_params(args_array);
01268 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_weight), &tmp_params, last);
01269 break;
01270 }
01271
01272 case SCC_WEIGHT_LONG: {
01273 assert(_settings_game.locale.units < lengthof(_units));
01274 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG))};
01275 StringParameters tmp_params(args_array);
01276 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, last);
01277 break;
01278 }
01279
01280 case SCC_COMPANY_NAME: {
01281 const Company *c = Company::GetIfValid(args->GetInt32());
01282 if (c == NULL) break;
01283
01284 if (c->name != NULL) {
01285 int64 args_array[] = {(uint64)(size_t)c->name};
01286 StringParameters tmp_params(args_array);
01287 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01288 } else {
01289 int64 args_array[] = {c->name_2};
01290 StringParameters tmp_params(args_array);
01291 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
01292 }
01293 break;
01294 }
01295
01296 case SCC_COMPANY_NUM: {
01297 CompanyID company = (CompanyID)args->GetInt32();
01298
01299
01300 if (Company::IsValidHumanID(company)) {
01301 int64 args_array[] = {company + 1};
01302 StringParameters tmp_params(args_array);
01303 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
01304 }
01305 break;
01306 }
01307
01308 case SCC_DEPOT_NAME: {
01309 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
01310 if (vt == VEH_AIRCRAFT) {
01311 uint64 args_array[] = {args->GetInt32()};
01312 WChar types_array[] = {SCC_STATION_NAME};
01313 StringParameters tmp_params(args_array, 1, types_array);
01314 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
01315 break;
01316 }
01317
01318 const Depot *d = Depot::Get(args->GetInt32());
01319 if (d->name != NULL) {
01320 int64 args_array[] = {(uint64)(size_t)d->name};
01321 StringParameters tmp_params(args_array);
01322 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01323 } else {
01324 int64 args_array[] = {d->town->index, d->town_cn + 1};
01325 StringParameters tmp_params(args_array);
01326 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
01327 }
01328 break;
01329 }
01330
01331 case SCC_ENGINE_NAME: {
01332 const Engine *e = Engine::GetIfValid(args->GetInt32(SCC_ENGINE_NAME));
01333 if (e == NULL) break;
01334
01335 if (e->name != NULL && e->IsEnabled()) {
01336 int64 args_array[] = {(uint64)(size_t)e->name};
01337 StringParameters tmp_params(args_array);
01338 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01339 } else {
01340 StringParameters tmp_params(NULL, 0, NULL);
01341 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
01342 }
01343 break;
01344 }
01345
01346 case SCC_GROUP_NAME: {
01347 const Group *g = Group::GetIfValid(args->GetInt32());
01348 if (g == NULL) break;
01349
01350 if (g->name != NULL) {
01351 int64 args_array[] = {(uint64)(size_t)g->name};
01352 StringParameters tmp_params(args_array);
01353 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01354 } else {
01355 int64 args_array[] = {g->index};
01356 StringParameters tmp_params(args_array);
01357
01358 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
01359 }
01360 break;
01361 }
01362
01363 case SCC_INDUSTRY_NAME: {
01364 const Industry *i = Industry::GetIfValid(args->GetInt32(SCC_INDUSTRY_NAME));
01365 if (i == NULL) break;
01366
01367 if (_scan_for_gender_data) {
01368
01369
01370 StringParameters tmp_params(NULL, 0, NULL);
01371 buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
01372 } else {
01373
01374 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
01375 StringParameters tmp_params(args_array);
01376
01377 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
01378 }
01379 next_substr_case_index = 0;
01380 break;
01381 }
01382
01383 case SCC_PRESIDENT_NAME: {
01384 const Company *c = Company::GetIfValid(args->GetInt32(SCC_PRESIDENT_NAME));
01385 if (c == NULL) break;
01386
01387 if (c->president_name != NULL) {
01388 int64 args_array[] = {(uint64)(size_t)c->president_name};
01389 StringParameters tmp_params(args_array);
01390 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01391 } else {
01392 int64 args_array[] = {c->president_name_2};
01393 StringParameters tmp_params(args_array);
01394 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
01395 }
01396 break;
01397 }
01398
01399 case SCC_STATION_NAME: {
01400 StationID sid = args->GetInt32(SCC_STATION_NAME);
01401 const Station *st = Station::GetIfValid(sid);
01402
01403 if (st == NULL) {
01404
01405
01406
01407 StringParameters tmp_params(NULL, 0, NULL);
01408 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
01409 break;
01410 }
01411
01412 if (st->name != NULL) {
01413 int64 args_array[] = {(uint64)(size_t)st->name};
01414 StringParameters tmp_params(args_array);
01415 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01416 } else {
01417 StringID str = st->string_id;
01418 if (st->indtype != IT_INVALID) {
01419
01420 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01421
01422
01423
01424
01425 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
01426 str = indsp->station_name;
01427 }
01428 }
01429
01430 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
01431 StringParameters tmp_params(args_array);
01432 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01433 }
01434 break;
01435 }
01436
01437 case SCC_TOWN_NAME: {
01438 const Town *t = Town::GetIfValid(args->GetInt32(SCC_TOWN_NAME));
01439 if (t == NULL) break;
01440
01441 if (t->name != NULL) {
01442 int64 args_array[] = {(uint64)(size_t)t->name};
01443 StringParameters tmp_params(args_array);
01444 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01445 } else {
01446 buff = GetTownName(buff, t, last);
01447 }
01448 break;
01449 }
01450
01451 case SCC_WAYPOINT_NAME: {
01452 Waypoint *wp = Waypoint::GetIfValid(args->GetInt32(SCC_WAYPOINT_NAME));
01453 if (wp == NULL) break;
01454
01455 if (wp->name != NULL) {
01456 int64 args_array[] = {(uint64)(size_t)wp->name};
01457 StringParameters tmp_params(args_array);
01458 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01459 } else {
01460 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
01461 StringParameters tmp_params(args_array);
01462 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
01463 if (wp->town_cn != 0) str++;
01464 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01465 }
01466 break;
01467 }
01468
01469 case SCC_VEHICLE_NAME: {
01470 const Vehicle *v = Vehicle::GetIfValid(args->GetInt32(SCC_VEHICLE_NAME));
01471 if (v == NULL) break;
01472
01473 if (v->name != NULL) {
01474 int64 args_array[] = {(uint64)(size_t)v->name};
01475 StringParameters tmp_params(args_array);
01476 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01477 } else {
01478 int64 args_array[] = {v->unitnumber};
01479 StringParameters tmp_params(args_array);
01480
01481 StringID str;
01482 switch (v->type) {
01483 default: NOT_REACHED();
01484 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
01485 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
01486 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
01487 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01488 }
01489
01490 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01491 }
01492 break;
01493 }
01494
01495 case SCC_SIGN_NAME: {
01496 const Sign *si = Sign::GetIfValid(args->GetInt32());
01497 if (si == NULL) break;
01498
01499 if (si->name != NULL) {
01500 int64 args_array[] = {(uint64)(size_t)si->name};
01501 StringParameters tmp_params(args_array);
01502 buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
01503 } else {
01504 StringParameters tmp_params(NULL, 0, NULL);
01505 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
01506 }
01507 break;
01508 }
01509
01510 case SCC_STATION_FEATURES: {
01511 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
01512 break;
01513 }
01514
01515 default:
01516 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01517 break;
01518 }
01519 }
01520 *buff = '\0';
01521 return buff;
01522 }
01523
01524
01525 static char *StationGetSpecialString(char *buff, int x, const char *last)
01526 {
01527 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01528 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01529 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01530 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01531 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01532 *buff = '\0';
01533 return buff;
01534 }
01535
01536 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01537 {
01538 return GenerateTownNameString(buff, last, ind, seed);
01539 }
01540
01541 static const char * const _silly_company_names[] = {
01542 "Bloggs Brothers",
01543 "Tiny Transport Ltd.",
01544 "Express Travel",
01545 "Comfy-Coach & Co.",
01546 "Crush & Bump Ltd.",
01547 "Broken & Late Ltd.",
01548 "Sam Speedy & Son",
01549 "Supersonic Travel",
01550 "Mike's Motors",
01551 "Lightning International",
01552 "Pannik & Loozit Ltd.",
01553 "Inter-City Transport",
01554 "Getout & Pushit Ltd."
01555 };
01556
01557 static const char * const _surname_list[] = {
01558 "Adams",
01559 "Allan",
01560 "Baker",
01561 "Bigwig",
01562 "Black",
01563 "Bloggs",
01564 "Brown",
01565 "Campbell",
01566 "Gordon",
01567 "Hamilton",
01568 "Hawthorn",
01569 "Higgins",
01570 "Green",
01571 "Gribble",
01572 "Jones",
01573 "McAlpine",
01574 "MacDonald",
01575 "McIntosh",
01576 "Muir",
01577 "Murphy",
01578 "Nelson",
01579 "O'Donnell",
01580 "Parker",
01581 "Phillips",
01582 "Pilkington",
01583 "Quigley",
01584 "Sharkey",
01585 "Thomson",
01586 "Watkins"
01587 };
01588
01589 static const char * const _silly_surname_list[] = {
01590 "Grumpy",
01591 "Dozy",
01592 "Speedy",
01593 "Nosey",
01594 "Dribble",
01595 "Mushroom",
01596 "Cabbage",
01597 "Sniffle",
01598 "Fishy",
01599 "Swindle",
01600 "Sneaky",
01601 "Nutkins"
01602 };
01603
01604 static const char _initial_name_letters[] = {
01605 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01606 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01607 };
01608
01609 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01610 {
01611 const char * const *base;
01612 uint num;
01613
01614 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01615 base = _silly_surname_list;
01616 num = lengthof(_silly_surname_list);
01617 } else {
01618 base = _surname_list;
01619 num = lengthof(_surname_list);
01620 }
01621
01622 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01623 buff = strecpy(buff, " & Co.", last);
01624
01625 return buff;
01626 }
01627
01628 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01629 {
01630 char initial[] = "?. ";
01631 const char * const *base;
01632 uint num;
01633 uint i;
01634
01635 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01636 buff = strecpy(buff, initial, last);
01637
01638 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01639 if (i < sizeof(_initial_name_letters)) {
01640 initial[0] = _initial_name_letters[i];
01641 buff = strecpy(buff, initial, last);
01642 }
01643
01644 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01645 base = _silly_surname_list;
01646 num = lengthof(_silly_surname_list);
01647 } else {
01648 base = _surname_list;
01649 num = lengthof(_surname_list);
01650 }
01651
01652 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01653
01654 return buff;
01655 }
01656
01657 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
01658 {
01659 switch (ind) {
01660 case 1:
01661 return strecpy(buff, _silly_company_names[min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last);
01662
01663 case 2:
01664 return GenAndCoName(buff, args->GetInt32(), last);
01665
01666 case 3:
01667 return GenPresidentName(buff, args->GetInt32(), last);
01668 }
01669
01670
01671 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01672 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
01673 return strecpy(buff, " Transport", last);
01674 }
01675
01676
01677 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01678 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01679 return strecpy(buff,
01680 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
01681 }
01682
01683
01684 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01685 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01686 buff += seprintf(
01687 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01688 );
01689 return buff;
01690 }
01691
01692
01693 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01694 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01695 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01696 }
01697
01698 NOT_REACHED();
01699 }
01700
01701 #ifdef ENABLE_NETWORK
01702 extern void SortNetworkLanguages();
01703 #else
01704 static inline void SortNetworkLanguages() {}
01705 #endif
01706
01711 bool LanguagePackHeader::IsValid() const
01712 {
01713 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
01714 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
01715 this->plural_form < LANGUAGE_MAX_PLURAL &&
01716 this->text_dir <= 1 &&
01717 this->newgrflangid < MAX_LANG &&
01718 this->num_genders < MAX_NUM_GENDERS &&
01719 this->num_cases < MAX_NUM_CASES &&
01720 StrValid(this->name, lastof(this->name)) &&
01721 StrValid(this->own_name, lastof(this->own_name)) &&
01722 StrValid(this->isocode, lastof(this->isocode)) &&
01723 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
01724 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
01725 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
01726 }
01727
01733 bool ReadLanguagePack(const LanguageMetadata *lang)
01734 {
01735
01736 size_t len;
01737 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
01738 if (lang_pack == NULL) return false;
01739
01740
01741 const char *end = (char *)lang_pack + len + 1;
01742
01743
01744 if (end <= lang_pack->data || !lang_pack->IsValid()) {
01745 free(lang_pack);
01746 return false;
01747 }
01748
01749 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01750 for (uint i = 0; i < TAB_COUNT; i++) {
01751 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01752 }
01753 #endif
01754
01755 uint count = 0;
01756 for (uint i = 0; i < TAB_COUNT; i++) {
01757 uint num = lang_pack->offsets[i];
01758 _langtab_start[i] = count;
01759 _langtab_num[i] = num;
01760 count += num;
01761 }
01762
01763
01764 char **langpack_offs = MallocT<char *>(count);
01765
01766
01767 char *s = lang_pack->data;
01768 len = (byte)*s++;
01769 for (uint i = 0; i < count; i++) {
01770 if (s + len >= end) {
01771 free(lang_pack);
01772 free(langpack_offs);
01773 return false;
01774 }
01775 if (len >= 0xC0) {
01776 len = ((len & 0x3F) << 8) + (byte)*s++;
01777 if (s + len >= end) {
01778 free(lang_pack);
01779 free(langpack_offs);
01780 return false;
01781 }
01782 }
01783 langpack_offs[i] = s;
01784 s += len;
01785 len = (byte)*s;
01786 *s++ = '\0';
01787 }
01788
01789 free(_langpack);
01790 _langpack = lang_pack;
01791
01792 free(_langpack_offs);
01793 _langpack_offs = langpack_offs;
01794
01795 _current_language = lang;
01796 _current_text_dir = (TextDirection)_current_language->text_dir;
01797 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
01798 strecpy(_config_language_file, c_file, lastof(_config_language_file));
01799 SetCurrentGrfLangID(_current_language->newgrflangid);
01800
01801 #ifdef WITH_ICU
01802
01803 if (_current_collator != NULL) {
01804 delete _current_collator;
01805 _current_collator = NULL;
01806 }
01807
01808
01809 UErrorCode status = U_ZERO_ERROR;
01810 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01811
01812 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01813
01814 if (U_FAILURE(status)) {
01815 delete _current_collator;
01816 _current_collator = NULL;
01817 }
01818 #endif
01819
01820
01821 ReconsiderGameScriptLanguage();
01822 InitializeSortedCargoSpecs();
01823 SortIndustryTypes();
01824 BuildIndustriesLegend();
01825 SortNetworkLanguages();
01826 InvalidateWindowClassesData(WC_BUILD_VEHICLE);
01827 InvalidateWindowClassesData(WC_TRAINS_LIST);
01828 InvalidateWindowClassesData(WC_ROADVEH_LIST);
01829 InvalidateWindowClassesData(WC_SHIPS_LIST);
01830 InvalidateWindowClassesData(WC_AIRCRAFT_LIST);
01831 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY);
01832 InvalidateWindowClassesData(WC_STATION_LIST);
01833
01834 return true;
01835 }
01836
01837
01838
01839 #if !(defined(WIN32) || defined(__APPLE__))
01840
01848 const char *GetCurrentLocale(const char *param)
01849 {
01850 const char *env;
01851
01852 env = getenv("LANGUAGE");
01853 if (env != NULL) return env;
01854
01855 env = getenv("LC_ALL");
01856 if (env != NULL) return env;
01857
01858 if (param != NULL) {
01859 env = getenv(param);
01860 if (env != NULL) return env;
01861 }
01862
01863 return getenv("LANG");
01864 }
01865 #else
01866 const char *GetCurrentLocale(const char *param);
01867 #endif
01868
01869 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01870 {
01871 char stra[512];
01872 char strb[512];
01873 GetString(stra, *a, lastof(stra));
01874 GetString(strb, *b, lastof(strb));
01875
01876 return strcmp(stra, strb);
01877 }
01878
01884 const LanguageMetadata *GetLanguage(byte newgrflangid)
01885 {
01886 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
01887 if (newgrflangid == lang->newgrflangid) return lang;
01888 }
01889
01890 return NULL;
01891 }
01892
01899 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
01900 {
01901 FILE *f = fopen(file, "rb");
01902 if (f == NULL) return false;
01903
01904 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01905 fclose(f);
01906
01907 bool ret = read == 1 && hdr->IsValid();
01908
01909
01910 if (ret) {
01911 hdr->missing = FROM_LE16(hdr->missing);
01912 hdr->winlangid = FROM_LE16(hdr->winlangid);
01913 }
01914 return ret;
01915 }
01916
01921 static void GetLanguageList(const char *path)
01922 {
01923 DIR *dir = ttd_opendir(path);
01924 if (dir != NULL) {
01925 struct dirent *dirent;
01926 while ((dirent = readdir(dir)) != NULL) {
01927 const char *d_name = FS2OTTD(dirent->d_name);
01928 const char *extension = strrchr(d_name, '.');
01929
01930
01931 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01932
01933 LanguageMetadata lmd;
01934 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
01935
01936
01937 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
01938 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
01939 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
01940 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
01941 } else {
01942 *_languages.Append() = lmd;
01943 }
01944 }
01945 closedir(dir);
01946 }
01947 }
01948
01953 void InitializeLanguagePacks()
01954 {
01955 Searchpath sp;
01956
01957 FOR_ALL_SEARCHPATHS(sp) {
01958 char path[MAX_PATH];
01959 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01960 GetLanguageList(path);
01961 }
01962 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
01963
01964
01965 const char *lang = GetCurrentLocale("LC_MESSAGES");
01966 if (lang == NULL) lang = "en_GB";
01967
01968 const LanguageMetadata *chosen_language = NULL;
01969 const LanguageMetadata *language_fallback = NULL;
01970 const LanguageMetadata *en_GB_fallback = _languages.Begin();
01971
01972
01973 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01974
01975
01976
01977 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
01978 if (strcmp(lang_file, _config_language_file) == 0) {
01979 chosen_language = lng;
01980 break;
01981 }
01982
01983 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
01984 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
01985 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
01986 }
01987
01988
01989
01990 if (chosen_language == NULL) {
01991 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
01992 }
01993
01994 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
01995 }
01996
02001 const char *GetCurrentLanguageIsoCode()
02002 {
02003 return _langpack->isocode;
02004 }
02005
02012 bool MissingGlyphSearcher::FindMissingGlyphs(const char **str)
02013 {
02014 InitFreeType(this->Monospace());
02015 const Sprite *question_mark[FS_END];
02016
02017 for (FontSize size = this->Monospace() ? FS_MONO : FS_BEGIN; size < (this->Monospace() ? FS_END : FS_MONO); size++) {
02018 question_mark[size] = GetGlyph(size, '?');
02019 }
02020
02021 this->Reset();
02022 for (const char *text = this->NextString(); text != NULL; text = this->NextString()) {
02023 FontSize size = this->DefaultSize();
02024 if (str != NULL) *str = text;
02025 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
02026 if (c == SCC_SETX) {
02027
02028
02029
02030 text++;
02031 } else if (c == SCC_SETXY) {
02032 text += 2;
02033 } else if (c == SCC_TINYFONT) {
02034 size = FS_SMALL;
02035 } else if (c == SCC_BIGFONT) {
02036 size = FS_LARGE;
02037 } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
02038
02039 return true;
02040 }
02041 }
02042 }
02043 return false;
02044 }
02045
02047 class LanguagePackGlyphSearcher : public MissingGlyphSearcher {
02048 uint i;
02049 uint j;
02050
02051 void Reset()
02052 {
02053 this->i = 0;
02054 this->j = 0;
02055 }
02056
02057 FontSize DefaultSize()
02058 {
02059 return FS_NORMAL;
02060 }
02061
02062 const char *NextString()
02063 {
02064 if (this->i >= TAB_COUNT) return NULL;
02065
02066 const char *ret = _langpack_offs[_langtab_start[this->i] + this->j];
02067
02068 this->j++;
02069 while (this->i < TAB_COUNT && this->j >= _langtab_num[this->i]) {
02070 this->i++;
02071 this->j = 0;
02072 }
02073
02074 return ret;
02075 }
02076
02077 bool Monospace()
02078 {
02079 return false;
02080 }
02081
02082 void SetFontNames(FreeTypeSettings *settings, const char *font_name)
02083 {
02084 #ifdef WITH_FREETYPE
02085 strecpy(settings->small_font, font_name, lastof(settings->small_font));
02086 strecpy(settings->medium_font, font_name, lastof(settings->medium_font));
02087 strecpy(settings->large_font, font_name, lastof(settings->large_font));
02088 #endif
02089 }
02090 };
02091
02105 void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
02106 {
02107 static LanguagePackGlyphSearcher pack_searcher;
02108 if (searcher == NULL) searcher = &pack_searcher;
02109 bool bad_font = !base_font || searcher->FindMissingGlyphs(NULL);
02110 #ifdef WITH_FREETYPE
02111 if (bad_font) {
02112
02113
02114 FreeTypeSettings backup;
02115 memcpy(&backup, &_freetype, sizeof(backup));
02116
02117 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, searcher);
02118
02119 memcpy(&_freetype, &backup, sizeof(backup));
02120
02121 if (bad_font && base_font) {
02122
02123
02124
02125 InitFreeType(searcher->Monospace());
02126 }
02127 }
02128 #endif
02129
02130 if (bad_font) {
02131
02132
02133
02134
02135
02136 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.");
02137 Utf8Encode(err_str, SCC_YELLOW);
02138 SetDParamStr(0, err_str);
02139 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
02140
02141
02142 LoadStringWidthTable(searcher->Monospace());
02143 return;
02144 }
02145
02146
02147 LoadStringWidthTable(searcher->Monospace());
02148
02149 #if !defined(WITH_ICU)
02150
02151
02152
02153
02154
02155
02156
02157
02158
02159
02160
02161
02162
02163 if (_current_text_dir != TD_LTR) {
02164 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
02165 Utf8Encode(err_str, SCC_YELLOW);
02166 SetDParamStr(0, err_str);
02167 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
02168 }
02169 #endif
02170 }