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