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 "industry.h"
00019 #include "newgrf_text.h"
00020 #include "fileio_func.h"
00021 #include "group.h"
00022 #include "signs_base.h"
00023 #include "cargotype.h"
00024 #include "fontcache.h"
00025 #include "gui.h"
00026 #include "strings_func.h"
00027 #include "rev.h"
00028 #include "core/endian_func.hpp"
00029 #include "date_func.h"
00030 #include "vehicle_base.h"
00031 #include "engine_base.h"
00032 #include "strgen/strgen.h"
00033 #include "townname_func.h"
00034 #include "string_func.h"
00035 #include "company_base.h"
00036
00037 #include "table/strings.h"
00038 #include "table/control_codes.h"
00039
00040 DynamicLanguages _dynlang;
00041 uint64 _decode_parameters[20];
00042
00043 static char *StationGetSpecialString(char *buff, int x, const char *last);
00044 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
00045 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const char *last);
00046
00047 static char *FormatString(char *buff, const char *str, int64 *argv, uint casei, const char *last);
00048
00049 struct LanguagePack : public LanguagePackHeader {
00050 char data[];
00051 };
00052
00053 static char **_langpack_offs;
00054 static LanguagePack *_langpack;
00055 static uint _langtab_num[32];
00056 static uint _langtab_start[32];
00057 static bool _keep_gender_data = false;
00058
00059
00061 static inline int64 GetInt64(int64 **argv)
00062 {
00063 assert(argv);
00064 return *(*argv)++;
00065 }
00066
00068 static inline int32 GetInt32(int64 **argv)
00069 {
00070 return (int32)GetInt64(argv);
00071 }
00072
00074 static inline int64 *GetArgvPtr(int64 **argv, int n)
00075 {
00076 int64 *result;
00077 assert(*argv);
00078 result = *argv;
00079 (*argv) += n;
00080 return result;
00081 }
00082
00083
00084 const char *GetStringPtr(StringID string)
00085 {
00086 switch (GB(string, 11, 5)) {
00087 case 28: return GetGRFStringPtr(GB(string, 0, 11));
00088 case 29: return GetGRFStringPtr(GB(string, 0, 11) + 0x0800);
00089 case 30: return GetGRFStringPtr(GB(string, 0, 11) + 0x1000);
00090 default: return _langpack_offs[_langtab_start[string >> 11] + (string & 0x7FF)];
00091 }
00092 }
00093
00104 char *GetStringWithArgs(char *buffr, uint string, int64 *argv, const char *last)
00105 {
00106 if (GB(string, 0, 16) == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, argv, last);
00107
00108 uint index = GB(string, 0, 11);
00109 uint tab = GB(string, 11, 5);
00110
00111 switch (tab) {
00112 case 4:
00113 if (index >= 0xC0)
00114 return GetSpecialTownNameString(buffr, index - 0xC0, GetInt32(&argv), last);
00115 break;
00116
00117 case 14:
00118 if (index >= 0xE4)
00119 return GetSpecialNameString(buffr, index - 0xE4, argv, last);
00120 break;
00121
00122 case 15:
00123
00124 error("Incorrect conversion of custom name string.");
00125
00126 case 26:
00127
00128 if (HasBit(index, 10)) {
00129 StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
00130 return GetStringWithArgs(buffr, string, argv, last);
00131 }
00132 break;
00133
00134 case 28:
00135 return FormatString(buffr, GetGRFStringPtr(index), argv, 0, last);
00136
00137 case 29:
00138 return FormatString(buffr, GetGRFStringPtr(index + 0x0800), argv, 0, last);
00139
00140 case 30:
00141 return FormatString(buffr, GetGRFStringPtr(index + 0x1000), argv, 0, last);
00142
00143 case 31:
00144 NOT_REACHED();
00145 }
00146
00147 if (index >= _langtab_num[tab]) {
00148 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
00149 }
00150
00151 return FormatString(buffr, GetStringPtr(GB(string, 0, 16)), argv, GB(string, 24, 8), last);
00152 }
00153
00154 char *GetString(char *buffr, StringID string, const char *last)
00155 {
00156 return GetStringWithArgs(buffr, string, (int64*)_decode_parameters, last);
00157 }
00158
00159
00160 char *InlineString(char *buf, StringID string)
00161 {
00162 buf += Utf8Encode(buf, SCC_STRING_ID);
00163 buf += Utf8Encode(buf, string);
00164 return buf;
00165 }
00166
00167
00172 void SetDParamStr(uint n, const char *str)
00173 {
00174 SetDParam(n, (uint64)(size_t)str);
00175 }
00176
00181 void InjectDParam(uint amount)
00182 {
00183 assert((uint)amount < lengthof(_decode_parameters));
00184 memmove(_decode_parameters + amount, _decode_parameters, sizeof(_decode_parameters) - amount * sizeof(uint64));
00185 }
00186
00187 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill_from = 19)
00188 {
00189 uint64 divisor = 10000000000000000000ULL;
00190
00191 if (number < 0) {
00192 buff += seprintf(buff, last, "-");
00193 number = -number;
00194 }
00195
00196 uint64 num = number;
00197 uint64 tot = 0;
00198 for (int i = 0; i < 20; i++) {
00199 uint64 quot = 0;
00200 if (num >= divisor) {
00201 quot = num / divisor;
00202 num = num % divisor;
00203 }
00204 if (tot |= quot || i >= zerofill_from) {
00205 buff += seprintf(buff, last, "%i", (int)quot);
00206 if ((i % 3) == 1 && i != 19) buff = strecpy(buff, separator, last);
00207 }
00208
00209 divisor /= 10;
00210 }
00211
00212 *buff = '\0';
00213
00214 return buff;
00215 }
00216
00217 static char *FormatCommaNumber(char *buff, int64 number, const char *last)
00218 {
00219 const char *separator = _settings_game.locale.digit_group_separator;
00220 if (separator == NULL) separator = _langpack->digit_group_separator;
00221 return FormatNumber(buff, number, last, separator);
00222 }
00223
00224 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
00225 {
00226 return FormatNumber(buff, number, last, "");
00227 }
00228
00229 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
00230 {
00231 return FormatNumber(buff, number, last, "", 20 - count);
00232 }
00233
00234 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
00235 {
00236 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
00237 }
00238
00246 static char *FormatBytes(char *buff, int64 number, const char *last)
00247 {
00248 assert(number >= 0);
00249
00250
00251 const char * const iec_prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
00252 uint id = 1;
00253 while (number >= 1024 * 1024) {
00254 number /= 1024;
00255 id++;
00256 }
00257
00258 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
00259 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
00260
00261 if (number < 1024) {
00262 id = 0;
00263 buff += seprintf(buff, last, "%i", (int)number);
00264 } else if (number < 1024 * 10) {
00265 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
00266 } else if (number < 1024 * 100) {
00267 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
00268 } else {
00269 assert(number < 1024 * 1024);
00270 buff += seprintf(buff, last, "%i", (int)number / 1024);
00271 }
00272
00273 assert(id < lengthof(iec_prefixes));
00274 buff += seprintf(buff, last, " %sB", iec_prefixes[id]);
00275
00276 return buff;
00277 }
00278
00279 static char *FormatYmdString(char *buff, Date date, const char *last)
00280 {
00281 YearMonthDay ymd;
00282 ConvertDateToYMD(date, &ymd);
00283
00284 int64 args[3] = { ymd.day + STR_ORDINAL_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year };
00285 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), args, 0, last);
00286 }
00287
00288 static char *FormatMonthAndYear(char *buff, Date date, const char *last)
00289 {
00290 YearMonthDay ymd;
00291 ConvertDateToYMD(date, &ymd);
00292
00293 int64 args[2] = { STR_MONTH_JAN + ymd.month, ymd.year };
00294 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), args, 0, last);
00295 }
00296
00297 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
00298 {
00299 YearMonthDay ymd;
00300 ConvertDateToYMD(date, &ymd);
00301
00302 char day[3];
00303 char month[3];
00304
00305 snprintf(day, lengthof(day), "%02i", ymd.day);
00306 snprintf(month, lengthof(month), "%02i", ymd.month + 1);
00307
00308 int64 args[3] = { (int64)(size_t)day, (int64)(size_t)month, ymd.year };
00309 return FormatString(buff, GetStringPtr(str), args, 0, last);
00310 }
00311
00312 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
00313 {
00314
00315
00316 bool negative = number < 0;
00317 const char *multiplier = "";
00318
00319 number *= spec->rate;
00320
00321
00322 if (number < 0) {
00323 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
00324 buff += Utf8Encode(buff, SCC_RED);
00325 buff = strecpy(buff, "-", last);
00326 number = -number;
00327 }
00328
00329
00330
00331
00332 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
00333
00334
00335 if (compact) {
00336
00337
00338 if (number >= 1000000000 - 500) {
00339 number = (number + 500000) / 1000000;
00340 multiplier = "M";
00341 } else if (number >= 1000000) {
00342 number = (number + 500) / 1000;
00343 multiplier = "k";
00344 }
00345 }
00346
00347 const char *separator = _settings_game.locale.digit_group_separator_currency;
00348 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
00349 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
00350 buff = FormatNumber(buff, number, last, separator);
00351 buff = strecpy(buff, multiplier, last);
00352
00353
00354
00355
00356 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
00357
00358 if (negative) {
00359 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
00360 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
00361 *buff = '\0';
00362 }
00363
00364 return buff;
00365 }
00366
00367 static int DeterminePluralForm(int64 count)
00368 {
00369
00370 uint64 n = abs(count);
00371
00372 switch (_langpack->plural_form) {
00373 default:
00374 NOT_REACHED();
00375
00376
00377
00378
00379
00380 case 0:
00381 return n != 1;
00382
00383
00384
00385
00386 case 1:
00387 return 0;
00388
00389
00390
00391
00392 case 2:
00393 return n > 1;
00394
00395
00396
00397
00398 case 3:
00399 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
00400
00401
00402
00403
00404 case 4:
00405 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
00406
00407
00408
00409
00410 case 5:
00411 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00412
00413
00414
00415
00416 case 6:
00417 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00418
00419
00420
00421
00422 case 7:
00423 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00424
00425
00426
00427
00428 case 8:
00429 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
00430
00431
00432
00433
00434 case 9:
00435 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
00436
00437
00438
00439
00440 case 10:
00441 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
00442
00443
00444
00445
00446
00447
00448
00449 case 11:
00450 switch (n % 10) {
00451 case 0:
00452 case 1:
00453 case 3:
00454 case 6:
00455 case 7:
00456 case 8:
00457 return 0;
00458
00459 case 2:
00460 case 4:
00461 case 5:
00462 case 9:
00463 return 1;
00464
00465 default:
00466 NOT_REACHED();
00467 }
00468
00469
00470
00471
00472 case 12:
00473 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
00474 }
00475 }
00476
00477 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
00478 {
00479
00480 uint n = (byte)*b++;
00481 uint pos, i, mypos = 0;
00482
00483 for (i = pos = 0; i != n; i++) {
00484 uint len = (byte)*b++;
00485 if (i == form) mypos = pos;
00486 pos += len;
00487 }
00488
00489 *dst += seprintf(*dst, last, "%s", b + mypos);
00490 return b + pos;
00491 }
00492
00493 struct Units {
00494 int s_m;
00495 int s_s;
00496 StringID velocity;
00497 int p_m;
00498 int p_s;
00499 StringID power;
00500 int w_m;
00501 int w_s;
00502 StringID s_weight;
00503 StringID l_weight;
00504 int v_m;
00505 int v_s;
00506 StringID s_volume;
00507 StringID l_volume;
00508 int f_m;
00509 int f_s;
00510 StringID force;
00511 };
00512
00513
00514 static const Units units[] = {
00515 {
00516 1, 0, STR_UNITS_VELOCITY_IMPERIAL,
00517 1, 0, STR_UNITS_POWER_IMPERIAL,
00518 1, 0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00519 1000, 0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00520 1, 0, STR_UNITS_FORCE_SI,
00521 },
00522 {
00523 103, 6, STR_UNITS_VELOCITY_METRIC,
00524 1, 0, STR_UNITS_POWER_METRIC,
00525 1, 0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00526 1000, 0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00527 1, 0, STR_UNITS_FORCE_SI,
00528 },
00529 {
00530 1831, 12, STR_UNITS_VELOCITY_SI,
00531 764, 10, STR_UNITS_POWER_SI,
00532 1000, 0, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00533 1, 0, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00534 1, 0, STR_UNITS_FORCE_SI,
00535 },
00536 };
00537
00543 uint ConvertSpeedToDisplaySpeed(uint speed)
00544 {
00545 return (speed * units[_settings_game.locale.units].s_m) >> units[_settings_game.locale.units].s_s;
00546 }
00547
00553 uint ConvertDisplaySpeedToSpeed(uint speed)
00554 {
00555 return ((speed << units[_settings_game.locale.units].s_s) + units[_settings_game.locale.units].s_m / 2) / units[_settings_game.locale.units].s_m;
00556 }
00557
00558 static char *FormatString(char *buff, const char *str, int64 *argv, uint casei, const char *last)
00559 {
00560 WChar b;
00561 int64 *argv_orig = argv;
00562 uint modifier = 0;
00563 char *buf_start = buff;
00564
00565 while ((b = Utf8Consume(&str)) != '\0') {
00566 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00567
00568 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, argv);
00569 if (b == 0) continue;
00570 }
00571
00572 switch (b) {
00573 case SCC_SETX:
00574 if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00575 buff += Utf8Encode(buff, SCC_SETX);
00576 *buff++ = *str++;
00577 }
00578 break;
00579
00580 case SCC_SETXY:
00581 if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00582 buff += Utf8Encode(buff, SCC_SETXY);
00583 *buff++ = *str++;
00584 *buff++ = *str++;
00585 }
00586 break;
00587
00588 case SCC_STRING_ID:
00589 buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, last);
00590 break;
00591
00592 case SCC_RAW_STRING_POINTER: {
00593 const char *str = (const char*)(size_t)GetInt64(&argv);
00594 buff = FormatString(buff, str, argv, casei, last);
00595 break;
00596 }
00597
00598 case SCC_DATE_LONG:
00599 buff = FormatYmdString(buff, GetInt32(&argv), last);
00600 break;
00601
00602 case SCC_DATE_SHORT:
00603 buff = FormatMonthAndYear(buff, GetInt32(&argv), last);
00604 break;
00605
00606 case SCC_VELOCITY: {
00607 int64 args[1];
00608 assert(_settings_game.locale.units < lengthof(units));
00609 args[0] = ConvertSpeedToDisplaySpeed(GetInt32(&argv) * 10 / 16);
00610 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].velocity), args, modifier >> 24, last);
00611 modifier = 0;
00612 break;
00613 }
00614
00615 case SCC_CURRENCY_COMPACT:
00616 buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), true, last);
00617 break;
00618
00619 case SCC_REVISION:
00620 buff = strecpy(buff, _openttd_revision, last);
00621 break;
00622
00623 case SCC_CARGO_SHORT: {
00624
00625
00626
00627 StringID cargo_str = CargoSpec::Get(GetInt32(&argv))->units_volume;
00628 switch (cargo_str) {
00629 case STR_TONS: {
00630 int64 args[1];
00631 assert(_settings_game.locale.units < lengthof(units));
00632 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00633 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00634 modifier = 0;
00635 break;
00636 }
00637
00638 case STR_LITERS: {
00639 int64 args[1];
00640 assert(_settings_game.locale.units < lengthof(units));
00641 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00642 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, modifier >> 24, last);
00643 modifier = 0;
00644 break;
00645 }
00646
00647 default:
00648 if (cargo_str >= 0xE000 && cargo_str < 0xF800) {
00649
00650
00651 buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00652 } else {
00653 buff = FormatCommaNumber(buff, GetInt32(&argv), last);
00654 buff = strecpy(buff, " ", last);
00655 buff = strecpy(buff, GetStringPtr(cargo_str), last);
00656 }
00657 break;
00658 }
00659 } break;
00660
00661 case SCC_STRING1: {
00662
00663 uint str = modifier + GetInt32(&argv);
00664 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 1), last);
00665 modifier = 0;
00666 break;
00667 }
00668
00669 case SCC_STRING2: {
00670
00671 uint str = modifier + GetInt32(&argv);
00672 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 2), last);
00673 modifier = 0;
00674 break;
00675 }
00676
00677 case SCC_STRING3: {
00678
00679 uint str = modifier + GetInt32(&argv);
00680 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 3), last);
00681 modifier = 0;
00682 break;
00683 }
00684
00685 case SCC_STRING4: {
00686
00687 uint str = modifier + GetInt32(&argv);
00688 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 4), last);
00689 modifier = 0;
00690 break;
00691 }
00692
00693 case SCC_STRING5: {
00694
00695 uint str = modifier + GetInt32(&argv);
00696 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 5), last);
00697 modifier = 0;
00698 break;
00699 }
00700
00701 case SCC_STATION_FEATURES: {
00702 buff = StationGetSpecialString(buff, GetInt32(&argv), last);
00703 break;
00704 }
00705
00706 case SCC_INDUSTRY_NAME: {
00707 const Industry *i = Industry::Get(GetInt32(&argv));
00708 int64 args[2];
00709
00710
00711 assert(i != NULL);
00712
00713
00714 args[0] = i->town->index;
00715 args[1] = GetIndustrySpec(i->type)->name;
00716 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), args, modifier >> 24, last);
00717 modifier = 0;
00718 break;
00719 }
00720
00721 case SCC_VOLUME: {
00722 int64 args[1];
00723 assert(_settings_game.locale.units < lengthof(units));
00724 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00725 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, modifier >> 24, last);
00726 modifier = 0;
00727 break;
00728 }
00729
00730 case SCC_GENDER_LIST: {
00731
00732 WChar fmt = SCC_CONTROL_START + (byte)*str++;
00733 byte offset = (byte)*str++;
00734
00735
00736
00737
00738 char input[4 + 1];
00739 char *p = input + Utf8Encode(input, fmt);
00740 *p = '\0';
00741
00742
00743 char buf[256];
00744 bool old_kgd = _keep_gender_data;
00745 _keep_gender_data = true;
00746 p = FormatString(buf, input, argv_orig + offset, 0, lastof(buf));
00747 _keep_gender_data = old_kgd;
00748 *p = '\0';
00749
00750
00751 int gender = 0;
00752 const char *s = buf;
00753 WChar c = Utf8Consume(&s);
00754
00755 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00756 str = ParseStringChoice(str, gender, &buff, last);
00757 break;
00758 }
00759
00760 case SCC_DATE_TINY: {
00761 buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_FORMAT_DATE_TINY, last);
00762 break;
00763 }
00764
00765 case SCC_DATE_ISO: {
00766 buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_FORMAT_DATE_ISO, last);
00767 break;
00768 }
00769
00770 case SCC_CARGO: {
00771
00772 CargoID cargo = GetInt32(&argv);
00773 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
00774 buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00775 break;
00776 }
00777
00778 case SCC_POWER: {
00779 int64 args[1];
00780 assert(_settings_game.locale.units < lengthof(units));
00781 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].p_m >> units[_settings_game.locale.units].p_s;
00782 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].power), args, modifier >> 24, last);
00783 modifier = 0;
00784 break;
00785 }
00786
00787 case SCC_VOLUME_SHORT: {
00788 int64 args[1];
00789 assert(_settings_game.locale.units < lengthof(units));
00790 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00791 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_volume), args, modifier >> 24, last);
00792 modifier = 0;
00793 break;
00794 }
00795
00796 case SCC_WEIGHT: {
00797 int64 args[1];
00798 assert(_settings_game.locale.units < lengthof(units));
00799 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00800 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00801 modifier = 0;
00802 break;
00803 }
00804
00805 case SCC_WEIGHT_SHORT: {
00806 int64 args[1];
00807 assert(_settings_game.locale.units < lengthof(units));
00808 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00809 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_weight), args, modifier >> 24, last);
00810 modifier = 0;
00811 break;
00812 }
00813
00814 case SCC_FORCE: {
00815 int64 args[1];
00816 assert(_settings_game.locale.units < lengthof(units));
00817 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].f_m >> units[_settings_game.locale.units].f_s;
00818 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].force), args, modifier >> 24, last);
00819 modifier = 0;
00820 break;
00821 }
00822
00823
00824
00825 case SCC_GENDER_INDEX:
00826 if (_keep_gender_data) {
00827 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
00828 *buff++ = *str++;
00829 } else {
00830 str++;
00831 }
00832 break;
00833
00834 case SCC_STRING: {
00835 uint str = modifier + GetInt32(&argv);
00836
00837
00838
00839 buff = GetStringWithArgs(buff, str, argv, last);
00840 modifier = 0;
00841 break;
00842 }
00843
00844 case SCC_COMMA:
00845 buff = FormatCommaNumber(buff, GetInt64(&argv), last);
00846 break;
00847
00848 case SCC_ARG_INDEX:
00849 argv = argv_orig + (byte)*str++;
00850 break;
00851
00852 case SCC_PLURAL_LIST: {
00853 int64 v = argv_orig[(byte)*str++];
00854 str = ParseStringChoice(str, DeterminePluralForm(v), &buff, last);
00855 break;
00856 }
00857
00858 case SCC_NUM:
00859 buff = FormatNoCommaNumber(buff, GetInt64(&argv), last);
00860 break;
00861
00862 case SCC_ZEROFILL_NUM: {
00863 int64 num = GetInt64(&argv);
00864 buff = FormatZerofillNumber(buff, num, GetInt64(&argv), last);
00865 } break;
00866
00867 case SCC_HEX:
00868 buff = FormatHexNumber(buff, (uint64)GetInt64(&argv), last);
00869 break;
00870
00871 case SCC_BYTES:
00872 buff = FormatBytes(buff, GetInt64(&argv), last);
00873 break;
00874
00875 case SCC_CURRENCY:
00876 buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
00877 break;
00878
00879 case SCC_WAYPOINT_NAME: {
00880 Waypoint *wp = Waypoint::Get(GetInt32(&argv));
00881
00882 assert(wp != NULL);
00883
00884 if (wp->name != NULL) {
00885 buff = strecpy(buff, wp->name, last);
00886 } else {
00887 int64 temp[2];
00888 temp[0] = wp->town->index;
00889 temp[1] = wp->town_cn + 1;
00890 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
00891 if (wp->town_cn != 0) str++;
00892 buff = GetStringWithArgs(buff, str, temp, last);
00893 }
00894 break;
00895 }
00896
00897 case SCC_STATION_NAME: {
00898 StationID sid = GetInt32(&argv);
00899 const Station *st = Station::GetIfValid(sid);
00900
00901 if (st == NULL) {
00902
00903
00904
00905 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, NULL, last);
00906 break;
00907 }
00908
00909 if (st->name != NULL) {
00910 buff = strecpy(buff, st->name, last);
00911 } else {
00912 StringID str = st->string_id;
00913 if (st->indtype != IT_INVALID) {
00914
00915 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
00916
00917
00918
00919
00920 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
00921 str = indsp->station_name;
00922 }
00923 }
00924
00925 int64 temp[3];
00926 temp[0] = STR_TOWN_NAME;
00927 temp[1] = st->town->index;
00928 temp[2] = st->index;
00929 buff = GetStringWithArgs(buff, str, temp, last);
00930 }
00931 break;
00932 }
00933
00934 case SCC_TOWN_NAME: {
00935 const Town *t = Town::Get(GetInt32(&argv));
00936
00937 assert(t != NULL);
00938
00939 if (t->name != NULL) {
00940 buff = strecpy(buff, t->name, last);
00941 } else {
00942 buff = GetTownName(buff, t, last);
00943 }
00944 break;
00945 }
00946
00947 case SCC_GROUP_NAME: {
00948 const Group *g = Group::Get(GetInt32(&argv));
00949
00950 assert(g != NULL);
00951
00952 if (g->name != NULL) {
00953 buff = strecpy(buff, g->name, last);
00954 } else {
00955 int64 args[1];
00956
00957 args[0] = g->index;
00958 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, args, last);
00959 }
00960 break;
00961 }
00962
00963 case SCC_ENGINE_NAME: {
00964 EngineID engine = (EngineID)GetInt32(&argv);
00965 const Engine *e = Engine::Get(engine);
00966
00967 assert(e != NULL);
00968
00969 if (e->name != NULL && e->info.string_id != STR_NEWGRF_INVALID_ENGINE) {
00970 buff = strecpy(buff, e->name, last);
00971 } else {
00972 buff = GetStringWithArgs(buff, e->info.string_id, NULL, last);
00973 }
00974 break;
00975 }
00976
00977 case SCC_VEHICLE_NAME: {
00978 const Vehicle *v = Vehicle::Get(GetInt32(&argv));
00979
00980 assert(v != NULL);
00981
00982 if (v->name != NULL) {
00983 buff = strecpy(buff, v->name, last);
00984 } else {
00985 int64 args[1];
00986 args[0] = v->unitnumber;
00987
00988 StringID str;
00989 switch (v->type) {
00990 default: NOT_REACHED();
00991 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
00992 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
00993 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
00994 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
00995 }
00996
00997 buff = GetStringWithArgs(buff, str, args, last);
00998 }
00999 break;
01000 }
01001
01002 case SCC_SIGN_NAME: {
01003 const Sign *si = Sign::Get(GetInt32(&argv));
01004 if (si->name != NULL) {
01005 buff = strecpy(buff, si->name, last);
01006 } else {
01007 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, NULL, last);
01008 }
01009 break;
01010 }
01011
01012 case SCC_COMPANY_NAME: {
01013 const Company *c = Company::Get((CompanyID)GetInt32(&argv));
01014
01015 if (c->name != NULL) {
01016 buff = strecpy(buff, c->name, last);
01017 } else {
01018 int64 args[1];
01019 args[0] = c->name_2;
01020 buff = GetStringWithArgs(buff, c->name_1, args, last);
01021 }
01022 break;
01023 }
01024
01025 case SCC_COMPANY_NUM: {
01026 CompanyID company = (CompanyID)GetInt32(&argv);
01027
01028
01029 if (Company::IsValidHumanID(company)) {
01030 int64 args[1];
01031 args[0] = company + 1;
01032 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, args, last);
01033 }
01034 break;
01035 }
01036
01037 case SCC_PRESIDENT_NAME: {
01038 const Company *c = Company::Get((CompanyID)GetInt32(&argv));
01039
01040 if (c->president_name != NULL) {
01041 buff = strecpy(buff, c->president_name, last);
01042 } else {
01043 int64 args[1];
01044 args[0] = c->president_name_2;
01045 buff = GetStringWithArgs(buff, c->president_name_1, args, last);
01046 }
01047 break;
01048 }
01049
01050 case SCC_SETCASE: {
01051
01052
01053 modifier = (byte)*str++ << 24;
01054 break;
01055 }
01056
01057 case SCC_SWITCH_CASE: {
01058
01059
01060 uint num = (byte)*str++;
01061 while (num) {
01062 if ((byte)str[0] == casei) {
01063
01064 str += 3;
01065 break;
01066 }
01067
01068 str += 3 + (str[1] << 8) + str[2];
01069 num--;
01070 }
01071 break;
01072 }
01073
01074 default:
01075 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01076 break;
01077 }
01078 }
01079 *buff = '\0';
01080 return buff;
01081 }
01082
01083
01084 static char *StationGetSpecialString(char *buff, int x, const char *last)
01085 {
01086 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01087 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01088 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01089 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01090 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01091 *buff = '\0';
01092 return buff;
01093 }
01094
01095 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01096 {
01097 return GenerateTownNameString(buff, last, ind, seed);
01098 }
01099
01100 static const char * const _silly_company_names[] = {
01101 "Bloggs Brothers",
01102 "Tiny Transport Ltd.",
01103 "Express Travel",
01104 "Comfy-Coach & Co.",
01105 "Crush & Bump Ltd.",
01106 "Broken & Late Ltd.",
01107 "Sam Speedy & Son",
01108 "Supersonic Travel",
01109 "Mike's Motors",
01110 "Lightning International",
01111 "Pannik & Loozit Ltd.",
01112 "Inter-City Transport",
01113 "Getout & Pushit Ltd."
01114 };
01115
01116 static const char * const _surname_list[] = {
01117 "Adams",
01118 "Allan",
01119 "Baker",
01120 "Bigwig",
01121 "Black",
01122 "Bloggs",
01123 "Brown",
01124 "Campbell",
01125 "Gordon",
01126 "Hamilton",
01127 "Hawthorn",
01128 "Higgins",
01129 "Green",
01130 "Gribble",
01131 "Jones",
01132 "McAlpine",
01133 "MacDonald",
01134 "McIntosh",
01135 "Muir",
01136 "Murphy",
01137 "Nelson",
01138 "O'Donnell",
01139 "Parker",
01140 "Phillips",
01141 "Pilkington",
01142 "Quigley",
01143 "Sharkey",
01144 "Thomson",
01145 "Watkins"
01146 };
01147
01148 static const char * const _silly_surname_list[] = {
01149 "Grumpy",
01150 "Dozy",
01151 "Speedy",
01152 "Nosey",
01153 "Dribble",
01154 "Mushroom",
01155 "Cabbage",
01156 "Sniffle",
01157 "Fishy",
01158 "Swindle",
01159 "Sneaky",
01160 "Nutkins"
01161 };
01162
01163 static const char _initial_name_letters[] = {
01164 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01165 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01166 };
01167
01168 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01169 {
01170 const char * const *base;
01171 uint num;
01172
01173 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01174 base = _silly_surname_list;
01175 num = lengthof(_silly_surname_list);
01176 } else {
01177 base = _surname_list;
01178 num = lengthof(_surname_list);
01179 }
01180
01181 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01182 buff = strecpy(buff, " & Co.", last);
01183
01184 return buff;
01185 }
01186
01187 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01188 {
01189 char initial[] = "?. ";
01190 const char * const *base;
01191 uint num;
01192 uint i;
01193
01194 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01195 buff = strecpy(buff, initial, last);
01196
01197 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01198 if (i < sizeof(_initial_name_letters)) {
01199 initial[0] = _initial_name_letters[i];
01200 buff = strecpy(buff, initial, last);
01201 }
01202
01203 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01204 base = _silly_surname_list;
01205 num = lengthof(_silly_surname_list);
01206 } else {
01207 base = _surname_list;
01208 num = lengthof(_surname_list);
01209 }
01210
01211 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01212
01213 return buff;
01214 }
01215
01216 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const char *last)
01217 {
01218 switch (ind) {
01219 case 1:
01220 return strecpy(buff, _silly_company_names[GetInt32(&argv) & 0xFFFF], last);
01221
01222 case 2:
01223 return GenAndCoName(buff, GetInt32(&argv), last);
01224
01225 case 3:
01226 return GenPresidentName(buff, GetInt32(&argv), last);
01227 }
01228
01229
01230 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01231 buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv), last);
01232 return strecpy(buff, " Transport", last);
01233 }
01234
01235
01236 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01237 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01238 return strecpy(buff,
01239 i == _dynlang.curr ? _langpack->own_name : _dynlang.ent[i].name, last);
01240 }
01241
01242
01243 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01244 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01245 buff += seprintf(
01246 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01247 );
01248 return buff;
01249 }
01250
01251
01252 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01253 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01254 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01255 }
01256
01257 NOT_REACHED();
01258 }
01259
01260 #ifdef ENABLE_NETWORK
01261 extern void SortNetworkLanguages();
01262 #else
01263 static inline void SortNetworkLanguages() {}
01264 #endif
01265
01266 bool ReadLanguagePack(int lang_index)
01267 {
01268
01269 size_t len;
01270 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(_dynlang.ent[lang_index].file, &len, 200000);
01271 if (lang_pack == NULL) return false;
01272
01273
01274 const char *end = (char *)lang_pack + len + 1;
01275
01276
01277 if (end <= lang_pack->data ||
01278 lang_pack->ident != TO_LE32(LANGUAGE_PACK_IDENT) ||
01279 lang_pack->version != TO_LE32(LANGUAGE_PACK_VERSION)) {
01280 free(lang_pack);
01281 return false;
01282 }
01283
01284 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01285 for (uint i = 0; i < 32; i++) {
01286 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01287 }
01288 #endif
01289
01290 uint count = 0;
01291 for (uint i = 0; i < 32; i++) {
01292 uint num = lang_pack->offsets[i];
01293 _langtab_start[i] = count;
01294 _langtab_num[i] = num;
01295 count += num;
01296 }
01297
01298
01299 char **langpack_offs = MallocT<char *>(count);
01300
01301
01302 char *s = lang_pack->data;
01303 len = (byte)*s++;
01304 for (uint i = 0; i < count; i++) {
01305 if (s + len >= end) {
01306 free(lang_pack);
01307 free(langpack_offs);
01308 return false;
01309 }
01310 if (len >= 0xC0) {
01311 len = ((len & 0x3F) << 8) + (byte)*s++;
01312 if (s + len >= end) {
01313 free(lang_pack);
01314 free(langpack_offs);
01315 return false;
01316 }
01317 }
01318 langpack_offs[i] = s;
01319 s += len;
01320 len = (byte)*s;
01321 *s++ = '\0';
01322 }
01323
01324 free(_langpack);
01325 _langpack = lang_pack;
01326
01327 free(_langpack_offs);
01328 _langpack_offs = langpack_offs;
01329
01330 const char *c_file = strrchr(_dynlang.ent[lang_index].file, PATHSEPCHAR) + 1;
01331 strecpy(_dynlang.curr_file, c_file, lastof(_dynlang.curr_file));
01332
01333 _dynlang.curr = lang_index;
01334 _dynlang.text_dir = (TextDirection)lang_pack->text_dir;
01335 SetCurrentGrfLangID(_langpack->newgrflangid);
01336 SortNetworkLanguages();
01337 return true;
01338 }
01339
01340
01341
01342 #if !(defined(WIN32) || defined(__APPLE__))
01343
01349 const char *GetCurrentLocale(const char *param)
01350 {
01351 const char *env;
01352
01353 env = getenv("LANGUAGE");
01354 if (env != NULL) return env;
01355
01356 env = getenv("LC_ALL");
01357 if (env != NULL) return env;
01358
01359 if (param != NULL) {
01360 env = getenv(param);
01361 if (env != NULL) return env;
01362 }
01363
01364 return getenv("LANG");
01365 }
01366 #else
01367 const char *GetCurrentLocale(const char *param);
01368 #endif
01369
01370 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01371 {
01372 char stra[512];
01373 char strb[512];
01374 GetString(stra, *a, lastof(stra));
01375 GetString(strb, *b, lastof(strb));
01376
01377 return strcmp(stra, strb);
01378 }
01379
01387 static bool UniqueLanguageFile(const Language *langs, uint max, const char *language)
01388 {
01389 for (uint i = 0; i < max; i++) {
01390 const char *f_name = strrchr(langs[i].file, PATHSEPCHAR) + 1;
01391 if (strcmp(f_name, language) == 0) return false;
01392 }
01393
01394 return true;
01395 }
01396
01403 static bool GetLanguageFileHeader(const char *file, LanguagePack *hdr)
01404 {
01405 FILE *f = fopen(file, "rb");
01406 if (f == NULL) return false;
01407
01408 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01409 fclose(f);
01410
01411 bool ret = read == 1 &&
01412 hdr->ident == TO_LE32(LANGUAGE_PACK_IDENT) &&
01413 hdr->version == TO_LE32(LANGUAGE_PACK_VERSION);
01414
01415
01416 if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01417 return ret;
01418 }
01419
01428 static int GetLanguageList(Language *langs, int start, int max, const char *path)
01429 {
01430 int i = start;
01431
01432 DIR *dir = ttd_opendir(path);
01433 if (dir != NULL) {
01434 struct dirent *dirent;
01435 while ((dirent = readdir(dir)) != NULL && i < max) {
01436 const char *d_name = FS2OTTD(dirent->d_name);
01437 const char *extension = strrchr(d_name, '.');
01438
01439
01440 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01441
01442
01443 if (!UniqueLanguageFile(langs, i, d_name)) continue;
01444
01445 langs[i].file = str_fmt("%s%s", path, d_name);
01446
01447
01448 LanguagePack hdr;
01449 if (!GetLanguageFileHeader(langs[i].file, &hdr)) {
01450 free(langs[i].file);
01451 continue;
01452 }
01453
01454 i++;
01455 }
01456 closedir(dir);
01457 }
01458 return i - start;
01459 }
01460
01465 void InitializeLanguagePacks()
01466 {
01467 Searchpath sp;
01468 Language files[MAX_LANG];
01469 uint language_count = 0;
01470
01471 FOR_ALL_SEARCHPATHS(sp) {
01472 char path[MAX_PATH];
01473 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01474 language_count += GetLanguageList(files, language_count, lengthof(files), path);
01475 }
01476 if (language_count == 0) usererror("No available language packs (invalid versions?)");
01477
01478
01479 const char *lang = GetCurrentLocale("LC_MESSAGES");
01480 if (lang == NULL) lang = "en_GB";
01481
01482 int chosen_language = -1;
01483 int language_fallback = -1;
01484 int en_GB_fallback = 0;
01485
01486 DynamicLanguages *dl = &_dynlang;
01487 dl->num = 0;
01488
01489 for (uint i = 0; i < language_count; i++) {
01490
01491 LanguagePack hdr;
01492 if (!GetLanguageFileHeader(files[i].file, &hdr)) continue;
01493
01494 dl->ent[dl->num].file = files[i].file;
01495 dl->ent[dl->num].name = strdup(hdr.name);
01496
01497
01498
01499
01500 const char *lang_file = strrchr(dl->ent[dl->num].file, PATHSEPCHAR) + 1;
01501 if (strcmp(lang_file, dl->curr_file) == 0) chosen_language = dl->num;
01502
01503 if (chosen_language == -1) {
01504 if (strcmp (hdr.isocode, "en_GB") == 0) en_GB_fallback = dl->num;
01505 if (strncmp(hdr.isocode, lang, 5) == 0) chosen_language = dl->num;
01506 if (strncmp(hdr.isocode, lang, 2) == 0) language_fallback = dl->num;
01507 }
01508
01509 dl->num++;
01510 }
01511
01512 if (dl->num == 0) usererror("Invalid version of language packs");
01513
01514
01515
01516 if (chosen_language == -1) {
01517 chosen_language = (language_fallback != -1) ? language_fallback : en_GB_fallback;
01518 }
01519
01520 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", dl->ent[chosen_language].file);
01521 }
01522
01527 const char *GetCurrentLanguageIsoCode()
01528 {
01529 return _langpack->isocode;
01530 }
01531
01539 static bool FindMissingGlyphs(const char **str)
01540 {
01541 const Sprite *question_mark = GetGlyph(FS_NORMAL, '?');
01542 for (uint i = 0; i != 32; i++) {
01543 for (uint j = 0; j < _langtab_num[i]; j++) {
01544 const char *text = _langpack_offs[_langtab_start[i] + j];
01545 *str = text;
01546 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
01547 if (c == SCC_SETX) {
01548
01549
01550
01551 text++;
01552 } else if (c == SCC_SETXY) {
01553 text += 2;
01554 } else if (IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) {
01555
01556 return true;
01557 }
01558 }
01559 }
01560 }
01561 return false;
01562 }
01563
01574 void CheckForMissingGlyphsInLoadedLanguagePack()
01575 {
01576 #ifdef WITH_FREETYPE
01577
01578
01579 UninitFreeType();
01580 InitFreeType();
01581 #endif
01582
01583 const char *str;
01584 bool bad_font = FindMissingGlyphs(&str);
01585 #ifdef WITH_FREETYPE
01586 if (bad_font) {
01587
01588
01589 FreeTypeSettings backup;
01590 memcpy(&backup, &_freetype, sizeof(backup));
01591
01592 bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, str);
01593 if (success) {
01594 UninitFreeType();
01595 InitFreeType();
01596 }
01597
01598 memcpy(&_freetype, &backup, sizeof(backup));
01599
01600 if (success) {
01601 bad_font = FindMissingGlyphs(&str);
01602 if (bad_font) {
01603
01604
01605
01606 UninitFreeType();
01607 InitFreeType();
01608 }
01609 }
01610 }
01611 #endif
01612
01613 if (bad_font) {
01614
01615
01616
01617
01618
01619 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.");
01620 Utf8Encode(err_str, SCC_YELLOW);
01621 SetDParamStr(0, err_str);
01622 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, 0, 0);
01623
01624
01625 LoadStringWidthTable();
01626 return;
01627 }
01628
01629
01630 LoadStringWidthTable();
01631
01632 #if !defined(WITH_ICU)
01633
01634
01635
01636
01637
01638
01639
01640
01641
01642
01643
01644
01645
01646 if (_dynlang.text_dir != TD_LTR) {
01647 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01648 Utf8Encode(err_str, SCC_YELLOW);
01649 SetDParamStr(0, err_str);
01650 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, 0, 0);
01651 }
01652 #endif
01653 }