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