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