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