mirror of https://github.com/OpenTTD/OpenTTD
Codechange: Use StringConsumer in FormatString.
parent
15a17d832f
commit
f5ffd4789b
|
@ -861,10 +861,10 @@ static void ProcessNewGRFStringControlCode(char32_t scc, StringConsumer &consume
|
||||||
/**
|
/**
|
||||||
* Emit OpenTTD's internal string code for the different NewGRF string codes.
|
* Emit OpenTTD's internal string code for the different NewGRF string codes.
|
||||||
* @param scc NewGRF string code.
|
* @param scc NewGRF string code.
|
||||||
* @param[in,out] str String iterator, moved forward if SCC_NEWGRF_PUSH_WORD is found.
|
* @param consumer String consumer, moved forward if SCC_NEWGRF_PUSH_WORD is found.
|
||||||
* @returns String code to use.
|
* @returns String code to use.
|
||||||
*/
|
*/
|
||||||
char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str)
|
char32_t RemapNewGRFStringControlCode(char32_t scc, StringConsumer &consumer)
|
||||||
{
|
{
|
||||||
switch (scc) {
|
switch (scc) {
|
||||||
default:
|
default:
|
||||||
|
@ -932,7 +932,7 @@ char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str)
|
||||||
|
|
||||||
/* These NewGRF string codes modify the NewGRF stack or otherwise do not map to OpenTTD string codes. */
|
/* These NewGRF string codes modify the NewGRF stack or otherwise do not map to OpenTTD string codes. */
|
||||||
case SCC_NEWGRF_PUSH_WORD:
|
case SCC_NEWGRF_PUSH_WORD:
|
||||||
Utf8Consume(str);
|
consumer.SkipUtf8();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case SCC_NEWGRF_DISCARD_WORD:
|
case SCC_NEWGRF_DISCARD_WORD:
|
||||||
|
|
146
src/strings.cpp
146
src/strings.cpp
|
@ -644,7 +644,7 @@ static void FormatGenericCurrency(StringBuilder &builder, const CurrencySpec *sp
|
||||||
* @param plural_form The plural form we want an index for.
|
* @param plural_form The plural form we want an index for.
|
||||||
* @return The plural index for the given form.
|
* @return The plural index for the given form.
|
||||||
*/
|
*/
|
||||||
static int DeterminePluralForm(int64_t count, int plural_form)
|
static int DeterminePluralForm(int64_t count, uint plural_form)
|
||||||
{
|
{
|
||||||
/* The absolute value determines plurality */
|
/* The absolute value determines plurality */
|
||||||
uint64_t n = abs(count);
|
uint64_t n = abs(count);
|
||||||
|
@ -765,22 +765,25 @@ static int DeterminePluralForm(int64_t count, int plural_form)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *ParseStringChoice(const char *b, uint form, StringBuilder &builder)
|
static void ParseStringChoice(StringConsumer &consumer, uint form, StringBuilder &builder)
|
||||||
{
|
{
|
||||||
/* <NUM> {Length of each string} {each string} */
|
/* <NUM> {Length of each string} {each string} */
|
||||||
uint n = (uint8_t)*b++;
|
uint n = consumer.ReadUint8();
|
||||||
size_t form_offset = 0, form_len = 0, total_len = 0;
|
size_t form_pre = 0, form_len = 0, form_post = 0;
|
||||||
for (uint i = 0; i != n; i++) {
|
for (uint i = 0; i != n; i++) {
|
||||||
uint len = (uint8_t)*b++;
|
uint len = consumer.ReadUint8();
|
||||||
if (i == form) {
|
if (i < form) {
|
||||||
form_offset = total_len;
|
form_pre += len;
|
||||||
|
} else if (i > form) {
|
||||||
|
form_post += len;
|
||||||
|
} else {
|
||||||
form_len = len;
|
form_len = len;
|
||||||
}
|
}
|
||||||
total_len += len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
builder += std::string_view(b + form_offset, form_len);
|
consumer.Skip(form_pre);
|
||||||
return b + total_len;
|
builder += consumer.Read(form_len);
|
||||||
|
consumer.Skip(form_post);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Helper for unit conversion. */
|
/** Helper for unit conversion. */
|
||||||
|
@ -982,67 +985,59 @@ uint ConvertDisplaySpeedToKmhishSpeed(uint speed, VehicleType type)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes an encoded string during FormatString.
|
* Decodes an encoded string during FormatString.
|
||||||
* @param str The buffer of the encoded string.
|
* @param consumer The encoded string.
|
||||||
* @param game_script Set if decoding a GameScript-encoded string. This affects how string IDs are handled.
|
* @param game_script Set if decoding a GameScript-encoded string. This affects how string IDs are handled.
|
||||||
* @param builder The string builder to write the string to.
|
* @param builder The string builder to write the string to.
|
||||||
* @returns Updated position position in input buffer.
|
|
||||||
*/
|
*/
|
||||||
static const char *DecodeEncodedString(const char *str, bool game_script, StringBuilder &builder)
|
static void DecodeEncodedString(StringConsumer &consumer, bool game_script, StringBuilder &builder)
|
||||||
{
|
{
|
||||||
std::vector<StringParameter> sub_args;
|
std::vector<StringParameter> sub_args;
|
||||||
|
|
||||||
char *p;
|
StringIndexInTab id(consumer.ReadIntegerBase<uint32_t>(16));
|
||||||
StringIndexInTab id(std::strtoul(str, &p, 16));
|
if (consumer.AnyBytesLeft() && !consumer.ReadUtf8If(SCC_RECORD_SEPARATOR)) {
|
||||||
if (*p != SCC_RECORD_SEPARATOR && *p != '\0') {
|
consumer.SkipAll();
|
||||||
while (*p != '\0') p++;
|
|
||||||
builder += "(invalid SCC_ENCODED)";
|
builder += "(invalid SCC_ENCODED)";
|
||||||
return p;
|
return;
|
||||||
}
|
}
|
||||||
if (game_script && id >= TAB_SIZE_GAMESCRIPT) {
|
if (game_script && id >= TAB_SIZE_GAMESCRIPT) {
|
||||||
while (*p != '\0') p++;
|
consumer.SkipAll();
|
||||||
builder += "(invalid StringID)";
|
builder += "(invalid StringID)";
|
||||||
return p;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (*p != '\0') {
|
while (consumer.AnyBytesLeft()) {
|
||||||
/* The start of parameter. */
|
StringConsumer record(consumer.ReadUntilUtf8(SCC_RECORD_SEPARATOR, StringConsumer::SKIP_ONE_SEPARATOR));
|
||||||
const char *s = ++p;
|
|
||||||
|
|
||||||
/* Find end of the parameter. */
|
if (!record.AnyBytesLeft()) {
|
||||||
for (; *p != '\0' && *p != SCC_RECORD_SEPARATOR; ++p) {}
|
|
||||||
|
|
||||||
if (s == p) {
|
|
||||||
/* This is an empty parameter. */
|
/* This is an empty parameter. */
|
||||||
sub_args.emplace_back(std::monostate{});
|
sub_args.emplace_back(std::monostate{});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the parameter type. */
|
/* Get the parameter type. */
|
||||||
char32_t parameter_type;
|
char32_t parameter_type = record.ReadUtf8();
|
||||||
size_t len = Utf8Decode(¶meter_type, s);
|
|
||||||
s += len;
|
|
||||||
|
|
||||||
switch (parameter_type) {
|
switch (parameter_type) {
|
||||||
case SCC_ENCODED: {
|
case SCC_ENCODED: {
|
||||||
uint64_t param = std::strtoull(s, &p, 16);
|
uint64_t param = record.ReadIntegerBase<uint64_t>(16);
|
||||||
if (param >= TAB_SIZE_GAMESCRIPT) {
|
if (param >= TAB_SIZE_GAMESCRIPT) {
|
||||||
while (*p != '\0') p++;
|
|
||||||
builder += "(invalid sub-StringID)";
|
builder += "(invalid sub-StringID)";
|
||||||
return p;
|
return;
|
||||||
}
|
}
|
||||||
|
assert(!record.AnyBytesLeft());
|
||||||
param = MakeStringID(TEXT_TAB_GAMESCRIPT_START, StringIndexInTab(param));
|
param = MakeStringID(TEXT_TAB_GAMESCRIPT_START, StringIndexInTab(param));
|
||||||
sub_args.emplace_back(param);
|
sub_args.emplace_back(param);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SCC_ENCODED_NUMERIC: {
|
case SCC_ENCODED_NUMERIC: {
|
||||||
uint64_t param = std::strtoull(s, &p, 16);
|
uint64_t param = record.ReadIntegerBase<uint64_t>(16);
|
||||||
|
assert(!record.AnyBytesLeft());
|
||||||
sub_args.emplace_back(param);
|
sub_args.emplace_back(param);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SCC_ENCODED_STRING: {
|
case SCC_ENCODED_STRING: {
|
||||||
sub_args.emplace_back(std::string(s, p - s));
|
sub_args.emplace_back(std::string(record.Read(StringConsumer::npos)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1055,8 +1050,6 @@ static const char *DecodeEncodedString(const char *str, bool game_script, String
|
||||||
|
|
||||||
StringID stringid = game_script ? MakeStringID(TEXT_TAB_GAMESCRIPT_START, id) : StringID{id.base()};
|
StringID stringid = game_script ? MakeStringID(TEXT_TAB_GAMESCRIPT_START, id) : StringID{id.base()};
|
||||||
GetStringWithArgs(builder, stringid, sub_args, true);
|
GetStringWithArgs(builder, stringid, sub_args, true);
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1087,13 +1080,12 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin
|
||||||
}
|
}
|
||||||
uint next_substr_case_index = 0;
|
uint next_substr_case_index = 0;
|
||||||
struct StrStackItem {
|
struct StrStackItem {
|
||||||
const char *str;
|
StringConsumer consumer;
|
||||||
const char *end;
|
|
||||||
size_t first_param_offset;
|
size_t first_param_offset;
|
||||||
uint case_index;
|
uint case_index;
|
||||||
|
|
||||||
StrStackItem(std::string_view view, size_t first_param_offset, uint case_index)
|
StrStackItem(std::string_view view, size_t first_param_offset, uint case_index)
|
||||||
: str(view.data()), end(view.data() + view.size()), first_param_offset(first_param_offset), case_index(case_index)
|
: consumer(view), first_param_offset(first_param_offset), case_index(case_index)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
std::stack<StrStackItem, std::vector<StrStackItem>> str_stack;
|
std::stack<StrStackItem, std::vector<StrStackItem>> str_stack;
|
||||||
|
@ -1101,19 +1093,19 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
try {
|
try {
|
||||||
while (!str_stack.empty() && str_stack.top().str >= str_stack.top().end) {
|
while (!str_stack.empty() && !str_stack.top().consumer.AnyBytesLeft()) {
|
||||||
str_stack.pop();
|
str_stack.pop();
|
||||||
}
|
}
|
||||||
if (str_stack.empty()) break;
|
if (str_stack.empty()) break;
|
||||||
const char *&str = str_stack.top().str;
|
StringConsumer &consumer = str_stack.top().consumer;
|
||||||
const size_t ref_param_offset = str_stack.top().first_param_offset;
|
const size_t ref_param_offset = str_stack.top().first_param_offset;
|
||||||
const uint case_index = str_stack.top().case_index;
|
const uint case_index = str_stack.top().case_index;
|
||||||
char32_t b = Utf8Consume(&str);
|
char32_t b = consumer.ReadUtf8();
|
||||||
assert(b != 0);
|
assert(b != 0);
|
||||||
|
|
||||||
if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
|
if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) {
|
||||||
/* We need to pass some stuff as it might be modified. */
|
/* We need to pass some stuff as it might be modified. */
|
||||||
b = RemapNewGRFStringControlCode(b, &str);
|
b = RemapNewGRFStringControlCode(b, consumer);
|
||||||
if (b == 0) continue;
|
if (b == 0) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1126,13 +1118,13 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin
|
||||||
switch (b) {
|
switch (b) {
|
||||||
case SCC_ENCODED:
|
case SCC_ENCODED:
|
||||||
case SCC_ENCODED_INTERNAL:
|
case SCC_ENCODED_INTERNAL:
|
||||||
str = DecodeEncodedString(str, b == SCC_ENCODED, builder);
|
DecodeEncodedString(consumer, b == SCC_ENCODED, builder);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCC_NEWGRF_STRINL: {
|
case SCC_NEWGRF_STRINL: {
|
||||||
StringID substr = Utf8Consume(&str);
|
StringID substr = consumer.ReadUtf8(STR_NULL);
|
||||||
std::string_view ptr = GetStringPtr(substr);
|
std::string_view ptr = GetStringPtr(substr);
|
||||||
str_stack.emplace(ptr, args.GetOffset(), next_substr_case_index); // this may invalidate "str"
|
str_stack.emplace(ptr, args.GetOffset(), next_substr_case_index); // this may invalidate "consumer"
|
||||||
next_substr_case_index = 0;
|
next_substr_case_index = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1140,15 +1132,15 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin
|
||||||
case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
|
case SCC_NEWGRF_PRINT_WORD_STRING_ID: {
|
||||||
StringID substr = args.GetNextParameter<StringID>();
|
StringID substr = args.GetNextParameter<StringID>();
|
||||||
std::string_view ptr = GetStringPtr(substr);
|
std::string_view ptr = GetStringPtr(substr);
|
||||||
str_stack.emplace(ptr, args.GetOffset(), next_substr_case_index); // this may invalidate "str"
|
str_stack.emplace(ptr, args.GetOffset(), next_substr_case_index); // this may invalidate "consumer"
|
||||||
next_substr_case_index = 0;
|
next_substr_case_index = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SCC_GENDER_LIST: { // {G 0 Der Die Das}
|
case SCC_GENDER_LIST: { // {G 0 Der Die Das}
|
||||||
/* First read the meta data from the language file. */
|
/* First read the meta data from the language file. */
|
||||||
size_t offset = ref_param_offset + (uint8_t)*str++;
|
size_t offset = ref_param_offset + consumer.ReadUint8();
|
||||||
int gender = 0;
|
uint8_t gender = 0;
|
||||||
if (offset >= args.GetNumParameters()) {
|
if (offset >= args.GetNumParameters()) {
|
||||||
/* The offset may come from an external NewGRF, and be invalid. */
|
/* The offset may come from an external NewGRF, and be invalid. */
|
||||||
builder += "(invalid GENDER parameter)";
|
builder += "(invalid GENDER parameter)";
|
||||||
|
@ -1170,37 +1162,38 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin
|
||||||
FormatString(tmp_builder, input, tmp_params);
|
FormatString(tmp_builder, input, tmp_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The gender is stored at the start of the formatted string. */
|
/* The gender is stored at the start of the formatted string.
|
||||||
const char *s = buffer.c_str();
|
* Does this string have a gender, if so, set it. */
|
||||||
char32_t c = Utf8Consume(&s);
|
StringConsumer gender_consumer(buffer);
|
||||||
/* Does this string have a gender, if so, set it */
|
if (gender_consumer.ReadUtf8If(SCC_GENDER_INDEX)) {
|
||||||
if (c == SCC_GENDER_INDEX) gender = (uint8_t)s[0];
|
gender = gender_consumer.ReadUint8();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
str = ParseStringChoice(str, gender, builder);
|
ParseStringChoice(consumer, gender, builder);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This sets up the gender for the string.
|
/* This sets up the gender for the string.
|
||||||
* We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
|
* We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
|
||||||
case SCC_GENDER_INDEX: // {GENDER 0}
|
case SCC_GENDER_INDEX: { // {GENDER 0}
|
||||||
|
uint8_t gender = consumer.ReadUint8();
|
||||||
if (_scan_for_gender_data) {
|
if (_scan_for_gender_data) {
|
||||||
builder.PutUtf8(SCC_GENDER_INDEX);
|
builder.PutUtf8(SCC_GENDER_INDEX);
|
||||||
builder.PutUint8(*str++);
|
builder.PutUint8(gender);
|
||||||
} else {
|
|
||||||
str++;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case SCC_PLURAL_LIST: { // {P}
|
case SCC_PLURAL_LIST: { // {P}
|
||||||
int plural_form = *str++; // contains the plural form for this string
|
uint8_t plural_form = consumer.ReadUint8(); // contains the plural form for this string
|
||||||
size_t offset = ref_param_offset + (uint8_t)*str++;
|
size_t offset = ref_param_offset + consumer.ReadUint8();
|
||||||
const uint64_t *v = nullptr;
|
const uint64_t *v = nullptr;
|
||||||
/* The offset may come from an external NewGRF, and be invalid. */
|
/* The offset may come from an external NewGRF, and be invalid. */
|
||||||
if (offset < args.GetNumParameters()) {
|
if (offset < args.GetNumParameters()) {
|
||||||
v = std::get_if<uint64_t>(&args.GetParam(offset)); // contains the number that determines plural
|
v = std::get_if<uint64_t>(&args.GetParam(offset)); // contains the number that determines plural
|
||||||
}
|
}
|
||||||
if (v != nullptr) {
|
if (v != nullptr) {
|
||||||
str = ParseStringChoice(str, DeterminePluralForm(static_cast<int64_t>(*v), plural_form), builder);
|
ParseStringChoice(consumer, DeterminePluralForm(static_cast<int64_t>(*v), plural_form), builder);
|
||||||
} else {
|
} else {
|
||||||
builder += "(invalid PLURAL parameter)";
|
builder += "(invalid PLURAL parameter)";
|
||||||
}
|
}
|
||||||
|
@ -1208,38 +1201,35 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin
|
||||||
}
|
}
|
||||||
|
|
||||||
case SCC_ARG_INDEX: { // Move argument pointer
|
case SCC_ARG_INDEX: { // Move argument pointer
|
||||||
args.SetOffset(ref_param_offset + (uint8_t)*str++);
|
args.SetOffset(ref_param_offset + consumer.ReadUint8());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SCC_SET_CASE: { // {SET_CASE}
|
case SCC_SET_CASE: { // {SET_CASE}
|
||||||
/* This is a pseudo command, it's outputted when someone does {STRING.ack}
|
/* This is a pseudo command, it's outputted when someone does {STRING.ack}
|
||||||
* The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
|
* The modifier is added to all subsequent GetStringWithArgs that accept the modifier. */
|
||||||
next_substr_case_index = (uint8_t)*str++;
|
next_substr_case_index = consumer.ReadUint8();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SCC_SWITCH_CASE: { // {Used to implement case switching}
|
case SCC_SWITCH_CASE: { // {Used to implement case switching}
|
||||||
/* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <LENDEFAULT> <STRINGDEFAULT>
|
/* <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <LENDEFAULT> <STRINGDEFAULT>
|
||||||
* Each LEN is printed using 2 bytes in little endian order. */
|
* Each LEN is printed using 2 bytes in little endian order. */
|
||||||
uint num = (uint8_t)*str++;
|
uint num = consumer.ReadUint8();
|
||||||
std::optional<std::string_view> found;
|
std::optional<std::string_view> found;
|
||||||
for (; num > 0; --num) {
|
for (; num > 0; --num) {
|
||||||
uint8_t index = static_cast<uint8_t>(str[0]);
|
uint8_t index = consumer.ReadUint8();
|
||||||
uint16_t len = static_cast<uint8_t>(str[1]) + (static_cast<uint8_t>(str[2]) << 8);
|
uint16_t len = consumer.ReadUint16LE();
|
||||||
str += 3;
|
auto case_str = consumer.Read(len);
|
||||||
if (index == case_index) {
|
if (index == case_index) {
|
||||||
/* Found the case */
|
/* Found the case */
|
||||||
found.emplace(str, len);
|
found = case_str;
|
||||||
}
|
}
|
||||||
str += len;
|
|
||||||
}
|
}
|
||||||
uint16_t default_len = static_cast<uint8_t>(str[0]) + (static_cast<uint8_t>(str[1]) << 8);
|
uint16_t default_len = consumer.ReadUint16LE();
|
||||||
str += 2;
|
auto default_str = consumer.Read(default_len);
|
||||||
if (!found.has_value()) found.emplace(str, default_len);
|
if (!found.has_value()) found = default_str;
|
||||||
str += default_len;
|
str_stack.emplace(*found, ref_param_offset, case_index); // this may invalidate "consumer"
|
||||||
assert(str <= str_stack.top().end);
|
|
||||||
str_stack.emplace(*found, ref_param_offset, case_index); // this may invalidate "str"
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "strings_func.h"
|
#include "strings_func.h"
|
||||||
#include "string_func.h"
|
#include "string_func.h"
|
||||||
#include "core/string_builder.hpp"
|
#include "core/string_builder.hpp"
|
||||||
|
#include "core/string_consumer.hpp"
|
||||||
|
|
||||||
class StringParameters {
|
class StringParameters {
|
||||||
protected:
|
protected:
|
||||||
|
@ -214,6 +215,6 @@ void GenerateTownNameString(StringBuilder &builder, size_t lang, uint32_t seed);
|
||||||
void GetTownName(StringBuilder &builder, const struct Town *t);
|
void GetTownName(StringBuilder &builder, const struct Town *t);
|
||||||
void GRFTownNameGenerate(StringBuilder &builder, uint32_t grfid, uint16_t gen, uint32_t seed);
|
void GRFTownNameGenerate(StringBuilder &builder, uint32_t grfid, uint16_t gen, uint32_t seed);
|
||||||
|
|
||||||
char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str);
|
char32_t RemapNewGRFStringControlCode(char32_t scc, StringConsumer &consumer);
|
||||||
|
|
||||||
#endif /* STRINGS_INTERNAL_H */
|
#endif /* STRINGS_INTERNAL_H */
|
||||||
|
|
Loading…
Reference in New Issue