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