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