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