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