40 #include "table/strings.h"
62 assert(this->
type !=
nullptr);
74 Debug(misc, 0,
"Trying to read invalid string parameter");
77 if (this->type !=
nullptr) {
78 if (this->type[this->
offset] != 0 && this->type[this->
offset] != type) {
79 Debug(misc, 0,
"Trying to read string parameter with wrong type");
98 while (max_value >= 10) {
116 uint64 val = count > 1 ? front : next;
117 for (; count > 1; count--) {
118 val = 10 * val + next;
159 for (
int i = 0; i < num; i++) {
161 strings[i] =
stredup((
const char *)(
size_t)_global_string_params.GetParam(i));
162 dst[i] = (size_t)strings[i];
164 strings[i] =
nullptr;
169 static char *StationGetSpecialString(
char *buff,
int x,
const char *last);
170 static char *GetSpecialTownNameString(
char *buff,
int ind, uint32 seed,
const char *last);
171 static char *GetSpecialNameString(
char *buff,
int ind,
StringParameters *args,
const char *last);
173 static char *
FormatString(
char *buff,
const char *str,
StringParameters *args,
const char *last, uint case_index = 0,
bool game_script =
false,
bool dry_run =
false);
183 delete[]
reinterpret_cast<char*
>(langpack);
188 std::unique_ptr<LanguagePack, LanguagePackDeleter> langpack;
190 std::vector<char *> offsets;
201 const char *GetStringPtr(
StringID string)
206 case TEXT_TAB_OLD_NEWGRF: NOT_REACHED();
231 if (index >= 0xC0 && !game_script) {
232 return GetSpecialTownNameString(buffr, index - 0xC0, args->
GetInt32(), last);
236 case TEXT_TAB_SPECIAL:
237 if (index >= 0xE4 && !game_script) {
238 return GetSpecialNameString(buffr, index - 0xE4, args, last);
242 case TEXT_TAB_OLD_CUSTOM:
245 error(
"Incorrect conversion of custom name string.");
252 case TEXT_TAB_OLD_NEWGRF:
266 error(
"String 0x%X is invalid. You are probably using an old version of the .lng file.\n",
string);
269 return FormatString(buffr, GetStringPtr(
string), args, last, case_index);
275 _global_string_params.
offset = 0;
324 static char *
FormatNumber(
char *buff, int64 number,
const char *last,
const char *separator,
int zerofill = 1,
int fractional_digits = 0)
326 static const int max_digits = 20;
327 uint64 divisor = 10000000000000000000ULL;
328 zerofill += fractional_digits;
329 int thousands_offset = (max_digits - fractional_digits - 1) % 3;
338 for (
int i = 0; i < max_digits; i++) {
339 if (i == max_digits - fractional_digits) {
341 if (
StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator;
342 buff +=
seprintf(buff, last,
"%s", decimal_separator);
346 if (num >= divisor) {
347 quot = num / divisor;
350 if ((tot |= quot) || i >= max_digits - zerofill) {
351 buff +=
seprintf(buff, last,
"%i", (
int)quot);
352 if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff =
strecpy(buff, separator, last);
363 static char *FormatCommaNumber(
char *buff, int64 number,
const char *last,
int fractional_digits = 0)
366 if (
StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator;
367 return FormatNumber(buff, number, last, separator, 1, fractional_digits);
370 static char *FormatNoCommaNumber(
char *buff, int64 number,
const char *last)
375 static char *FormatZerofillNumber(
char *buff, int64 number, int64 count,
const char *last)
380 static char *FormatHexNumber(
char *buff, uint64 number,
const char *last)
382 return buff +
seprintf(buff, last,
"0x" OTTD_PRINTFHEX64, number);
392 static char *
FormatBytes(
char *buff, int64 number,
const char *last)
397 const char *
const iec_prefixes[] = {
"",
"Ki",
"Mi",
"Gi",
"Ti",
"Pi",
"Ei"};
399 while (number >= 1024 * 1024) {
405 if (
StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator;
409 buff +=
seprintf(buff, last,
"%i", (
int)number);
410 }
else if (number < 1024 * 10) {
411 buff +=
seprintf(buff, last,
"%i%s%02i", (
int)number / 1024, decimal_separator, (
int)(number % 1024) * 100 / 1024);
412 }
else if (number < 1024 * 100) {
413 buff +=
seprintf(buff, last,
"%i%s%01i", (
int)number / 1024, decimal_separator, (
int)(number % 1024) * 10 / 1024);
415 assert(number < 1024 * 1024);
416 buff +=
seprintf(buff, last,
"%i", (
int)number / 1024);
419 assert(
id <
lengthof(iec_prefixes));
420 buff +=
seprintf(buff, last,
NBSP "%sB", iec_prefixes[
id]);
425 static char *FormatYmdString(
char *buff,
Date date,
const char *last, uint case_index)
430 int64 args[] = {ymd.
day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.
month, ymd.
year};
432 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
435 static char *FormatMonthAndYear(
char *buff,
Date date,
const char *last, uint case_index)
440 int64 args[] = {STR_MONTH_JAN + ymd.
month, ymd.
year};
442 return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
445 static char *FormatTinyOrISODate(
char *buff,
Date date,
StringID str,
const char *last)
456 int64 args[] = {(int64)(
size_t)day, (int64)(
size_t)month, ymd.
year};
458 return FormatString(buff, GetStringPtr(str), &tmp_params, last);
461 static char *FormatGenericCurrency(
char *buff,
const CurrencySpec *spec,
Money number,
bool compact,
const char *last)
465 bool negative = number < 0;
466 const char *multiplier =
"";
468 number *= spec->
rate;
472 if (buff +
Utf8CharLen(SCC_PUSH_COLOUR) > last)
return buff;
474 if (buff +
Utf8CharLen(SCC_RED) > last)
return buff;
476 buff =
strecpy(buff,
"-", last);
489 if (number >= 1000000000 - 500) {
490 number = (number + 500000) / 1000000;
491 multiplier =
NBSP "M";
492 }
else if (number >= 1000000) {
493 number = (number + 500) / 1000;
494 multiplier =
NBSP "k";
499 if (
StrEmpty(separator)) separator = _currency->separator.c_str();
500 if (
StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator_currency;
502 buff =
strecpy(buff, multiplier, last);
510 if (buff +
Utf8CharLen(SCC_POP_COLOUR) > last)
return buff;
527 uint64 n =
abs(count);
529 switch (plural_form) {
538 return n != 1 ? 1 : 0;
550 return n > 1 ? 1 : 0;
557 return n % 10 == 1 && n % 100 != 11 ? 0 : n != 0 ? 1 : 2;
563 return n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4;
569 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
575 return n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
581 return n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
587 return n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : n % 100 == 3 || n % 100 == 4 ? 2 : 3;
593 return n % 10 == 1 && n % 100 != 11 ? 0 : 1;
599 return n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2;
630 return (n == 1 ? 0 : n == 0 || (n % 100 > 1 && n % 100 < 11) ? 1 : (n % 100 > 10 && n % 100 < 20) ? 2 : 3);
635 return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3);
641 return n == 1 ? 0 : (n == 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2;
645 static const char *ParseStringChoice(
const char *b, uint form,
char **dst,
const char *last)
649 uint pos, i, mypos = 0;
651 for (i = pos = 0; i != n; i++) {
652 uint len = (byte)*b++;
653 if (i == form) mypos = pos;
657 *dst +=
seprintf(*dst, last,
"%s", b + mypos);
674 return ((input * this->multiplier) + (round && this->shift != 0 ? 1 << (this->shift - 1) : 0)) >> this->
shift;
684 int64
FromDisplay(int64 input,
bool round =
true, int64 divider = 1)
const
686 return ((input << this->shift) + (round ? (this->multiplier * divider) - 1 : 0)) / (this->multiplier * divider);
706 { { 1, 0}, STR_UNITS_VELOCITY_IMPERIAL, 0 },
707 { { 103, 6}, STR_UNITS_VELOCITY_METRIC, 0 },
708 { { 1831, 12}, STR_UNITS_VELOCITY_SI, 0 },
709 { {37888, 16}, STR_UNITS_VELOCITY_GAMEUNITS, 1 },
714 { { 1, 0}, STR_UNITS_POWER_IMPERIAL, 0 },
715 { {4153, 12}, STR_UNITS_POWER_METRIC, 0 },
716 { {6109, 13}, STR_UNITS_POWER_SI, 0 },
721 { { 29, 5}, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_IMPERIAL, 1},
722 { { 1, 0}, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_METRIC, 1},
723 { { 1, 0}, STR_UNITS_POWER_IMPERIAL_TO_WEIGHT_SI, 1},
724 { { 59, 6}, STR_UNITS_POWER_METRIC_TO_WEIGHT_IMPERIAL, 1},
725 { { 65, 6}, STR_UNITS_POWER_METRIC_TO_WEIGHT_METRIC, 1},
726 { { 65, 6}, STR_UNITS_POWER_METRIC_TO_WEIGHT_SI, 1},
727 { { 173, 8}, STR_UNITS_POWER_SI_TO_WEIGHT_IMPERIAL, 1},
728 { { 3, 2}, STR_UNITS_POWER_SI_TO_WEIGHT_METRIC, 1},
729 { { 3, 2}, STR_UNITS_POWER_SI_TO_WEIGHT_SI, 1},
734 { {4515, 12}, STR_UNITS_WEIGHT_SHORT_IMPERIAL, STR_UNITS_WEIGHT_LONG_IMPERIAL },
735 { { 1, 0}, STR_UNITS_WEIGHT_SHORT_METRIC, STR_UNITS_WEIGHT_LONG_METRIC },
736 { {1000, 0}, STR_UNITS_WEIGHT_SHORT_SI, STR_UNITS_WEIGHT_LONG_SI },
741 { {4227, 4}, STR_UNITS_VOLUME_SHORT_IMPERIAL, STR_UNITS_VOLUME_LONG_IMPERIAL },
742 { {1000, 0}, STR_UNITS_VOLUME_SHORT_METRIC, STR_UNITS_VOLUME_LONG_METRIC },
743 { { 1, 0}, STR_UNITS_VOLUME_SHORT_SI, STR_UNITS_VOLUME_LONG_SI },
748 { {3597, 4}, STR_UNITS_FORCE_IMPERIAL, 0 },
749 { {3263, 5}, STR_UNITS_FORCE_METRIC, 0 },
750 { { 1, 0}, STR_UNITS_FORCE_SI, 0 },
755 { { 3, 0}, STR_UNITS_HEIGHT_IMPERIAL, 0 },
756 { { 1, 0}, STR_UNITS_HEIGHT_METRIC, 0 },
757 { { 1, 0}, STR_UNITS_HEIGHT_SI, 0 },
812 uint orig_offset = args->
offset;
824 FormatString(buff, str_arg, args, last, case_index, game_script,
true);
827 FormatString(buff, str_arg, args, last, case_index, game_script,
true);
830 args->
offset = orig_offset;
833 uint next_substr_case_index = 0;
834 char *buf_start = buff;
835 std::stack<const char *, std::vector<const char *>> str_stack;
836 str_stack.push(str_arg);
839 while (!str_stack.empty() && (b = Utf8Consume(&str_stack.top())) ==
'\0') {
842 if (str_stack.empty())
break;
843 const char *&str = str_stack.top();
849 if (b == 0)
continue;
854 uint64 sub_args_data[20];
855 WChar sub_args_type[20];
856 bool sub_args_need_free[20];
860 memset(sub_args_need_free, 0,
sizeof(sub_args_need_free));
863 uint32 stringid = strtoul(str, &p, 16);
864 if (*p !=
':' && *p !=
'\0') {
865 while (*p !=
'\0') p++;
867 buff =
strecat(buff,
"(invalid SCC_ENCODED)", last);
871 while (*p !=
'\0') p++;
873 buff =
strecat(buff,
"(invalid StringID)", last);
878 while (*p !=
'\0' && i < 20) {
883 bool instring =
false;
890 if (*p ==
'"' && escape) {
897 instring = !instring;
904 if (*p ==
':')
break;
905 if (*p ==
'\0')
break;
912 bool lookup = (l == SCC_ENCODED);
913 if (lookup) s += len;
915 param = strtoull(s, &p, 16);
919 while (*p !=
'\0') p++;
921 buff =
strecat(buff,
"(invalid sub-StringID)", last);
927 sub_args.SetParam(i++, param);
932 sub_args_need_free[i] =
true;
933 sub_args.SetParam(i++, (uint64)(
size_t)g);
942 for (
int i = 0; i < 20; i++) {
943 if (sub_args_need_free[i])
free((
void *)sub_args.GetParam(i));
949 StringID substr = Utf8Consume(&str);
950 str_stack.push(GetStringPtr(substr));
956 str_stack.push(GetStringPtr(substr));
957 case_index = next_substr_case_index;
958 next_substr_case_index = 0;
963 case SCC_GENDER_LIST: {
965 uint offset = orig_offset + (byte)*str++;
986 WChar c = Utf8Consume(&s);
988 if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
990 str = ParseStringChoice(str, gender, &buff, last);
996 case SCC_GENDER_INDEX:
1005 case SCC_PLURAL_LIST: {
1006 int plural_form = *str++;
1007 uint offset = orig_offset + (byte)*str++;
1013 case SCC_ARG_INDEX: {
1014 args->
offset = orig_offset + (byte)*str++;
1018 case SCC_SET_CASE: {
1021 next_substr_case_index = (byte)*str++;
1025 case SCC_SWITCH_CASE: {
1028 uint num = (byte)*str++;
1030 if ((
byte)str[0] == case_index) {
1036 str += 3 + (str[1] << 8) + str[2];
1043 buff =
strecpy(buff, _openttd_revision, last);
1046 case SCC_RAW_STRING_POINTER: {
1047 if (game_script)
break;
1048 const char *str = (
const char *)(
size_t)args->
GetInt64(SCC_RAW_STRING_POINTER);
1060 buff =
GetStringWithArgs(buff, str, &tmp_params, last, next_substr_case_index, game_script);
1061 next_substr_case_index = 0;
1075 uint size = b - SCC_STRING1 + 1;
1077 buff =
strecat(buff,
"(too many parameters)", last);
1080 buff =
GetStringWithArgs(buff, str, &sub_args, last, next_substr_case_index, game_script);
1082 next_substr_case_index = 0;
1087 buff = FormatCommaNumber(buff, args->
GetInt64(SCC_COMMA), last);
1091 int64 number = args->
GetInt64(SCC_DECIMAL);
1092 int digits = args->
GetInt32(SCC_DECIMAL);
1093 buff = FormatCommaNumber(buff, number, last, digits);
1098 buff = FormatNoCommaNumber(buff, args->
GetInt64(SCC_NUM), last);
1101 case SCC_ZEROFILL_NUM: {
1103 buff = FormatZerofillNumber(buff, num, args->
GetInt64(), last);
1108 buff = FormatHexNumber(buff, (uint64)args->
GetInt64(SCC_HEX), last);
1115 case SCC_CARGO_TINY: {
1124 switch (cargo_str) {
1139 buff = FormatCommaNumber(buff, amount, last);
1143 case SCC_CARGO_SHORT: {
1151 switch (cargo_str) {
1177 case SCC_CARGO_LONG: {
1188 case SCC_CARGO_LIST: {
1189 CargoTypes cmask = args->
GetInt64(SCC_CARGO_LIST);
1193 if (!
HasBit(cmask, cs->Index()))
continue;
1195 if (buff >= last - 2)
break;
1205 buff =
GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
1209 if (first) buff =
GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
1212 next_substr_case_index = 0;
1215 assert(buff < last);
1219 case SCC_CURRENCY_SHORT:
1220 buff = FormatGenericCurrency(buff, _currency, args->
GetInt64(),
true, last);
1223 case SCC_CURRENCY_LONG:
1224 buff = FormatGenericCurrency(buff, _currency, args->
GetInt64(SCC_CURRENCY_LONG),
false, last);
1228 buff = FormatTinyOrISODate(buff, args->
GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
1231 case SCC_DATE_SHORT:
1232 buff = FormatMonthAndYear(buff, args->
GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
1233 next_substr_case_index = 0;
1237 buff = FormatYmdString(buff, args->
GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
1238 next_substr_case_index = 0;
1242 buff = FormatTinyOrISODate(buff, args->
GetInt32(), STR_FORMAT_DATE_ISO, last);
1269 case SCC_POWER_TO_WEIGHT: {
1275 int64 args_array[] = {x.c.ToDisplay(args->
GetInt64()), x.decimal_places};
1278 buff =
FormatString(buff, GetStringPtr(x.s), &tmp_params, last);
1282 case SCC_VELOCITY: {
1291 case SCC_VOLUME_SHORT: {
1299 case SCC_VOLUME_LONG: {
1307 case SCC_WEIGHT_SHORT: {
1315 case SCC_WEIGHT_LONG: {
1323 case SCC_COMPANY_NAME: {
1325 if (c ==
nullptr)
break;
1327 if (!c->
name.empty()) {
1328 int64 args_array[] = {(int64)(
size_t)c->
name.c_str()};
1332 int64 args_array[] = {c->
name_2};
1339 case SCC_COMPANY_NUM: {
1344 int64 args_array[] = {company + 1};
1351 case SCC_DEPOT_NAME: {
1354 uint64 args_array[] = {(uint64)args->
GetInt32()};
1355 WChar types_array[] = {SCC_STATION_NAME};
1357 buff =
GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
1362 if (!d->name.empty()) {
1363 int64 args_array[] = {(int64)(
size_t)d->name.c_str()};
1369 buff =
GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->
town_cn == 0 ? 0 : 1), &tmp_params, last);
1374 case SCC_ENGINE_NAME: {
1375 int64 arg = args->
GetInt64(SCC_ENGINE_NAME);
1377 if (e ==
nullptr)
break;
1380 int64 args_array[] = {(int64)(
size_t)e->
name.c_str()};
1392 assert(grffile !=
nullptr);
1395 uint64 tmp_dparam[6] = { 0 };
1396 WChar tmp_type[6] = { 0 };
1410 case SCC_GROUP_NAME: {
1412 if (g ==
nullptr)
break;
1414 if (!g->
name.empty()) {
1415 int64 args_array[] = {(int64)(
size_t)g->
name.c_str()};
1419 int64 args_array[] = {g->
index};
1427 case SCC_INDUSTRY_NAME: {
1429 if (i ==
nullptr)
break;
1441 buff =
FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
1443 next_substr_case_index = 0;
1447 case SCC_PRESIDENT_NAME: {
1449 if (c ==
nullptr)
break;
1463 case SCC_STATION_NAME: {
1464 StationID sid = args->
GetInt32(SCC_STATION_NAME);
1467 if (st ==
nullptr) {
1476 if (!st->
name.empty()) {
1477 int64 args_array[] = {(int64)(
size_t)st->
name.c_str()};
1482 if (st->
indtype != IT_INVALID) {
1494 uint64 args_array[] = {STR_TOWN_NAME, st->
town->
index, st->
index};
1495 WChar types_array[] = {0, SCC_TOWN_NAME, SCC_NUM};
1502 case SCC_TOWN_NAME: {
1504 if (t ==
nullptr)
break;
1506 if (!t->
name.empty()) {
1507 int64 args_array[] = {(int64)(
size_t)t->
name.c_str()};
1516 case SCC_WAYPOINT_NAME: {
1518 if (wp ==
nullptr)
break;
1520 if (!wp->
name.empty()) {
1521 int64 args_array[] = {(int64)(
size_t)wp->
name.c_str()};
1527 StringID str = ((wp->
string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
1534 case SCC_VEHICLE_NAME: {
1536 if (v ==
nullptr)
break;
1538 if (!v->
name.empty()) {
1539 int64 args_array[] = {(int64)(
size_t)v->
name.c_str()};
1546 buff =
GetStringWithArgs(buff, STR_FORMAT_GROUP_VEHICLE_NAME, &tmp_params, last);
1553 default: str = STR_INVALID_VEHICLE;
break;
1554 case VEH_TRAIN: str = STR_SV_TRAIN_NAME;
break;
1555 case VEH_ROAD: str = STR_SV_ROAD_VEHICLE_NAME;
break;
1556 case VEH_SHIP: str = STR_SV_SHIP_NAME;
break;
1565 case SCC_SIGN_NAME: {
1567 if (si ==
nullptr)
break;
1569 if (!si->name.empty()) {
1570 int64 args_array[] = {(int64)(
size_t)si->name.c_str()};
1580 case SCC_STATION_FEATURES: {
1581 buff = StationGetSpecialString(buff, args->
GetInt32(SCC_STATION_FEATURES), last);
1595 static char *StationGetSpecialString(
char *buff,
int x,
const char *last)
1606 static char *GetSpecialTownNameString(
char *buff,
int ind, uint32 seed,
const char *last)
1611 static const char *
const _silly_company_names[] = {
1613 "Tiny Transport Ltd.",
1615 "Comfy-Coach & Co.",
1616 "Crush & Bump Ltd.",
1617 "Broken & Late Ltd.",
1619 "Supersonic Travel",
1621 "Lightning International",
1622 "Pannik & Loozit Ltd.",
1623 "Inter-City Transport",
1624 "Getout & Pushit Ltd."
1627 static const char *
const _surname_list[] = {
1659 static const char *
const _silly_surname_list[] = {
1674 static const char _initial_name_letters[] = {
1675 'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
1676 'K',
'L',
'M',
'N',
'P',
'R',
'S',
'T',
'W',
1679 static char *GenAndCoName(
char *buff, uint32 arg,
const char *last)
1681 const char *
const *base;
1685 base = _silly_surname_list;
1686 num =
lengthof(_silly_surname_list);
1688 base = _surname_list;
1692 buff =
strecpy(buff, base[num *
GB(arg, 16, 8) >> 8], last);
1693 buff =
strecpy(buff,
" & Co.", last);
1698 static char *GenPresidentName(
char *buff, uint32 x,
const char *last)
1700 char initial[] =
"?. ";
1701 const char *
const *base;
1705 initial[0] = _initial_name_letters[
sizeof(_initial_name_letters) *
GB(x, 0, 8) >> 8];
1706 buff =
strecpy(buff, initial, last);
1708 i = (
sizeof(_initial_name_letters) + 35) *
GB(x, 8, 8) >> 8;
1709 if (i <
sizeof(_initial_name_letters)) {
1710 initial[0] = _initial_name_letters[i];
1711 buff =
strecpy(buff, initial, last);
1715 base = _silly_surname_list;
1716 num =
lengthof(_silly_surname_list);
1718 base = _surname_list;
1722 buff =
strecpy(buff, base[num *
GB(x, 16, 8) >> 8], last);
1727 static char *GetSpecialNameString(
char *buff,
int ind,
StringParameters *args,
const char *last)
1731 return strecpy(buff, _silly_company_names[std::min<uint>(args->
GetInt32() & 0xFFFF,
lengthof(_silly_company_names) - 1)], last);
1734 return GenAndCoName(buff, args->
GetInt32(), last);
1737 return GenPresidentName(buff, args->
GetInt32(), last);
1741 if (
IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) {
1742 buff = GetSpecialTownNameString(buff, ind - 6, args->
GetInt32(), last);
1743 return strecpy(buff,
" Transport", last);
1756 this->
version == TO_LE32(LANGUAGE_PACK_VERSION) &&
1776 return 4 * this->
missing < LANGUAGE_TOTAL_STRINGS;
1788 std::unique_ptr<LanguagePack, LanguagePackDeleter> lang_pack(
reinterpret_cast<LanguagePack *
>(
ReadFileToMem(lang->
file, len, 1U << 20).release()));
1789 if (!lang_pack)
return false;
1792 const char *end = (
char *)lang_pack.get() + len + 1;
1795 if (end <= lang_pack->data || !lang_pack->IsValid()) {
1799 #if TTD_ENDIAN == TTD_BIG_ENDIAN
1801 lang_pack->offsets[i] = ReadLE16Aligned(&lang_pack->offsets[i]);
1805 std::array<uint, TEXT_TAB_END> tab_start, tab_num;
1809 uint16 num = lang_pack->offsets[i];
1812 tab_start[i] = count;
1818 std::vector<char *> offs(count);
1821 char *s = lang_pack->data;
1823 for (uint i = 0; i < count; i++) {
1824 if (s + len >= end)
return false;
1827 len = ((len & 0x3F) << 8) + (byte)*s++;
1828 if (s + len >= end)
return false;
1836 _langpack.langpack = std::move(lang_pack);
1837 _langpack.offsets = std::move(offs);
1848 extern void Win32SetCurrentLocaleName(
const char *iso_code);
1857 #ifdef WITH_ICU_I18N
1859 UErrorCode status = U_ZERO_ERROR;
1864 if (U_FAILURE(status)) {
1888 #if !(defined(_WIN32) || defined(__APPLE__))
1901 env = std::getenv(
"LANGUAGE");
1902 if (env !=
nullptr)
return env;
1904 env = std::getenv(
"LC_ALL");
1905 if (env !=
nullptr)
return env;
1907 if (param !=
nullptr) {
1908 env = std::getenv(param);
1909 if (env !=
nullptr)
return env;
1912 return std::getenv(
"LANG");
1936 if (newgrflangid == lang.newgrflangid)
return ⟨
1950 FILE *f = fopen(file,
"rb");
1951 if (f ==
nullptr)
return false;
1953 size_t read = fread(hdr,
sizeof(*hdr), 1, f);
1956 bool ret = read == 1 && hdr->
IsValid();
1973 if (dir !=
nullptr) {
1974 struct dirent *dirent;
1975 while ((dirent = readdir(dir)) !=
nullptr) {
1976 std::string d_name =
FS2OTTD(dirent->d_name);
1977 const char *extension = strrchr(d_name.c_str(),
'.');
1980 if (extension ==
nullptr || strcmp(extension,
".lng") != 0)
continue;
1987 Debug(misc, 3,
"{} is not a valid language file", lmd.
file);
1989 Debug(misc, 3,
"{}'s language ID is already known", lmd.
file);
2005 std::string path = FioGetDirectory(sp,
LANG_DIR);
2012 if (lang ==
nullptr) lang =
"en_GB";
2023 const char *lang_file = strrchr(lng.file, PATHSEPCHAR) + 1;
2025 chosen_language = &lng;
2029 if (strcmp (lng.isocode,
"en_GB") == 0) en_GB_fallback = &lng;
2032 if (!lng.IsReasonablyFinished())
continue;
2034 if (strncmp(lng.isocode, lang, 5) == 0) chosen_language = &lng;
2035 if (strncmp(lng.isocode, lang, 2) == 0) language_fallback = &lng;
2040 if (chosen_language ==
nullptr) {
2041 chosen_language = (language_fallback !=
nullptr) ? language_fallback : en_GB_fallback;
2053 return _langpack.langpack->isocode;
2063 const Sprite *question_mark[FS_END];
2066 question_mark[size] =
GetGlyph(size,
'?');
2072 for (
WChar c = Utf8Consume(&text); c !=
'\0'; c = Utf8Consume(&text)) {
2073 if (c >= SCC_FIRST_FONT && c <= SCC_LAST_FONT) {
2074 size = (
FontSize)(c - SCC_FIRST_FONT);
2077 std::string size_name;
2080 case 0: size_name =
"medium";
break;
2081 case 1: size_name =
"small";
break;
2082 case 2: size_name =
"large";
break;
2083 case 3: size_name =
"mono";
break;
2084 default: NOT_REACHED();
2087 Debug(fontcache, 0,
"Font is missing glyphs to display char 0x{:X} in {} font size", (
int)c, size_name);
2115 const char *ret = _langpack.offsets[_langpack.
langtab_start[this->
i] + this->
j];
2118 while (this->i < TEXT_TAB_END && this->
j >= _langpack.
langtab_num[this->i]) {
2133 #if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2138 settings->small.os_handle = os_data;
2139 settings->medium.os_handle = os_data;
2140 settings->large.os_handle = os_data;
2161 if (searcher ==
nullptr) searcher = &pack_searcher;
2163 #if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
2167 bool any_font_configured = !_fcsettings.
medium.
font.empty();
2173 bad_font = !
SetFallbackFont(&_fcsettings, _langpack.langpack->isocode, _langpack.langpack->winlangid, searcher);
2175 _fcsettings = backup;
2177 if (!bad_font && any_font_configured) {
2184 static char *err_str =
stredup(
"XXXThe current font is missing some of the characters used in the texts for this language. Using system fallback font instead.");
2190 if (bad_font && base_font) {
2205 static char *err_str =
stredup(
"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.");
2218 #if !defined(WITH_ICU_LX) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
2233 static char *err_str =
stredup(
"XXXThis version of OpenTTD does not support right-to-left languages. Recompile with icu enabled.");