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 "group.h"
00023 #include "signs_base.h"
00024 #include "cargotype.h"
00025 #include "fontcache.h"
00026 #include "gui.h"
00027 #include "strings_func.h"
00028 #include "rev.h"
00029 #include "core/endian_func.hpp"
00030 #include "date_func.h"
00031 #include "vehicle_base.h"
00032 #include "engine_base.h"
00033 #include "language.h"
00034 #include "townname_func.h"
00035 #include "string_func.h"
00036 #include "company_base.h"
00037 #include "smallmap_gui.h"
00038 #include "window_func.h"
00039 #include "debug.h"
00040 #include <stack>
00041
00042 #include "table/strings.h"
00043 #include "table/control_codes.h"
00044
00045 char _config_language_file[MAX_PATH];
00046 LanguageList _languages;
00047 const LanguageMetadata *_current_language = NULL;
00048
00049 TextDirection _current_text_dir;
00050 uint64 _decode_parameters[20];
00051 WChar _parameter_type[20];
00052
00053 #ifdef WITH_ICU
00054 Collator *_current_collator = NULL;
00055 #endif
00056
00057 static char *StationGetSpecialString(char *buff, int x, const char *last);
00058 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
00059 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const int64 *argve, const char *last, WChar *argt = NULL);
00060
00061 static char *FormatString(char *buff, const char *str, int64 *argv, const int64 *argve, uint casei, const char *last, WChar *argt = NULL, bool dry_run = false);
00062
00063 struct LanguagePack : public LanguagePackHeader {
00064 char data[];
00065 };
00066
00067 static char **_langpack_offs;
00068 static LanguagePack *_langpack;
00069 static uint _langtab_num[32];
00070 static uint _langtab_start[32];
00071 static bool _keep_gender_data = false;
00072
00073
00084 static inline int64 GetInt64(int64 **argv, const int64 *argve, WChar **argt, WChar type = 0)
00085 {
00086 assert(*argv != NULL);
00087 assert(*argv < argve);
00088 if (*argt != NULL) {
00089 assert(**argt == 0 || **argt == type);
00090 **argt = type;
00091 (*argt)++;
00092 }
00093 return *(*argv)++;
00094 }
00095
00097 static inline int32 GetInt32(int64 **argv, const int64 *argve, WChar **argt, WChar type = 0)
00098 {
00099 return (int32)GetInt64(argv, argve, argt, type);
00100 }
00101
00110 static inline int64 *GetArgvPtr(int64 **argv, int n, const int64 *argve, WChar **argt)
00111 {
00112 int64 *result;
00113 assert(*argv != NULL);
00114 assert((*argv + n) <= argve);
00115 result = *argv;
00116 (*argv) += n;
00117 if (*argt != NULL) (*argt) += n;
00118 return result;
00119 }
00120
00121
00122 const char *GetStringPtr(StringID string)
00123 {
00124 switch (GB(string, 11, 5)) {
00125
00126 case 26: return GetStringPtr(GetGRFStringID(0, 0xD000 + GB(string, 0, 10)));
00127 case 28: return GetGRFStringPtr(GB(string, 0, 11));
00128 case 29: return GetGRFStringPtr(GB(string, 0, 11) + 0x0800);
00129 case 30: return GetGRFStringPtr(GB(string, 0, 11) + 0x1000);
00130 default: return _langpack_offs[_langtab_start[string >> 11] + (string & 0x7FF)];
00131 }
00132 }
00133
00147 char *GetStringWithArgs(char *buffr, uint string, int64 *argv, const int64 *argve, const char *last, WChar *argt)
00148 {
00149 if (GB(string, 0, 16) == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, argv, argve, last, argt);
00150
00151 uint index = GB(string, 0, 11);
00152 uint tab = GB(string, 11, 5);
00153
00154 switch (tab) {
00155 case 4:
00156 if (index >= 0xC0) {
00157 return GetSpecialTownNameString(buffr, index - 0xC0, GetInt32(&argv, argve, &argt), last);
00158 }
00159 break;
00160
00161 case 14:
00162 if (index >= 0xE4) {
00163 return GetSpecialNameString(buffr, index - 0xE4, argv, argve, last, argt);
00164 }
00165 break;
00166
00167 case 15:
00168
00169 error("Incorrect conversion of custom name string.");
00170
00171 case 26:
00172
00173 if (HasBit(index, 10)) {
00174 StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
00175 return GetStringWithArgs(buffr, string, argv, argve, last, argt);
00176 }
00177 break;
00178
00179 case 28:
00180 return FormatString(buffr, GetGRFStringPtr(index), argv, argve, GB(string, 24, 8), last, argt);
00181
00182 case 29:
00183 return FormatString(buffr, GetGRFStringPtr(index + 0x0800), argv, argve, GB(string, 24, 8), last, argt);
00184
00185 case 30:
00186 return FormatString(buffr, GetGRFStringPtr(index + 0x1000), argv, argve, GB(string, 24, 8), last, argt);
00187
00188 case 31:
00189 NOT_REACHED();
00190 }
00191
00192 if (index >= _langtab_num[tab]) {
00193 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
00194 }
00195
00196 return FormatString(buffr, GetStringPtr(GB(string, 0, 16)), argv, argve, GB(string, 24, 8), last, argt);
00197 }
00198
00199 char *GetString(char *buffr, StringID string, const char *last)
00200 {
00201 memset(_parameter_type, 0, sizeof(_parameter_type));
00202 return GetStringWithArgs(buffr, string, (int64*)_decode_parameters, (int64*)endof(_decode_parameters), last, _parameter_type);
00203 }
00204
00205
00206 char *InlineString(char *buf, StringID string)
00207 {
00208 buf += Utf8Encode(buf, SCC_STRING_ID);
00209 buf += Utf8Encode(buf, string);
00210 return buf;
00211 }
00212
00213
00219 void SetDParamStr(uint n, const char *str)
00220 {
00221 SetDParam(n, (uint64)(size_t)str);
00222 }
00223
00228 void InjectDParam(uint amount)
00229 {
00230 assert((uint)amount < lengthof(_decode_parameters));
00231 memmove(_decode_parameters + amount, _decode_parameters, sizeof(_decode_parameters) - amount * sizeof(uint64));
00232 }
00233
00245 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
00246 {
00247 static const int max_digits = 20;
00248 uint64 divisor = 10000000000000000000ULL;
00249 zerofill += fractional_digits;
00250 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
00251
00252 if (number < 0) {
00253 buff += seprintf(buff, last, "-");
00254 number = -number;
00255 }
00256
00257 uint64 num = number;
00258 uint64 tot = 0;
00259 for (int i = 0; i < max_digits; i++) {
00260 if (i == max_digits - fractional_digits) {
00261 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
00262 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
00263 buff += seprintf(buff, last, "%s", decimal_separator);
00264 }
00265
00266 uint64 quot = 0;
00267 if (num >= divisor) {
00268 quot = num / divisor;
00269 num = num % divisor;
00270 }
00271 if (tot |= quot || i >= max_digits - zerofill) {
00272 buff += seprintf(buff, last, "%i", (int)quot);
00273 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
00274 }
00275
00276 divisor /= 10;
00277 }
00278
00279 *buff = '\0';
00280
00281 return buff;
00282 }
00283
00284 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
00285 {
00286 const char *separator = _settings_game.locale.digit_group_separator;
00287 if (separator == NULL) separator = _langpack->digit_group_separator;
00288 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
00289 }
00290
00291 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
00292 {
00293 return FormatNumber(buff, number, last, "");
00294 }
00295
00296 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
00297 {
00298 return FormatNumber(buff, number, last, "", count);
00299 }
00300
00301 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
00302 {
00303 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
00304 }
00305
00313 static char *FormatBytes(char *buff, int64 number, const char *last)
00314 {
00315 assert(number >= 0);
00316
00317
00318 const char * const iec_prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
00319 uint id = 1;
00320 while (number >= 1024 * 1024) {
00321 number /= 1024;
00322 id++;
00323 }
00324
00325 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
00326 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
00327
00328 if (number < 1024) {
00329 id = 0;
00330 buff += seprintf(buff, last, "%i", (int)number);
00331 } else if (number < 1024 * 10) {
00332 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
00333 } else if (number < 1024 * 100) {
00334 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
00335 } else {
00336 assert(number < 1024 * 1024);
00337 buff += seprintf(buff, last, "%i", (int)number / 1024);
00338 }
00339
00340 assert(id < lengthof(iec_prefixes));
00341 buff += seprintf(buff, last, " %sB", iec_prefixes[id]);
00342
00343 return buff;
00344 }
00345
00346 static char *FormatYmdString(char *buff, Date date, uint modifier, const char *last)
00347 {
00348 YearMonthDay ymd;
00349 ConvertDateToYMD(date, &ymd);
00350
00351 int64 args[3] = { ymd.day + STR_ORDINAL_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year };
00352 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), args, endof(args), modifier >> 24, last);
00353 }
00354
00355 static char *FormatMonthAndYear(char *buff, Date date, uint modifier, const char *last)
00356 {
00357 YearMonthDay ymd;
00358 ConvertDateToYMD(date, &ymd);
00359
00360 int64 args[2] = { STR_MONTH_JAN + ymd.month, ymd.year };
00361 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), args, endof(args), modifier >> 24, last);
00362 }
00363
00364 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
00365 {
00366 YearMonthDay ymd;
00367 ConvertDateToYMD(date, &ymd);
00368
00369 char day[3];
00370 char month[3];
00371
00372 snprintf(day, lengthof(day), "%02i", ymd.day);
00373 snprintf(month, lengthof(month), "%02i", ymd.month + 1);
00374
00375 int64 args[3] = { (int64)(size_t)day, (int64)(size_t)month, ymd.year };
00376 return FormatString(buff, GetStringPtr(str), args, endof(args), 0, last);
00377 }
00378
00379 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
00380 {
00381
00382
00383 bool negative = number < 0;
00384 const char *multiplier = "";
00385
00386 number *= spec->rate;
00387
00388
00389 if (number < 0) {
00390 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
00391 buff += Utf8Encode(buff, SCC_RED);
00392 buff = strecpy(buff, "-", last);
00393 number = -number;
00394 }
00395
00396
00397
00398
00399 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
00400
00401
00402 if (compact) {
00403
00404
00405 if (number >= 1000000000 - 500) {
00406 number = (number + 500000) / 1000000;
00407 multiplier = "M";
00408 } else if (number >= 1000000) {
00409 number = (number + 500) / 1000;
00410 multiplier = "k";
00411 }
00412 }
00413
00414 const char *separator = _settings_game.locale.digit_group_separator_currency;
00415 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
00416 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
00417 buff = FormatNumber(buff, number, last, separator);
00418 buff = strecpy(buff, multiplier, last);
00419
00420
00421
00422
00423 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
00424
00425 if (negative) {
00426 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
00427 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
00428 *buff = '\0';
00429 }
00430
00431 return buff;
00432 }
00433
00440 static int DeterminePluralForm(int64 count, int plural_form)
00441 {
00442
00443 uint64 n = abs(count);
00444
00445 switch (plural_form) {
00446 default:
00447 NOT_REACHED();
00448
00449
00450
00451
00452
00453 case 0:
00454 return n != 1;
00455
00456
00457
00458
00459 case 1:
00460 return 0;
00461
00462
00463
00464
00465 case 2:
00466 return n > 1;
00467
00468
00469
00470
00471 case 3:
00472 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
00473
00474
00475
00476
00477 case 4:
00478 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
00479
00480
00481
00482
00483 case 5:
00484 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00485
00486
00487
00488
00489 case 6:
00490 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00491
00492
00493
00494
00495 case 7:
00496 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00497
00498
00499
00500
00501 case 8:
00502 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
00503
00504
00505
00506
00507 case 9:
00508 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
00509
00510
00511
00512
00513 case 10:
00514 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
00515
00516
00517
00518
00519
00520
00521
00522 case 11:
00523 switch (n % 10) {
00524 case 0:
00525 case 1:
00526 case 3:
00527 case 6:
00528 case 7:
00529 case 8:
00530 return 0;
00531
00532 case 2:
00533 case 4:
00534 case 5:
00535 case 9:
00536 return 1;
00537
00538 default:
00539 NOT_REACHED();
00540 }
00541
00542
00543
00544
00545 case 12:
00546 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
00547 }
00548 }
00549
00550 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
00551 {
00552
00553 uint n = (byte)*b++;
00554 uint pos, i, mypos = 0;
00555
00556 for (i = pos = 0; i != n; i++) {
00557 uint len = (byte)*b++;
00558 if (i == form) mypos = pos;
00559 pos += len;
00560 }
00561
00562 *dst += seprintf(*dst, last, "%s", b + mypos);
00563 return b + pos;
00564 }
00565
00567 struct UnitConversion {
00568 int multiplier;
00569 int shift;
00570
00577 int64 ToDisplay(int64 input, bool round = true) const
00578 {
00579 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
00580 }
00581
00588 int64 FromDisplay(int64 input, bool round = true) const
00589 {
00590 return ((input << this->shift) + (round ? this->multiplier / 2 : 0)) / this->multiplier;
00591 }
00592 };
00593
00594 struct Units {
00595 UnitConversion c_velocity;
00596 StringID velocity;
00597 UnitConversion c_power;
00598 StringID power;
00599 UnitConversion c_weight;
00600 StringID s_weight;
00601 StringID l_weight;
00602 UnitConversion c_volume;
00603 StringID s_volume;
00604 StringID l_volume;
00605 UnitConversion c_force;
00606 StringID force;
00607 UnitConversion c_height;
00608 StringID height;
00609 };
00610
00611
00612 static const Units _units[] = {
00613 {
00614 { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL,
00615 { 1, 0}, STR_UNITS_POWER_IMPERIAL,
00616 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00617 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00618 { 1, 0}, STR_UNITS_FORCE_SI,
00619 { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL,
00620 },
00621 {
00622 { 103, 6}, STR_UNITS_VELOCITY_METRIC,
00623 {4153, 12}, STR_UNITS_POWER_METRIC,
00624 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00625 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00626 { 1, 0}, STR_UNITS_FORCE_SI,
00627 { 1, 0}, STR_UNITS_HEIGHT_SI,
00628 },
00629 {
00630 {1831, 12}, STR_UNITS_VELOCITY_SI,
00631 {6109, 13}, STR_UNITS_POWER_SI,
00632 {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00633 { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00634 { 1, 0}, STR_UNITS_FORCE_SI,
00635 { 1, 0}, STR_UNITS_HEIGHT_SI,
00636 },
00637 };
00638
00644 uint ConvertSpeedToDisplaySpeed(uint speed)
00645 {
00646
00647
00648
00649 return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed, false);
00650 }
00651
00657 uint ConvertDisplaySpeedToSpeed(uint speed)
00658 {
00659 return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed);
00660 }
00661
00673 static char *FormatString(char *buff, const char *str_arg, int64 *argv, const int64 *argve, uint casei, const char *last, WChar *argt, bool dry_run)
00674 {
00675
00676 if (argt == NULL) dry_run = true;
00677 if (UsingNewGRFTextStack() && !dry_run) {
00678
00679
00680
00681
00682
00683
00684 struct TextRefStack *backup = CreateTextRefStackBackup();
00685 FormatString(buff, str_arg, argv, argve, casei, last, argt, true);
00686 RestoreTextRefStackBackup(backup);
00687 } else if (!dry_run) {
00688 FormatString(buff, str_arg, argv, argve, casei, last, argt, true);
00689 }
00690 WChar b;
00691 int64 *argv_orig = argv;
00692 WChar *argt_orig = argt;
00693 uint modifier = 0;
00694 char *buf_start = buff;
00695 std::stack<const char *> str_stack;
00696 str_stack.push(str_arg);
00697
00698 while (true) {
00699 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
00700 str_stack.pop();
00701 }
00702 if (str_stack.empty()) break;
00703 const char *&str = str_stack.top();
00704
00705 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00706
00707
00708 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, argv);
00709 if (b == 0) continue;
00710 }
00711
00712 switch (b) {
00713 case SCC_NEWGRF_STRINL: {
00714 StringID substr = Utf8Consume(&str);
00715 str_stack.push(GetStringPtr(substr));
00716 break;
00717 }
00718
00719 case SCC_NEWGRF_PRINT_STRING_ID: {
00720 StringID substr = GetInt32(&argv, argve, &argt, SCC_NEWGRF_PRINT_STRING_ID);
00721 str_stack.push(GetStringPtr(substr));
00722 break;
00723 }
00724
00725
00726 case SCC_SETX:
00727 if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00728 buff += Utf8Encode(buff, SCC_SETX);
00729 *buff++ = *str++;
00730 }
00731 break;
00732
00733 case SCC_SETXY:
00734 if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00735 buff += Utf8Encode(buff, SCC_SETXY);
00736 *buff++ = *str++;
00737 *buff++ = *str++;
00738 }
00739 break;
00740
00741 case SCC_STRING_ID:
00742 buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, argve, last, argt);
00743 break;
00744
00745 case SCC_RAW_STRING_POINTER: {
00746 const char *str = (const char*)(size_t)GetInt64(&argv, argve, &argt);
00747 buff = FormatString(buff, str, argv, argve, casei, last, argt);
00748 break;
00749 }
00750
00751 case SCC_DATE_LONG:
00752 buff = FormatYmdString(buff, GetInt32(&argv, argve, &argt, SCC_DATE_LONG), modifier, last);
00753 break;
00754
00755 case SCC_DATE_SHORT:
00756 buff = FormatMonthAndYear(buff, GetInt32(&argv, argve, &argt, SCC_DATE_SHORT), modifier, last);
00757 break;
00758
00759 case SCC_VELOCITY: {
00760 int64 args[1];
00761 assert(_settings_game.locale.units < lengthof(_units));
00762 args[0] = ConvertSpeedToDisplaySpeed(GetInt64(&argv, argve, &argt, SCC_VELOCITY) * 10 / 16);
00763 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].velocity), args, endof(args), modifier >> 24, last);
00764 modifier = 0;
00765 break;
00766 }
00767
00768 case SCC_HEIGHT: {
00769 int64 args[1] = {_units[_settings_game.locale.units].c_height.ToDisplay(GetInt64(&argv, argve, &argt))};
00770 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].height), args, endof(args), modifier >> 24, last);
00771 modifier = 0;
00772 break;
00773 }
00774
00775 case SCC_CURRENCY_COMPACT:
00776 buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv, argve, &argt), true, last);
00777 break;
00778
00779 case SCC_REVISION:
00780 buff = strecpy(buff, _openttd_revision, last);
00781 break;
00782
00783 case SCC_CARGO_SHORT: {
00784
00785
00786
00787 StringID cargo_str = CargoSpec::Get(GetInt32(&argv, argve, &argt, SCC_CARGO_SHORT))->units_volume;
00788 switch (cargo_str) {
00789 case STR_TONS: {
00790 int64 args[1];
00791 assert(_settings_game.locale.units < lengthof(_units));
00792 args[0] = _units[_settings_game.locale.units].c_weight.ToDisplay(GetInt64(&argv, argve, &argt));
00793 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), args, endof(args), modifier >> 24, last);
00794 modifier = 0;
00795 break;
00796 }
00797
00798 case STR_LITERS: {
00799 int64 args[1];
00800 assert(_settings_game.locale.units < lengthof(_units));
00801 args[0] = _units[_settings_game.locale.units].c_volume.ToDisplay(GetInt64(&argv, argve, &argt));
00802 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), args, endof(args), modifier >> 24, last);
00803 modifier = 0;
00804 break;
00805 }
00806
00807 default:
00808 buff = GetStringWithArgs(buff, cargo_str, argv++, argve, last, argt++);
00809 break;
00810 }
00811 break;
00812 }
00813
00814 case SCC_STRING1: {
00815
00816 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING1);
00817 WChar *orig_argt = argt;
00818 int64 *args = GetArgvPtr(&argv, 1, argve, &argt);
00819 buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00820 modifier = 0;
00821 break;
00822 }
00823
00824 case SCC_STRING2: {
00825
00826 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING2);
00827 WChar *orig_argt = argt;
00828 int64 *args = GetArgvPtr(&argv, 2, argve, &argt);
00829 buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00830 modifier = 0;
00831 break;
00832 }
00833
00834 case SCC_STRING3: {
00835
00836 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING3);
00837 WChar *orig_argt = argt;
00838 int64 *args = GetArgvPtr(&argv, 3, argve, &argt);
00839 buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00840 modifier = 0;
00841 break;
00842 }
00843
00844 case SCC_STRING4: {
00845
00846 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING4);
00847 WChar *orig_argt = argt;
00848 int64 *args = GetArgvPtr(&argv, 4, argve, &argt);
00849 buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00850 modifier = 0;
00851 break;
00852 }
00853
00854 case SCC_STRING5: {
00855
00856 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING5);
00857 WChar *orig_argt = argt;
00858 int64 *args = GetArgvPtr(&argv, 5, argve, &argt);
00859 buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00860 modifier = 0;
00861 break;
00862 }
00863
00864 case SCC_STATION_FEATURES: {
00865 buff = StationGetSpecialString(buff, GetInt32(&argv, argve, &argt, SCC_STATION_FEATURES), last);
00866 break;
00867 }
00868
00869 case SCC_INDUSTRY_NAME: {
00870 const Industry *i = Industry::Get(GetInt32(&argv, argve, &argt, SCC_INDUSTRY_NAME));
00871 int64 args[2];
00872
00873
00874 assert(i != NULL);
00875
00876
00877 args[0] = i->town->index;
00878 args[1] = GetIndustrySpec(i->type)->name;
00879 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), args, endof(args), modifier >> 24, last);
00880 modifier = 0;
00881 break;
00882 }
00883
00884 case SCC_VOLUME: {
00885 int64 args[1];
00886 assert(_settings_game.locale.units < lengthof(_units));
00887 args[0] = _units[_settings_game.locale.units].c_volume.ToDisplay(GetInt64(&argv, argve, &argt, SCC_VOLUME));
00888 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), args, endof(args), modifier >> 24, last);
00889 modifier = 0;
00890 break;
00891 }
00892
00893 case SCC_GENDER_LIST: {
00894
00895 byte offset = (byte)*str++;
00896 assert(argv_orig + offset < argve);
00897 int gender = 0;
00898 if (!dry_run && argt != NULL && argt_orig[offset] != 0) {
00899
00900
00901
00902 char input[4 + 1];
00903 char *p = input + Utf8Encode(input, argt_orig[offset]);
00904 *p = '\0';
00905
00906
00907 char buf[256];
00908 bool old_kgd = _keep_gender_data;
00909 _keep_gender_data = true;
00910 p = FormatString(buf, input, argv_orig + offset, argve, 0, lastof(buf));
00911 _keep_gender_data = old_kgd;
00912 *p = '\0';
00913
00914
00915 const char *s = buf;
00916 WChar c = Utf8Consume(&s);
00917
00918 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00919 }
00920 str = ParseStringChoice(str, gender, &buff, last);
00921 break;
00922 }
00923
00924 case SCC_DATE_TINY: {
00925 buff = FormatTinyOrISODate(buff, GetInt32(&argv, argve, &argt, SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
00926 break;
00927 }
00928
00929 case SCC_DATE_ISO: {
00930 buff = FormatTinyOrISODate(buff, GetInt32(&argv, argve, &argt), STR_FORMAT_DATE_ISO, last);
00931 break;
00932 }
00933
00934 case SCC_CARGO: {
00935
00936 CargoID cargo = GetInt32(&argv, argve, &argt, SCC_CARGO);
00937 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
00938 buff = GetStringWithArgs(buff, cargo_str, argv++, argve, last);
00939 break;
00940 }
00941
00942 case SCC_POWER: {
00943 int64 args[1];
00944 assert(_settings_game.locale.units < lengthof(_units));
00945 args[0] = _units[_settings_game.locale.units].c_power.ToDisplay(GetInt64(&argv, argve, &argt));
00946 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].power), args, endof(args), modifier >> 24, last);
00947 modifier = 0;
00948 break;
00949 }
00950
00951 case SCC_VOLUME_SHORT: {
00952 int64 args[1];
00953 assert(_settings_game.locale.units < lengthof(_units));
00954 args[0] = _units[_settings_game.locale.units].c_volume.ToDisplay(GetInt64(&argv, argve, &argt));
00955 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_volume), args, endof(args), modifier >> 24, last);
00956 modifier = 0;
00957 break;
00958 }
00959
00960 case SCC_WEIGHT: {
00961 int64 args[1];
00962 assert(_settings_game.locale.units < lengthof(_units));
00963 args[0] = _units[_settings_game.locale.units].c_weight.ToDisplay(GetInt64(&argv, argve, &argt, SCC_WEIGHT));
00964 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), args, endof(args), modifier >> 24, last);
00965 modifier = 0;
00966 break;
00967 }
00968
00969 case SCC_WEIGHT_SHORT: {
00970 int64 args[1];
00971 assert(_settings_game.locale.units < lengthof(_units));
00972 args[0] = _units[_settings_game.locale.units].c_weight.ToDisplay(GetInt64(&argv, argve, &argt));
00973 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_weight), args, endof(args), modifier >> 24, last);
00974 modifier = 0;
00975 break;
00976 }
00977
00978 case SCC_FORCE: {
00979 int64 args[1];
00980 assert(_settings_game.locale.units < lengthof(_units));
00981 args[0] = _units[_settings_game.locale.units].c_force.ToDisplay(GetInt64(&argv, argve, &argt));
00982 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].force), args, endof(args), modifier >> 24, last);
00983 modifier = 0;
00984 break;
00985 }
00986
00987
00988
00989 case SCC_GENDER_INDEX:
00990 if (_keep_gender_data) {
00991 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
00992 *buff++ = *str++;
00993 } else {
00994 str++;
00995 }
00996 break;
00997
00998 case SCC_STRING: {
00999 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING);
01000
01001
01002
01003 buff = GetStringWithArgs(buff, str, argv, argve, last);
01004 modifier = 0;
01005 break;
01006 }
01007
01008 case SCC_COMMA:
01009 buff = FormatCommaNumber(buff, GetInt64(&argv, argve, &argt, SCC_COMMA), last);
01010 break;
01011
01012 case SCC_DECIMAL: {
01013 int64 number = GetInt64(&argv, argve, &argt, SCC_DECIMAL);
01014 int digits = GetInt32(&argv, argve, &argt, SCC_DECIMAL);
01015 buff = FormatCommaNumber(buff, number, last, digits);
01016 break;
01017 }
01018
01019 case SCC_ARG_INDEX: {
01020 byte offset = (byte)*str++;
01021 argv = argv_orig + offset;
01022 if (argt_orig != NULL) argt = argt_orig + offset;
01023 break;
01024 }
01025
01026 case SCC_PLURAL_LIST: {
01027 int plural_form = *str++;
01028 byte idx = *str++;
01029 assert(argv_orig + idx < argve);
01030 int64 v = argv_orig[idx];
01031 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
01032 break;
01033 }
01034
01035 case SCC_NUM:
01036 buff = FormatNoCommaNumber(buff, GetInt64(&argv, argve, &argt, SCC_NUM), last);
01037 break;
01038
01039 case SCC_ZEROFILL_NUM: {
01040 int64 num = GetInt64(&argv, argve, &argt);
01041 buff = FormatZerofillNumber(buff, num, GetInt64(&argv, argve, &argt), last);
01042 break;
01043 }
01044
01045 case SCC_HEX:
01046 buff = FormatHexNumber(buff, (uint64)GetInt64(&argv, argve, &argt, SCC_HEX), last);
01047 break;
01048
01049 case SCC_BYTES:
01050 buff = FormatBytes(buff, GetInt64(&argv, argve, &argt), last);
01051 break;
01052
01053 case SCC_CURRENCY:
01054 buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv, argve, &argt, SCC_CURRENCY), false, last);
01055 break;
01056
01057 case SCC_WAYPOINT_NAME: {
01058 Waypoint *wp = Waypoint::Get(GetInt32(&argv, argve, &argt, SCC_WAYPOINT_NAME));
01059
01060 assert(wp != NULL);
01061
01062 if (wp->name != NULL) {
01063 buff = strecpy(buff, wp->name, last);
01064 } else {
01065 int64 args[2];
01066 args[0] = wp->town->index;
01067 args[1] = wp->town_cn + 1;
01068 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
01069 if (wp->town_cn != 0) str++;
01070 buff = GetStringWithArgs(buff, str, args, endof(args), last);
01071 }
01072 break;
01073 }
01074
01075 case SCC_STATION_NAME: {
01076 StationID sid = GetInt32(&argv, argve, &argt, SCC_STATION_NAME);
01077 const Station *st = Station::GetIfValid(sid);
01078
01079 if (st == NULL) {
01080
01081
01082
01083 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, NULL, NULL, last);
01084 break;
01085 }
01086
01087 if (st->name != NULL) {
01088 buff = strecpy(buff, st->name, last);
01089 } else {
01090 StringID str = st->string_id;
01091 if (st->indtype != IT_INVALID) {
01092
01093 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01094
01095
01096
01097
01098 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
01099 str = indsp->station_name;
01100 }
01101 }
01102
01103 int64 args[3];
01104 args[0] = STR_TOWN_NAME;
01105 args[1] = st->town->index;
01106 args[2] = st->index;
01107 buff = GetStringWithArgs(buff, str, args, endof(args), last);
01108 }
01109 break;
01110 }
01111
01112 case SCC_DEPOT_NAME: {
01113 VehicleType vt = (VehicleType)GetInt32(&argv, argve, &argt, SCC_DEPOT_NAME);
01114 if (vt == VEH_AIRCRAFT) {
01115 int64 args[] = { GetInt32(&argv, argve, &argt) };
01116 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, args, endof(args), last);
01117 break;
01118 }
01119
01120 const Depot *d = Depot::Get(GetInt32(&argv, argve, &argt));
01121 if (d->name != NULL) {
01122 buff = strecpy(buff, d->name, last);
01123 } else {
01124 int64 args[] = { d->town->index, d->town_cn + 1 };
01125 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), args, endof(args), last);
01126 }
01127 break;
01128 }
01129
01130 case SCC_TOWN_NAME: {
01131 const Town *t = Town::Get(GetInt32(&argv, argve, &argt, SCC_TOWN_NAME));
01132
01133 assert(t != NULL);
01134
01135 if (t->name != NULL) {
01136 buff = strecpy(buff, t->name, last);
01137 } else {
01138 buff = GetTownName(buff, t, last);
01139 }
01140 break;
01141 }
01142
01143 case SCC_GROUP_NAME: {
01144 const Group *g = Group::Get(GetInt32(&argv, argve, &argt));
01145
01146 assert(g != NULL);
01147
01148 if (g->name != NULL) {
01149 buff = strecpy(buff, g->name, last);
01150 } else {
01151 int64 args[1];
01152
01153 args[0] = g->index;
01154 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, args, endof(args), last);
01155 }
01156 break;
01157 }
01158
01159 case SCC_ENGINE_NAME: {
01160 EngineID engine = (EngineID)GetInt32(&argv, argve, &argt, SCC_ENGINE_NAME);
01161 const Engine *e = Engine::Get(engine);
01162
01163 assert(e != NULL);
01164
01165 if (e->name != NULL && e->IsEnabled()) {
01166 buff = strecpy(buff, e->name, last);
01167 } else {
01168 buff = GetStringWithArgs(buff, e->info.string_id, NULL, NULL, last);
01169 }
01170 break;
01171 }
01172
01173 case SCC_VEHICLE_NAME: {
01174 const Vehicle *v = Vehicle::Get(GetInt32(&argv, argve, &argt, SCC_VEHICLE_NAME));
01175
01176 assert(v != NULL);
01177
01178 if (v->name != NULL) {
01179 buff = strecpy(buff, v->name, last);
01180 } else {
01181 int64 args[1];
01182 args[0] = v->unitnumber;
01183
01184 StringID str;
01185 switch (v->type) {
01186 default: NOT_REACHED();
01187 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
01188 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
01189 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
01190 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01191 }
01192
01193 buff = GetStringWithArgs(buff, str, args, endof(args), last);
01194 }
01195 break;
01196 }
01197
01198 case SCC_SIGN_NAME: {
01199 const Sign *si = Sign::Get(GetInt32(&argv, argve, &argt));
01200 if (si->name != NULL) {
01201 buff = strecpy(buff, si->name, last);
01202 } else {
01203 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, NULL, NULL, last);
01204 }
01205 break;
01206 }
01207
01208 case SCC_COMPANY_NAME: {
01209 const Company *c = Company::Get((CompanyID)GetInt32(&argv, argve, &argt));
01210
01211 if (c->name != NULL) {
01212 buff = strecpy(buff, c->name, last);
01213 } else {
01214 int64 args[1];
01215 args[0] = c->name_2;
01216 buff = GetStringWithArgs(buff, c->name_1, args, endof(args), last);
01217 }
01218 break;
01219 }
01220
01221 case SCC_COMPANY_NUM: {
01222 CompanyID company = (CompanyID)GetInt32(&argv, argve, &argt);
01223
01224
01225 if (Company::IsValidHumanID(company)) {
01226 int64 args[1];
01227 args[0] = company + 1;
01228 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, args, endof(args), last);
01229 }
01230 break;
01231 }
01232
01233 case SCC_PRESIDENT_NAME: {
01234 const Company *c = Company::Get((CompanyID)GetInt32(&argv, argve, &argt, SCC_PRESIDENT_NAME));
01235
01236 if (c->president_name != NULL) {
01237 buff = strecpy(buff, c->president_name, last);
01238 } else {
01239 int64 args[1];
01240 args[0] = c->president_name_2;
01241 buff = GetStringWithArgs(buff, c->president_name_1, args, endof(args), last);
01242 }
01243 break;
01244 }
01245
01246 case SCC_SETCASE: {
01247
01248
01249 modifier = (byte)*str++ << 24;
01250 break;
01251 }
01252
01253 case SCC_SWITCH_CASE: {
01254
01255
01256 uint num = (byte)*str++;
01257 while (num) {
01258 if ((byte)str[0] == casei) {
01259
01260 str += 3;
01261 break;
01262 }
01263
01264 str += 3 + (str[1] << 8) + str[2];
01265 num--;
01266 }
01267 break;
01268 }
01269
01270 default:
01271 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01272 break;
01273 }
01274 }
01275 *buff = '\0';
01276 return buff;
01277 }
01278
01279
01280 static char *StationGetSpecialString(char *buff, int x, const char *last)
01281 {
01282 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01283 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01284 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01285 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01286 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01287 *buff = '\0';
01288 return buff;
01289 }
01290
01291 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01292 {
01293 return GenerateTownNameString(buff, last, ind, seed);
01294 }
01295
01296 static const char * const _silly_company_names[] = {
01297 "Bloggs Brothers",
01298 "Tiny Transport Ltd.",
01299 "Express Travel",
01300 "Comfy-Coach & Co.",
01301 "Crush & Bump Ltd.",
01302 "Broken & Late Ltd.",
01303 "Sam Speedy & Son",
01304 "Supersonic Travel",
01305 "Mike's Motors",
01306 "Lightning International",
01307 "Pannik & Loozit Ltd.",
01308 "Inter-City Transport",
01309 "Getout & Pushit Ltd."
01310 };
01311
01312 static const char * const _surname_list[] = {
01313 "Adams",
01314 "Allan",
01315 "Baker",
01316 "Bigwig",
01317 "Black",
01318 "Bloggs",
01319 "Brown",
01320 "Campbell",
01321 "Gordon",
01322 "Hamilton",
01323 "Hawthorn",
01324 "Higgins",
01325 "Green",
01326 "Gribble",
01327 "Jones",
01328 "McAlpine",
01329 "MacDonald",
01330 "McIntosh",
01331 "Muir",
01332 "Murphy",
01333 "Nelson",
01334 "O'Donnell",
01335 "Parker",
01336 "Phillips",
01337 "Pilkington",
01338 "Quigley",
01339 "Sharkey",
01340 "Thomson",
01341 "Watkins"
01342 };
01343
01344 static const char * const _silly_surname_list[] = {
01345 "Grumpy",
01346 "Dozy",
01347 "Speedy",
01348 "Nosey",
01349 "Dribble",
01350 "Mushroom",
01351 "Cabbage",
01352 "Sniffle",
01353 "Fishy",
01354 "Swindle",
01355 "Sneaky",
01356 "Nutkins"
01357 };
01358
01359 static const char _initial_name_letters[] = {
01360 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01361 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01362 };
01363
01364 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01365 {
01366 const char * const *base;
01367 uint num;
01368
01369 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01370 base = _silly_surname_list;
01371 num = lengthof(_silly_surname_list);
01372 } else {
01373 base = _surname_list;
01374 num = lengthof(_surname_list);
01375 }
01376
01377 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01378 buff = strecpy(buff, " & Co.", last);
01379
01380 return buff;
01381 }
01382
01383 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01384 {
01385 char initial[] = "?. ";
01386 const char * const *base;
01387 uint num;
01388 uint i;
01389
01390 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01391 buff = strecpy(buff, initial, last);
01392
01393 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01394 if (i < sizeof(_initial_name_letters)) {
01395 initial[0] = _initial_name_letters[i];
01396 buff = strecpy(buff, initial, last);
01397 }
01398
01399 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01400 base = _silly_surname_list;
01401 num = lengthof(_silly_surname_list);
01402 } else {
01403 base = _surname_list;
01404 num = lengthof(_surname_list);
01405 }
01406
01407 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01408
01409 return buff;
01410 }
01411
01412 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const int64 *argve, const char *last, WChar *argt)
01413 {
01414 switch (ind) {
01415 case 1:
01416 return strecpy(buff, _silly_company_names[GetInt32(&argv, argve, &argt) & 0xFFFF], last);
01417
01418 case 2:
01419 return GenAndCoName(buff, GetInt32(&argv, argve, &argt), last);
01420
01421 case 3:
01422 return GenPresidentName(buff, GetInt32(&argv, argve, &argt), last);
01423 }
01424
01425
01426 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01427 buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv, argve, &argt), last);
01428 return strecpy(buff, " Transport", last);
01429 }
01430
01431
01432 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01433 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01434 return strecpy(buff,
01435 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
01436 }
01437
01438
01439 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01440 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01441 buff += seprintf(
01442 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01443 );
01444 return buff;
01445 }
01446
01447
01448 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01449 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01450 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01451 }
01452
01453 NOT_REACHED();
01454 }
01455
01456 #ifdef ENABLE_NETWORK
01457 extern void SortNetworkLanguages();
01458 #else
01459 static inline void SortNetworkLanguages() {}
01460 #endif
01461
01466 bool LanguagePackHeader::IsValid() const
01467 {
01468 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
01469 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
01470 this->plural_form < LANGUAGE_MAX_PLURAL &&
01471 this->text_dir <= 1 &&
01472 this->newgrflangid < MAX_LANG &&
01473 this->num_genders < MAX_NUM_GENDERS &&
01474 this->num_cases < MAX_NUM_CASES &&
01475 StrValid(this->name, lastof(this->name)) &&
01476 StrValid(this->own_name, lastof(this->own_name)) &&
01477 StrValid(this->isocode, lastof(this->isocode)) &&
01478 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
01479 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
01480 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
01481 }
01482
01483 bool ReadLanguagePack(const LanguageMetadata *lang)
01484 {
01485
01486 size_t len;
01487 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
01488 if (lang_pack == NULL) return false;
01489
01490
01491 const char *end = (char *)lang_pack + len + 1;
01492
01493
01494 if (end <= lang_pack->data || !lang_pack->IsValid()) {
01495 free(lang_pack);
01496 return false;
01497 }
01498
01499 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01500 for (uint i = 0; i < 32; i++) {
01501 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01502 }
01503 #endif
01504
01505 uint count = 0;
01506 for (uint i = 0; i < 32; i++) {
01507 uint num = lang_pack->offsets[i];
01508 _langtab_start[i] = count;
01509 _langtab_num[i] = num;
01510 count += num;
01511 }
01512
01513
01514 char **langpack_offs = MallocT<char *>(count);
01515
01516
01517 char *s = lang_pack->data;
01518 len = (byte)*s++;
01519 for (uint i = 0; i < count; i++) {
01520 if (s + len >= end) {
01521 free(lang_pack);
01522 free(langpack_offs);
01523 return false;
01524 }
01525 if (len >= 0xC0) {
01526 len = ((len & 0x3F) << 8) + (byte)*s++;
01527 if (s + len >= end) {
01528 free(lang_pack);
01529 free(langpack_offs);
01530 return false;
01531 }
01532 }
01533 langpack_offs[i] = s;
01534 s += len;
01535 len = (byte)*s;
01536 *s++ = '\0';
01537 }
01538
01539 free(_langpack);
01540 _langpack = lang_pack;
01541
01542 free(_langpack_offs);
01543 _langpack_offs = langpack_offs;
01544
01545 _current_language = lang;
01546 _current_text_dir = (TextDirection)_current_language->text_dir;
01547 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
01548 strecpy(_config_language_file, c_file, lastof(_config_language_file));
01549 SetCurrentGrfLangID(_current_language->newgrflangid);
01550
01551 #ifdef WITH_ICU
01552
01553 if (_current_collator != NULL) {
01554 delete _current_collator;
01555 _current_collator = NULL;
01556 }
01557
01558
01559 UErrorCode status = U_ZERO_ERROR;
01560 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01561
01562 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01563
01564 if (U_FAILURE(status)) {
01565 delete _current_collator;
01566 _current_collator = NULL;
01567 }
01568 #endif
01569
01570
01571 InitializeSortedCargoSpecs();
01572 SortIndustryTypes();
01573 BuildIndustriesLegend();
01574 SortNetworkLanguages();
01575 InvalidateWindowClassesData(WC_BUILD_VEHICLE);
01576 InvalidateWindowClassesData(WC_TRAINS_LIST);
01577 InvalidateWindowClassesData(WC_ROADVEH_LIST);
01578 InvalidateWindowClassesData(WC_SHIPS_LIST);
01579 InvalidateWindowClassesData(WC_AIRCRAFT_LIST);
01580 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY);
01581 InvalidateWindowClassesData(WC_STATION_LIST);
01582
01583 return true;
01584 }
01585
01586
01587
01588 #if !(defined(WIN32) || defined(__APPLE__))
01589
01597 const char *GetCurrentLocale(const char *param)
01598 {
01599 const char *env;
01600
01601 env = getenv("LANGUAGE");
01602 if (env != NULL) return env;
01603
01604 env = getenv("LC_ALL");
01605 if (env != NULL) return env;
01606
01607 if (param != NULL) {
01608 env = getenv(param);
01609 if (env != NULL) return env;
01610 }
01611
01612 return getenv("LANG");
01613 }
01614 #else
01615 const char *GetCurrentLocale(const char *param);
01616 #endif
01617
01618 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01619 {
01620 char stra[512];
01621 char strb[512];
01622 GetString(stra, *a, lastof(stra));
01623 GetString(strb, *b, lastof(strb));
01624
01625 return strcmp(stra, strb);
01626 }
01627
01633 const LanguageMetadata *GetLanguage(byte newgrflangid)
01634 {
01635 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
01636 if (newgrflangid == lang->newgrflangid) return lang;
01637 }
01638
01639 return NULL;
01640 }
01641
01648 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
01649 {
01650 FILE *f = fopen(file, "rb");
01651 if (f == NULL) return false;
01652
01653 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01654 fclose(f);
01655
01656 bool ret = read == 1 && hdr->IsValid();
01657
01658
01659 if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01660 return ret;
01661 }
01662
01667 static void GetLanguageList(const char *path)
01668 {
01669 DIR *dir = ttd_opendir(path);
01670 if (dir != NULL) {
01671 struct dirent *dirent;
01672 while ((dirent = readdir(dir)) != NULL) {
01673 const char *d_name = FS2OTTD(dirent->d_name);
01674 const char *extension = strrchr(d_name, '.');
01675
01676
01677 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01678
01679 LanguageMetadata lmd;
01680 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
01681
01682
01683 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
01684 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
01685 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
01686 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
01687 } else {
01688 *_languages.Append() = lmd;
01689 }
01690 }
01691 closedir(dir);
01692 }
01693 }
01694
01699 void InitializeLanguagePacks()
01700 {
01701 Searchpath sp;
01702
01703 FOR_ALL_SEARCHPATHS(sp) {
01704 char path[MAX_PATH];
01705 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01706 GetLanguageList(path);
01707 }
01708 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
01709
01710
01711 const char *lang = GetCurrentLocale("LC_MESSAGES");
01712 if (lang == NULL) lang = "en_GB";
01713
01714 const LanguageMetadata *chosen_language = NULL;
01715 const LanguageMetadata *language_fallback = NULL;
01716 const LanguageMetadata *en_GB_fallback = _languages.Begin();
01717
01718
01719 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01720
01721
01722
01723 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
01724 if (strcmp(lang_file, _config_language_file) == 0) {
01725 chosen_language = lng;
01726 break;
01727 }
01728
01729 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
01730 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
01731 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
01732 }
01733
01734
01735
01736 if (chosen_language == NULL) {
01737 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
01738 }
01739
01740 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
01741 }
01742
01747 const char *GetCurrentLanguageIsoCode()
01748 {
01749 return _langpack->isocode;
01750 }
01751
01758 static bool FindMissingGlyphs(const char **str)
01759 {
01760 #ifdef WITH_FREETYPE
01761 UninitFreeType();
01762 InitFreeType();
01763 #endif
01764 const Sprite *question_mark[FS_END];
01765 FontSize size;
01766
01767 for (size = FS_BEGIN; size < FS_END; size++) {
01768 question_mark[size] = GetGlyph(size, '?');
01769 }
01770
01771 for (uint i = 0; i != 32; i++) {
01772 for (uint j = 0; j < _langtab_num[i]; j++) {
01773 size = FS_NORMAL;
01774 const char *text = _langpack_offs[_langtab_start[i] + j];
01775 if (str != NULL) *str = text;
01776 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
01777 if (c == SCC_SETX) {
01778
01779
01780
01781 text++;
01782 } else if (c == SCC_SETXY) {
01783 text += 2;
01784 } else if (c == SCC_TINYFONT) {
01785 size = FS_SMALL;
01786 } else if (c == SCC_BIGFONT) {
01787 size = FS_LARGE;
01788 } else if (IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
01789
01790 return true;
01791 }
01792 }
01793 }
01794 }
01795 return false;
01796 }
01797
01808 void CheckForMissingGlyphsInLoadedLanguagePack()
01809 {
01810 bool bad_font = FindMissingGlyphs(NULL);
01811 #ifdef WITH_FREETYPE
01812 if (bad_font) {
01813
01814
01815 FreeTypeSettings backup;
01816 memcpy(&backup, &_freetype, sizeof(backup));
01817
01818 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, &FindMissingGlyphs);
01819
01820 memcpy(&_freetype, &backup, sizeof(backup));
01821
01822 if (bad_font) {
01823
01824
01825
01826 UninitFreeType();
01827 InitFreeType();
01828 }
01829 }
01830 #endif
01831
01832 if (bad_font) {
01833
01834
01835
01836
01837
01838 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.");
01839 Utf8Encode(err_str, SCC_YELLOW);
01840 SetDParamStr(0, err_str);
01841 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
01842
01843
01844 LoadStringWidthTable();
01845 return;
01846 }
01847
01848
01849 LoadStringWidthTable();
01850
01851 #if !defined(WITH_ICU)
01852
01853
01854
01855
01856
01857
01858
01859
01860
01861
01862
01863
01864
01865 if (_current_text_dir != TD_LTR) {
01866 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01867 Utf8Encode(err_str, SCC_YELLOW);
01868 SetDParamStr(0, err_str);
01869 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
01870 }
01871 #endif
01872 }