44 #include "table/strings.h"
46 #include "3rdparty/fmt/std.h"
48 #include "strings_internal.h"
73 for (
auto ¶m : this->
parameters) param.type = 0;
88 throw std::out_of_range(
"Trying to read invalid string parameter");
92 if (param.type != 0 && param.type != this->next_type) {
94 throw std::out_of_range(
"Trying to read string parameter with wrong type");
109 _global_string_params.SetParam(n, v);
119 return _global_string_params.GetParam(n);
133 while (max_value >= 10) {
151 uint64_t val = count > 1 ? front : next;
152 for (; count > 1; count--) {
153 val = 10 * val + next;
164 for (
size_t i = 0; i < backup.size(); i++) {
165 auto &value = backup[i];
166 if (value.string.has_value()) {
167 _global_string_params.SetParam(i, value.string.value());
169 _global_string_params.SetParam(i, value.data);
182 for (
size_t i = 0; i < backup.size(); i++) {
183 const char *str = _global_string_params.GetParamStr(i);
184 if (str !=
nullptr) {
187 backup[i] = _global_string_params.GetParam(i);
199 bool changed =
false;
200 for (
size_t i = 0; !changed && i < backup.size(); i++) {
201 bool global_has_string = _global_string_params.GetParamStr(i) !=
nullptr;
202 if (global_has_string != backup[i].
string.has_value())
return true;
204 if (global_has_string) {
205 changed = backup[i].string.value() != _global_string_params.GetParamStr(i);
207 changed = backup[i].data != _global_string_params.GetParam(i);
214 static void GetSpecialTownNameString(
StringBuilder &builder,
int ind, uint32_t seed);
227 delete[]
reinterpret_cast<char*
>(langpack);
232 std::unique_ptr<LanguagePack, LanguagePackDeleter> langpack;
234 std::vector<char *> offsets;
245 const char *GetStringPtr(
StringID string)
250 case TEXT_TAB_OLD_NEWGRF: NOT_REACHED();
276 if (index >= 0xC0 && !game_script) {
277 GetSpecialTownNameString(builder, index - 0xC0, args.
GetNextParameter<uint32_t>());
282 case TEXT_TAB_SPECIAL:
283 if (index >= 0xE4 && !game_script) {
284 GetSpecialNameString(builder, index - 0xE4, args);
289 case TEXT_TAB_OLD_CUSTOM:
292 FatalError(
"Incorrect conversion of custom name string.");
301 case TEXT_TAB_OLD_NEWGRF:
317 FatalError(
"String 0x{:X} is invalid. You are probably using an old version of the .lng file.\n",
string);
320 FormatString(builder, GetStringPtr(
string), args, case_index);
332 _global_string_params.PrepareForNextRun();
357 _global_string_params.SetParam(n, str);
368 _global_string_params.SetParam(n, str);
380 _global_string_params.SetParam(n, std::move(str));
383 static const char *GetDecimalSeparator()
386 if (
StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator;
387 return decimal_separator;
390 void InitializeNumberFormats()
392 bool loaded_number_format =
false;
395 if (res.has_value()) UserError(
"The setting 'number_format' under 'gui' is invalid: {}", *res);
396 loaded_number_format = !res.has_value();
400 bool loaded_number_abbreviations =
false;
403 if (res.has_value()) UserError(
"The setting 'number_abbreviations' under 'gui' is invalid: {}", *res);
404 loaded_number_abbreviations = !res.has_value();
407 _number_abbreviations.emplace_back(1, _number_format_separators);
423 uint64_t divisor = 10000000000000000000ULL;
424 uint64_t num = number;
426 for (
size_t i = 0; i < separators.size(); i++) {
428 if (num >= divisor) {
429 quot = num / divisor;
432 if ((tot |= quot) != 0 || i == separators.size() - 1) {
433 builder +=
'0' + quot;
434 builder += separators[i].data();
441 static void FormatNoCommaNumber(
StringBuilder &builder, int64_t number)
443 fmt::format_to(builder,
"{}", number);
446 static void FormatZerofillNumber(
StringBuilder &builder, int64_t number,
int count)
448 fmt::format_to(builder,
"{:0{}d}", number, count);
451 static void FormatHexNumber(
StringBuilder &builder, uint64_t number)
453 fmt::format_to(builder,
"0x{:X}", number);
466 const char *
const iec_prefixes[] = {
"",
"Ki",
"Mi",
"Gi",
"Ti",
"Pi",
"Ei"};
468 while (number >= 1024 * 1024) {
475 fmt::format_to(builder,
"{}", number);
476 }
else if (number < 1024 * 10) {
477 fmt::format_to(builder,
"{}{}{:02}", number / 1024, GetDecimalSeparator(), (number % 1024) * 100 / 1024);
478 }
else if (number < 1024 * 100) {
479 fmt::format_to(builder,
"{}{}{:01}", number / 1024, GetDecimalSeparator(), (number % 1024) * 10 / 1024);
481 assert(number < 1024 * 1024);
482 fmt::format_to(builder,
"{}", number / 1024);
485 assert(
id <
lengthof(iec_prefixes));
486 fmt::format_to(builder,
NBSP "{}B", iec_prefixes[
id]);
489 static void FormatYmdString(
StringBuilder &builder, TimerGameCalendar::Date date, uint case_index)
493 auto tmp_params = MakeParameters(ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year);
494 FormatString(builder, GetStringPtr(STR_FORMAT_DATE_LONG), tmp_params, case_index);
497 static void FormatMonthAndYear(
StringBuilder &builder, TimerGameCalendar::Date date, uint case_index)
501 auto tmp_params = MakeParameters(STR_MONTH_JAN + ymd.month, ymd.year);
502 FormatString(builder, GetStringPtr(STR_FORMAT_DATE_SHORT), tmp_params, case_index);
510 auto tmp_params = MakeParameters(ymd.day, 2, ymd.month + 1, 2, ymd.year);
518 bool negative = number < 0;
520 number *= spec->
rate;
539 auto it = _number_abbreviations.begin();
541 int64_t threshold = it->threshold;
543 if (it == _number_abbreviations.end())
break;
545 int64_t divisor = it->threshold;
546 threshold -= divisor / 2;
548 if ((int64_t)number > threshold) {
549 format = &it->format;
550 number += divisor / 2;
578 uint64_t n =
abs(count);
580 switch (plural_form) {
589 return n != 1 ? 1 : 0;
601 return n > 1 ? 1 : 0;
608 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
614 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
620 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
626 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
632 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
638 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
644 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
650 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
681 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
686 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
692 return n == 1 ? 0 : (n == 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2;
696 static const char *ParseStringChoice(
const char *b, uint form,
StringBuilder &builder)
700 uint pos, i, mypos = 0;
702 for (i = pos = 0; i != n; i++) {
703 uint len = (byte)*b++;
704 if (i == form) mypos = pos;
708 builder += b + mypos;
722 int64_t
ToDisplay(int64_t input,
bool round =
true)
const
725 ? (int64_t)std::round(input * this->factor)
726 : (int64_t)(input * this->factor);
736 int64_t
FromDisplay(int64_t input,
bool round =
true, int64_t divider = 1)
const
739 ? (int64_t)std::round(input / this->factor / divider)
740 : (int64_t)(input / this->factor / divider);
761 { { 1.0 }, STR_UNITS_VELOCITY_IMPERIAL, 0 },
762 { { 1.609344 }, STR_UNITS_VELOCITY_METRIC, 0 },
763 { { 0.44704 }, STR_UNITS_VELOCITY_SI, 0 },
764 { { 0.578125 }, STR_UNITS_VELOCITY_GAMEUNITS_DAY, 1 },
765 { { 0.868976 }, STR_UNITS_VELOCITY_KNOTS, 0 },
770 { { 1.0 }, STR_UNITS_VELOCITY_IMPERIAL, 0 },
771 { { 1.609344 }, STR_UNITS_VELOCITY_METRIC, 0 },
772 { { 0.44704 }, STR_UNITS_VELOCITY_SI, 0 },
773 { { 0.289352 }, STR_UNITS_VELOCITY_GAMEUNITS_SEC, 1 },
774 { { 0.868976 }, STR_UNITS_VELOCITY_KNOTS, 0 },
779 { { 1.0 }, STR_UNITS_POWER_IMPERIAL, 0 },
780 { { 1.01387 }, STR_UNITS_POWER_METRIC, 0 },
781 { { 0.745699 }, STR_UNITS_POWER_SI, 0 },
786 { { 0.907185 }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_IMPERIAL, 1 },
787 { { 1.0 }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_METRIC, 1 },
788 { { 1.0 }, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_SI, 1 },
789 { { 0.919768 }, STR_UNITS_POWER_METRIC_TO_WEIGHT_IMPERIAL, 1 },
790 { { 1.01387 }, STR_UNITS_POWER_METRIC_TO_WEIGHT_METRIC, 1 },
791 { { 1.01387 }, STR_UNITS_POWER_METRIC_TO_WEIGHT_SI, 1 },
792 { { 0.676487 }, STR_UNITS_POWER_SI_TO_WEIGHT_IMPERIAL, 1 },
793 { { 0.745699 }, STR_UNITS_POWER_SI_TO_WEIGHT_METRIC, 1 },
794 { { 0.745699 }, STR_UNITS_POWER_SI_TO_WEIGHT_SI, 1 },
799 { { 1.102311 }, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL, 0 },
800 { { 1.0 }, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC, 0 },
801 { { 1000.0 }, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI, 0 },
806 { { 264.172 }, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL, 0 },
807 { { 1000.0 }, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC, 0 },
808 { { 1.0 }, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI, 0 },
813 { { 0.224809 }, STR_UNITS_FORCE_IMPERIAL, 0 },
814 { { 0.101972 }, STR_UNITS_FORCE_METRIC, 0 },
815 { { 0.001 }, STR_UNITS_FORCE_SI, 0 },
820 { { 3.0 }, STR_UNITS_HEIGHT_IMPERIAL, 0 },
821 { { 1.0 }, STR_UNITS_HEIGHT_METRIC, 0 },
822 { { 1.0 }, STR_UNITS_HEIGHT_SI, 0 },
827 { { 1 }, STR_UNITS_DAYS, 0 },
828 { { 2 }, STR_UNITS_SECONDS, 0 },
833 { { 1 }, STR_UNITS_MONTHS, 0 },
834 { { 1 }, STR_UNITS_MINUTES, 0 },
839 { { 1 }, STR_UNITS_YEARS, 0 },
840 { { 1 }, STR_UNITS_PERIODS, 0 },
845 { { 1 }, STR_UNITS_YEARS, 0 },
846 { { 12 }, STR_UNITS_MINUTES, 0 },
939 FormatString(dry_run_builder, str_arg, args, case_index, game_script,
true);
942 FormatString(dry_run_builder, str_arg, args, case_index, game_script,
true);
948 uint next_substr_case_index = 0;
949 std::stack<const char *, std::vector<const char *>> str_stack;
950 str_stack.push(str_arg);
954 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) ==
'\0') {
957 if (str_stack.empty())
break;
958 const char *&str = str_stack.top();
964 if (b == 0)
continue;
967 if (b < SCC_CONTROL_START || b > SCC_CONTROL_END) {
972 args.SetTypeOfNextParameter(b);
978 uint32_t stringid = std::strtoul(str, &p, 16);
979 if (*p !=
':' && *p !=
'\0') {
980 while (*p !=
'\0') p++;
982 builder +=
"(invalid SCC_ENCODED)";
986 while (*p !=
'\0') p++;
988 builder +=
"(invalid StringID)";
993 while (*p !=
'\0' && i < 20) {
998 bool instring =
false;
1005 if (*p ==
'"' && escape) {
1012 instring = !instring;
1019 if (*p ==
':')
break;
1020 if (*p ==
'\0')
break;
1027 bool lookup = (l == SCC_ENCODED);
1028 if (lookup) s += len;
1030 param = std::strtoull(s, &p, 16);
1034 while (*p !=
'\0') p++;
1036 builder +=
"(invalid sub-StringID)";
1042 sub_args.SetParam(i++, param);
1045 sub_args.SetParam(i++, std::string(s, p - s - 1));
1057 StringID substr = Utf8Consume(&str);
1058 str_stack.push(GetStringPtr(substr));
1064 str_stack.push(GetStringPtr(substr));
1065 case_index = next_substr_case_index;
1066 next_substr_case_index = 0;
1071 case SCC_GENDER_LIST: {
1073 size_t offset = orig_offset + (byte)*str++;
1093 const char *s = buffer.c_str();
1094 char32_t c = Utf8Consume(&s);
1096 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
1098 str = ParseStringChoice(str, gender, builder);
1104 case SCC_GENDER_INDEX:
1113 case SCC_PLURAL_LIST: {
1114 int plural_form = *str++;
1115 size_t offset = orig_offset + (byte)*str++;
1116 int64_t v = args.GetParam(offset);
1121 case SCC_ARG_INDEX: {
1122 args.
SetOffset(orig_offset + (
byte)*str++);
1126 case SCC_SET_CASE: {
1129 next_substr_case_index = (byte)*str++;
1133 case SCC_SWITCH_CASE: {
1136 uint num = (byte)*str++;
1138 if ((
byte)str[0] == case_index) {
1144 str += 3 + (str[1] << 8) + str[2];
1151 builder += _openttd_revision;
1154 case SCC_RAW_STRING_POINTER: {
1157 if (raw_string ==
nullptr) {
1158 builder +=
"(invalid RAW_STRING parameter)";
1170 GetStringWithArgs(builder, string_id, tmp_params, next_substr_case_index, game_script);
1171 next_substr_case_index = 0;
1185 uint size = b - SCC_STRING1 + 1;
1187 builder +=
"(too many parameters)";
1190 GetStringWithArgs(builder, string_id, sub_args, next_substr_case_index, game_script);
1193 next_substr_case_index = 0;
1206 int64_t fractional = number % divisor;
1208 FormatNumber(builder, number, _number_format_separators);
1209 fmt::format_to(builder,
"{}{:0{}d}", GetDecimalSeparator(), fractional, digits);
1217 case SCC_ZEROFILL_NUM: {
1231 case SCC_CARGO_TINY: {
1240 switch (cargo_str) {
1255 FormatNumber(builder, amount, _number_format_separators);
1259 case SCC_CARGO_SHORT: {
1267 switch (cargo_str) {
1271 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1279 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1293 case SCC_CARGO_LONG: {
1304 case SCC_CARGO_LIST: {
1309 if (!
HasBit(cmask, cs->Index()))
continue;
1318 GetStringWithArgs(builder, cs->name, args, next_substr_case_index, game_script);
1322 if (first)
GetStringWithArgs(builder, STR_JUST_NOTHING, args, next_substr_case_index, game_script);
1324 next_substr_case_index = 0;
1328 case SCC_CURRENCY_SHORT:
1329 FormatGenericCurrency(builder, _currency, args.
GetNextParameter<int64_t>(),
true);
1332 case SCC_CURRENCY_LONG:
1333 FormatGenericCurrency(builder, _currency, args.
GetNextParameter<int64_t>(),
false);
1337 FormatTinyOrISODate(builder, args.
GetNextParameter<TimerGameCalendar::Date>(), STR_FORMAT_DATE_TINY);
1340 case SCC_DATE_SHORT:
1341 FormatMonthAndYear(builder, args.
GetNextParameter<TimerGameCalendar::Date>(), next_substr_case_index);
1342 next_substr_case_index = 0;
1346 FormatYmdString(builder, args.
GetNextParameter<TimerGameCalendar::Date>(), next_substr_case_index);
1347 next_substr_case_index = 0;
1351 FormatTinyOrISODate(builder, args.
GetNextParameter<TimerGameCalendar::Date>(), STR_FORMAT_DATE_ISO);
1357 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1365 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1373 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1378 case SCC_POWER_TO_WEIGHT: {
1382 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1387 case SCC_VELOCITY: {
1397 case SCC_VOLUME_SHORT: {
1400 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1405 case SCC_VOLUME_LONG: {
1408 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1413 case SCC_WEIGHT_SHORT: {
1416 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1421 case SCC_WEIGHT_LONG: {
1424 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1429 case SCC_UNITS_DAYS_OR_SECONDS: {
1432 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1437 case SCC_UNITS_MONTHS_OR_MINUTES: {
1440 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1445 case SCC_UNITS_YEARS_OR_PERIODS: {
1448 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1453 case SCC_UNITS_YEARS_OR_MINUTES: {
1456 auto tmp_params = MakeParameters(x.c.ToDisplay(args.
GetNextParameter<int64_t>()), x.decimal_places);
1461 case SCC_COMPANY_NAME: {
1463 if (c ==
nullptr)
break;
1465 if (!c->
name.empty()) {
1466 auto tmp_params = MakeParameters(c->
name);
1469 auto tmp_params = MakeParameters(c->
name_2);
1475 case SCC_COMPANY_NUM: {
1480 auto tmp_params = MakeParameters(company + 1);
1486 case SCC_DEPOT_NAME: {
1495 if (!d->name.empty()) {
1496 auto tmp_params = MakeParameters(d->name);
1499 auto tmp_params = MakeParameters(d->town->
index, d->
town_cn + 1);
1505 case SCC_ENGINE_NAME: {
1508 if (e ==
nullptr)
break;
1510 if (!e->name.empty() && e->IsEnabled()) {
1511 auto tmp_params = MakeParameters(e->name);
1520 const GRFFile *grffile = e->GetGRF();
1521 assert(grffile !=
nullptr);
1537 case SCC_GROUP_NAME: {
1539 if (g ==
nullptr)
break;
1541 if (!g->
name.empty()) {
1542 auto tmp_params = MakeParameters(g->
name);
1545 auto tmp_params = MakeParameters(g->
index);
1551 case SCC_INDUSTRY_NAME: {
1553 if (i ==
nullptr)
break;
1555 static bool use_cache =
true;
1561 }
else if (use_cache) {
1563 builder += i->GetCachedName();
1567 FormatString(builder, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), tmp_params, next_substr_case_index);
1569 next_substr_case_index = 0;
1573 case SCC_PRESIDENT_NAME: {
1575 if (c ==
nullptr)
break;
1587 case SCC_STATION_NAME: {
1591 if (st ==
nullptr) {
1600 static bool use_cache =
true;
1603 builder += st->GetCachedName();
1604 }
else if (!st->name.empty()) {
1605 auto tmp_params = MakeParameters(st->name);
1608 StringID string_id = st->string_id;
1609 if (st->indtype != IT_INVALID) {
1621 auto tmp_params = MakeParameters(STR_TOWN_NAME, st->town->index, st->index);
1627 case SCC_TOWN_NAME: {
1629 if (t ==
nullptr)
break;
1631 static bool use_cache =
true;
1634 builder += t->GetCachedName();
1635 }
else if (!t->
name.empty()) {
1636 auto tmp_params = MakeParameters(t->
name);
1644 case SCC_WAYPOINT_NAME: {
1646 if (wp ==
nullptr)
break;
1648 if (!wp->
name.empty()) {
1649 auto tmp_params = MakeParameters(wp->
name);
1653 StringID string_id = ((wp->
string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1654 if (wp->
town_cn != 0) string_id++;
1660 case SCC_VEHICLE_NAME: {
1662 if (v ==
nullptr)
break;
1664 if (!v->
name.empty()) {
1665 auto tmp_params = MakeParameters(v->
name);
1672 auto tmp_params = MakeParameters(v->
unitnumber);
1676 default: string_id = STR_INVALID_VEHICLE;
break;
1677 case VEH_TRAIN: string_id = STR_SV_TRAIN_NAME;
break;
1678 case VEH_ROAD: string_id = STR_SV_ROAD_VEHICLE_NAME;
break;
1679 case VEH_SHIP: string_id = STR_SV_SHIP_NAME;
break;
1680 case VEH_AIRCRAFT: string_id = STR_SV_AIRCRAFT_NAME;
break;
1688 case SCC_SIGN_NAME: {
1690 if (si ==
nullptr)
break;
1692 if (!si->name.empty()) {
1693 auto tmp_params = MakeParameters(si->name);
1702 case SCC_STATION_FEATURES: {
1717 }
catch (std::out_of_range &e) {
1718 Debug(misc, 0,
"FormatString: {}", e.what());
1719 builder +=
"(invalid parameter)";
1734 static void GetSpecialTownNameString(
StringBuilder &builder,
int ind, uint32_t seed)
1739 static const char *
const _silly_company_names[] = {
1741 "Tiny Transport Ltd.",
1743 "Comfy-Coach & Co.",
1744 "Crush & Bump Ltd.",
1745 "Broken & Late Ltd.",
1747 "Supersonic Travel",
1749 "Lightning International",
1750 "Pannik & Loozit Ltd.",
1751 "Inter-City Transport",
1752 "Getout & Pushit Ltd."
1755 static const char *
const _surname_list[] = {
1787 static const char *
const _silly_surname_list[] = {
1802 static const char _initial_name_letters[] = {
1803 'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
1804 'K',
'L',
'M',
'N',
'P',
'R',
'S',
'T',
'W',
1807 static void GenAndCoName(
StringBuilder &builder, uint32_t arg)
1809 const char *
const *base;
1813 base = _silly_surname_list;
1814 num =
lengthof(_silly_surname_list);
1816 base = _surname_list;
1820 builder += base[num *
GB(arg, 16, 8) >> 8];
1821 builder +=
" & Co.";
1824 static void GenPresidentName(
StringBuilder &builder, uint32_t x)
1826 char initial[] =
"?. ";
1827 const char *
const *base;
1831 initial[0] = _initial_name_letters[
sizeof(_initial_name_letters) *
GB(x, 0, 8) >> 8];
1834 i = (
sizeof(_initial_name_letters) + 35) *
GB(x, 8, 8) >> 8;
1835 if (i <
sizeof(_initial_name_letters)) {
1836 initial[0] = _initial_name_letters[i];
1841 base = _silly_surname_list;
1842 num =
lengthof(_silly_surname_list);
1844 base = _surname_list;
1848 builder += base[num *
GB(x, 16, 8) >> 8];
1855 builder += _silly_company_names[std::min<uint>(args.
GetNextParameter<uint16_t>(),
lengthof(_silly_company_names) - 1)];
1868 if (
IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1869 GetSpecialTownNameString(builder, ind - 6, args.
GetNextParameter<uint32_t>());
1870 builder +=
" Transport";
1884 this->
version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1904 return 4 * this->
missing < LANGUAGE_TOTAL_STRINGS;
1916 std::unique_ptr<LanguagePack, LanguagePackDeleter> lang_pack(
reinterpret_cast<LanguagePack *
>(
ReadFileToMem(lang->
file.string(), len, 1U << 20).release()));
1917 if (!lang_pack)
return false;
1920 const char *end = (
char *)lang_pack.get() + len + 1;
1923 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1927 std::array<uint, TEXT_TAB_END> tab_start, tab_num;
1931 uint16_t num = FROM_LE16(lang_pack->offsets[i]);
1934 tab_start[i] = count;
1940 std::vector<char *> offs(count);
1943 char *s = lang_pack->data;
1945 for (uint i = 0; i < count; i++) {
1946 if (s + len >= end)
return false;
1949 len = ((len & 0x3F) << 8) + (byte)*s++;
1950 if (s + len >= end)
return false;
1958 _langpack.langpack = std::move(lang_pack);
1959 _langpack.offsets = std::move(offs);
1969 extern void Win32SetCurrentLocaleName(std::string iso_code);
1978 #ifdef WITH_ICU_I18N
1980 UErrorCode status = U_ZERO_ERROR;
1985 if (U_FAILURE(status)) {
1990 InitializeNumberFormats();
2013 #if !(defined(_WIN32) || defined(__APPLE__))
2026 env = std::getenv(
"LANGUAGE");
2027 if (env !=
nullptr)
return env;
2029 env = std::getenv(
"LC_ALL");
2030 if (env !=
nullptr)
return env;
2032 if (param !=
nullptr) {
2033 env = std::getenv(param);
2034 if (env !=
nullptr)
return env;
2037 return std::getenv(
"LANG");
2051 if (newgrflangid == lang.newgrflangid)
return ⟨
2065 FILE *f = fopen(file,
"rb");
2066 if (f ==
nullptr)
return false;
2068 size_t read = fread(hdr,
sizeof(*hdr), 1, f);
2071 bool ret = read == 1 && hdr->
IsValid();
2088 if (dir !=
nullptr) {
2089 struct dirent *dirent;
2090 while ((dirent = readdir(dir)) !=
nullptr) {
2091 std::string d_name =
FS2OTTD(dirent->d_name);
2092 const char *extension = strrchr(d_name.c_str(),
'.');
2095 if (extension ==
nullptr || strcmp(extension,
".lng") != 0)
continue;
2098 lmd.
file = path + d_name;
2102 Debug(misc, 3,
"{} is not a valid language file", lmd.
file);
2104 Debug(misc, 3,
"{}'s language ID is already known", lmd.
file);
2122 if (
_languages.empty()) UserError(
"No available language packs (invalid versions?)");
2126 if (lang ==
nullptr) lang =
"en_GB";
2138 chosen_language = &lng;
2142 if (strcmp (lng.isocode,
"en_GB") == 0) en_GB_fallback = &lng;
2145 if (!lng.IsReasonablyFinished())
continue;
2147 if (strncmp(lng.isocode, lang, 5) == 0) chosen_language = &lng;
2148 if (strncmp(lng.isocode, lang, 2) == 0) language_fallback = &lng;
2153 if (chosen_language ==
nullptr) {
2154 chosen_language = (language_fallback !=
nullptr) ? language_fallback : en_GB_fallback;
2157 if (!
ReadLanguagePack(chosen_language)) UserError(
"Can't read language pack '{}'", chosen_language->
file);
2166 return _langpack.langpack->isocode;
2176 const Sprite *question_mark[FS_END];
2179 question_mark[size] =
GetGlyph(size,
'?');
2184 auto src = text->cbegin();
2187 while (src != text->cend()) {
2188 char32_t c = Utf8Consume(src);
2190 if (c >= SCC_FIRST_FONT && c <= SCC_LAST_FONT) {
2191 size = (
FontSize)(c - SCC_FIRST_FONT);
2194 std::string size_name;
2197 case FS_NORMAL: size_name =
"medium";
break;
2198 case FS_SMALL: size_name =
"small";
break;
2199 case FS_LARGE: size_name =
"large";
break;
2200 case FS_MONO: size_name =
"mono";
break;
2201 default: NOT_REACHED();
2204 Debug(fontcache, 0,
"Font is missing glyphs to display char 0x{:X} in {} font size", (
int)c, size_name);
2232 const char *ret = _langpack.offsets[_langpack.
langtab_start[this->
i] + this->
j];
2235 while (this->i < TEXT_TAB_END && this->
j >= _langpack.
langtab_num[this->i]) {
2248 void SetFontNames([[maybe_unused]]
FontCacheSettings *
settings, [[maybe_unused]]
const char *font_name, [[maybe_unused]]
const void *os_data)
override
2250 #if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2255 settings->small.os_handle = os_data;
2256 settings->medium.os_handle = os_data;
2257 settings->large.os_handle = os_data;
2278 if (searcher ==
nullptr) searcher = &pack_searcher;
2280 #if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2284 bool any_font_configured = !_fcsettings.
medium.
font.empty();
2290 bad_font = !
SetFallbackFont(&_fcsettings, _langpack.langpack->isocode, _langpack.langpack->winlangid, searcher);
2292 _fcsettings = backup;
2294 if (!bad_font && any_font_configured) {
2301 static std::string err_str(
"XXXThe current font is missing some of the characters used in the texts for this language. Using system fallback font instead.");
2307 if (bad_font && base_font) {
2322 static std::string err_str(
"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.");
2335 #if !(defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
2350 static std::string err_str(
"XXXThis version of OpenTTD does not support right-to-left languages. Recompile with ICU + Harfbuzz enabled.");