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