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 static bool _keep_gender_data = false;
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
00723 WChar fmt = SCC_CONTROL_START + (byte)*str++;
00724 byte offset = (byte)*str++;
00725
00726
00727
00728
00729 char input[4 + 1];
00730 char *p = input + Utf8Encode(input, fmt);
00731 *p = '\0';
00732
00733
00734 char buf[256];
00735 bool old_kgd = _keep_gender_data;
00736 _keep_gender_data = true;
00737 p = FormatString(buf, input, argv_orig + offset, 0, lastof(buf));
00738 _keep_gender_data = old_kgd;
00739 *p = '\0';
00740
00741
00742 int gender = 0;
00743 const char *s = buf;
00744 WChar c = Utf8Consume(&s);
00745
00746 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00747 str = ParseStringChoice(str, gender, &buff, last);
00748 break;
00749 }
00750
00751 case SCC_DATE_TINY: {
00752 buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_FORMAT_DATE_TINY, last);
00753 break;
00754 }
00755
00756 case SCC_DATE_ISO: {
00757 buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_FORMAT_DATE_ISO, last);
00758 break;
00759 }
00760
00761 case SCC_CARGO: {
00762
00763 CargoID cargo = GetInt32(&argv);
00764 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
00765 buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00766 break;
00767 }
00768
00769 case SCC_POWER: {
00770 int64 args[1];
00771 assert(_settings_game.locale.units < lengthof(units));
00772 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].p_m >> units[_settings_game.locale.units].p_s;
00773 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].power), args, modifier >> 24, last);
00774 modifier = 0;
00775 break;
00776 }
00777
00778 case SCC_VOLUME_SHORT: {
00779 int64 args[1];
00780 assert(_settings_game.locale.units < lengthof(units));
00781 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00782 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_volume), args, modifier >> 24, last);
00783 modifier = 0;
00784 break;
00785 }
00786
00787 case SCC_WEIGHT: {
00788 int64 args[1];
00789 assert(_settings_game.locale.units < lengthof(units));
00790 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00791 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00792 modifier = 0;
00793 break;
00794 }
00795
00796 case SCC_WEIGHT_SHORT: {
00797 int64 args[1];
00798 assert(_settings_game.locale.units < lengthof(units));
00799 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00800 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_weight), args, modifier >> 24, last);
00801 modifier = 0;
00802 break;
00803 }
00804
00805 case SCC_FORCE: {
00806 int64 args[1];
00807 assert(_settings_game.locale.units < lengthof(units));
00808 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].f_m >> units[_settings_game.locale.units].f_s;
00809 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].force), args, modifier >> 24, last);
00810 modifier = 0;
00811 break;
00812 }
00813
00814
00815
00816 case SCC_GENDER_INDEX:
00817 if (_keep_gender_data) {
00818 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
00819 *buff++ = *str++;
00820 } else {
00821 str++;
00822 }
00823 break;
00824
00825 case SCC_STRING: {
00826 uint str = modifier + GetInt32(&argv);
00827
00828
00829
00830 buff = GetStringWithArgs(buff, str, argv, last);
00831 modifier = 0;
00832 break;
00833 }
00834
00835 case SCC_COMMA:
00836 buff = FormatCommaNumber(buff, GetInt64(&argv), last);
00837 break;
00838
00839 case SCC_ARG_INDEX:
00840 argv = argv_orig + (byte)*str++;
00841 break;
00842
00843 case SCC_PLURAL_LIST: {
00844 int64 v = argv_orig[(byte)*str++];
00845 str = ParseStringChoice(str, DeterminePluralForm(v), &buff, last);
00846 break;
00847 }
00848
00849 case SCC_NUM:
00850 buff = FormatNoCommaNumber(buff, GetInt64(&argv), last);
00851 break;
00852
00853 case SCC_ZEROFILL_NUM: {
00854 int64 num = GetInt64(&argv);
00855 buff = FormatZerofillNumber(buff, num, GetInt64(&argv), last);
00856 } break;
00857
00858 case SCC_HEX:
00859 buff = FormatHexNumber(buff, GetInt64(&argv), last);
00860 break;
00861
00862 case SCC_BYTES:
00863 buff = FormatBytes(buff, GetInt64(&argv), last);
00864 break;
00865
00866 case SCC_CURRENCY:
00867 buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
00868 break;
00869
00870 case SCC_WAYPOINT_NAME: {
00871 Waypoint *wp = Waypoint::Get(GetInt32(&argv));
00872
00873 assert(wp != NULL);
00874
00875 if (wp->name != NULL) {
00876 buff = strecpy(buff, wp->name, last);
00877 } else {
00878 int64 temp[2];
00879 temp[0] = wp->town->index;
00880 temp[1] = wp->town_cn + 1;
00881 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
00882 if (wp->town_cn != 0) str++;
00883 buff = GetStringWithArgs(buff, str, temp, last);
00884 }
00885 break;
00886 }
00887
00888 case SCC_STATION_NAME: {
00889 StationID sid = GetInt32(&argv);
00890 const Station *st = Station::GetIfValid(sid);
00891
00892 if (st == NULL) {
00893
00894
00895
00896 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, NULL, last);
00897 break;
00898 }
00899
00900 if (st->name != NULL) {
00901 buff = strecpy(buff, st->name, last);
00902 } else {
00903 StringID str = st->string_id;
00904 if (st->indtype != IT_INVALID) {
00905
00906 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
00907
00908
00909
00910
00911 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
00912 str = indsp->station_name;
00913 }
00914 }
00915
00916 int64 temp[3];
00917 temp[0] = STR_TOWN_NAME;
00918 temp[1] = st->town->index;
00919 temp[2] = st->index;
00920 buff = GetStringWithArgs(buff, str, temp, last);
00921 }
00922 break;
00923 }
00924
00925 case SCC_TOWN_NAME: {
00926 const Town *t = Town::Get(GetInt32(&argv));
00927
00928 assert(t != NULL);
00929
00930 if (t->name != NULL) {
00931 buff = strecpy(buff, t->name, last);
00932 } else {
00933 buff = GetTownName(buff, t, last);
00934 }
00935 break;
00936 }
00937
00938 case SCC_GROUP_NAME: {
00939 const Group *g = Group::Get(GetInt32(&argv));
00940
00941 assert(g != NULL);
00942
00943 if (g->name != NULL) {
00944 buff = strecpy(buff, g->name, last);
00945 } else {
00946 int64 args[1];
00947
00948 args[0] = g->index;
00949 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, args, last);
00950 }
00951 break;
00952 }
00953
00954 case SCC_ENGINE_NAME: {
00955 EngineID engine = (EngineID)GetInt32(&argv);
00956 const Engine *e = Engine::Get(engine);
00957
00958 assert(e != NULL);
00959
00960 if (e->name != NULL) {
00961 buff = strecpy(buff, e->name, last);
00962 } else {
00963 buff = GetStringWithArgs(buff, e->info.string_id, NULL, last);
00964 }
00965 break;
00966 }
00967
00968 case SCC_VEHICLE_NAME: {
00969 const Vehicle *v = Vehicle::Get(GetInt32(&argv));
00970
00971 assert(v != NULL);
00972
00973 if (v->name != NULL) {
00974 buff = strecpy(buff, v->name, last);
00975 } else {
00976 int64 args[1];
00977 args[0] = v->unitnumber;
00978
00979 StringID str;
00980 switch (v->type) {
00981 default: NOT_REACHED();
00982 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
00983 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
00984 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
00985 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
00986 }
00987
00988 buff = GetStringWithArgs(buff, str, args, last);
00989 }
00990 break;
00991 }
00992
00993 case SCC_SIGN_NAME: {
00994 const Sign *si = Sign::Get(GetInt32(&argv));
00995 if (si->name != NULL) {
00996 buff = strecpy(buff, si->name, last);
00997 } else {
00998 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, NULL, last);
00999 }
01000 break;
01001 }
01002
01003 case SCC_COMPANY_NAME: {
01004 const Company *c = Company::Get((CompanyID)GetInt32(&argv));
01005
01006 if (c->name != NULL) {
01007 buff = strecpy(buff, c->name, last);
01008 } else {
01009 int64 args[1];
01010 args[0] = c->name_2;
01011 buff = GetStringWithArgs(buff, c->name_1, args, last);
01012 }
01013 break;
01014 }
01015
01016 case SCC_COMPANY_NUM: {
01017 CompanyID company = (CompanyID)GetInt32(&argv);
01018
01019
01020 if (Company::IsValidHumanID(company)) {
01021 int64 args[1];
01022 args[0] = company + 1;
01023 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, args, last);
01024 }
01025 break;
01026 }
01027
01028 case SCC_PRESIDENT_NAME: {
01029 const Company *c = Company::Get((CompanyID)GetInt32(&argv));
01030
01031 if (c->president_name != NULL) {
01032 buff = strecpy(buff, c->president_name, last);
01033 } else {
01034 int64 args[1];
01035 args[0] = c->president_name_2;
01036 buff = GetStringWithArgs(buff, c->president_name_1, args, last);
01037 }
01038 break;
01039 }
01040
01041 case SCC_SETCASE: {
01042
01043
01044 modifier = (byte)*str++ << 24;
01045 break;
01046 }
01047
01048 case SCC_SWITCH_CASE: {
01049
01050
01051 uint num = (byte)*str++;
01052 while (num) {
01053 if ((byte)str[0] == casei) {
01054
01055 str += 3;
01056 break;
01057 }
01058
01059 str += 3 + (str[1] << 8) + str[2];
01060 num--;
01061 }
01062 break;
01063 }
01064
01065 default:
01066 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01067 break;
01068 }
01069 }
01070 *buff = '\0';
01071 return buff;
01072 }
01073
01074
01075 static char *StationGetSpecialString(char *buff, int x, const char *last)
01076 {
01077 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01078 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01079 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01080 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01081 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01082 *buff = '\0';
01083 return buff;
01084 }
01085
01086 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01087 {
01088 return GenerateTownNameString(buff, last, ind, seed);
01089 }
01090
01091 static const char * const _silly_company_names[] = {
01092 "Bloggs Brothers",
01093 "Tiny Transport Ltd.",
01094 "Express Travel",
01095 "Comfy-Coach & Co.",
01096 "Crush & Bump Ltd.",
01097 "Broken & Late Ltd.",
01098 "Sam Speedy & Son",
01099 "Supersonic Travel",
01100 "Mike's Motors",
01101 "Lightning International",
01102 "Pannik & Loozit Ltd.",
01103 "Inter-City Transport",
01104 "Getout & Pushit Ltd."
01105 };
01106
01107 static const char * const _surname_list[] = {
01108 "Adams",
01109 "Allan",
01110 "Baker",
01111 "Bigwig",
01112 "Black",
01113 "Bloggs",
01114 "Brown",
01115 "Campbell",
01116 "Gordon",
01117 "Hamilton",
01118 "Hawthorn",
01119 "Higgins",
01120 "Green",
01121 "Gribble",
01122 "Jones",
01123 "McAlpine",
01124 "MacDonald",
01125 "McIntosh",
01126 "Muir",
01127 "Murphy",
01128 "Nelson",
01129 "O'Donnell",
01130 "Parker",
01131 "Phillips",
01132 "Pilkington",
01133 "Quigley",
01134 "Sharkey",
01135 "Thomson",
01136 "Watkins"
01137 };
01138
01139 static const char * const _silly_surname_list[] = {
01140 "Grumpy",
01141 "Dozy",
01142 "Speedy",
01143 "Nosey",
01144 "Dribble",
01145 "Mushroom",
01146 "Cabbage",
01147 "Sniffle",
01148 "Fishy",
01149 "Swindle",
01150 "Sneaky",
01151 "Nutkins"
01152 };
01153
01154 static const char _initial_name_letters[] = {
01155 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01156 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01157 };
01158
01159 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01160 {
01161 const char * const *base;
01162 uint num;
01163
01164 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01165 base = _silly_surname_list;
01166 num = lengthof(_silly_surname_list);
01167 } else {
01168 base = _surname_list;
01169 num = lengthof(_surname_list);
01170 }
01171
01172 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01173 buff = strecpy(buff, " & Co.", last);
01174
01175 return buff;
01176 }
01177
01178 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01179 {
01180 char initial[] = "?. ";
01181 const char * const *base;
01182 uint num;
01183 uint i;
01184
01185 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01186 buff = strecpy(buff, initial, last);
01187
01188 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01189 if (i < sizeof(_initial_name_letters)) {
01190 initial[0] = _initial_name_letters[i];
01191 buff = strecpy(buff, initial, last);
01192 }
01193
01194 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01195 base = _silly_surname_list;
01196 num = lengthof(_silly_surname_list);
01197 } else {
01198 base = _surname_list;
01199 num = lengthof(_surname_list);
01200 }
01201
01202 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01203
01204 return buff;
01205 }
01206
01207 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const char *last)
01208 {
01209 switch (ind) {
01210 case 1:
01211 return strecpy(buff, _silly_company_names[GetInt32(&argv) & 0xFFFF], last);
01212
01213 case 2:
01214 return GenAndCoName(buff, GetInt32(&argv), last);
01215
01216 case 3:
01217 return GenPresidentName(buff, GetInt32(&argv), last);
01218 }
01219
01220
01221 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01222 buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv), last);
01223 return strecpy(buff, " Transport", last);
01224 }
01225
01226
01227 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01228 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01229 return strecpy(buff,
01230 i == _dynlang.curr ? _langpack->own_name : _dynlang.ent[i].name, last);
01231 }
01232
01233
01234 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01235 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01236 buff += seprintf(
01237 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01238 );
01239 return buff;
01240 }
01241
01242
01243 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01244 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01245 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01246 }
01247
01248 NOT_REACHED();
01249 }
01250
01251 #ifdef ENABLE_NETWORK
01252 extern void SortNetworkLanguages();
01253 #else
01254 static inline void SortNetworkLanguages() {}
01255 #endif
01256
01257 bool ReadLanguagePack(int lang_index)
01258 {
01259
01260 size_t len;
01261 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(_dynlang.ent[lang_index].file, &len, 200000);
01262 if (lang_pack == NULL) return false;
01263
01264
01265 const char *end = (char *)lang_pack + len + 1;
01266
01267
01268 if (end <= lang_pack->data ||
01269 lang_pack->ident != TO_LE32(LANGUAGE_PACK_IDENT) ||
01270 lang_pack->version != TO_LE32(LANGUAGE_PACK_VERSION)) {
01271 free(lang_pack);
01272 return false;
01273 }
01274
01275 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01276 for (uint i = 0; i < 32; i++) {
01277 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01278 }
01279 #endif
01280
01281 uint count = 0;
01282 for (uint i = 0; i < 32; i++) {
01283 uint num = lang_pack->offsets[i];
01284 _langtab_start[i] = count;
01285 _langtab_num[i] = num;
01286 count += num;
01287 }
01288
01289
01290 char **langpack_offs = MallocT<char *>(count);
01291
01292
01293 char *s = lang_pack->data;
01294 len = (byte)*s++;
01295 for (uint i = 0; i < count; i++) {
01296 if (s + len >= end) {
01297 free(lang_pack);
01298 free(langpack_offs);
01299 return false;
01300 }
01301 if (len >= 0xC0) {
01302 len = ((len & 0x3F) << 8) + (byte)*s++;
01303 if (s + len >= end) {
01304 free(lang_pack);
01305 free(langpack_offs);
01306 return false;
01307 }
01308 }
01309 langpack_offs[i] = s;
01310 s += len;
01311 len = (byte)*s;
01312 *s++ = '\0';
01313 }
01314
01315 free(_langpack);
01316 _langpack = lang_pack;
01317
01318 free(_langpack_offs);
01319 _langpack_offs = langpack_offs;
01320
01321 const char *c_file = strrchr(_dynlang.ent[lang_index].file, PATHSEPCHAR) + 1;
01322 strecpy(_dynlang.curr_file, c_file, lastof(_dynlang.curr_file));
01323
01324 _dynlang.curr = lang_index;
01325 _dynlang.text_dir = (TextDirection)lang_pack->text_dir;
01326 SetCurrentGrfLangID(_langpack->newgrflangid);
01327 SortNetworkLanguages();
01328 return true;
01329 }
01330
01331
01332
01333 #if !(defined(WIN32) || defined(__APPLE__))
01334
01340 const char *GetCurrentLocale(const char *param)
01341 {
01342 const char *env;
01343
01344 env = getenv("LANGUAGE");
01345 if (env != NULL) return env;
01346
01347 env = getenv("LC_ALL");
01348 if (env != NULL) return env;
01349
01350 if (param != NULL) {
01351 env = getenv(param);
01352 if (env != NULL) return env;
01353 }
01354
01355 return getenv("LANG");
01356 }
01357 #else
01358 const char *GetCurrentLocale(const char *param);
01359 #endif
01360
01361 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01362 {
01363 char stra[512];
01364 char strb[512];
01365 GetString(stra, *a, lastof(stra));
01366 GetString(strb, *b, lastof(strb));
01367
01368 return strcmp(stra, strb);
01369 }
01370
01378 static bool UniqueLanguageFile(const Language *langs, uint max, const char *language)
01379 {
01380 for (uint i = 0; i < max; i++) {
01381 const char *f_name = strrchr(langs[i].file, PATHSEPCHAR) + 1;
01382 if (strcmp(f_name, language) == 0) return false;
01383 }
01384
01385 return true;
01386 }
01387
01394 static bool GetLanguageFileHeader(const char *file, LanguagePack *hdr)
01395 {
01396 FILE *f = fopen(file, "rb");
01397 if (f == NULL) return false;
01398
01399 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01400 fclose(f);
01401
01402 bool ret = read == 1 &&
01403 hdr->ident == TO_LE32(LANGUAGE_PACK_IDENT) &&
01404 hdr->version == TO_LE32(LANGUAGE_PACK_VERSION);
01405
01406
01407 if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01408 return ret;
01409 }
01410
01419 static int GetLanguageList(Language *langs, int start, int max, const char *path)
01420 {
01421 int i = start;
01422
01423 DIR *dir = ttd_opendir(path);
01424 if (dir != NULL) {
01425 struct dirent *dirent;
01426 while ((dirent = readdir(dir)) != NULL && i < max) {
01427 const char *d_name = FS2OTTD(dirent->d_name);
01428 const char *extension = strrchr(d_name, '.');
01429
01430
01431 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01432
01433
01434 if (!UniqueLanguageFile(langs, i, d_name)) continue;
01435
01436 langs[i].file = str_fmt("%s%s", path, d_name);
01437
01438
01439 LanguagePack hdr;
01440 if (!GetLanguageFileHeader(langs[i].file, &hdr)) {
01441 free(langs[i].file);
01442 continue;
01443 }
01444
01445 i++;
01446 }
01447 closedir(dir);
01448 }
01449 return i - start;
01450 }
01451
01456 void InitializeLanguagePacks()
01457 {
01458 Searchpath sp;
01459 Language files[MAX_LANG];
01460 uint language_count = 0;
01461
01462 FOR_ALL_SEARCHPATHS(sp) {
01463 char path[MAX_PATH];
01464 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01465 language_count += GetLanguageList(files, language_count, lengthof(files), path);
01466 }
01467 if (language_count == 0) usererror("No available language packs (invalid versions?)");
01468
01469
01470 const char *lang = GetCurrentLocale("LC_MESSAGES");
01471 if (lang == NULL) lang = "en_GB";
01472
01473 int chosen_language = -1;
01474 int language_fallback = -1;
01475 int en_GB_fallback = 0;
01476
01477 DynamicLanguages *dl = &_dynlang;
01478 dl->num = 0;
01479
01480 for (uint i = 0; i < language_count; i++) {
01481
01482 LanguagePack hdr;
01483 if (!GetLanguageFileHeader(files[i].file, &hdr)) continue;
01484
01485 dl->ent[dl->num].file = files[i].file;
01486 dl->ent[dl->num].name = strdup(hdr.name);
01487
01488
01489
01490
01491 const char *lang_file = strrchr(dl->ent[dl->num].file, PATHSEPCHAR) + 1;
01492 if (strcmp(lang_file, dl->curr_file) == 0) chosen_language = dl->num;
01493
01494 if (chosen_language == -1) {
01495 if (strcmp (hdr.isocode, "en_GB") == 0) en_GB_fallback = dl->num;
01496 if (strncmp(hdr.isocode, lang, 5) == 0) chosen_language = dl->num;
01497 if (strncmp(hdr.isocode, lang, 2) == 0) language_fallback = dl->num;
01498 }
01499
01500 dl->num++;
01501 }
01502
01503 if (dl->num == 0) usererror("Invalid version of language packs");
01504
01505
01506
01507 if (chosen_language == -1) {
01508 chosen_language = (language_fallback != -1) ? language_fallback : en_GB_fallback;
01509 }
01510
01511 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", dl->ent[chosen_language].file);
01512 }
01513
01518 const char *GetCurrentLanguageIsoCode()
01519 {
01520 return _langpack->isocode;
01521 }
01522
01533 void CheckForMissingGlyphsInLoadedLanguagePack()
01534 {
01535 #ifdef WITH_FREETYPE
01536
01537
01538 UninitFreeType();
01539 InitFreeType();
01540 bool retry = false;
01541 #endif
01542
01543 for (;;) {
01544 const Sprite *question_mark = GetGlyph(FS_NORMAL, '?');
01545
01546 for (uint i = 0; i != 32; i++) {
01547 for (uint j = 0; j < _langtab_num[i]; j++) {
01548 const char *string = _langpack_offs[_langtab_start[i] + j];
01549 WChar c;
01550 while ((c = Utf8Consume(&string)) != '\0') {
01551 if (c == SCC_SETX) {
01552
01553
01554
01555
01556
01557
01558 string++;
01559 } else if (c == SCC_SETXY) {
01560 string += 2;
01561 } else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) {
01562 #ifdef WITH_FREETYPE
01563 if (!retry) {
01564
01565
01566
01567 retry = true;
01568
01569 FreeTypeSettings backup;
01570 memcpy(&backup, &_freetype, sizeof(backup));
01571
01572 bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, string);
01573 if (success) {
01574 UninitFreeType();
01575 InitFreeType();
01576 }
01577
01578 memcpy(&_freetype, &backup, sizeof(backup));
01579
01580 if (success) continue;
01581 } else {
01582
01583
01584
01585 UninitFreeType();
01586 InitFreeType();
01587 }
01588 #endif
01589
01590
01591
01592
01593
01594
01595
01596
01597
01598
01599
01600
01601
01602 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.");
01603 Utf8Encode(err_str, SCC_YELLOW);
01604 SetDParamStr(0, err_str);
01605 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, 0, 0);
01606
01607
01608 LoadStringWidthTable();
01609 return;
01610 }
01611 }
01612 }
01613 }
01614 break;
01615 }
01616
01617
01618 LoadStringWidthTable();
01619
01620 #if !defined(WITH_ICU)
01621
01622
01623
01624
01625
01626
01627
01628
01629
01630
01631
01632
01633
01634 if (_dynlang.text_dir != TD_LTR) {
01635 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01636 Utf8Encode(err_str, SCC_YELLOW);
01637 SetDParamStr(0, err_str);
01638 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, 0, 0);
01639 }
01640 #endif
01641 }