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