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 static const char *ParseStringChoice(const char *b, uint form, char *dst, int *dstlen)
00473 {
00474
00475 uint n = (byte)*b++;
00476 uint pos, i, mylen = 0, mypos = 0;
00477
00478 for (i = pos = 0; i != n; i++) {
00479 uint len = (byte)*b++;
00480 if (i == form) {
00481 mypos = pos;
00482 mylen = len;
00483 }
00484 pos += len;
00485 }
00486 *dstlen = mylen;
00487 memcpy(dst, b + mypos, mylen);
00488 return b + pos;
00489 }
00490
00491 struct Units {
00492 int s_m;
00493 int s_s;
00494 StringID velocity;
00495 int p_m;
00496 int p_s;
00497 StringID power;
00498 int w_m;
00499 int w_s;
00500 StringID s_weight;
00501 StringID l_weight;
00502 int v_m;
00503 int v_s;
00504 StringID s_volume;
00505 StringID l_volume;
00506 int f_m;
00507 int f_s;
00508 StringID force;
00509 };
00510
00511
00512 static const Units units[] = {
00513 {
00514 1, 0, STR_UNITS_VELOCITY_IMPERIAL,
00515 1, 0, STR_UNITS_POWER_IMPERIAL,
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 103, 6, STR_UNITS_VELOCITY_METRIC,
00522 1, 0, STR_UNITS_POWER_METRIC,
00523 1, 0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00524 1000, 0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00525 1, 0, STR_UNITS_FORCE_SI,
00526 },
00527 {
00528 1831, 12, STR_UNITS_VELOCITY_SI,
00529 764, 10, STR_UNITS_POWER_SI,
00530 1000, 0, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00531 1, 0, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00532 1, 0, STR_UNITS_FORCE_SI,
00533 },
00534 };
00535
00541 uint ConvertSpeedToDisplaySpeed(uint speed)
00542 {
00543 return (speed * units[_settings_game.locale.units].s_m) >> units[_settings_game.locale.units].s_s;
00544 }
00545
00551 uint ConvertDisplaySpeedToSpeed(uint speed)
00552 {
00553 return ((speed << units[_settings_game.locale.units].s_s) + units[_settings_game.locale.units].s_m / 2) / units[_settings_game.locale.units].s_m;
00554 }
00555
00556 static char *FormatString(char *buff, const char *str, const int64 *argv, uint casei, const char *last)
00557 {
00558 WChar b;
00559 const int64 *argv_orig = argv;
00560 uint modifier = 0;
00561
00562 while ((b = Utf8Consume(&str)) != '\0') {
00563 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00564
00565 b = RemapNewGRFStringControlCode(b, &buff, &str, (int64*)argv);
00566 if (b == 0) continue;
00567 }
00568
00569 switch (b) {
00570 case SCC_SETX:
00571 if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00572 buff += Utf8Encode(buff, SCC_SETX);
00573 *buff++ = *str++;
00574 }
00575 break;
00576
00577 case SCC_SETXY:
00578 if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00579 buff += Utf8Encode(buff, SCC_SETXY);
00580 *buff++ = *str++;
00581 *buff++ = *str++;
00582 }
00583 break;
00584
00585 case SCC_STRING_ID:
00586 buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, last);
00587 break;
00588
00589 case SCC_RAW_STRING_POINTER: {
00590 const char *str = (const char*)(size_t)GetInt64(&argv);
00591 buff = FormatString(buff, str, argv, casei, last);
00592 break;
00593 }
00594
00595 case SCC_DATE_LONG:
00596 buff = FormatYmdString(buff, GetInt32(&argv), last);
00597 break;
00598
00599 case SCC_DATE_SHORT:
00600 buff = FormatMonthAndYear(buff, GetInt32(&argv), last);
00601 break;
00602
00603 case SCC_VELOCITY: {
00604 int64 args[1];
00605 assert(_settings_game.locale.units < lengthof(units));
00606 args[0] = ConvertSpeedToDisplaySpeed(GetInt32(&argv) * 10 / 16);
00607 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].velocity), args, modifier >> 24, last);
00608 modifier = 0;
00609 break;
00610 }
00611
00612 case SCC_CURRENCY_COMPACT:
00613 buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), true, last);
00614 break;
00615
00616 case SCC_REVISION:
00617 buff = strecpy(buff, _openttd_revision, last);
00618 break;
00619
00620 case SCC_CARGO_SHORT: {
00621
00622
00623
00624 StringID cargo_str = GetCargo(GetInt32(&argv))->units_volume;
00625 switch (cargo_str) {
00626 case STR_TONS: {
00627 int64 args[1];
00628 assert(_settings_game.locale.units < lengthof(units));
00629 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00630 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00631 modifier = 0;
00632 break;
00633 }
00634
00635 case STR_LITERS: {
00636 int64 args[1];
00637 assert(_settings_game.locale.units < lengthof(units));
00638 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00639 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, modifier >> 24, last);
00640 modifier = 0;
00641 break;
00642 }
00643
00644 default:
00645 if (cargo_str >= 0xE000 && cargo_str < 0xF800) {
00646
00647
00648 buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00649 } else {
00650 buff = FormatCommaNumber(buff, GetInt32(&argv), last);
00651 buff = strecpy(buff, " ", last);
00652 buff = strecpy(buff, GetStringPtr(cargo_str), last);
00653 }
00654 break;
00655 }
00656 } break;
00657
00658 case SCC_STRING1: {
00659
00660 uint str = modifier + GetInt32(&argv);
00661 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 1), last);
00662 modifier = 0;
00663 break;
00664 }
00665
00666 case SCC_STRING2: {
00667
00668 uint str = modifier + GetInt32(&argv);
00669 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 2), last);
00670 modifier = 0;
00671 break;
00672 }
00673
00674 case SCC_STRING3: {
00675
00676 uint str = modifier + GetInt32(&argv);
00677 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 3), last);
00678 modifier = 0;
00679 break;
00680 }
00681
00682 case SCC_STRING4: {
00683
00684 uint str = modifier + GetInt32(&argv);
00685 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 4), last);
00686 modifier = 0;
00687 break;
00688 }
00689
00690 case SCC_STRING5: {
00691
00692 uint str = modifier + GetInt32(&argv);
00693 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 5), last);
00694 modifier = 0;
00695 break;
00696 }
00697
00698 case SCC_STATION_FEATURES: {
00699 buff = StationGetSpecialString(buff, GetInt32(&argv), last);
00700 break;
00701 }
00702
00703 case SCC_INDUSTRY_NAME: {
00704 const Industry *i = GetIndustry(GetInt32(&argv));
00705 int64 args[2];
00706
00707
00708 if (!i->IsValid()) break;
00709
00710
00711 args[0] = i->town->index;
00712 args[1] = GetIndustrySpec(i->type)->name;
00713 buff = FormatString(buff, GetStringPtr(STR_INDUSTRY_FORMAT), args, modifier >> 24, last);
00714 modifier = 0;
00715 break;
00716 }
00717
00718 case SCC_VOLUME: {
00719 int64 args[1];
00720 assert(_settings_game.locale.units < lengthof(units));
00721 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00722 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, modifier >> 24, last);
00723 modifier = 0;
00724 break;
00725 }
00726
00727 case SCC_GENDER_LIST: {
00728 const char *s = GetStringPtr(argv_orig[(byte)*str++]);
00729 int len;
00730 int gender = 0;
00731 if (s != NULL) {
00732 wchar_t c = Utf8Consume(&s);
00733
00734 if (c == SCC_SWITCH_CASE) {
00735
00736 for (uint num = (byte)*s++; num != 0; num--) s += 3 + (s[1] << 8) + s[2];
00737
00738 c = Utf8Consume(&s);
00739 }
00740
00741 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00742 }
00743 str = ParseStringChoice(str, gender, buff, &len);
00744 buff += len;
00745 break;
00746 }
00747
00748 case SCC_DATE_TINY: {
00749 buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_DATE_TINY, last);
00750 break;
00751 }
00752
00753 case SCC_DATE_ISO: {
00754 buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_DATE_ISO, last);
00755 break;
00756 }
00757
00758 case SCC_CARGO: {
00759
00760
00761
00762 CargoID cargo = GetInt32(&argv);
00763 StringID cargo_str = (cargo == CT_INVALID) ? STR_8838_N_A : GetCargo(cargo)->quantifier;
00764 buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00765 break;
00766 }
00767
00768 case SCC_POWER: {
00769 int64 args[1];
00770 assert(_settings_game.locale.units < lengthof(units));
00771 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].p_m >> units[_settings_game.locale.units].p_s;
00772 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].power), args, modifier >> 24, last);
00773 modifier = 0;
00774 break;
00775 }
00776
00777 case SCC_VOLUME_SHORT: {
00778 int64 args[1];
00779 assert(_settings_game.locale.units < lengthof(units));
00780 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00781 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_volume), args, modifier >> 24, last);
00782 modifier = 0;
00783 break;
00784 }
00785
00786 case SCC_WEIGHT: {
00787 int64 args[1];
00788 assert(_settings_game.locale.units < lengthof(units));
00789 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00790 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00791 modifier = 0;
00792 break;
00793 }
00794
00795 case SCC_WEIGHT_SHORT: {
00796 int64 args[1];
00797 assert(_settings_game.locale.units < lengthof(units));
00798 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00799 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_weight), args, modifier >> 24, last);
00800 modifier = 0;
00801 break;
00802 }
00803
00804 case SCC_FORCE: {
00805 int64 args[1];
00806 assert(_settings_game.locale.units < lengthof(units));
00807 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].f_m >> units[_settings_game.locale.units].f_s;
00808 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].force), args, modifier >> 24, last);
00809 modifier = 0;
00810 break;
00811 }
00812
00813 case SCC_SKIP:
00814 argv++;
00815 break;
00816
00817
00818
00819 case SCC_GENDER_INDEX:
00820 str++;
00821 break;
00822
00823 case SCC_STRING: {
00824 uint str = modifier + GetInt32(&argv);
00825
00826
00827
00828 buff = GetStringWithArgs(buff, str, argv, last);
00829 modifier = 0;
00830 break;
00831 }
00832
00833 case SCC_COMMA:
00834 buff = FormatCommaNumber(buff, GetInt64(&argv), last);
00835 break;
00836
00837 case SCC_ARG_INDEX:
00838 argv = argv_orig + (byte)*str++;
00839 break;
00840
00841 case SCC_PLURAL_LIST: {
00842 int64 v = argv_orig[(byte)*str++];
00843 int len;
00844 str = ParseStringChoice(str, DeterminePluralForm(v), buff, &len);
00845 buff += len;
00846 break;
00847 }
00848
00849 case SCC_NUM:
00850 buff = FormatNoCommaNumber(buff, GetInt64(&argv), last);
00851 break;
00852
00853 case SCC_HEX:
00854 buff = FormatHexNumber(buff, GetInt64(&argv), last);
00855 break;
00856
00857 case SCC_BYTES:
00858 buff = FormatBytes(buff, GetInt64(&argv), last);
00859 break;
00860
00861 case SCC_CURRENCY:
00862 buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
00863 break;
00864
00865 case SCC_WAYPOINT_NAME: {
00866 Waypoint *wp = GetWaypoint(GetInt32(&argv));
00867
00868 assert(wp->IsValid());
00869
00870 if (wp->name != NULL) {
00871 buff = strecpy(buff, wp->name, last);
00872 } else {
00873 int64 temp[2];
00874 temp[0] = wp->town_index;
00875 temp[1] = wp->town_cn + 1;
00876 StringID str = wp->town_cn == 0 ? STR_WAYPOINTNAME_CITY : STR_WAYPOINTNAME_CITY_SERIAL;
00877
00878 buff = GetStringWithArgs(buff, str, temp, last);
00879 }
00880 break;
00881 }
00882
00883 case SCC_STATION_NAME: {
00884 StationID sid = GetInt32(&argv);
00885
00886 if (!IsValidStationID(sid)) {
00887
00888
00889
00890 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, NULL, last);
00891 break;
00892 }
00893
00894 const Station *st = GetStation(sid);
00895 if (st->name != NULL) {
00896 buff = strecpy(buff, st->name, last);
00897 } else {
00898 StringID str = st->string_id;
00899 if (st->indtype != IT_INVALID) {
00900
00901 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
00902
00903
00904
00905
00906 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
00907 str = indsp->station_name;
00908 }
00909 }
00910
00911 int64 temp[3];
00912 temp[0] = STR_TOWN;
00913 temp[1] = st->town->index;
00914 temp[2] = st->index;
00915 buff = GetStringWithArgs(buff, str, temp, last);
00916 }
00917 break;
00918 }
00919
00920 case SCC_TOWN_NAME: {
00921 const Town *t = GetTown(GetInt32(&argv));
00922 int64 temp[1];
00923
00924 assert(t->IsValid());
00925
00926 temp[0] = t->townnameparts;
00927 uint32 grfid = t->townnamegrfid;
00928
00929 if (t->name != NULL) {
00930 buff = strecpy(buff, t->name, last);
00931 } else if (grfid == 0) {
00932
00933 buff = GetStringWithArgs(buff, t->townnametype, temp, last);
00934 } else {
00935
00936 if (GetGRFTownName(grfid) != NULL) {
00937
00938 buff = GRFTownNameGenerate(buff, t->townnamegrfid, t->townnametype, t->townnameparts, last);
00939 } else {
00940
00941 buff = GetStringWithArgs(buff, SPECSTR_TOWNNAME_ENGLISH, temp, last);
00942 }
00943 }
00944 break;
00945 }
00946
00947 case SCC_GROUP_NAME: {
00948 const Group *g = GetGroup(GetInt32(&argv));
00949
00950 assert(g->IsValid());
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_GROUP_NAME_FORMAT, args, last);
00959 }
00960 break;
00961 }
00962
00963 case SCC_ENGINE_NAME: {
00964 EngineID engine = (EngineID)GetInt32(&argv);
00965 const Engine *e = GetEngine(engine);
00966
00967 if (e->name != NULL) {
00968 buff = strecpy(buff, e->name, last);
00969 } else {
00970 buff = GetStringWithArgs(buff, e->info.string_id, NULL, last);
00971 }
00972 break;
00973 }
00974
00975 case SCC_VEHICLE_NAME: {
00976 const Vehicle *v = GetVehicle(GetInt32(&argv));
00977
00978 if (v->name != NULL) {
00979 buff = strecpy(buff, v->name, last);
00980 } else {
00981 int64 args[1];
00982 args[0] = v->unitnumber;
00983
00984 StringID str;
00985 switch (v->type) {
00986 default: NOT_REACHED();
00987 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
00988 case VEH_ROAD: str = STR_SV_ROADVEH_NAME; break;
00989 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
00990 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
00991 }
00992
00993 buff = GetStringWithArgs(buff, str, args, last);
00994 }
00995 break;
00996 }
00997
00998 case SCC_SIGN_NAME: {
00999 const Sign *si = GetSign(GetInt32(&argv));
01000 if (si->name != NULL) {
01001 buff = strecpy(buff, si->name, last);
01002 } else {
01003 buff = GetStringWithArgs(buff, STR_280A_SIGN, NULL, last);
01004 }
01005 break;
01006 }
01007
01008 case SCC_COMPANY_NAME: {
01009 const Company *c = GetCompany((CompanyID)GetInt32(&argv));
01010
01011 if (c->name != NULL) {
01012 buff = strecpy(buff, c->name, last);
01013 } else {
01014 int64 args[1];
01015 args[0] = c->name_2;
01016 buff = GetStringWithArgs(buff, c->name_1, args, last);
01017 }
01018 break;
01019 }
01020
01021 case SCC_COMPANY_NUM: {
01022 CompanyID company = (CompanyID)GetInt32(&argv);
01023
01024
01025 if (IsValidCompanyID(company) && IsHumanCompany(company)) {
01026 int64 args[1];
01027 args[0] = company + 1;
01028 buff = GetStringWithArgs(buff, STR_7002_COMPANY, args, last);
01029 }
01030 break;
01031 }
01032
01033 case SCC_PRESIDENT_NAME: {
01034 const Company *c = GetCompany((CompanyID)GetInt32(&argv));
01035
01036 if (c->president_name != NULL) {
01037 buff = strecpy(buff, c->president_name, last);
01038 } else {
01039 int64 args[1];
01040 args[0] = c->president_name_2;
01041 buff = GetStringWithArgs(buff, c->president_name_1, args, last);
01042 }
01043 break;
01044 }
01045
01046 case SCC_SETCASE: {
01047
01048
01049 modifier = (byte)*str++ << 24;
01050 break;
01051 }
01052
01053 case SCC_SWITCH_CASE: {
01054
01055
01056 uint num = (byte)*str++;
01057 while (num) {
01058 if ((byte)str[0] == casei) {
01059
01060 str += 3;
01061 break;
01062 }
01063
01064 str += 3 + (str[1] << 8) + str[2];
01065 num--;
01066 }
01067 break;
01068 }
01069
01070 default:
01071 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01072 break;
01073 }
01074 }
01075 *buff = '\0';
01076 return buff;
01077 }
01078
01079
01080 static char *StationGetSpecialString(char *buff, int x, const char *last)
01081 {
01082 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01083 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01084 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01085 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01086 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01087 *buff = '\0';
01088 return buff;
01089 }
01090
01091 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01092 {
01093 char name[512];
01094
01095 _town_name_generators[ind](name, seed, lastof(name));
01096 return strecpy(buff, name, last);
01097 }
01098
01099 static const char * const _silly_company_names[] = {
01100 "Bloggs Brothers",
01101 "Tiny Transport Ltd.",
01102 "Express Travel",
01103 "Comfy-Coach & Co.",
01104 "Crush & Bump Ltd.",
01105 "Broken & Late Ltd.",
01106 "Sam Speedy & Son",
01107 "Supersonic Travel",
01108 "Mike's Motors",
01109 "Lightning International",
01110 "Pannik & Loozit Ltd.",
01111 "Inter-City Transport",
01112 "Getout & Pushit Ltd."
01113 };
01114
01115 static const char * const _surname_list[] = {
01116 "Adams",
01117 "Allan",
01118 "Baker",
01119 "Bigwig",
01120 "Black",
01121 "Bloggs",
01122 "Brown",
01123 "Campbell",
01124 "Gordon",
01125 "Hamilton",
01126 "Hawthorn",
01127 "Higgins",
01128 "Green",
01129 "Gribble",
01130 "Jones",
01131 "McAlpine",
01132 "MacDonald",
01133 "McIntosh",
01134 "Muir",
01135 "Murphy",
01136 "Nelson",
01137 "O'Donnell",
01138 "Parker",
01139 "Phillips",
01140 "Pilkington",
01141 "Quigley",
01142 "Sharkey",
01143 "Thomson",
01144 "Watkins"
01145 };
01146
01147 static const char * const _silly_surname_list[] = {
01148 "Grumpy",
01149 "Dozy",
01150 "Speedy",
01151 "Nosey",
01152 "Dribble",
01153 "Mushroom",
01154 "Cabbage",
01155 "Sniffle",
01156 "Fishy",
01157 "Swindle",
01158 "Sneaky",
01159 "Nutkins"
01160 };
01161
01162 static const char _initial_name_letters[] = {
01163 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01164 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01165 };
01166
01167 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01168 {
01169 const char * const *base;
01170 uint num;
01171
01172 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01173 base = _silly_surname_list;
01174 num = lengthof(_silly_surname_list);
01175 } else {
01176 base = _surname_list;
01177 num = lengthof(_surname_list);
01178 }
01179
01180 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01181 buff = strecpy(buff, " & Co.", last);
01182
01183 return buff;
01184 }
01185
01186 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01187 {
01188 char initial[] = "?. ";
01189 const char * const *base;
01190 uint num;
01191 uint i;
01192
01193 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01194 buff = strecpy(buff, initial, last);
01195
01196 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01197 if (i < sizeof(_initial_name_letters)) {
01198 initial[0] = _initial_name_letters[i];
01199 buff = strecpy(buff, initial, last);
01200 }
01201
01202 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01203 base = _silly_surname_list;
01204 num = lengthof(_silly_surname_list);
01205 } else {
01206 base = _surname_list;
01207 num = lengthof(_surname_list);
01208 }
01209
01210 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01211
01212 return buff;
01213 }
01214
01215 static char *GetSpecialNameString(char *buff, int ind, const int64 *argv, const char *last)
01216 {
01217 switch (ind) {
01218 case 1:
01219 return strecpy(buff, _silly_company_names[GetInt32(&argv) & 0xFFFF], last);
01220
01221 case 2:
01222 return GenAndCoName(buff, GetInt32(&argv), last);
01223
01224 case 3:
01225 return GenPresidentName(buff, GetInt32(&argv), last);
01226
01227 case 4:
01228 return strecpy(buff, _origin_songs_specs[GetInt32(&argv) - 1].song_name, last);
01229 }
01230
01231
01232 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01233 buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv), last);
01234 return strecpy(buff, " Transport", last);
01235 }
01236
01237
01238 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01239 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01240 return strecpy(buff,
01241 i == _dynlang.curr ? _langpack->own_name : _dynlang.ent[i].name, last);
01242 }
01243
01244
01245 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01246 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01247 buff += seprintf(
01248 buff, last, "%dx%d", _resolutions[i].width, _resolutions[i].height
01249 );
01250 return buff;
01251 }
01252
01253
01254 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01255 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01256 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01257 }
01258
01259 assert(0);
01260 return NULL;
01261 }
01262
01263 #ifdef ENABLE_NETWORK
01264 extern void SortNetworkLanguages();
01265 #else
01266 static inline void SortNetworkLanguages() {}
01267 #endif
01268
01269 bool ReadLanguagePack(int lang_index)
01270 {
01271 int tot_count, i;
01272 size_t len;
01273 char **langpack_offs;
01274 char *s;
01275
01276 LanguagePack *lang_pack = (LanguagePack*)ReadFileToMem(_dynlang.ent[lang_index].file, &len, 200000);
01277
01278 if (lang_pack == NULL) return false;
01279 if (len < sizeof(LanguagePack) ||
01280 lang_pack->ident != TO_LE32(LANGUAGE_PACK_IDENT) ||
01281 lang_pack->version != TO_LE32(LANGUAGE_PACK_VERSION)) {
01282 free(lang_pack);
01283 return false;
01284 }
01285
01286 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01287 for (i = 0; i != 32; i++) {
01288 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01289 }
01290 #endif
01291
01292 tot_count = 0;
01293 for (i = 0; i != 32; i++) {
01294 uint num = lang_pack->offsets[i];
01295 _langtab_start[i] = tot_count;
01296 _langtab_num[i] = num;
01297 tot_count += num;
01298 }
01299
01300
01301 langpack_offs = MallocT<char*>(tot_count);
01302
01303
01304 s = lang_pack->data;
01305 for (i = 0; i != tot_count; i++) {
01306 len = (byte)*s;
01307 *s++ = '\0';
01308 if (len >= 0xC0) len = ((len & 0x3F) << 8) + (byte)*s++;
01309 langpack_offs[i] = s;
01310 s += len;
01311 }
01312
01313 free(_langpack);
01314 _langpack = lang_pack;
01315
01316 free(_langpack_offs);
01317 _langpack_offs = langpack_offs;
01318
01319 const char *c_file = strrchr(_dynlang.ent[lang_index].file, PATHSEPCHAR) + 1;
01320 strecpy(_dynlang.curr_file, c_file, lastof(_dynlang.curr_file));
01321
01322 _dynlang.curr = lang_index;
01323 _dynlang.text_dir = (TextDirection)lang_pack->text_dir;
01324 SetCurrentGrfLangID(_langpack->newgrflangid);
01325 SortNetworkLanguages();
01326 return true;
01327 }
01328
01329
01330
01331 #if !(defined(WIN32) || defined(__APPLE__))
01332
01338 const char *GetCurrentLocale(const char *param)
01339 {
01340 const char *env;
01341
01342 env = getenv("LANGUAGE");
01343 if (env != NULL) return env;
01344
01345 env = getenv("LC_ALL");
01346 if (env != NULL) return env;
01347
01348 if (param != NULL) {
01349 env = getenv(param);
01350 if (env != NULL) return env;
01351 }
01352
01353 return getenv("LANG");
01354 }
01355 #else
01356 const char *GetCurrentLocale(const char *param);
01357 #endif
01358
01359 int CDECL StringIDSorter(const void *a, const void *b)
01360 {
01361 const StringID va = *(const StringID*)a;
01362 const StringID vb = *(const StringID*)b;
01363 char stra[512];
01364 char strb[512];
01365 GetString(stra, va, lastof(stra));
01366 GetString(strb, vb, lastof(strb));
01367
01368 return strcmp(stra, strb);
01369 }
01370
01378 static bool UniqueLanguageFile(const Language *langs, uint max, const char *language)
01379 {
01380 for (uint i = 0; i < max; i++) {
01381 const char *f_name = strrchr(langs[i].file, PATHSEPCHAR) + 1;
01382 if (strcmp(f_name, language) == 0) return false;
01383 }
01384
01385 return true;
01386 }
01387
01394 static bool GetLanguageFileHeader(const char *file, LanguagePack *hdr)
01395 {
01396 FILE *f = fopen(file, "rb");
01397 if (f == NULL) return false;
01398
01399 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01400 fclose(f);
01401
01402 bool ret = read == 1 &&
01403 hdr->ident == TO_LE32(LANGUAGE_PACK_IDENT) &&
01404 hdr->version == TO_LE32(LANGUAGE_PACK_VERSION);
01405
01406
01407 if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01408 return ret;
01409 }
01410
01419 static int GetLanguageList(Language *langs, int start, int max, const char *path)
01420 {
01421 int i = start;
01422
01423 DIR *dir = ttd_opendir(path);
01424 if (dir != NULL) {
01425 struct dirent *dirent;
01426 while ((dirent = readdir(dir)) != NULL && i < max) {
01427 const char *d_name = FS2OTTD(dirent->d_name);
01428 const char *extension = strrchr(d_name, '.');
01429
01430
01431 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01432
01433
01434 if (!UniqueLanguageFile(langs, i, d_name)) continue;
01435
01436 langs[i].file = str_fmt("%s%s", path, d_name);
01437
01438
01439 LanguagePack hdr;
01440 if (!GetLanguageFileHeader(langs[i].file, &hdr)) {
01441 free(langs[i].file);
01442 continue;
01443 }
01444
01445 i++;
01446 }
01447 closedir(dir);
01448 }
01449 return i - start;
01450 }
01451
01456 void InitializeLanguagePacks()
01457 {
01458 Searchpath sp;
01459 Language files[MAX_LANG];
01460 uint language_count = 0;
01461
01462 FOR_ALL_SEARCHPATHS(sp) {
01463 char path[MAX_PATH];
01464 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01465 language_count += GetLanguageList(files, language_count, lengthof(files), path);
01466 }
01467 if (language_count == 0) usererror("No available language packs (invalid versions?)");
01468
01469
01470 const char *lang = GetCurrentLocale("LC_MESSAGES");
01471 if (lang == NULL) lang = "en_GB";
01472
01473 int chosen_language = -1;
01474 int language_fallback = -1;
01475 int en_GB_fallback = 0;
01476
01477 DynamicLanguages *dl = &_dynlang;
01478 dl->num = 0;
01479
01480 for (uint i = 0; i < language_count; i++) {
01481
01482 LanguagePack hdr;
01483 if (!GetLanguageFileHeader(files[i].file, &hdr)) continue;
01484
01485 dl->ent[dl->num].file = files[i].file;
01486 dl->ent[dl->num].name = strdup(hdr.name);
01487
01488
01489
01490
01491 const char *lang_file = strrchr(dl->ent[dl->num].file, PATHSEPCHAR) + 1;
01492 if (strcmp(lang_file, dl->curr_file) == 0) chosen_language = dl->num;
01493
01494 if (chosen_language == -1) {
01495 if (strcmp (hdr.isocode, "en_GB") == 0) en_GB_fallback = dl->num;
01496 if (strncmp(hdr.isocode, lang, 5) == 0) chosen_language = dl->num;
01497 if (strncmp(hdr.isocode, lang, 2) == 0) language_fallback = dl->num;
01498 }
01499
01500 dl->num++;
01501 }
01502
01503 if (dl->num == 0) usererror("Invalid version of language packs");
01504
01505
01506
01507 if (chosen_language == -1) {
01508 chosen_language = (language_fallback != -1) ? language_fallback : en_GB_fallback;
01509 }
01510
01511 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", dl->ent[chosen_language].file);
01512 }
01513
01524 void CheckForMissingGlyphsInLoadedLanguagePack()
01525 {
01526 #ifdef WITH_FREETYPE
01527
01528
01529 UninitFreeType();
01530 InitFreeType();
01531 bool retry = false;
01532 #endif
01533
01534 for (;;) {
01535 const Sprite *question_mark = GetGlyph(FS_NORMAL, '?');
01536
01537 for (uint i = 0; i != 32; i++) {
01538 for (uint j = 0; j < _langtab_num[i]; j++) {
01539 const char *string = _langpack_offs[_langtab_start[i] + j];
01540 WChar c;
01541 while ((c = Utf8Consume(&string)) != '\0') {
01542 if (c == SCC_SETX) {
01543
01544
01545
01546
01547
01548
01549 string++;
01550 } else if (c == SCC_SETXY) {
01551 string += 2;
01552 } else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) {
01553 #ifdef WITH_FREETYPE
01554 if (!retry) {
01555
01556
01557
01558 retry = true;
01559
01560 FreeTypeSettings backup;
01561 memcpy(&backup, &_freetype, sizeof(backup));
01562
01563 bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid);
01564 if (success) {
01565 UninitFreeType();
01566 InitFreeType();
01567 }
01568
01569 memcpy(&_freetype, &backup, sizeof(backup));
01570
01571 if (success) continue;
01572 } else {
01573
01574
01575
01576 UninitFreeType();
01577 InitFreeType();
01578 }
01579 #endif
01580
01581
01582
01583
01584
01585
01586
01587
01588
01589
01590
01591
01592
01593 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.");
01594 Utf8Encode(err_str, SCC_YELLOW);
01595 SetDParamStr(0, err_str);
01596 ShowErrorMessage(INVALID_STRING_ID, STR_JUST_RAW_STRING, 0, 0);
01597
01598
01599 LoadStringWidthTable();
01600 return;
01601 }
01602 }
01603 }
01604 }
01605 break;
01606 }
01607
01608
01609 LoadStringWidthTable();
01610
01611 #if !defined(WITH_ICU)
01612
01613
01614
01615
01616
01617
01618
01619
01620
01621
01622
01623
01624
01625 if (_dynlang.text_dir != TD_LTR) {
01626 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01627 Utf8Encode(err_str, SCC_YELLOW);
01628 SetDParamStr(0, err_str);
01629 ShowErrorMessage(INVALID_STRING_ID, STR_JUST_RAW_STRING, 0, 0);
01630 }
01631 #endif
01632 }