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