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 "depot_base.h"
00019 #include "industry.h"
00020 #include "newgrf_text.h"
00021 #include "fileio_func.h"
00022 #include "group.h"
00023 #include "signs_base.h"
00024 #include "cargotype.h"
00025 #include "fontcache.h"
00026 #include "gui.h"
00027 #include "strings_func.h"
00028 #include "rev.h"
00029 #include "core/alloc_type.hpp"
00030 #include "core/endian_func.hpp"
00031 #include "date_func.h"
00032 #include "vehicle_base.h"
00033 #include "engine_base.h"
00034 #include "language.h"
00035 #include "townname_func.h"
00036 #include "string_func.h"
00037 #include "company_base.h"
00038 #include "smallmap_gui.h"
00039 #include "window_func.h"
00040 #include "debug.h"
00041 #include <stack>
00042
00043 #include "table/strings.h"
00044 #include "table/control_codes.h"
00045
00046 char _config_language_file[MAX_PATH];
00047 LanguageList _languages;
00048 const LanguageMetadata *_current_language = NULL;
00049
00050 TextDirection _current_text_dir;
00051
00052 #ifdef WITH_ICU
00053 Collator *_current_collator = NULL;
00054 #endif
00055
00056 static uint64 _global_string_params_data[20];
00057 static WChar _global_string_params_type[20];
00058 StringParameters _global_string_params(_global_string_params_data, 20, _global_string_params_type);
00059
00061 void StringParameters::ClearTypeInformation()
00062 {
00063 assert(this->type != NULL);
00064 MemSetT(this->type, 0, this->num_param);
00065 }
00066
00071 void StringParameters::ShiftParameters(uint amount)
00072 {
00073 assert(amount <= this->num_param);
00074 MemMoveT(this->data + amount, this->data, this->num_param - amount);
00075 }
00076
00083 void CopyInDParam(int offs, const uint64 *src, int num)
00084 {
00085 MemCpyT(_global_string_params.GetPointerToOffset(offs), src, num);
00086 }
00087
00094 void CopyOutDParam(uint64 *dst, int offs, int num)
00095 {
00096 MemCpyT(dst, _global_string_params.GetPointerToOffset(offs), num);
00097 }
00098
00099 static char *StationGetSpecialString(char *buff, int x, const char *last);
00100 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
00101 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
00102
00103 static char *FormatString(char *buff, const char *str, StringParameters *args, uint casei, const char *last, bool dry_run = false);
00104
00105 struct LanguagePack : public LanguagePackHeader {
00106 char data[];
00107 };
00108
00109 static char **_langpack_offs;
00110 static LanguagePack *_langpack;
00111 static uint _langtab_num[32];
00112 static uint _langtab_start[32];
00113 static bool _keep_gender_data = false;
00114
00115
00116 const char *GetStringPtr(StringID string)
00117 {
00118 switch (GB(string, 11, 5)) {
00119
00120 case 26: return GetStringPtr(GetGRFStringID(0, 0xD000 + GB(string, 0, 10)));
00121 case 28: return GetGRFStringPtr(GB(string, 0, 11));
00122 case 29: return GetGRFStringPtr(GB(string, 0, 11) + 0x0800);
00123 case 30: return GetGRFStringPtr(GB(string, 0, 11) + 0x1000);
00124 default: return _langpack_offs[_langtab_start[string >> 11] + (string & 0x7FF)];
00125 }
00126 }
00127
00139 char *GetStringWithArgs(char *buffr, uint string, StringParameters *args, const char *last)
00140 {
00141 if (GB(string, 0, 16) == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last);
00142
00143 uint index = GB(string, 0, 11);
00144 uint tab = GB(string, 11, 5);
00145
00146 switch (tab) {
00147 case 4:
00148 if (index >= 0xC0) {
00149 return GetSpecialTownNameString(buffr, index - 0xC0, args->GetInt32(), last);
00150 }
00151 break;
00152
00153 case 14:
00154 if (index >= 0xE4) {
00155 return GetSpecialNameString(buffr, index - 0xE4, args, last);
00156 }
00157 break;
00158
00159 case 15:
00160
00161 error("Incorrect conversion of custom name string.");
00162
00163 case 26:
00164
00165 if (HasBit(index, 10)) {
00166 StringID string = GetGRFStringID(0, 0xD000 + GB(index, 0, 10));
00167 return GetStringWithArgs(buffr, string, args, last);
00168 }
00169 break;
00170
00171 case 28:
00172 return FormatString(buffr, GetGRFStringPtr(index), args, GB(string, 24, 8), last);
00173
00174 case 29:
00175 return FormatString(buffr, GetGRFStringPtr(index + 0x0800), args, GB(string, 24, 8), last);
00176
00177 case 30:
00178 return FormatString(buffr, GetGRFStringPtr(index + 0x1000), args, GB(string, 24, 8), last);
00179
00180 case 31:
00181 NOT_REACHED();
00182 }
00183
00184 if (index >= _langtab_num[tab]) {
00185 error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string);
00186 }
00187
00188 return FormatString(buffr, GetStringPtr(GB(string, 0, 16)), args, GB(string, 24, 8), last);
00189 }
00190
00191 char *GetString(char *buffr, StringID string, const char *last)
00192 {
00193 _global_string_params.ClearTypeInformation();
00194 _global_string_params.offset = 0;
00195 return GetStringWithArgs(buffr, string, &_global_string_params, last);
00196 }
00197
00198
00199 char *InlineString(char *buf, StringID string)
00200 {
00201 buf += Utf8Encode(buf, SCC_STRING_ID);
00202 buf += Utf8Encode(buf, string);
00203 return buf;
00204 }
00205
00206
00212 void SetDParamStr(uint n, const char *str)
00213 {
00214 SetDParam(n, (uint64)(size_t)str);
00215 }
00216
00221 void InjectDParam(uint amount)
00222 {
00223 _global_string_params.ShiftParameters(amount);
00224 }
00225
00237 static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
00238 {
00239 static const int max_digits = 20;
00240 uint64 divisor = 10000000000000000000ULL;
00241 zerofill += fractional_digits;
00242 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
00243
00244 if (number < 0) {
00245 buff += seprintf(buff, last, "-");
00246 number = -number;
00247 }
00248
00249 uint64 num = number;
00250 uint64 tot = 0;
00251 for (int i = 0; i < max_digits; i++) {
00252 if (i == max_digits - fractional_digits) {
00253 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
00254 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
00255 buff += seprintf(buff, last, "%s", decimal_separator);
00256 }
00257
00258 uint64 quot = 0;
00259 if (num >= divisor) {
00260 quot = num / divisor;
00261 num = num % divisor;
00262 }
00263 if (tot |= quot || i >= max_digits - zerofill) {
00264 buff += seprintf(buff, last, "%i", (int)quot);
00265 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
00266 }
00267
00268 divisor /= 10;
00269 }
00270
00271 *buff = '\0';
00272
00273 return buff;
00274 }
00275
00276 static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
00277 {
00278 const char *separator = _settings_game.locale.digit_group_separator;
00279 if (separator == NULL) separator = _langpack->digit_group_separator;
00280 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
00281 }
00282
00283 static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
00284 {
00285 return FormatNumber(buff, number, last, "");
00286 }
00287
00288 static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
00289 {
00290 return FormatNumber(buff, number, last, "", count);
00291 }
00292
00293 static char *FormatHexNumber(char *buff, uint64 number, const char *last)
00294 {
00295 return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number);
00296 }
00297
00305 static char *FormatBytes(char *buff, int64 number, const char *last)
00306 {
00307 assert(number >= 0);
00308
00309
00310 const char * const iec_prefixes[] = {"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"};
00311 uint id = 1;
00312 while (number >= 1024 * 1024) {
00313 number /= 1024;
00314 id++;
00315 }
00316
00317 const char *decimal_separator = _settings_game.locale.digit_decimal_separator;
00318 if (decimal_separator == NULL) decimal_separator = _langpack->digit_decimal_separator;
00319
00320 if (number < 1024) {
00321 id = 0;
00322 buff += seprintf(buff, last, "%i", (int)number);
00323 } else if (number < 1024 * 10) {
00324 buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
00325 } else if (number < 1024 * 100) {
00326 buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
00327 } else {
00328 assert(number < 1024 * 1024);
00329 buff += seprintf(buff, last, "%i", (int)number / 1024);
00330 }
00331
00332 assert(id < lengthof(iec_prefixes));
00333 buff += seprintf(buff, last, " %sB", iec_prefixes[id]);
00334
00335 return buff;
00336 }
00337
00338 static char *FormatWallClockString(char *buff, DateTicks ticks, const char *last, bool show_date)
00339 {
00340 Minutes minutes = ticks / _settings_client.gui.ticks_per_minute;
00341 char hour[3], minute[3];
00342 snprintf(hour, lengthof(hour), "%02i", MINUTES_HOUR(minutes) );
00343 snprintf(minute, lengthof(minute), "%02i", MINUTES_MINUTE(minutes));
00344 if (show_date) {
00345 int64 args[3] = { (int64)hour, (int64)minute, (int64)ticks / DAY_TICKS };
00346 if (_settings_client.gui.date_with_time == 1) {
00347 YearMonthDay ymd;
00348 ConvertDateToYMD(args[2], &ymd);
00349 args[2] = ymd.year;
00350 }
00351
00352 StringParameters tmp_params(args);
00353 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_MINUTES + _settings_client.gui.date_with_time), &tmp_params, 0, last);
00354 } else {
00355 int64 args[2] = { (int64)hour, (int64)minute };
00356 StringParameters tmp_params(args);
00357 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_MINUTES), &tmp_params, 0, last);
00358 }
00359 }
00360
00361 static char *FormatYmdString(char *buff, Date date, uint modifier, const char *last)
00362 {
00363 YearMonthDay ymd;
00364 ConvertDateToYMD(date, &ymd);
00365
00366 int64 args[] = {ymd.day + STR_ORDINAL_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
00367 StringParameters tmp_params(args);
00368 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, modifier >> 24, last);
00369 }
00370
00371 static char *FormatMonthAndYear(char *buff, Date date, uint modifier, const char *last)
00372 {
00373 YearMonthDay ymd;
00374 ConvertDateToYMD(date, &ymd);
00375
00376 int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
00377 StringParameters tmp_params(args);
00378 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, modifier >> 24, last);
00379 }
00380
00381 static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last)
00382 {
00383 YearMonthDay ymd;
00384 ConvertDateToYMD(date, &ymd);
00385
00386 char day[3];
00387 char month[3];
00388
00389 snprintf(day, lengthof(day), "%02i", ymd.day);
00390 snprintf(month, lengthof(month), "%02i", ymd.month + 1);
00391
00392 int64 args[] = {(int64)(size_t)day, (int64)(size_t)month, ymd.year};
00393 StringParameters tmp_params(args);
00394 return FormatString(buff, GetStringPtr(str), &tmp_params, 0, last);
00395 }
00396
00397 static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
00398 {
00399
00400
00401 bool negative = number < 0;
00402 const char *multiplier = "";
00403
00404 number *= spec->rate;
00405
00406
00407 if (number < 0) {
00408 if (buff + Utf8CharLen(SCC_RED) > last) return buff;
00409 buff += Utf8Encode(buff, SCC_RED);
00410 buff = strecpy(buff, "-", last);
00411 number = -number;
00412 }
00413
00414
00415
00416
00417 if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix, last);
00418
00419
00420 if (compact) {
00421
00422
00423 if (number >= 1000000000 - 500) {
00424 number = (number + 500000) / 1000000;
00425 multiplier = "M";
00426 } else if (number >= 1000000) {
00427 number = (number + 500) / 1000;
00428 multiplier = "k";
00429 }
00430 }
00431
00432 const char *separator = _settings_game.locale.digit_group_separator_currency;
00433 if (separator == NULL && !StrEmpty(_currency->separator)) separator = _currency->separator;
00434 if (separator == NULL) separator = _langpack->digit_group_separator_currency;
00435 buff = FormatNumber(buff, number, last, separator);
00436 buff = strecpy(buff, multiplier, last);
00437
00438
00439
00440
00441 if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix, last);
00442
00443 if (negative) {
00444 if (buff + Utf8CharLen(SCC_PREVIOUS_COLOUR) > last) return buff;
00445 buff += Utf8Encode(buff, SCC_PREVIOUS_COLOUR);
00446 *buff = '\0';
00447 }
00448
00449 return buff;
00450 }
00451
00458 static int DeterminePluralForm(int64 count, int plural_form)
00459 {
00460
00461 uint64 n = abs(count);
00462
00463 switch (plural_form) {
00464 default:
00465 NOT_REACHED();
00466
00467
00468
00469
00470
00471 case 0:
00472 return n != 1;
00473
00474
00475
00476
00477 case 1:
00478 return 0;
00479
00480
00481
00482
00483 case 2:
00484 return n > 1;
00485
00486
00487
00488
00489 case 3:
00490 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
00491
00492
00493
00494
00495 case 4:
00496 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
00497
00498
00499
00500
00501 case 5:
00502 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00503
00504
00505
00506
00507 case 6:
00508 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00509
00510
00511
00512
00513 case 7:
00514 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
00515
00516
00517
00518
00519 case 8:
00520 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
00521
00522
00523
00524
00525 case 9:
00526 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
00527
00528
00529
00530
00531 case 10:
00532 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
00533
00534
00535
00536
00537
00538
00539
00540 case 11:
00541 switch (n % 10) {
00542 case 0:
00543 case 1:
00544 case 3:
00545 case 6:
00546 case 7:
00547 case 8:
00548 return 0;
00549
00550 case 2:
00551 case 4:
00552 case 5:
00553 case 9:
00554 return 1;
00555
00556 default:
00557 NOT_REACHED();
00558 }
00559
00560
00561
00562
00563 case 12:
00564 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
00565 }
00566 }
00567
00568 static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
00569 {
00570
00571 uint n = (byte)*b++;
00572 uint pos, i, mypos = 0;
00573
00574 for (i = pos = 0; i != n; i++) {
00575 uint len = (byte)*b++;
00576 if (i == form) mypos = pos;
00577 pos += len;
00578 }
00579
00580 *dst += seprintf(*dst, last, "%s", b + mypos);
00581 return b + pos;
00582 }
00583
00585 struct UnitConversion {
00586 int multiplier;
00587 int shift;
00588
00595 int64 ToDisplay(int64 input, bool round = true) const
00596 {
00597 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->shift;
00598 }
00599
00606 int64 FromDisplay(int64 input, bool round = true) const
00607 {
00608 return ((input << this->shift) + (round ? this->multiplier / 2 : 0)) / this->multiplier;
00609 }
00610 };
00611
00612 struct Units {
00613 UnitConversion c_velocity;
00614 StringID velocity;
00615 UnitConversion c_power;
00616 StringID power;
00617 UnitConversion c_weight;
00618 StringID s_weight;
00619 StringID l_weight;
00620 UnitConversion c_volume;
00621 StringID s_volume;
00622 StringID l_volume;
00623 UnitConversion c_force;
00624 StringID force;
00625 UnitConversion c_height;
00626 StringID height;
00627 };
00628
00629
00630 static const Units _units[] = {
00631 {
00632 { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL,
00633 { 1, 0}, STR_UNITS_POWER_IMPERIAL,
00634 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00635 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00636 { 1, 0}, STR_UNITS_FORCE_SI,
00637 { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL,
00638 },
00639 {
00640 { 103, 6}, STR_UNITS_VELOCITY_METRIC,
00641 {4153, 12}, STR_UNITS_POWER_METRIC,
00642 { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC,
00643 {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC,
00644 { 1, 0}, STR_UNITS_FORCE_SI,
00645 { 1, 0}, STR_UNITS_HEIGHT_SI,
00646 },
00647 {
00648 {1831, 12}, STR_UNITS_VELOCITY_SI,
00649 {6109, 13}, STR_UNITS_POWER_SI,
00650 {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI,
00651 { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI,
00652 { 1, 0}, STR_UNITS_FORCE_SI,
00653 { 1, 0}, STR_UNITS_HEIGHT_SI,
00654 },
00655 };
00656
00662 uint ConvertSpeedToDisplaySpeed(uint speed)
00663 {
00664
00665
00666
00667 return _units[_settings_game.locale.units].c_velocity.ToDisplay(speed, false);
00668 }
00669
00675 uint ConvertDisplaySpeedToSpeed(uint speed)
00676 {
00677 return _units[_settings_game.locale.units].c_velocity.FromDisplay(speed);
00678 }
00679
00689 static char *FormatString(char *buff, const char *str_arg, StringParameters *args, uint casei, const char *last, bool dry_run)
00690 {
00691 uint orig_offset = args->offset;
00692
00693
00694 if (!args->HasTypeInformation()) dry_run = true;
00695 if (!dry_run) {
00696 if (UsingNewGRFTextStack()) {
00697
00698
00699
00700
00701
00702
00703 struct TextRefStack *backup = CreateTextRefStackBackup();
00704 FormatString(buff, str_arg, args, casei, last, true);
00705 RestoreTextRefStackBackup(backup);
00706 } else {
00707 FormatString(buff, str_arg, args, casei, last, true);
00708 }
00709
00710 args->offset = orig_offset;
00711 }
00712 WChar b;
00713 uint modifier = 0;
00714 char *buf_start = buff;
00715 std::stack<const char *> str_stack;
00716 str_stack.push(str_arg);
00717
00718 for (;;) {
00719 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) == '\0') {
00720 str_stack.pop();
00721 }
00722 if (str_stack.empty()) break;
00723 const char *&str = str_stack.top();
00724
00725 if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
00726
00727
00728 b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer());
00729 if (b == 0) continue;
00730 }
00731
00732 switch (b) {
00733 case SCC_NEWGRF_STRINL: {
00734 StringID substr = Utf8Consume(&str);
00735 str_stack.push(GetStringPtr(substr));
00736 break;
00737 }
00738
00739 case SCC_NEWGRF_PRINT_STRING_ID: {
00740 StringID substr = args->GetInt32(SCC_NEWGRF_PRINT_STRING_ID);
00741 str_stack.push(GetStringPtr(substr));
00742 break;
00743 }
00744
00745
00746 case SCC_SETX:
00747 if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
00748 buff += Utf8Encode(buff, SCC_SETX);
00749 *buff++ = *str++;
00750 }
00751 break;
00752
00753 case SCC_SETXY:
00754 if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
00755 buff += Utf8Encode(buff, SCC_SETXY);
00756 *buff++ = *str++;
00757 *buff++ = *str++;
00758 }
00759 break;
00760
00761 case SCC_STRING_ID:
00762 buff = GetStringWithArgs(buff, Utf8Consume(&str), args, last);
00763 break;
00764
00765 case SCC_RAW_STRING_POINTER: {
00766 const char *str = (const char *)(size_t)args->GetInt64();
00767 buff = FormatString(buff, str, args, casei, last);
00768 break;
00769 }
00770
00771 case SCC_DATE_WALLCLOCK_LONG:
00772 if (_settings_client.gui.time_in_minutes) {
00773 buff = FormatWallClockString(buff, args->GetInt64(SCC_DATE_WALLCLOCK_SHORT), last, _settings_client.gui.date_with_time);
00774 } else {
00775 buff = FormatYmdString(buff, args->GetInt64(SCC_DATE_WALLCLOCK_SHORT) / DAY_TICKS, modifier, last);
00776 }
00777 break;
00778
00779 case SCC_DATE_LONG:
00780 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), modifier, last);
00781 break;
00782
00783 case SCC_DATE_WALLCLOCK_SHORT:
00784 if (_settings_client.gui.time_in_minutes) {
00785 buff = FormatWallClockString(buff, args->GetInt32(SCC_DATE_WALLCLOCK_SHORT), last, _settings_client.gui.date_with_time);
00786 } else {
00787 buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_WALLCLOCK_SHORT) / DAY_TICKS, modifier, last);
00788 }
00789 break;
00790
00791 case SCC_DATE_SHORT:
00792 buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), modifier, last);
00793 break;
00794
00795 case SCC_VELOCITY: {
00796 assert(_settings_game.locale.units < lengthof(_units));
00797 int64 args_array[] = {ConvertSpeedToDisplaySpeed(args->GetInt64(SCC_VELOCITY) * 10 / 16)};
00798 StringParameters tmp_params(args_array);
00799 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].velocity), &tmp_params, modifier >> 24, last);
00800 modifier = 0;
00801 break;
00802 }
00803
00804 case SCC_HEIGHT: {
00805 int64 args_array[] = {_units[_settings_game.locale.units].c_height.ToDisplay(args->GetInt64())};
00806 StringParameters tmp_params(args_array);
00807 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].height), &tmp_params, modifier >> 24, last);
00808 modifier = 0;
00809 break;
00810 }
00811
00812 case SCC_CURRENCY_COMPACT:
00813 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
00814 break;
00815
00816 case SCC_REVISION:
00817 buff = strecpy(buff, _openttd_revision, last);
00818 break;
00819
00820 case SCC_CARGO_SHORT: {
00821
00822
00823
00824 StringID cargo_str = CargoSpec::Get(args->GetInt32(SCC_CARGO_SHORT))->units_volume;
00825 switch (cargo_str) {
00826 case STR_TONS: {
00827 assert(_settings_game.locale.units < lengthof(_units));
00828 int64 args_array[] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
00829 StringParameters tmp_params(args_array);
00830 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, modifier >> 24, last);
00831 modifier = 0;
00832 break;
00833 }
00834
00835 case STR_LITERS: {
00836 assert(_settings_game.locale.units < lengthof(_units));
00837 int64 args_array[] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
00838 StringParameters tmp_params(args_array);
00839 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, modifier >> 24, last);
00840 modifier = 0;
00841 break;
00842 }
00843
00844 default: {
00845 StringParameters tmp_params(*args, 1);
00846 buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
00847 break;
00848 }
00849 }
00850 break;
00851 }
00852
00853 case SCC_STRING1: {
00854
00855 uint str = modifier + args->GetInt32(SCC_STRING1);
00856 StringParameters sub_args(*args, 1);
00857 buff = GetStringWithArgs(buff, str, &sub_args, last);
00858 modifier = 0;
00859 break;
00860 }
00861
00862 case SCC_STRING2: {
00863
00864 uint str = modifier + args->GetInt32(SCC_STRING2);
00865 StringParameters sub_args(*args, 2);
00866 buff = GetStringWithArgs(buff, str, &sub_args, last);
00867 modifier = 0;
00868 break;
00869 }
00870
00871 case SCC_STRING3: {
00872
00873 uint str = modifier + args->GetInt32(SCC_STRING3);
00874 StringParameters sub_args(*args, 3);
00875 buff = GetStringWithArgs(buff, str, &sub_args, last);
00876 modifier = 0;
00877 break;
00878 }
00879
00880 case SCC_STRING4: {
00881
00882 uint str = modifier + args->GetInt32(SCC_STRING4);
00883 StringParameters sub_args(*args, 4);
00884 buff = GetStringWithArgs(buff, str, &sub_args, last);
00885 modifier = 0;
00886 break;
00887 }
00888
00889 case SCC_STRING5: {
00890
00891 uint str = modifier + args->GetInt32(SCC_STRING5);
00892 StringParameters sub_args(*args, 5);
00893 buff = GetStringWithArgs(buff, str, &sub_args, last);
00894 modifier = 0;
00895 break;
00896 }
00897
00898 case SCC_STATION_FEATURES: {
00899 buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
00900 break;
00901 }
00902
00903 case SCC_INDUSTRY_NAME: {
00904 const Industry *i = Industry::Get(args->GetInt32(SCC_INDUSTRY_NAME));
00905
00906
00907 assert(i != NULL);
00908
00909
00910 int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
00911 StringParameters tmp_params(args_array);
00912
00913 buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, modifier >> 24, last);
00914 modifier = 0;
00915 break;
00916 }
00917
00918 case SCC_VOLUME: {
00919 assert(_settings_game.locale.units < lengthof(_units));
00920 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64(SCC_VOLUME))};
00921 StringParameters tmp_params(args_array);
00922 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_volume), &tmp_params, modifier >> 24, last);
00923 modifier = 0;
00924 break;
00925 }
00926
00927 case SCC_GENDER_LIST: {
00928
00929 uint offset = orig_offset + (byte)*str++;
00930 int gender = 0;
00931 if (!dry_run && args->GetTypeAtOffset(offset) != 0) {
00932
00933
00934
00935 char input[4 + 1];
00936 char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
00937 *p = '\0';
00938
00939
00940 char buf[256];
00941 bool old_kgd = _keep_gender_data;
00942 _keep_gender_data = true;
00943 StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, NULL);
00944 p = FormatString(buf, input, &tmp_params, 0, lastof(buf));
00945 _keep_gender_data = old_kgd;
00946 *p = '\0';
00947
00948
00949 const char *s = buf;
00950 WChar c = Utf8Consume(&s);
00951
00952 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
00953 }
00954 str = ParseStringChoice(str, gender, &buff, last);
00955 break;
00956 }
00957
00958 case SCC_DATE_WALLCLOCK_TINY:
00959 if (_settings_client.gui.time_in_minutes) {
00960 buff = FormatWallClockString(buff, args->GetInt64(SCC_DATE_WALLCLOCK_TINY), last, false);
00961 } else {
00962 buff = FormatTinyOrISODate(buff, args->GetInt64(SCC_DATE_WALLCLOCK_TINY) / DAY_TICKS, STR_FORMAT_DATE_TINY, last);
00963 }
00964 break;
00965
00966 case SCC_DATE_TINY: {
00967 buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
00968 break;
00969 }
00970
00971 case SCC_DATE_WALLCLOCK_ISO:
00972 if (_settings_client.gui.time_in_minutes) {
00973 buff = FormatWallClockString(buff, args->GetInt64(SCC_DATE_WALLCLOCK_ISO), last, false);
00974 } else {
00975 buff = FormatTinyOrISODate(buff, args->GetInt64(SCC_DATE_WALLCLOCK_ISO) / DAY_TICKS, STR_FORMAT_DATE_ISO, last);
00976 }
00977 break;
00978
00979 case SCC_DATE_ISO: {
00980 buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
00981 break;
00982 }
00983
00984 case SCC_CARGO: {
00985
00986 CargoID cargo = args->GetInt32(SCC_CARGO);
00987 StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
00988 StringParameters tmp_args(*args, 1);
00989 buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
00990 break;
00991 }
00992
00993 case SCC_POWER: {
00994 assert(_settings_game.locale.units < lengthof(_units));
00995 int64 args_array[1] = {_units[_settings_game.locale.units].c_power.ToDisplay(args->GetInt64())};
00996 StringParameters tmp_params(args_array);
00997 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].power), &tmp_params, modifier >> 24, last);
00998 modifier = 0;
00999 break;
01000 }
01001
01002 case SCC_VOLUME_SHORT: {
01003 assert(_settings_game.locale.units < lengthof(_units));
01004 int64 args_array[1] = {_units[_settings_game.locale.units].c_volume.ToDisplay(args->GetInt64())};
01005 StringParameters tmp_params(args_array);
01006 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_volume), &tmp_params, modifier >> 24, last);
01007 modifier = 0;
01008 break;
01009 }
01010
01011 case SCC_WEIGHT: {
01012 assert(_settings_game.locale.units < lengthof(_units));
01013 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64(SCC_WEIGHT))};
01014 StringParameters tmp_params(args_array);
01015 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].l_weight), &tmp_params, modifier >> 24, last);
01016 modifier = 0;
01017 break;
01018 }
01019
01020 case SCC_WEIGHT_SHORT: {
01021 assert(_settings_game.locale.units < lengthof(_units));
01022 int64 args_array[1] = {_units[_settings_game.locale.units].c_weight.ToDisplay(args->GetInt64())};
01023 StringParameters tmp_params(args_array);
01024 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].s_weight), &tmp_params, modifier >> 24, last);
01025 modifier = 0;
01026 break;
01027 }
01028
01029 case SCC_FORCE: {
01030 assert(_settings_game.locale.units < lengthof(_units));
01031 int64 args_array[1] = {_units[_settings_game.locale.units].c_force.ToDisplay(args->GetInt64())};
01032 StringParameters tmp_params(args_array);
01033 buff = FormatString(buff, GetStringPtr(_units[_settings_game.locale.units].force), &tmp_params, modifier >> 24, last);
01034 modifier = 0;
01035 break;
01036 }
01037
01038
01039
01040 case SCC_GENDER_INDEX:
01041 if (_keep_gender_data) {
01042 buff += Utf8Encode(buff, SCC_GENDER_INDEX);
01043 *buff++ = *str++;
01044 } else {
01045 str++;
01046 }
01047 break;
01048
01049 case SCC_STRING: {
01050 uint str = modifier + args->GetInt32(SCC_STRING);
01051
01052
01053
01054 StringParameters tmp_params(args->GetDataPointer(), args->num_param - args->offset, NULL);
01055 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01056 modifier = 0;
01057 break;
01058 }
01059
01060 case SCC_COMMA:
01061 buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
01062 break;
01063
01064 case SCC_DECIMAL: {
01065 int64 number = args->GetInt64(SCC_DECIMAL);
01066 int digits = args->GetInt32(SCC_DECIMAL);
01067 buff = FormatCommaNumber(buff, number, last, digits);
01068 break;
01069 }
01070
01071 case SCC_ARG_INDEX: {
01072 args->offset = orig_offset + (byte)*str++;
01073 break;
01074 }
01075
01076 case SCC_PLURAL_LIST: {
01077 int plural_form = *str++;
01078 uint offset = orig_offset + (byte)*str++;
01079 int64 v = *args->GetPointerToOffset(offset);
01080 str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
01081 break;
01082 }
01083
01084 case SCC_NUM:
01085 buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
01086 break;
01087
01088 case SCC_ZEROFILL_NUM: {
01089 int64 num = args->GetInt64();
01090 buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
01091 break;
01092 }
01093
01094 case SCC_HEX:
01095 buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
01096 break;
01097
01098 case SCC_BYTES:
01099 buff = FormatBytes(buff, args->GetInt64(), last);
01100 break;
01101
01102 case SCC_CURRENCY:
01103 buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY), false, last);
01104 break;
01105
01106 case SCC_WAYPOINT_NAME: {
01107 Waypoint *wp = Waypoint::Get(args->GetInt32(SCC_WAYPOINT_NAME));
01108
01109 assert(wp != NULL);
01110
01111 if (wp->name != NULL) {
01112 buff = strecpy(buff, wp->name, last);
01113 } else {
01114 int64 args_array[] = {wp->town->index, wp->town_cn + 1};
01115 StringParameters tmp_params(args_array);
01116 StringID str = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
01117 if (wp->town_cn != 0) str++;
01118 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01119 }
01120 break;
01121 }
01122
01123 case SCC_STATION_NAME: {
01124 StationID sid = args->GetInt32(SCC_STATION_NAME);
01125 const Station *st = Station::GetIfValid(sid);
01126
01127 if (st == NULL) {
01128
01129
01130
01131 StringParameters tmp_params(NULL, 0, NULL);
01132 buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
01133 break;
01134 }
01135
01136 if (st->name != NULL) {
01137 buff = strecpy(buff, st->name, last);
01138 } else {
01139 StringID str = st->string_id;
01140 if (st->indtype != IT_INVALID) {
01141
01142 const IndustrySpec *indsp = GetIndustrySpec(st->indtype);
01143
01144
01145
01146
01147 if (indsp->station_name != STR_NULL && indsp->station_name != STR_UNDEFINED) {
01148 str = indsp->station_name;
01149 }
01150 }
01151
01152 int64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
01153 StringParameters tmp_params(args_array);
01154 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01155 }
01156 break;
01157 }
01158
01159 case SCC_DEPOT_NAME: {
01160 VehicleType vt = (VehicleType)args->GetInt32(SCC_DEPOT_NAME);
01161 if (vt == VEH_AIRCRAFT) {
01162 int64 args_array[] = {args->GetInt32()};
01163 StringParameters tmp_params(args_array);
01164 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
01165 break;
01166 }
01167
01168 const Depot *d = Depot::Get(args->GetInt32());
01169 if (d->name != NULL) {
01170 buff = strecpy(buff, d->name, last);
01171 } else {
01172 int64 args_array[] = {d->town->index, d->town_cn + 1};
01173 StringParameters tmp_params(args_array);
01174 buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
01175 }
01176 break;
01177 }
01178
01179 case SCC_TOWN_NAME: {
01180 const Town *t = Town::Get(args->GetInt32(SCC_TOWN_NAME));
01181
01182 assert(t != NULL);
01183
01184 if (t->name != NULL) {
01185 buff = strecpy(buff, t->name, last);
01186 } else {
01187 buff = GetTownName(buff, t, last);
01188 }
01189 break;
01190 }
01191
01192 case SCC_GROUP_NAME: {
01193 const Group *g = Group::Get(args->GetInt32());
01194
01195 assert(g != NULL);
01196
01197 if (g->name != NULL) {
01198 buff = strecpy(buff, g->name, last);
01199 } else {
01200 int64 args_array[] = {g->index};
01201 StringParameters tmp_params(args_array);
01202
01203 buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
01204 }
01205 break;
01206 }
01207
01208 case SCC_ENGINE_NAME: {
01209 EngineID engine = (EngineID)args->GetInt32(SCC_ENGINE_NAME);
01210 const Engine *e = Engine::Get(engine);
01211
01212 assert(e != NULL);
01213
01214 if (e->name != NULL && e->IsEnabled()) {
01215 buff = strecpy(buff, e->name, last);
01216 } else {
01217 StringParameters tmp_params(NULL, 0, NULL);
01218 buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
01219 }
01220 break;
01221 }
01222
01223 case SCC_VEHICLE_NAME: {
01224 const Vehicle *v = Vehicle::Get(args->GetInt32(SCC_VEHICLE_NAME));
01225
01226 assert(v != NULL);
01227
01228 if (v->name != NULL) {
01229 buff = strecpy(buff, v->name, last);
01230 } else {
01231 int64 args_array[] = {v->unitnumber};
01232 StringParameters tmp_params(args_array);
01233
01234 StringID str;
01235 switch (v->type) {
01236 default: NOT_REACHED();
01237 case VEH_TRAIN: str = STR_SV_TRAIN_NAME; break;
01238 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME; break;
01239 case VEH_SHIP: str = STR_SV_SHIP_NAME; break;
01240 case VEH_AIRCRAFT: str = STR_SV_AIRCRAFT_NAME; break;
01241 }
01242
01243 buff = GetStringWithArgs(buff, str, &tmp_params, last);
01244 }
01245 break;
01246 }
01247
01248 case SCC_SIGN_NAME: {
01249 const Sign *si = Sign::Get(args->GetInt32());
01250 if (si->name != NULL) {
01251 buff = strecpy(buff, si->name, last);
01252 } else {
01253 StringParameters tmp_params(NULL, 0, NULL);
01254 buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
01255 }
01256 break;
01257 }
01258
01259 case SCC_COMPANY_NAME: {
01260 const Company *c = Company::Get((CompanyID)args->GetInt32());
01261
01262 if (c->name != NULL) {
01263 buff = strecpy(buff, c->name, last);
01264 } else {
01265 int64 args_array[] = {c->name_2};
01266 StringParameters tmp_params(args_array);
01267 buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
01268 }
01269 break;
01270 }
01271
01272 case SCC_COMPANY_NUM: {
01273 CompanyID company = (CompanyID)args->GetInt32();
01274
01275
01276 if (Company::IsValidHumanID(company)) {
01277 int64 args_array[] = {company + 1};
01278 StringParameters tmp_params(args_array);
01279 buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
01280 }
01281 break;
01282 }
01283
01284 case SCC_PRESIDENT_NAME: {
01285 const Company *c = Company::Get((CompanyID)args->GetInt32(SCC_PRESIDENT_NAME));
01286
01287 if (c->president_name != NULL) {
01288 buff = strecpy(buff, c->president_name, last);
01289 } else {
01290 int64 args_array[] = {c->president_name_2};
01291 StringParameters tmp_params(args_array);
01292 buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
01293 }
01294 break;
01295 }
01296
01297 case SCC_SETCASE: {
01298
01299
01300 modifier = (byte)*str++ << 24;
01301 break;
01302 }
01303
01304 case SCC_SWITCH_CASE: {
01305
01306
01307 uint num = (byte)*str++;
01308 while (num) {
01309 if ((byte)str[0] == casei) {
01310
01311 str += 3;
01312 break;
01313 }
01314
01315 str += 3 + (str[1] << 8) + str[2];
01316 num--;
01317 }
01318 break;
01319 }
01320
01321 default:
01322 if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
01323 break;
01324 }
01325 }
01326 *buff = '\0';
01327 return buff;
01328 }
01329
01330
01331 static char *StationGetSpecialString(char *buff, int x, const char *last)
01332 {
01333 if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
01334 if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
01335 if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
01336 if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
01337 if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
01338 *buff = '\0';
01339 return buff;
01340 }
01341
01342 static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)
01343 {
01344 return GenerateTownNameString(buff, last, ind, seed);
01345 }
01346
01347 static const char * const _silly_company_names[] = {
01348 "Bloggs Brothers",
01349 "Tiny Transport Ltd.",
01350 "Express Travel",
01351 "Comfy-Coach & Co.",
01352 "Crush & Bump Ltd.",
01353 "Broken & Late Ltd.",
01354 "Sam Speedy & Son",
01355 "Supersonic Travel",
01356 "Mike's Motors",
01357 "Lightning International",
01358 "Pannik & Loozit Ltd.",
01359 "Inter-City Transport",
01360 "Getout & Pushit Ltd."
01361 };
01362
01363 static const char * const _surname_list[] = {
01364 "Adams",
01365 "Allan",
01366 "Baker",
01367 "Bigwig",
01368 "Black",
01369 "Bloggs",
01370 "Brown",
01371 "Campbell",
01372 "Gordon",
01373 "Hamilton",
01374 "Hawthorn",
01375 "Higgins",
01376 "Green",
01377 "Gribble",
01378 "Jones",
01379 "McAlpine",
01380 "MacDonald",
01381 "McIntosh",
01382 "Muir",
01383 "Murphy",
01384 "Nelson",
01385 "O'Donnell",
01386 "Parker",
01387 "Phillips",
01388 "Pilkington",
01389 "Quigley",
01390 "Sharkey",
01391 "Thomson",
01392 "Watkins"
01393 };
01394
01395 static const char * const _silly_surname_list[] = {
01396 "Grumpy",
01397 "Dozy",
01398 "Speedy",
01399 "Nosey",
01400 "Dribble",
01401 "Mushroom",
01402 "Cabbage",
01403 "Sniffle",
01404 "Fishy",
01405 "Swindle",
01406 "Sneaky",
01407 "Nutkins"
01408 };
01409
01410 static const char _initial_name_letters[] = {
01411 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
01412 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W',
01413 };
01414
01415 static char *GenAndCoName(char *buff, uint32 arg, const char *last)
01416 {
01417 const char * const *base;
01418 uint num;
01419
01420 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01421 base = _silly_surname_list;
01422 num = lengthof(_silly_surname_list);
01423 } else {
01424 base = _surname_list;
01425 num = lengthof(_surname_list);
01426 }
01427
01428 buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last);
01429 buff = strecpy(buff, " & Co.", last);
01430
01431 return buff;
01432 }
01433
01434 static char *GenPresidentName(char *buff, uint32 x, const char *last)
01435 {
01436 char initial[] = "?. ";
01437 const char * const *base;
01438 uint num;
01439 uint i;
01440
01441 initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8];
01442 buff = strecpy(buff, initial, last);
01443
01444 i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8;
01445 if (i < sizeof(_initial_name_letters)) {
01446 initial[0] = _initial_name_letters[i];
01447 buff = strecpy(buff, initial, last);
01448 }
01449
01450 if (_settings_game.game_creation.landscape == LT_TOYLAND) {
01451 base = _silly_surname_list;
01452 num = lengthof(_silly_surname_list);
01453 } else {
01454 base = _surname_list;
01455 num = lengthof(_surname_list);
01456 }
01457
01458 buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last);
01459
01460 return buff;
01461 }
01462
01463 static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last)
01464 {
01465 switch (ind) {
01466 case 1:
01467 return strecpy(buff, _silly_company_names[args->GetInt32() & 0xFFFF], last);
01468
01469 case 2:
01470 return GenAndCoName(buff, args->GetInt32(), last);
01471
01472 case 3:
01473 return GenPresidentName(buff, args->GetInt32(), last);
01474 }
01475
01476
01477 if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
01478 buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last);
01479 return strecpy(buff, " Transport", last);
01480 }
01481
01482
01483 if (IsInsideMM(ind, (SPECSTR_LANGUAGE_START - 0x70E4), (SPECSTR_LANGUAGE_END - 0x70E4) + 1)) {
01484 int i = ind - (SPECSTR_LANGUAGE_START - 0x70E4);
01485 return strecpy(buff,
01486 &_languages[i] == _current_language ? _current_language->own_name : _languages[i].name, last);
01487 }
01488
01489
01490 if (IsInsideMM(ind, (SPECSTR_RESOLUTION_START - 0x70E4), (SPECSTR_RESOLUTION_END - 0x70E4) + 1)) {
01491 int i = ind - (SPECSTR_RESOLUTION_START - 0x70E4);
01492 buff += seprintf(
01493 buff, last, "%ux%u", _resolutions[i].width, _resolutions[i].height
01494 );
01495 return buff;
01496 }
01497
01498
01499 if (IsInsideMM(ind, (SPECSTR_SCREENSHOT_START - 0x70E4), (SPECSTR_SCREENSHOT_END - 0x70E4) + 1)) {
01500 int i = ind - (SPECSTR_SCREENSHOT_START - 0x70E4);
01501 return strecpy(buff, GetScreenshotFormatDesc(i), last);
01502 }
01503
01504 NOT_REACHED();
01505 }
01506
01507 #ifdef ENABLE_NETWORK
01508 extern void SortNetworkLanguages();
01509 #else
01510 static inline void SortNetworkLanguages() {}
01511 #endif
01512
01517 bool LanguagePackHeader::IsValid() const
01518 {
01519 return this->ident == TO_LE32(LanguagePackHeader::IDENT) &&
01520 this->version == TO_LE32(LANGUAGE_PACK_VERSION) &&
01521 this->plural_form < LANGUAGE_MAX_PLURAL &&
01522 this->text_dir <= 1 &&
01523 this->newgrflangid < MAX_LANG &&
01524 this->num_genders < MAX_NUM_GENDERS &&
01525 this->num_cases < MAX_NUM_CASES &&
01526 StrValid(this->name, lastof(this->name)) &&
01527 StrValid(this->own_name, lastof(this->own_name)) &&
01528 StrValid(this->isocode, lastof(this->isocode)) &&
01529 StrValid(this->digit_group_separator, lastof(this->digit_group_separator)) &&
01530 StrValid(this->digit_group_separator_currency, lastof(this->digit_group_separator_currency)) &&
01531 StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator));
01532 }
01533
01539 bool ReadLanguagePack(const LanguageMetadata *lang)
01540 {
01541
01542 size_t len;
01543 LanguagePack *lang_pack = (LanguagePack *)ReadFileToMem(lang->file, &len, 1U << 20);
01544 if (lang_pack == NULL) return false;
01545
01546
01547 const char *end = (char *)lang_pack + len + 1;
01548
01549
01550 if (end <= lang_pack->data || !lang_pack->IsValid()) {
01551 free(lang_pack);
01552 return false;
01553 }
01554
01555 #if TTD_ENDIAN == TTD_BIG_ENDIAN
01556 for (uint i = 0; i < 32; i++) {
01557 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
01558 }
01559 #endif
01560
01561 uint count = 0;
01562 for (uint i = 0; i < 32; i++) {
01563 uint num = lang_pack->offsets[i];
01564 _langtab_start[i] = count;
01565 _langtab_num[i] = num;
01566 count += num;
01567 }
01568
01569
01570 char **langpack_offs = MallocT<char *>(count);
01571
01572
01573 char *s = lang_pack->data;
01574 len = (byte)*s++;
01575 for (uint i = 0; i < count; i++) {
01576 if (s + len >= end) {
01577 free(lang_pack);
01578 free(langpack_offs);
01579 return false;
01580 }
01581 if (len >= 0xC0) {
01582 len = ((len & 0x3F) << 8) + (byte)*s++;
01583 if (s + len >= end) {
01584 free(lang_pack);
01585 free(langpack_offs);
01586 return false;
01587 }
01588 }
01589 langpack_offs[i] = s;
01590 s += len;
01591 len = (byte)*s;
01592 *s++ = '\0';
01593 }
01594
01595 free(_langpack);
01596 _langpack = lang_pack;
01597
01598 free(_langpack_offs);
01599 _langpack_offs = langpack_offs;
01600
01601 _current_language = lang;
01602 _current_text_dir = (TextDirection)_current_language->text_dir;
01603 const char *c_file = strrchr(_current_language->file, PATHSEPCHAR) + 1;
01604 strecpy(_config_language_file, c_file, lastof(_config_language_file));
01605 SetCurrentGrfLangID(_current_language->newgrflangid);
01606
01607 #ifdef WITH_ICU
01608
01609 if (_current_collator != NULL) {
01610 delete _current_collator;
01611 _current_collator = NULL;
01612 }
01613
01614
01615 UErrorCode status = U_ZERO_ERROR;
01616 _current_collator = Collator::createInstance(Locale(_current_language->isocode), status);
01617
01618 if (_current_collator != NULL) _current_collator->setAttribute(UCOL_NUMERIC_COLLATION, UCOL_ON, status);
01619
01620 if (U_FAILURE(status)) {
01621 delete _current_collator;
01622 _current_collator = NULL;
01623 }
01624 #endif
01625
01626
01627 InitializeSortedCargoSpecs();
01628 SortIndustryTypes();
01629 BuildIndustriesLegend();
01630 SortNetworkLanguages();
01631 InvalidateWindowClassesData(WC_BUILD_VEHICLE);
01632 InvalidateWindowClassesData(WC_TRAINS_LIST);
01633 InvalidateWindowClassesData(WC_ROADVEH_LIST);
01634 InvalidateWindowClassesData(WC_SHIPS_LIST);
01635 InvalidateWindowClassesData(WC_AIRCRAFT_LIST);
01636 InvalidateWindowClassesData(WC_INDUSTRY_DIRECTORY);
01637 InvalidateWindowClassesData(WC_STATION_LIST);
01638
01639 return true;
01640 }
01641
01642
01643
01644 #if !(defined(WIN32) || defined(__APPLE__))
01645
01653 const char *GetCurrentLocale(const char *param)
01654 {
01655 const char *env;
01656
01657 env = getenv("LANGUAGE");
01658 if (env != NULL) return env;
01659
01660 env = getenv("LC_ALL");
01661 if (env != NULL) return env;
01662
01663 if (param != NULL) {
01664 env = getenv(param);
01665 if (env != NULL) return env;
01666 }
01667
01668 return getenv("LANG");
01669 }
01670 #else
01671 const char *GetCurrentLocale(const char *param);
01672 #endif
01673
01674 int CDECL StringIDSorter(const StringID *a, const StringID *b)
01675 {
01676 char stra[512];
01677 char strb[512];
01678 GetString(stra, *a, lastof(stra));
01679 GetString(strb, *b, lastof(strb));
01680
01681 return strcmp(stra, strb);
01682 }
01683
01689 const LanguageMetadata *GetLanguage(byte newgrflangid)
01690 {
01691 for (const LanguageMetadata *lang = _languages.Begin(); lang != _languages.End(); lang++) {
01692 if (newgrflangid == lang->newgrflangid) return lang;
01693 }
01694
01695 return NULL;
01696 }
01697
01704 static bool GetLanguageFileHeader(const char *file, LanguagePackHeader *hdr)
01705 {
01706 FILE *f = fopen(file, "rb");
01707 if (f == NULL) return false;
01708
01709 size_t read = fread(hdr, sizeof(*hdr), 1, f);
01710 fclose(f);
01711
01712 bool ret = read == 1 && hdr->IsValid();
01713
01714
01715 if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
01716 return ret;
01717 }
01718
01723 static void GetLanguageList(const char *path)
01724 {
01725 DIR *dir = ttd_opendir(path);
01726 if (dir != NULL) {
01727 struct dirent *dirent;
01728 while ((dirent = readdir(dir)) != NULL) {
01729 const char *d_name = FS2OTTD(dirent->d_name);
01730 const char *extension = strrchr(d_name, '.');
01731
01732
01733 if (extension == NULL || strcmp(extension, ".lng") != 0) continue;
01734
01735 LanguageMetadata lmd;
01736 seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name);
01737
01738
01739 if (!GetLanguageFileHeader(lmd.file, &lmd)) {
01740 DEBUG(misc, 3, "%s is not a valid language file", lmd.file);
01741 } else if (GetLanguage(lmd.newgrflangid) != NULL) {
01742 DEBUG(misc, 3, "%s's language ID is already known", lmd.file);
01743 } else {
01744 *_languages.Append() = lmd;
01745 }
01746 }
01747 closedir(dir);
01748 }
01749 }
01750
01755 void InitializeLanguagePacks()
01756 {
01757 Searchpath sp;
01758
01759 FOR_ALL_SEARCHPATHS(sp) {
01760 char path[MAX_PATH];
01761 FioAppendDirectory(path, lengthof(path), sp, LANG_DIR);
01762 GetLanguageList(path);
01763 }
01764 if (_languages.Length() == 0) usererror("No available language packs (invalid versions?)");
01765
01766
01767 const char *lang = GetCurrentLocale("LC_MESSAGES");
01768 if (lang == NULL) lang = "en_GB";
01769
01770 const LanguageMetadata *chosen_language = NULL;
01771 const LanguageMetadata *language_fallback = NULL;
01772 const LanguageMetadata *en_GB_fallback = _languages.Begin();
01773
01774
01775 for (const LanguageMetadata *lng = _languages.Begin(); lng != _languages.End(); lng++) {
01776
01777
01778
01779 const char *lang_file = strrchr(lng->file, PATHSEPCHAR) + 1;
01780 if (strcmp(lang_file, _config_language_file) == 0) {
01781 chosen_language = lng;
01782 break;
01783 }
01784
01785 if (strcmp (lng->isocode, "en_GB") == 0) en_GB_fallback = lng;
01786 if (strncmp(lng->isocode, lang, 5) == 0) chosen_language = lng;
01787 if (strncmp(lng->isocode, lang, 2) == 0) language_fallback = lng;
01788 }
01789
01790
01791
01792 if (chosen_language == NULL) {
01793 chosen_language = (language_fallback != NULL) ? language_fallback : en_GB_fallback;
01794 }
01795
01796 if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file);
01797 }
01798
01803 const char *GetCurrentLanguageIsoCode()
01804 {
01805 return _langpack->isocode;
01806 }
01807
01814 static bool FindMissingGlyphs(const char **str)
01815 {
01816 #ifdef WITH_FREETYPE
01817 UninitFreeType();
01818 InitFreeType();
01819 #endif
01820 const Sprite *question_mark[FS_END];
01821 FontSize size;
01822
01823 for (size = FS_BEGIN; size < FS_END; size++) {
01824 question_mark[size] = GetGlyph(size, '?');
01825 }
01826
01827 for (uint i = 0; i != 32; i++) {
01828 for (uint j = 0; j < _langtab_num[i]; j++) {
01829 size = FS_NORMAL;
01830 const char *text = _langpack_offs[_langtab_start[i] + j];
01831 if (str != NULL) *str = text;
01832 for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
01833 if (c == SCC_SETX) {
01834
01835
01836
01837 text++;
01838 } else if (c == SCC_SETXY) {
01839 text += 2;
01840 } else if (c == SCC_TINYFONT) {
01841 size = FS_SMALL;
01842 } else if (c == SCC_BIGFONT) {
01843 size = FS_LARGE;
01844 } else if (IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) {
01845
01846 return true;
01847 }
01848 }
01849 }
01850 }
01851 return false;
01852 }
01853
01864 void CheckForMissingGlyphsInLoadedLanguagePack()
01865 {
01866 bool bad_font = FindMissingGlyphs(NULL);
01867 #ifdef WITH_FREETYPE
01868 if (bad_font) {
01869
01870
01871 FreeTypeSettings backup;
01872 memcpy(&backup, &_freetype, sizeof(backup));
01873
01874 bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, &FindMissingGlyphs);
01875
01876 memcpy(&_freetype, &backup, sizeof(backup));
01877
01878 if (bad_font) {
01879
01880
01881
01882 UninitFreeType();
01883 InitFreeType();
01884 }
01885 }
01886 #endif
01887
01888 if (bad_font) {
01889
01890
01891
01892
01893
01894 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.");
01895 Utf8Encode(err_str, SCC_YELLOW);
01896 SetDParamStr(0, err_str);
01897 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING);
01898
01899
01900 LoadStringWidthTable();
01901 return;
01902 }
01903
01904
01905 LoadStringWidthTable();
01906
01907 #if !defined(WITH_ICU)
01908
01909
01910
01911
01912
01913
01914
01915
01916
01917
01918
01919
01920
01921 if (_current_text_dir != TD_LTR) {
01922 static char *err_str = strdup("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");
01923 Utf8Encode(err_str, SCC_YELLOW);
01924 SetDParamStr(0, err_str);
01925 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
01926 }
01927 #endif
01928 }