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