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