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
00546 struct Units {
00547 int s_m;
00548 int s_s;
00549 StringID velocity;
00550 int p_m;
00551 int p_s;
00552 StringID power;
00553 int w_m;
00554 int w_s;
00555 StringID s_weight;
00556 StringID l_weight;
00557 int v_m;
00558 int v_s;
00559 StringID s_volume;
00560 StringID l_volume;
00561 int f_m;
00562 int f_s;
00563 StringID force;
00564 int h_m;
00565 int h_s;
00566 StringID height;
00567 };
00568
00569
00570 static const Units units[] = {
00571 {
00572 1, 0, STR_UNITS_VELOCITY_IMPERIAL,
00573 1, 0, STR_UNITS_POWER_IMPERIAL,
00574 1, 0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00575 1000, 0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00576 1, 0, STR_UNITS_FORCE_SI,
00577 3, 0, STR_UNITS_HEIGHT_IMPERIAL,
00578 },
00579 {
00580 103, 6, STR_UNITS_VELOCITY_METRIC,
00581 1, 0, STR_UNITS_POWER_METRIC,
00582 1, 0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00583 1000, 0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00584 1, 0, STR_UNITS_FORCE_SI,
00585 1, 0, STR_UNITS_HEIGHT_SI,
00586 },
00587 {
00588 1831, 12, STR_UNITS_VELOCITY_SI,
00589 764, 10, STR_UNITS_POWER_SI,
00590 1000, 0, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00591 1, 0, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00592 1, 0, STR_UNITS_FORCE_SI,
00593 1, 0, STR_UNITS_HEIGHT_SI,
00594 },
00595 };
00596
00602 uint ConvertSpeedToDisplaySpeed(uint speed)
00603 {
00604 return (speed * units[_settings_game.locale.units].s_m) >> units[_settings_game.locale.units].s_s;
00605 }
00606
00612 uint ConvertDisplaySpeedToSpeed(uint speed)
00613 {
00614 return ((speed << units[_settings_game.locale.units].s_s) + units[_settings_game.locale.units].s_m / 2) / units[_settings_game.locale.units].s_m;
00615 }
00616
00628 static char *FormatString(char *buff, const char *str_arg, int64 *argv, const int64 *argve, uint casei, const char *last, WChar *argt, bool dry_run)
00629 {
00630
00631 if (argt == NULL) dry_run = true;
00632 if (UsingNewGRFTextStack() && !dry_run) {
00633
00634
00635
00636
00637
00638
00639 struct TextRefStack *backup = CreateTextRefStackBackup();
00640 FormatString(buff, str_arg, argv, argve, casei, last, argt, true);
00641 RestoreTextRefStackBackup(backup);
00642 } else if (!dry_run) {
00643 FormatString(buff, str_arg, argv, argve, casei, last, argt, true);
00644 }
00645 WChar b;
00646 int64 *argv_orig = argv;
00647 WChar *argt_orig = argt;
00648 uint modifier = 0;
00649 char *buf_start = buff;
00650 std::stack<const char *> str_stack;
00651 str_stack.push(str_arg);
00652
00653 while (true) {
00654 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
00655 str_stack.pop();
00656 }
00657 if (str_stack.empty()) break;
00658 const char *&str = str_stack.top();
00659
00660 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00661
00662
00663 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, argv);
00664 if (b == 0) continue;
00665 }
00666
00667 switch (b) {
00668 case SCC_NEWGRF_STRINL: {
00669 StringID substr = Utf8Consume(&str);
00670 str_stack.push(GetStringPtr(substr));
00671 break;
00672 }
00673
00674 case SCC_NEWGRF_PRINT_STRING_ID: {
00675 StringID substr = GetInt32(&argv, argve, &argt, SCC_NEWGRF_PRINT_STRING_ID);
00676 str_stack.push(GetStringPtr(substr));
00677 break;
00678 }
00679
00680
00681 case SCC_SETX:
00682 if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00683 buff += Utf8Encode(buff, SCC_SETX);
00684 *buff++ = *str++;
00685 }
00686 break;
00687
00688 case SCC_SETXY:
00689 if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00690 buff += Utf8Encode(buff, SCC_SETXY);
00691 *buff++ = *str++;
00692 *buff++ = *str++;
00693 }
00694 break;
00695
00696 case SCC_STRING_ID:
00697 buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, argve, last, argt);
00698 break;
00699
00700 case SCC_RAW_STRING_POINTER: {
00701 const char *str = (const char*)(size_t)GetInt64(&argv, argve, &argt);
00702 buff = FormatString(buff, str, argv, argve, casei, last, argt);
00703 break;
00704 }
00705
00706 case SCC_DATE_LONG:
00707 buff = FormatYmdString(buff, GetInt32(&argv, argve, &argt, SCC_DATE_LONG), modifier, last);
00708 break;
00709
00710 case SCC_DATE_SHORT:
00711 buff = FormatMonthAndYear(buff, GetInt32(&argv, argve, &argt, SCC_DATE_SHORT), modifier, last);
00712 break;
00713
00714 case SCC_VELOCITY: {
00715 int64 args[1];
00716 assert(_settings_game.locale.units < lengthof(units));
00717 args[0] = ConvertSpeedToDisplaySpeed(GetInt32(&argv, argve, &argt, SCC_VELOCITY) * 10 / 16);
00718 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].velocity), args, endof(args), modifier >> 24, last);
00719 modifier = 0;
00720 break;
00721 }
00722
00723 case SCC_HEIGHT: {
00724 int64 args[1] = {GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].h_m >> units[_settings_game.locale.units].h_s};
00725 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].height), args, endof(args), modifier >> 24, last);
00726 modifier = 0;
00727 break;
00728 }
00729
00730 case SCC_CURRENCY_COMPACT:
00731 buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv, argve, &argt), true, last);
00732 break;
00733
00734 case SCC_REVISION:
00735 buff = strecpy(buff, _openttd_revision, last);
00736 break;
00737
00738 case SCC_CARGO_SHORT: {
00739
00740
00741
00742 StringID cargo_str = CargoSpec::Get(GetInt32(&argv, argve, &argt, SCC_CARGO_SHORT))->units_volume;
00743 switch (cargo_str) {
00744 case STR_TONS: {
00745 int64 args[1];
00746 assert(_settings_game.locale.units < lengthof(units));
00747 args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00748 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, endof(args), modifier >> 24, last);
00749 modifier = 0;
00750 break;
00751 }
00752
00753 case STR_LITERS: {
00754 int64 args[1];
00755 assert(_settings_game.locale.units < lengthof(units));
00756 args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00757 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, endof(args), modifier >> 24, last);
00758 modifier = 0;
00759 break;
00760 }
00761
00762 default:
00763 buff = GetStringWithArgs(buff, cargo_str, argv++, argve, last, argt++);
00764 break;
00765 }
00766 break;
00767 }
00768
00769 case SCC_STRING1: {
00770
00771 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING1);
00772 WChar *orig_argt = argt;
00773 int64 *args = GetArgvPtr(&argv, 1, argve, &argt);
00774 buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00775 modifier = 0;
00776 break;
00777 }
00778
00779 case SCC_STRING2: {
00780
00781 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING2);
00782 WChar *orig_argt = argt;
00783 int64 *args = GetArgvPtr(&argv, 2, argve, &argt);
00784 buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00785 modifier = 0;
00786 break;
00787 }
00788
00789 case SCC_STRING3: {
00790
00791 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING3);
00792 WChar *orig_argt = argt;
00793 int64 *args = GetArgvPtr(&argv, 3, argve, &argt);
00794 buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00795 modifier = 0;
00796 break;
00797 }
00798
00799 case SCC_STRING4: {
00800
00801 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING4);
00802 WChar *orig_argt = argt;
00803 int64 *args = GetArgvPtr(&argv, 4, argve, &argt);
00804 buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00805 modifier = 0;
00806 break;
00807 }
00808
00809 case SCC_STRING5: {
00810
00811 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING5);
00812 WChar *orig_argt = argt;
00813 int64 *args = GetArgvPtr(&argv, 5, argve, &argt);
00814 buff = GetStringWithArgs(buff, str, args, argve, last, orig_argt);
00815 modifier = 0;
00816 break;
00817 }
00818
00819 case SCC_STATION_FEATURES: {
00820 buff = StationGetSpecialString(buff, GetInt32(&argv, argve, &argt, SCC_STATION_FEATURES), last);
00821 break;
00822 }
00823
00824 case SCC_INDUSTRY_NAME: {
00825 const Industry *i = Industry::Get(GetInt32(&argv, argve, &argt, SCC_INDUSTRY_NAME));
00826 int64 args[2];
00827
00828
00829 assert(i != NULL);
00830
00831
00832 args[0] = i->town->index;
00833 args[1] = GetIndustrySpec(i->type)->name;
00834 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), args, endof(args), modifier >> 24, last);
00835 modifier = 0;
00836 break;
00837 }
00838
00839 case SCC_VOLUME: {
00840 int64 args[1];
00841 assert(_settings_game.locale.units < lengthof(units));
00842 args[0] = GetInt32(&argv, argve, &argt, SCC_VOLUME) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00843 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, endof(args), modifier >> 24, last);
00844 modifier = 0;
00845 break;
00846 }
00847
00848 case SCC_GENDER_LIST: {
00849
00850 byte offset = (byte)*str++;
00851 assert(argv_orig + offset < argve);
00852 int gender = 0;
00853 if (!dry_run && argt != NULL && argt_orig[offset] != 0) {
00854
00855
00856
00857 char input[4 + 1];
00858 char *p = input + Utf8Encode(input, argt_orig[offset]);
00859 *p = '\0';
00860
00861
00862 char buf[256];
00863 bool old_kgd = _keep_gender_data;
00864 _keep_gender_data = true;
00865 p = FormatString(buf, input, argv_orig + offset, argve, 0, lastof(buf));
00866 _keep_gender_data = old_kgd;
00867 *p = '\0';
00868
00869
00870 const char *s = buf;
00871 WChar c = Utf8Consume(&s);
00872
00873 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00874 }
00875 str = ParseStringChoice(str, gender, &buff, last);
00876 break;
00877 }
00878
00879 case SCC_DATE_TINY: {
00880 buff = FormatTinyOrISODate(buff, GetInt32(&argv, argve, &argt, SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
00881 break;
00882 }
00883
00884 case SCC_DATE_ISO: {
00885 buff = FormatTinyOrISODate(buff, GetInt32(&argv, argve, &argt), STR_FORMAT_DATE_ISO, last);
00886 break;
00887 }
00888
00889 case SCC_CARGO: {
00890
00891 CargoID cargo = GetInt32(&argv, argve, &argt, SCC_CARGO);
00892 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
00893 buff = GetStringWithArgs(buff, cargo_str, argv++, argve, last);
00894 break;
00895 }
00896
00897 case SCC_POWER: {
00898 int64 args[1];
00899 assert(_settings_game.locale.units < lengthof(units));
00900 args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].p_m >> units[_settings_game.locale.units].p_s;
00901 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].power), args, endof(args), modifier >> 24, last);
00902 modifier = 0;
00903 break;
00904 }
00905
00906 case SCC_VOLUME_SHORT: {
00907 int64 args[1];
00908 assert(_settings_game.locale.units < lengthof(units));
00909 args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00910 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_volume), args, endof(args), modifier >> 24, last);
00911 modifier = 0;
00912 break;
00913 }
00914
00915 case SCC_WEIGHT: {
00916 int64 args[1];
00917 assert(_settings_game.locale.units < lengthof(units));
00918 args[0] = GetInt32(&argv, argve, &argt, SCC_WEIGHT) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00919 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, endof(args), modifier >> 24, last);
00920 modifier = 0;
00921 break;
00922 }
00923
00924 case SCC_WEIGHT_SHORT: {
00925 int64 args[1];
00926 assert(_settings_game.locale.units < lengthof(units));
00927 args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00928 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_weight), args, endof(args), modifier >> 24, last);
00929 modifier = 0;
00930 break;
00931 }
00932
00933 case SCC_FORCE: {
00934 int64 args[1];
00935 assert(_settings_game.locale.units < lengthof(units));
00936 args[0] = GetInt32(&argv, argve, &argt) * units[_settings_game.locale.units].f_m >> units[_settings_game.locale.units].f_s;
00937 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].force), args, endof(args), modifier >> 24, last);
00938 modifier = 0;
00939 break;
00940 }
00941
00942
00943
00944 case SCC_GENDER_INDEX:
00945 if (_keep_gender_data) {
00946 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
00947 *buff++ = *str++;
00948 } else {
00949 str++;
00950 }
00951 break;
00952
00953 case SCC_STRING: {
00954 uint str = modifier + GetInt32(&argv, argve, &argt, SCC_STRING);
00955
00956
00957
00958 buff = GetStringWithArgs(buff, str, argv, argve, last);
00959 modifier = 0;
00960 break;
00961 }
00962
00963 case SCC_COMMA:
00964 buff = FormatCommaNumber(buff, GetInt64(&argv, argve, &argt, SCC_COMMA), last);
00965 break;
00966
00967 case SCC_ARG_INDEX: {
00968 byte offset = (byte)*str++;
00969 argv = argv_orig + offset;
00970 if (argt_orig != NULL) argt = argt_orig + offset;
00971 break;
00972 }
00973
00974 case SCC_PLURAL_LIST: {
00975 int plural_form = *str++;
00976 byte idx = *str++;
00977 assert(argv_orig + idx < argve);
00978 int64 v = argv_orig[idx];
00979 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
00980 break;
00981 }
00982
00983 case SCC_NUM:
00984 buff = FormatNoCommaNumber(buff, GetInt64(&argv, argve, &argt, SCC_NUM), last);
00985 break;
00986
00987 case SCC_ZEROFILL_NUM: {
00988 int64 num = GetInt64(&argv, argve, &argt);
00989 buff = FormatZerofillNumber(buff, num, GetInt64(&argv, argve, &argt), last);
00990 break;
00991 }
00992
00993 case SCC_HEX:
00994 buff = FormatHexNumber(buff, (uint64)GetInt64(&argv, argve, &argt, SCC_HEX), last);
00995 break;
00996
00997 case SCC_BYTES:
00998 buff = FormatBytes(buff, GetInt64(&argv, argve, &argt), last);
00999 break;
01000
01001 case SCC_CURRENCY:
01002 buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv, argve, &argt, SCC_CURRENCY), false, last);
01003 break;
01004
01005 case SCC_WAYPOINT_NAME: {
01006 Waypoint *wp = Waypoint::Get(GetInt32(&argv, argve, &argt, SCC_WAYPOINT_NAME));
01007
01008 assert(wp != NULL);
01009
01010 if (wp->name != NULL) {
01011 buff = strecpy(buff, wp->name, last);
01012 } else {
01013 int64 args[2];
01014 args[0] = wp->town->index;
01015 args[1] = wp->town_cn + 1;
01016 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
01017 if (wp->town_cn != 0) str++;
01018 buff = GetStringWithArgs(buff, str, args, endof(args), last);
01019 }
01020 break;
01021 }
01022
01023 case SCC_STATION_NAME: {
01024 StationID sid = GetInt32(&argv, argve, &argt, SCC_STATION_NAME);
01025 const Station *st = Station::GetIfValid(sid);
01026
01027 if (st == NULL) {
01028
01029
01030
01031 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, NULL, NULL, last);
01032 break;
01033 }
01034
01035 if (st->name != NULL) {
01036 buff = strecpy(buff, st->name, last);
01037 } else {
01038 StringID str = st->string_id;
01039 if (st->indtype != IT_INVALID) {
01040
01041 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01042
01043
01044
01045
01046 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
01047 str = indsp->station_name;
01048 }
01049 }
01050
01051 int64 args[3];
01052 args[0] = STR_TOWN_NAME;
01053 args[1] = st->town->index;
01054 args[2] = st->index;
01055 buff = GetStringWithArgs(buff, str, args, endof(args), last);
01056 }
01057 break;
01058 }
01059
01060 case SCC_DEPOT_NAME: {
01061 VehicleType vt = (VehicleType)GetInt32(&argv, argve, &argt, SCC_DEPOT_NAME);
01062 if (vt == VEH_AIRCRAFT) {
01063 int64 args[] = { GetInt32(&argv, argve, &argt) };
01064 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, args, endof(args), last);
01065 break;
01066 }
01067
01068 const Depot *d = Depot::Get(GetInt32(&argv, argve, &argt));
01069 if (d->name != NULL) {
01070 buff = strecpy(buff, d->name, last);
01071 } else {
01072 int64 args[] = { d->town->index, d->town_cn + 1 };
01073 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), args, endof(args), last);
01074 }
01075 break;
01076 }
01077
01078 case SCC_TOWN_NAME: {
01079 const Town *t = Town::Get(GetInt32(&argv, argve, &argt, SCC_TOWN_NAME));
01080
01081 assert(t != NULL);
01082
01083 if (t->name != NULL) {
01084 buff = strecpy(buff, t->name, last);
01085 } else {
01086 buff = GetTownName(buff, t, last);
01087 }
01088 break;
01089 }
01090
01091 case SCC_GROUP_NAME: {
01092 const Group *g = Group::Get(GetInt32(&argv, argve, &argt));
01093
01094 assert(g != NULL);
01095
01096 if (g->name != NULL) {
01097 buff = strecpy(buff, g->name, last);
01098 } else {
01099 int64 args[1];
01100
01101 args[0] = g->index;
01102 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, args, endof(args), last);
01103 }
01104 break;
01105 }
01106
01107 case SCC_ENGINE_NAME: {
01108 EngineID engine = (EngineID)GetInt32(&argv, argve, &argt, SCC_ENGINE_NAME);
01109 const Engine *e = Engine::Get(engine);
01110
01111 assert(e != NULL);
01112
01113 if (e->name != NULL && e->info.string_id != STR_NEWGRF_INVALID_ENGINE) {
01114 buff = strecpy(buff, e->name, last);
01115 } else {
01116 buff = GetStringWithArgs(buff, e->info.string_id, NULL, NULL, last);
01117 }
01118 break;
01119 }
01120
01121 case SCC_VEHICLE_NAME: {
01122 const Vehicle *v = Vehicle::Get(GetInt32(&argv, argve, &argt, SCC_VEHICLE_NAME));
01123
01124 assert(v != NULL);
01125
01126 if (v->name != NULL) {
01127 buff = strecpy(buff, v->name, last);
01128 } else {
01129 int64 args[1];
01130 args[0] = v->unitnumber;
01131
01132 StringID str;
01133 switch (v->type) {
01134 default: NOT_REACHED();
01135 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
01136 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
01137 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
01138 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01139 }
01140
01141 buff = GetStringWithArgs(buff, str, args, endof(args), last);
01142 }
01143 break;
01144 }
01145
01146 case SCC_SIGN_NAME: {
01147 const Sign *si = Sign::Get(GetInt32(&argv, argve, &argt));
01148 if (si->name != NULL) {
01149 buff = strecpy(buff, si->name, last);
01150 } else {
01151 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, NULL, NULL, last);
01152 }
01153 break;
01154 }
01155
01156 case SCC_COMPANY_NAME: {
01157 const Company *c = Company::Get((CompanyID)GetInt32(&argv, argve, &argt));
01158
01159 if (c->name != NULL) {
01160 buff = strecpy(buff, c->name, last);
01161 } else {
01162 int64 args[1];
01163 args[0] = c->name_2;
01164 buff = GetStringWithArgs(buff, c->name_1, args, endof(args), last);
01165 }
01166 break;
01167 }
01168
01169 case SCC_COMPANY_NUM: {
01170 CompanyID company = (CompanyID)GetInt32(&argv, argve, &argt);
01171
01172
01173 if (Company::IsValidHumanID(company)) {
01174 int64 args[1];
01175 args[0] = company + 1;
01176 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, args, endof(args), last);
01177 }
01178 break;
01179 }
01180
01181 case SCC_PRESIDENT_NAME: {
01182 const Company *c = Company::Get((CompanyID)GetInt32(&argv, argve, &argt, SCC_PRESIDENT_NAME));
01183
01184 if (c->president_name != NULL) {
01185 buff = strecpy(buff, c->president_name, last);
01186 } else {
01187 int64 args[1];
01188 args[0] = c->president_name_2;
01189 buff = GetStringWithArgs(buff, c->president_name_1, args, endof(args), last);
01190 }
01191 break;
01192 }
01193
01194 case SCC_SETCASE: {
01195
01196
01197 modifier = (byte)*str++ << 24;
01198 break;
01199 }
01200
01201 case SCC_SWITCH_CASE: {
01202
01203
01204 uint num = (byte)*str++;
01205 while (num) {
01206 if ((byte)str[0] == casei) {
01207
01208 str += 3;
01209 break;
01210 }
01211
01212 str += 3 + (str[1] << 8) + str[2];
01213 num--;
01214 }
01215 break;
01216 }
01217
01218 default:
01219 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01220 break;
01221 }
01222 }
01223 *buff = '\0';
01224 return buff;
01225 }
01226
01227
01228 static char *StationGetSpecialString(char *buff, int x, const char *last)
01229 {
01230 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01231 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01232 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01233 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01234 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01235 *buff = '\0';
01236 return buff;
01237 }
01238
01239 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01240 {
01241 return GenerateTownNameString(buff, last, ind, seed);
01242 }
01243
01244 static const char * const _silly_company_names[] = {
01245 "Bloggs Brothers",
01246 "Tiny Transport Ltd.",
01247 "Express Travel",
01248 "Comfy-Coach & Co.",
01249 "Crush & Bump Ltd.",
01250 "Broken & Late Ltd.",
01251 "Sam Speedy & Son",
01252 "Supersonic Travel",
01253 "Mike's Motors",
01254 "Lightning International",
01255 "Pannik & Loozit Ltd.",
01256 "Inter-City Transport",
01257 "Getout & Pushit Ltd."
01258 };
01259
01260 static const char * const _surname_list[] = {
01261 "Adams",
01262 "Allan",
01263 "Baker",
01264 "Bigwig",
01265 "Black",
01266 "Bloggs",
01267 "Brown",
01268 "Campbell",
01269 "Gordon",
01270 "Hamilton",
01271 "Hawthorn",
01272 "Higgins",
01273 "Green",
01274 "Gribble",
01275 "Jones",
01276 "McAlpine",
01277 "MacDonald",
01278 "McIntosh",
01279 "Muir",
01280 "Murphy",
01281 "Nelson",
01282 "O'Donnell",
01283 "Parker",
01284 "Phillips",
01285 "Pilkington",
01286 "Quigley",
01287 "Sharkey",
01288 "Thomson",
01289 "Watkins"
01290 };
01291
01292 static const char * const _silly_surname_list[] = {
01293 "Grumpy",
01294 "Dozy",
01295 "Speedy",
01296 "Nosey",
01297 "Dribble",
01298 "Mushroom",
01299 "Cabbage",
01300 "Sniffle",
01301 "Fishy",
01302 "Swindle",
01303 "Sneaky",
01304 "Nutkins"
01305 };
01306
01307 static const char _initial_name_letters[] = {
01308 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01309 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01310 };
01311
01312 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01313 {
01314 const char * const *base;
01315 uint num;
01316
01317 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01318 base = _silly_surname_list;
01319 num = lengthof(_silly_surname_list);
01320 } else {
01321 base = _surname_list;
01322 num = lengthof(_surname_list);
01323 }
01324
01325 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01326 buff = strecpy(buff, " & Co.", last);
01327
01328 return buff;
01329 }
01330
01331 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01332 {
01333 char initial[] = "?. ";
01334 const char * const *base;
01335 uint num;
01336 uint i;
01337
01338 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01339 buff = strecpy(buff, initial, last);
01340
01341 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01342 if (i < sizeof(_initial_name_letters)) {
01343 initial[0] = _initial_name_letters[i];
01344 buff = strecpy(buff, initial, last);
01345 }
01346
01347 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01348 base = _silly_surname_list;
01349 num = lengthof(_silly_surname_list);
01350 } else {
01351 base = _surname_list;
01352 num = lengthof(_surname_list);
01353 }
01354
01355 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01356
01357 return buff;
01358 }
01359
01360 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const int64 *argve, const char *last, WChar *argt)
01361 {
01362 switch (ind) {
01363 case 1:
01364 return strecpy(buff, _silly_company_names[GetInt32(&argv, argve, &argt) & 0xFFFF], last);
01365
01366 case 2:
01367 return GenAndCoName(buff, GetInt32(&argv, argve, &argt), last);
01368
01369 case 3:
01370 return GenPresidentName(buff, GetInt32(&argv, argve, &argt), last);
01371 }
01372
01373
01374 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01375 buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv, argve, &argt), last);
01376 return strecpy(buff, " Transport", last);
01377 }
01378
01379
01380 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01381 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01382 return strecpy(buff,
01383 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
01384 }
01385
01386
01387 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01388 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01389 buff += seprintf(
01390 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01391 );
01392 return buff;
01393 }
01394
01395
01396 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01397 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01398 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01399 }
01400
01401 NOT_REACHED();
01402 }
01403
01404 #ifdef ENABLE_NETWORK
01405 extern void SortNetworkLanguages();
01406 #else
01407 static inline void SortNetworkLanguages() {}
01408 #endif
01409
01414 bool LanguagePackHeader::IsValid() const
01415 {
01416 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
01417 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
01418 this->plural_form < LANGUAGE_MAX_PLURAL &&
01419 this->text_dir <= 1 &&
01420 this->newgrflangid < MAX_LANG &&
01421 this->num_genders < MAX_NUM_GENDERS &&
01422 this->num_cases < MAX_NUM_CASES &&
01423 StrValid(this->name, lastof(this->name)) &&
01424 StrValid(this->own_name, lastof(this->own_name)) &&
01425 StrValid(this->isocode, lastof(this->isocode)) &&
01426 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
01427 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
01428 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
01429 }
01430
01431 bool ReadLanguagePack(const LanguageMetadata *lang)
01432 {
01433
01434 size_t len;
01435 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 200000);
01436 if (lang_pack == NULL) return false;
01437
01438
01439 const char *end = (char *)lang_pack + len + 1;
01440
01441
01442 if (end <= lang_pack->data || !lang_pack->IsValid()) {
01443 free(lang_pack);
01444 return false;
01445 }
01446
01447 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01448 for (uint i = 0; i < 32; i++) {
01449 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01450 }
01451 #endif
01452
01453 uint count = 0;
01454 for (uint i = 0; i < 32; i++) {
01455 uint num = lang_pack->offsets[i];
01456 _langtab_start[i] = count;
01457 _langtab_num[i] = num;
01458 count += num;
01459 }
01460
01461
01462 char **langpack_offs = MallocT<char *>(count);
01463
01464
01465 char *s = lang_pack->data;
01466 len = (byte)*s++;
01467 for (uint i = 0; i < count; i++) {
01468 if (s + len >= end) {
01469 free(lang_pack);
01470 free(langpack_offs);
01471 return false;
01472 }
01473 if (len >= 0xC0) {
01474 len = ((len & 0x3F) << 8) + (byte)*s++;
01475 if (s + len >= end) {
01476 free(lang_pack);
01477 free(langpack_offs);
01478 return false;
01479 }
01480 }
01481 langpack_offs[i] = s;
01482 s += len;
01483 len = (byte)*s;
01484 *s++ = '\0';
01485 }
01486
01487 free(_langpack);
01488 _langpack = lang_pack;
01489
01490 free(_langpack_offs);
01491 _langpack_offs = langpack_offs;
01492
01493 _current_language = lang;
01494 _current_text_dir = (TextDirection)_current_language->text_dir;
01495 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
01496 strecpy(_config_language_file, c_file, lastof(_config_language_file));
01497 SetCurrentGrfLangID(_current_language->newgrflangid);
01498
01499 #ifdef WITH_ICU
01500
01501 if (_current_collator != NULL) {
01502 delete _current_collator;
01503 _current_collator = NULL;
01504 }
01505
01506
01507 UErrorCode status = U_ZERO_ERROR;
01508 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01509
01510 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01511
01512 if (U_FAILURE(status)) {
01513 delete _current_collator;
01514 _current_collator = NULL;
01515 }
01516 #endif
01517
01518
01519 InitializeSortedCargoSpecs();
01520 SortIndustryTypes();
01521 BuildIndustriesLegend();
01522 SortNetworkLanguages();
01523 InvalidateWindowClassesData(WC_BUILD_VEHICLE);
01524 InvalidateWindowClassesData(WC_TRAINS_LIST);
01525 InvalidateWindowClassesData(WC_ROADVEH_LIST);
01526 InvalidateWindowClassesData(WC_SHIPS_LIST);
01527 InvalidateWindowClassesData(WC_AIRCRAFT_LIST);
01528 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY);
01529 InvalidateWindowClassesData(WC_STATION_LIST);
01530
01531 return true;
01532 }
01533
01534
01535
01536 #if !(defined(WIN32) || defined(__APPLE__))
01537
01545 const char *GetCurrentLocale(const char *param)
01546 {
01547 const char *env;
01548
01549 env = getenv("LANGUAGE");
01550 if (env != NULL) return env;
01551
01552 env = getenv("LC_ALL");
01553 if (env != NULL) return env;
01554
01555 if (param != NULL) {
01556 env = getenv(param);
01557 if (env != NULL) return env;
01558 }
01559
01560 return getenv("LANG");
01561 }
01562 #else
01563 const char *GetCurrentLocale(const char *param);
01564 #endif
01565
01566 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01567 {
01568 char stra[512];
01569 char strb[512];
01570 GetString(stra, *a, lastof(stra));
01571 GetString(strb, *b, lastof(strb));
01572
01573 return strcmp(stra, strb);
01574 }
01575
01581 const LanguageMetadata *GetLanguage(byte newgrflangid)
01582 {
01583 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
01584 if (newgrflangid == lang->newgrflangid) return lang;
01585 }
01586
01587 return NULL;
01588 }
01589
01596 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
01597 {
01598 FILE *f = fopen(file, "rb");
01599 if (f == NULL) return false;
01600
01601 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01602 fclose(f);
01603
01604 bool ret = read == 1 && hdr->IsValid();
01605
01606
01607 if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01608 return ret;
01609 }
01610
01615 static void GetLanguageList(const char *path)
01616 {
01617 DIR *dir = ttd_opendir(path);
01618 if (dir != NULL) {
01619 struct dirent *dirent;
01620 while ((dirent = readdir(dir)) != NULL) {
01621 const char *d_name = FS2OTTD(dirent->d_name);
01622 const char *extension = strrchr(d_name, '.');
01623
01624
01625 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01626
01627 LanguageMetadata lmd;
01628 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
01629
01630
01631 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
01632 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
01633 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
01634 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
01635 } else {
01636 *_languages.Append() = lmd;
01637 }
01638 }
01639 closedir(dir);
01640 }
01641 }
01642
01647 void InitializeLanguagePacks()
01648 {
01649 Searchpath sp;
01650
01651 FOR_ALL_SEARCHPATHS(sp) {
01652 char path[MAX_PATH];
01653 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01654 GetLanguageList(path);
01655 }
01656 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
01657
01658
01659 const char *lang = GetCurrentLocale("LC_MESSAGES");
01660 if (lang == NULL) lang = "en_GB";
01661
01662 const LanguageMetadata *chosen_language = NULL;
01663 const LanguageMetadata *language_fallback = NULL;
01664 const LanguageMetadata *en_GB_fallback = _languages.Begin();
01665
01666
01667 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01668
01669
01670
01671 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
01672 if (strcmp(lang_file, _config_language_file) == 0) {
01673 chosen_language = lng;
01674 break;
01675 }
01676
01677 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
01678 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
01679 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
01680 }
01681
01682
01683
01684 if (chosen_language == NULL) {
01685 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
01686 }
01687
01688 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
01689 }
01690
01695 const char *GetCurrentLanguageIsoCode()
01696 {
01697 return _langpack->isocode;
01698 }
01699
01706 static bool FindMissingGlyphs(const char **str)
01707 {
01708 #ifdef WITH_FREETYPE
01709 UninitFreeType();
01710 InitFreeType();
01711 #endif
01712 const Sprite *question_mark[FS_END];
01713 FontSize size;
01714
01715 for (size = FS_BEGIN; size < FS_END; size++) {
01716 question_mark[size] = GetGlyph(size, '?');
01717 }
01718
01719 for (uint i = 0; i != 32; i++) {
01720 for (uint j = 0; j < _langtab_num[i]; j++) {
01721 size = FS_NORMAL;
01722 const char *text = _langpack_offs[_langtab_start[i] + j];
01723 if (str != NULL) *str = text;
01724 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
01725 if (c == SCC_SETX) {
01726
01727
01728
01729 text++;
01730 } else if (c == SCC_SETXY) {
01731 text += 2;
01732 } else if (c == SCC_TINYFONT) {
01733 size = FS_SMALL;
01734 } else if (c == SCC_BIGFONT) {
01735 size = FS_LARGE;
01736 } else if (IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
01737
01738 return true;
01739 }
01740 }
01741 }
01742 }
01743 return false;
01744 }
01745
01756 void CheckForMissingGlyphsInLoadedLanguagePack()
01757 {
01758 bool bad_font = FindMissingGlyphs(NULL);
01759 #ifdef WITH_FREETYPE
01760 if (bad_font) {
01761
01762
01763 FreeTypeSettings backup;
01764 memcpy(&backup, &_freetype, sizeof(backup));
01765
01766 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, &FindMissingGlyphs);
01767
01768 memcpy(&_freetype, &backup, sizeof(backup));
01769
01770 if (bad_font) {
01771
01772
01773
01774 UninitFreeType();
01775 InitFreeType();
01776 }
01777 }
01778 #endif
01779
01780 if (bad_font) {
01781
01782
01783
01784
01785
01786 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.");
01787 Utf8Encode(err_str, SCC_YELLOW);
01788 SetDParamStr(0, err_str);
01789 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
01790
01791
01792 LoadStringWidthTable();
01793 return;
01794 }
01795
01796
01797 LoadStringWidthTable();
01798
01799 #if !defined(WITH_ICU)
01800
01801
01802
01803
01804
01805
01806
01807
01808
01809
01810
01811
01812
01813 if (_current_text_dir != TD_LTR) {
01814 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01815 Utf8Encode(err_str, SCC_YELLOW);
01816 SetDParamStr(0, err_str);
01817 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
01818 }
01819 #endif
01820 }