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