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, uint64 number, const char *last)
00235 {
00236 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, 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 : n < 7 ? 2 : n < 11 ? 3 : 4;
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
00470 case 12:
00471 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
00472 }
00473 }
00474
00475 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
00476 {
00477
00478 uint n = (byte)*b++;
00479 uint pos, i, mypos = 0;
00480
00481 for (i = pos = 0; i != n; i++) {
00482 uint len = (byte)*b++;
00483 if (i == form) mypos = pos;
00484 pos += len;
00485 }
00486
00487 *dst += seprintf(*dst, last, "%s", b + mypos);
00488 return b + pos;
00489 }
00490
00491 struct Units {
00492 int s_m;
00493 int s_s;
00494 StringID velocity;
00495 int p_m;
00496 int p_s;
00497 StringID power;
00498 int w_m;
00499 int w_s;
00500 StringID s_weight;
00501 StringID l_weight;
00502 int v_m;
00503 int v_s;
00504 StringID s_volume;
00505 StringID l_volume;
00506 int f_m;
00507 int f_s;
00508 StringID force;
00509 };
00510
00511
00512 static const Units units[] = {
00513 {
00514 1, 0, STR_UNITS_VELOCITY_IMPERIAL,
00515 1, 0, STR_UNITS_POWER_IMPERIAL,
00516 1, 0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00517 1000, 0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00518 1, 0, STR_UNITS_FORCE_SI,
00519 },
00520 {
00521 103, 6, STR_UNITS_VELOCITY_METRIC,
00522 1, 0, STR_UNITS_POWER_METRIC,
00523 1, 0, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00524 1000, 0, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00525 1, 0, STR_UNITS_FORCE_SI,
00526 },
00527 {
00528 1831, 12, STR_UNITS_VELOCITY_SI,
00529 764, 10, STR_UNITS_POWER_SI,
00530 1000, 0, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00531 1, 0, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00532 1, 0, STR_UNITS_FORCE_SI,
00533 },
00534 };
00535
00541 uint ConvertSpeedToDisplaySpeed(uint speed)
00542 {
00543 return (speed * units[_settings_game.locale.units].s_m) >> units[_settings_game.locale.units].s_s;
00544 }
00545
00551 uint ConvertDisplaySpeedToSpeed(uint speed)
00552 {
00553 return ((speed << units[_settings_game.locale.units].s_s) + units[_settings_game.locale.units].s_m / 2) / units[_settings_game.locale.units].s_m;
00554 }
00555
00556 static char *FormatString(char *buff, const char *str, int64 *argv, uint casei, const char *last)
00557 {
00558 WChar b;
00559 int64 *argv_orig = argv;
00560 uint modifier = 0;
00561 char *buf_start = buff;
00562
00563 while ((b = Utf8Consume(&str)) != '\0') {
00564 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00565
00566 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, argv);
00567 if (b == 0) continue;
00568 }
00569
00570 switch (b) {
00571 case SCC_SETX:
00572 if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00573 buff += Utf8Encode(buff, SCC_SETX);
00574 *buff++ = *str++;
00575 }
00576 break;
00577
00578 case SCC_SETXY:
00579 if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00580 buff += Utf8Encode(buff, SCC_SETXY);
00581 *buff++ = *str++;
00582 *buff++ = *str++;
00583 }
00584 break;
00585
00586 case SCC_STRING_ID:
00587 buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, last);
00588 break;
00589
00590 case SCC_RAW_STRING_POINTER: {
00591 const char *str = (const char*)(size_t)GetInt64(&argv);
00592 buff = FormatString(buff, str, argv, casei, last);
00593 break;
00594 }
00595
00596 case SCC_DATE_LONG:
00597 buff = FormatYmdString(buff, GetInt32(&argv), last);
00598 break;
00599
00600 case SCC_DATE_SHORT:
00601 buff = FormatMonthAndYear(buff, GetInt32(&argv), last);
00602 break;
00603
00604 case SCC_VELOCITY: {
00605 int64 args[1];
00606 assert(_settings_game.locale.units < lengthof(units));
00607 args[0] = ConvertSpeedToDisplaySpeed(GetInt32(&argv) * 10 / 16);
00608 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].velocity), args, modifier >> 24, last);
00609 modifier = 0;
00610 break;
00611 }
00612
00613 case SCC_CURRENCY_COMPACT:
00614 buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), true, last);
00615 break;
00616
00617 case SCC_REVISION:
00618 buff = strecpy(buff, _openttd_revision, last);
00619 break;
00620
00621 case SCC_CARGO_SHORT: {
00622
00623
00624
00625 StringID cargo_str = CargoSpec::Get(GetInt32(&argv))->units_volume;
00626 switch (cargo_str) {
00627 case STR_TONS: {
00628 int64 args[1];
00629 assert(_settings_game.locale.units < lengthof(units));
00630 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00631 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00632 modifier = 0;
00633 break;
00634 }
00635
00636 case STR_LITERS: {
00637 int64 args[1];
00638 assert(_settings_game.locale.units < lengthof(units));
00639 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00640 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, modifier >> 24, last);
00641 modifier = 0;
00642 break;
00643 }
00644
00645 default:
00646 if (cargo_str >= 0xE000 && cargo_str < 0xF800) {
00647
00648
00649 buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00650 } else {
00651 buff = FormatCommaNumber(buff, GetInt32(&argv), last);
00652 buff = strecpy(buff, " ", last);
00653 buff = strecpy(buff, GetStringPtr(cargo_str), last);
00654 }
00655 break;
00656 }
00657 } break;
00658
00659 case SCC_STRING1: {
00660
00661 uint str = modifier + GetInt32(&argv);
00662 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 1), last);
00663 modifier = 0;
00664 break;
00665 }
00666
00667 case SCC_STRING2: {
00668
00669 uint str = modifier + GetInt32(&argv);
00670 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 2), last);
00671 modifier = 0;
00672 break;
00673 }
00674
00675 case SCC_STRING3: {
00676
00677 uint str = modifier + GetInt32(&argv);
00678 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 3), last);
00679 modifier = 0;
00680 break;
00681 }
00682
00683 case SCC_STRING4: {
00684
00685 uint str = modifier + GetInt32(&argv);
00686 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 4), last);
00687 modifier = 0;
00688 break;
00689 }
00690
00691 case SCC_STRING5: {
00692
00693 uint str = modifier + GetInt32(&argv);
00694 buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 5), last);
00695 modifier = 0;
00696 break;
00697 }
00698
00699 case SCC_STATION_FEATURES: {
00700 buff = StationGetSpecialString(buff, GetInt32(&argv), last);
00701 break;
00702 }
00703
00704 case SCC_INDUSTRY_NAME: {
00705 const Industry *i = Industry::Get(GetInt32(&argv));
00706 int64 args[2];
00707
00708
00709 assert(i != NULL);
00710
00711
00712 args[0] = i->town->index;
00713 args[1] = GetIndustrySpec(i->type)->name;
00714 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), args, modifier >> 24, last);
00715 modifier = 0;
00716 break;
00717 }
00718
00719 case SCC_VOLUME: {
00720 int64 args[1];
00721 assert(_settings_game.locale.units < lengthof(units));
00722 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00723 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_volume), args, modifier >> 24, last);
00724 modifier = 0;
00725 break;
00726 }
00727
00728 case SCC_GENDER_LIST: {
00729
00730 WChar fmt = SCC_CONTROL_START + (byte)*str++;
00731 byte offset = (byte)*str++;
00732
00733
00734
00735
00736 char input[4 + 1];
00737 char *p = input + Utf8Encode(input, fmt);
00738 *p = '\0';
00739
00740
00741 char buf[256];
00742 bool old_kgd = _keep_gender_data;
00743 _keep_gender_data = true;
00744 p = FormatString(buf, input, argv_orig + offset, 0, lastof(buf));
00745 _keep_gender_data = old_kgd;
00746 *p = '\0';
00747
00748
00749 int gender = 0;
00750 const char *s = buf;
00751 WChar c = Utf8Consume(&s);
00752
00753 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00754 str = ParseStringChoice(str, gender, &buff, last);
00755 break;
00756 }
00757
00758 case SCC_DATE_TINY: {
00759 buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_FORMAT_DATE_TINY, last);
00760 break;
00761 }
00762
00763 case SCC_DATE_ISO: {
00764 buff = FormatTinyOrISODate(buff, GetInt32(&argv), STR_FORMAT_DATE_ISO, last);
00765 break;
00766 }
00767
00768 case SCC_CARGO: {
00769
00770 CargoID cargo = GetInt32(&argv);
00771 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
00772 buff = GetStringWithArgs(buff, cargo_str, argv++, last);
00773 break;
00774 }
00775
00776 case SCC_POWER: {
00777 int64 args[1];
00778 assert(_settings_game.locale.units < lengthof(units));
00779 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].p_m >> units[_settings_game.locale.units].p_s;
00780 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].power), args, modifier >> 24, last);
00781 modifier = 0;
00782 break;
00783 }
00784
00785 case SCC_VOLUME_SHORT: {
00786 int64 args[1];
00787 assert(_settings_game.locale.units < lengthof(units));
00788 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].v_m >> units[_settings_game.locale.units].v_s;
00789 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_volume), args, modifier >> 24, last);
00790 modifier = 0;
00791 break;
00792 }
00793
00794 case SCC_WEIGHT: {
00795 int64 args[1];
00796 assert(_settings_game.locale.units < lengthof(units));
00797 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00798 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].l_weight), args, modifier >> 24, last);
00799 modifier = 0;
00800 break;
00801 }
00802
00803 case SCC_WEIGHT_SHORT: {
00804 int64 args[1];
00805 assert(_settings_game.locale.units < lengthof(units));
00806 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].w_m >> units[_settings_game.locale.units].w_s;
00807 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].s_weight), args, modifier >> 24, last);
00808 modifier = 0;
00809 break;
00810 }
00811
00812 case SCC_FORCE: {
00813 int64 args[1];
00814 assert(_settings_game.locale.units < lengthof(units));
00815 args[0] = GetInt32(&argv) * units[_settings_game.locale.units].f_m >> units[_settings_game.locale.units].f_s;
00816 buff = FormatString(buff, GetStringPtr(units[_settings_game.locale.units].force), args, modifier >> 24, last);
00817 modifier = 0;
00818 break;
00819 }
00820
00821
00822
00823 case SCC_GENDER_INDEX:
00824 if (_keep_gender_data) {
00825 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
00826 *buff++ = *str++;
00827 } else {
00828 str++;
00829 }
00830 break;
00831
00832 case SCC_STRING: {
00833 uint str = modifier + GetInt32(&argv);
00834
00835
00836
00837 buff = GetStringWithArgs(buff, str, argv, last);
00838 modifier = 0;
00839 break;
00840 }
00841
00842 case SCC_COMMA:
00843 buff = FormatCommaNumber(buff, GetInt64(&argv), last);
00844 break;
00845
00846 case SCC_ARG_INDEX:
00847 argv = argv_orig + (byte)*str++;
00848 break;
00849
00850 case SCC_PLURAL_LIST: {
00851 int64 v = argv_orig[(byte)*str++];
00852 str = ParseStringChoice(str, DeterminePluralForm(v), &buff, last);
00853 break;
00854 }
00855
00856 case SCC_NUM:
00857 buff = FormatNoCommaNumber(buff, GetInt64(&argv), last);
00858 break;
00859
00860 case SCC_ZEROFILL_NUM: {
00861 int64 num = GetInt64(&argv);
00862 buff = FormatZerofillNumber(buff, num, GetInt64(&argv), last);
00863 } break;
00864
00865 case SCC_HEX:
00866 buff = FormatHexNumber(buff, (uint64)GetInt64(&argv), last);
00867 break;
00868
00869 case SCC_BYTES:
00870 buff = FormatBytes(buff, GetInt64(&argv), last);
00871 break;
00872
00873 case SCC_CURRENCY:
00874 buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
00875 break;
00876
00877 case SCC_WAYPOINT_NAME: {
00878 Waypoint *wp = Waypoint::Get(GetInt32(&argv));
00879
00880 assert(wp != NULL);
00881
00882 if (wp->name != NULL) {
00883 buff = strecpy(buff, wp->name, last);
00884 } else {
00885 int64 temp[2];
00886 temp[0] = wp->town->index;
00887 temp[1] = wp->town_cn + 1;
00888 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
00889 if (wp->town_cn != 0) str++;
00890 buff = GetStringWithArgs(buff, str, temp, last);
00891 }
00892 break;
00893 }
00894
00895 case SCC_STATION_NAME: {
00896 StationID sid = GetInt32(&argv);
00897 const Station *st = Station::GetIfValid(sid);
00898
00899 if (st == NULL) {
00900
00901
00902
00903 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, NULL, last);
00904 break;
00905 }
00906
00907 if (st->name != NULL) {
00908 buff = strecpy(buff, st->name, last);
00909 } else {
00910 StringID str = st->string_id;
00911 if (st->indtype != IT_INVALID) {
00912
00913 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
00914
00915
00916
00917
00918 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
00919 str = indsp->station_name;
00920 }
00921 }
00922
00923 int64 temp[3];
00924 temp[0] = STR_TOWN_NAME;
00925 temp[1] = st->town->index;
00926 temp[2] = st->index;
00927 buff = GetStringWithArgs(buff, str, temp, last);
00928 }
00929 break;
00930 }
00931
00932 case SCC_TOWN_NAME: {
00933 const Town *t = Town::Get(GetInt32(&argv));
00934
00935 assert(t != NULL);
00936
00937 if (t->name != NULL) {
00938 buff = strecpy(buff, t->name, last);
00939 } else {
00940 buff = GetTownName(buff, t, last);
00941 }
00942 break;
00943 }
00944
00945 case SCC_GROUP_NAME: {
00946 const Group *g = Group::Get(GetInt32(&argv));
00947
00948 assert(g != NULL);
00949
00950 if (g->name != NULL) {
00951 buff = strecpy(buff, g->name, last);
00952 } else {
00953 int64 args[1];
00954
00955 args[0] = g->index;
00956 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, args, last);
00957 }
00958 break;
00959 }
00960
00961 case SCC_ENGINE_NAME: {
00962 EngineID engine = (EngineID)GetInt32(&argv);
00963 const Engine *e = Engine::Get(engine);
00964
00965 assert(e != NULL);
00966
00967 if (e->name != NULL) {
00968 buff = strecpy(buff, e->name, last);
00969 } else {
00970 buff = GetStringWithArgs(buff, e->info.string_id, NULL, last);
00971 }
00972 break;
00973 }
00974
00975 case SCC_VEHICLE_NAME: {
00976 const Vehicle *v = Vehicle::Get(GetInt32(&argv));
00977
00978 assert(v != NULL);
00979
00980 if (v->name != NULL) {
00981 buff = strecpy(buff, v->name, last);
00982 } else {
00983 int64 args[1];
00984 args[0] = v->unitnumber;
00985
00986 StringID str;
00987 switch (v->type) {
00988 default: NOT_REACHED();
00989 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
00990 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
00991 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
00992 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
00993 }
00994
00995 buff = GetStringWithArgs(buff, str, args, last);
00996 }
00997 break;
00998 }
00999
01000 case SCC_SIGN_NAME: {
01001 const Sign *si = Sign::Get(GetInt32(&argv));
01002 if (si->name != NULL) {
01003 buff = strecpy(buff, si->name, last);
01004 } else {
01005 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, NULL, last);
01006 }
01007 break;
01008 }
01009
01010 case SCC_COMPANY_NAME: {
01011 const Company *c = Company::Get((CompanyID)GetInt32(&argv));
01012
01013 if (c->name != NULL) {
01014 buff = strecpy(buff, c->name, last);
01015 } else {
01016 int64 args[1];
01017 args[0] = c->name_2;
01018 buff = GetStringWithArgs(buff, c->name_1, args, last);
01019 }
01020 break;
01021 }
01022
01023 case SCC_COMPANY_NUM: {
01024 CompanyID company = (CompanyID)GetInt32(&argv);
01025
01026
01027 if (Company::IsValidHumanID(company)) {
01028 int64 args[1];
01029 args[0] = company + 1;
01030 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, args, last);
01031 }
01032 break;
01033 }
01034
01035 case SCC_PRESIDENT_NAME: {
01036 const Company *c = Company::Get((CompanyID)GetInt32(&argv));
01037
01038 if (c->president_name != NULL) {
01039 buff = strecpy(buff, c->president_name, last);
01040 } else {
01041 int64 args[1];
01042 args[0] = c->president_name_2;
01043 buff = GetStringWithArgs(buff, c->president_name_1, args, last);
01044 }
01045 break;
01046 }
01047
01048 case SCC_SETCASE: {
01049
01050
01051 modifier = (byte)*str++ << 24;
01052 break;
01053 }
01054
01055 case SCC_SWITCH_CASE: {
01056
01057
01058 uint num = (byte)*str++;
01059 while (num) {
01060 if ((byte)str[0] == casei) {
01061
01062 str += 3;
01063 break;
01064 }
01065
01066 str += 3 + (str[1] << 8) + str[2];
01067 num--;
01068 }
01069 break;
01070 }
01071
01072 default:
01073 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01074 break;
01075 }
01076 }
01077 *buff = '\0';
01078 return buff;
01079 }
01080
01081
01082 static char *StationGetSpecialString(char *buff, int x, const char *last)
01083 {
01084 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01085 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01086 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01087 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01088 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01089 *buff = '\0';
01090 return buff;
01091 }
01092
01093 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01094 {
01095 return GenerateTownNameString(buff, last, ind, seed);
01096 }
01097
01098 static const char * const _silly_company_names[] = {
01099 "Bloggs Brothers",
01100 "Tiny Transport Ltd.",
01101 "Express Travel",
01102 "Comfy-Coach & Co.",
01103 "Crush & Bump Ltd.",
01104 "Broken & Late Ltd.",
01105 "Sam Speedy & Son",
01106 "Supersonic Travel",
01107 "Mike's Motors",
01108 "Lightning International",
01109 "Pannik & Loozit Ltd.",
01110 "Inter-City Transport",
01111 "Getout & Pushit Ltd."
01112 };
01113
01114 static const char * const _surname_list[] = {
01115 "Adams",
01116 "Allan",
01117 "Baker",
01118 "Bigwig",
01119 "Black",
01120 "Bloggs",
01121 "Brown",
01122 "Campbell",
01123 "Gordon",
01124 "Hamilton",
01125 "Hawthorn",
01126 "Higgins",
01127 "Green",
01128 "Gribble",
01129 "Jones",
01130 "McAlpine",
01131 "MacDonald",
01132 "McIntosh",
01133 "Muir",
01134 "Murphy",
01135 "Nelson",
01136 "O'Donnell",
01137 "Parker",
01138 "Phillips",
01139 "Pilkington",
01140 "Quigley",
01141 "Sharkey",
01142 "Thomson",
01143 "Watkins"
01144 };
01145
01146 static const char * const _silly_surname_list[] = {
01147 "Grumpy",
01148 "Dozy",
01149 "Speedy",
01150 "Nosey",
01151 "Dribble",
01152 "Mushroom",
01153 "Cabbage",
01154 "Sniffle",
01155 "Fishy",
01156 "Swindle",
01157 "Sneaky",
01158 "Nutkins"
01159 };
01160
01161 static const char _initial_name_letters[] = {
01162 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01163 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01164 };
01165
01166 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01167 {
01168 const char * const *base;
01169 uint num;
01170
01171 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01172 base = _silly_surname_list;
01173 num = lengthof(_silly_surname_list);
01174 } else {
01175 base = _surname_list;
01176 num = lengthof(_surname_list);
01177 }
01178
01179 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01180 buff = strecpy(buff, " & Co.", last);
01181
01182 return buff;
01183 }
01184
01185 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01186 {
01187 char initial[] = "?. ";
01188 const char * const *base;
01189 uint num;
01190 uint i;
01191
01192 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01193 buff = strecpy(buff, initial, last);
01194
01195 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01196 if (i < sizeof(_initial_name_letters)) {
01197 initial[0] = _initial_name_letters[i];
01198 buff = strecpy(buff, initial, last);
01199 }
01200
01201 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01202 base = _silly_surname_list;
01203 num = lengthof(_silly_surname_list);
01204 } else {
01205 base = _surname_list;
01206 num = lengthof(_surname_list);
01207 }
01208
01209 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01210
01211 return buff;
01212 }
01213
01214 static char *GetSpecialNameString(char *buff, int ind, int64 *argv, const char *last)
01215 {
01216 switch (ind) {
01217 case 1:
01218 return strecpy(buff, _silly_company_names[GetInt32(&argv) & 0xFFFF], last);
01219
01220 case 2:
01221 return GenAndCoName(buff, GetInt32(&argv), last);
01222
01223 case 3:
01224 return GenPresidentName(buff, GetInt32(&argv), last);
01225 }
01226
01227
01228 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01229 buff = GetSpecialTownNameString(buff, ind - 6, GetInt32(&argv), last);
01230 return strecpy(buff, " Transport", last);
01231 }
01232
01233
01234 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01235 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01236 return strecpy(buff,
01237 i == _dynlang.curr ? _langpack->own_name : _dynlang.ent[i].name, last);
01238 }
01239
01240
01241 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01242 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01243 buff += seprintf(
01244 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01245 );
01246 return buff;
01247 }
01248
01249
01250 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01251 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01252 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01253 }
01254
01255 NOT_REACHED();
01256 }
01257
01258 #ifdef ENABLE_NETWORK
01259 extern void SortNetworkLanguages();
01260 #else
01261 static inline void SortNetworkLanguages() {}
01262 #endif
01263
01264 bool ReadLanguagePack(int lang_index)
01265 {
01266
01267 size_t len;
01268 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(_dynlang.ent[lang_index].file, &len, 200000);
01269 if (lang_pack == NULL) return false;
01270
01271
01272 const char *end = (char *)lang_pack + len + 1;
01273
01274
01275 if (end <= lang_pack->data ||
01276 lang_pack->ident != TO_LE32(LANGUAGE_PACK_IDENT) ||
01277 lang_pack->version != TO_LE32(LANGUAGE_PACK_VERSION)) {
01278 free(lang_pack);
01279 return false;
01280 }
01281
01282 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01283 for (uint i = 0; i < 32; i++) {
01284 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01285 }
01286 #endif
01287
01288 uint count = 0;
01289 for (uint i = 0; i < 32; i++) {
01290 uint num = lang_pack->offsets[i];
01291 _langtab_start[i] = count;
01292 _langtab_num[i] = num;
01293 count += num;
01294 }
01295
01296
01297 char **langpack_offs = MallocT<char *>(count);
01298
01299
01300 char *s = lang_pack->data;
01301 len = (byte)*s++;
01302 for (uint i = 0; i < count; i++) {
01303 if (s + len >= end) {
01304 free(lang_pack);
01305 free(langpack_offs);
01306 return false;
01307 }
01308 if (len >= 0xC0) {
01309 len = ((len & 0x3F) << 8) + (byte)*s++;
01310 if (s + len >= end) {
01311 free(lang_pack);
01312 free(langpack_offs);
01313 return false;
01314 }
01315 }
01316 langpack_offs[i] = s;
01317 s += len;
01318 len = (byte)*s;
01319 *s++ = '\0';
01320 }
01321
01322 free(_langpack);
01323 _langpack = lang_pack;
01324
01325 free(_langpack_offs);
01326 _langpack_offs = langpack_offs;
01327
01328 const char *c_file = strrchr(_dynlang.ent[lang_index].file, PATHSEPCHAR) + 1;
01329 strecpy(_dynlang.curr_file, c_file, lastof(_dynlang.curr_file));
01330
01331 _dynlang.curr = lang_index;
01332 _dynlang.text_dir = (TextDirection)lang_pack->text_dir;
01333 SetCurrentGrfLangID(_langpack->newgrflangid);
01334 SortNetworkLanguages();
01335 return true;
01336 }
01337
01338
01339
01340 #if !(defined(WIN32) || defined(__APPLE__))
01341
01347 const char *GetCurrentLocale(const char *param)
01348 {
01349 const char *env;
01350
01351 env = getenv("LANGUAGE");
01352 if (env != NULL) return env;
01353
01354 env = getenv("LC_ALL");
01355 if (env != NULL) return env;
01356
01357 if (param != NULL) {
01358 env = getenv(param);
01359 if (env != NULL) return env;
01360 }
01361
01362 return getenv("LANG");
01363 }
01364 #else
01365 const char *GetCurrentLocale(const char *param);
01366 #endif
01367
01368 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01369 {
01370 char stra[512];
01371 char strb[512];
01372 GetString(stra, *a, lastof(stra));
01373 GetString(strb, *b, lastof(strb));
01374
01375 return strcmp(stra, strb);
01376 }
01377
01385 static bool UniqueLanguageFile(const Language *langs, uint max, const char *language)
01386 {
01387 for (uint i = 0; i < max; i++) {
01388 const char *f_name = strrchr(langs[i].file, PATHSEPCHAR) + 1;
01389 if (strcmp(f_name, language) == 0) return false;
01390 }
01391
01392 return true;
01393 }
01394
01401 static bool GetLanguageFileHeader(const char *file, LanguagePack *hdr)
01402 {
01403 FILE *f = fopen(file, "rb");
01404 if (f == NULL) return false;
01405
01406 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01407 fclose(f);
01408
01409 bool ret = read == 1 &&
01410 hdr->ident == TO_LE32(LANGUAGE_PACK_IDENT) &&
01411 hdr->version == TO_LE32(LANGUAGE_PACK_VERSION);
01412
01413
01414 if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01415 return ret;
01416 }
01417
01426 static int GetLanguageList(Language *langs, int start, int max, const char *path)
01427 {
01428 int i = start;
01429
01430 DIR *dir = ttd_opendir(path);
01431 if (dir != NULL) {
01432 struct dirent *dirent;
01433 while ((dirent = readdir(dir)) != NULL && i < max) {
01434 const char *d_name = FS2OTTD(dirent->d_name);
01435 const char *extension = strrchr(d_name, '.');
01436
01437
01438 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01439
01440
01441 if (!UniqueLanguageFile(langs, i, d_name)) continue;
01442
01443 langs[i].file = str_fmt("%s%s", path, d_name);
01444
01445
01446 LanguagePack hdr;
01447 if (!GetLanguageFileHeader(langs[i].file, &hdr)) {
01448 free(langs[i].file);
01449 continue;
01450 }
01451
01452 i++;
01453 }
01454 closedir(dir);
01455 }
01456 return i - start;
01457 }
01458
01463 void InitializeLanguagePacks()
01464 {
01465 Searchpath sp;
01466 Language files[MAX_LANG];
01467 uint language_count = 0;
01468
01469 FOR_ALL_SEARCHPATHS(sp) {
01470 char path[MAX_PATH];
01471 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01472 language_count += GetLanguageList(files, language_count, lengthof(files), path);
01473 }
01474 if (language_count == 0) usererror("No available language packs (invalid versions?)");
01475
01476
01477 const char *lang = GetCurrentLocale("LC_MESSAGES");
01478 if (lang == NULL) lang = "en_GB";
01479
01480 int chosen_language = -1;
01481 int language_fallback = -1;
01482 int en_GB_fallback = 0;
01483
01484 DynamicLanguages *dl = &_dynlang;
01485 dl->num = 0;
01486
01487 for (uint i = 0; i < language_count; i++) {
01488
01489 LanguagePack hdr;
01490 if (!GetLanguageFileHeader(files[i].file, &hdr)) continue;
01491
01492 dl->ent[dl->num].file = files[i].file;
01493 dl->ent[dl->num].name = strdup(hdr.name);
01494
01495
01496
01497
01498 const char *lang_file = strrchr(dl->ent[dl->num].file, PATHSEPCHAR) + 1;
01499 if (strcmp(lang_file, dl->curr_file) == 0) chosen_language = dl->num;
01500
01501 if (chosen_language == -1) {
01502 if (strcmp (hdr.isocode, "en_GB") == 0) en_GB_fallback = dl->num;
01503 if (strncmp(hdr.isocode, lang, 5) == 0) chosen_language = dl->num;
01504 if (strncmp(hdr.isocode, lang, 2) == 0) language_fallback = dl->num;
01505 }
01506
01507 dl->num++;
01508 }
01509
01510 if (dl->num == 0) usererror("Invalid version of language packs");
01511
01512
01513
01514 if (chosen_language == -1) {
01515 chosen_language = (language_fallback != -1) ? language_fallback : en_GB_fallback;
01516 }
01517
01518 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", dl->ent[chosen_language].file);
01519 }
01520
01525 const char *GetCurrentLanguageIsoCode()
01526 {
01527 return _langpack->isocode;
01528 }
01529
01540 void CheckForMissingGlyphsInLoadedLanguagePack()
01541 {
01542 #ifdef WITH_FREETYPE
01543
01544
01545 UninitFreeType();
01546 InitFreeType();
01547 bool retry = false;
01548 #endif
01549
01550 for (;;) {
01551 const Sprite *question_mark = GetGlyph(FS_NORMAL, '?');
01552
01553 for (uint i = 0; i != 32; i++) {
01554 for (uint j = 0; j < _langtab_num[i]; j++) {
01555 const char *string = _langpack_offs[_langtab_start[i] + j];
01556 WChar c;
01557 while ((c = Utf8Consume(&string)) != '\0') {
01558 if (c == SCC_SETX) {
01559
01560
01561
01562
01563
01564
01565 string++;
01566 } else if (c == SCC_SETXY) {
01567 string += 2;
01568 } else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) {
01569 #ifdef WITH_FREETYPE
01570 if (!retry) {
01571
01572
01573
01574 retry = true;
01575
01576 FreeTypeSettings backup;
01577 memcpy(&backup, &_freetype, sizeof(backup));
01578
01579 bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, string);
01580 if (success) {
01581 UninitFreeType();
01582 InitFreeType();
01583 }
01584
01585 memcpy(&_freetype, &backup, sizeof(backup));
01586
01587 if (success) continue;
01588 } else {
01589
01590
01591
01592 UninitFreeType();
01593 InitFreeType();
01594 }
01595 #endif
01596
01597
01598
01599
01600
01601
01602
01603
01604
01605
01606
01607
01608
01609 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.");
01610 Utf8Encode(err_str, SCC_YELLOW);
01611 SetDParamStr(0, err_str);
01612 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, 0, 0);
01613
01614
01615 LoadStringWidthTable();
01616 return;
01617 }
01618 }
01619 }
01620 }
01621 break;
01622 }
01623
01624
01625 LoadStringWidthTable();
01626
01627 #if !defined(WITH_ICU)
01628
01629
01630
01631
01632
01633
01634
01635
01636
01637
01638
01639
01640
01641 if (_dynlang.text_dir != TD_LTR) {
01642 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01643 Utf8Encode(err_str, SCC_YELLOW);
01644 SetDParamStr(0, err_str);
01645 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, 0, 0);
01646 }
01647 #endif
01648 }