Codechange: let FormatString use StringBuilder

This commit is contained in:
Rubidium 2023-05-18 17:25:35 +02:00 committed by rubidium42
parent 993f90b6a0
commit 2dffa7d0c6
2 changed files with 149 additions and 139 deletions

View File

@ -171,11 +171,11 @@ void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num)
}
}
static char *StationGetSpecialString(char *buff, int x, const char *last);
static void StationGetSpecialString(StringBuilder &builder, int x);
static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last);
static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last);
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);
static void FormatString(StringBuilder &builder, const char *str, StringParameters *args, uint case_index = 0, bool game_script = false, bool dry_run = false);
struct LanguagePack : public LanguagePackHeader {
char data[]; // list of strings
@ -251,14 +251,20 @@ char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, co
}
break;
case TEXT_TAB_GAMESCRIPT_START:
return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true);
case TEXT_TAB_GAMESCRIPT_START: {
StringBuilder builder(buffr, last);
FormatString(builder, GetGameStringPtr(index), args, case_index, true);
return builder.GetEnd();
}
case TEXT_TAB_OLD_NEWGRF:
NOT_REACHED();
case TEXT_TAB_NEWGRF_START:
return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index);
case TEXT_TAB_NEWGRF_START: {
StringBuilder builder(buffr, last);
FormatString(builder, GetGRFStringPtr(index), args, case_index);
return builder.GetEnd();
}
default:
break;
@ -271,7 +277,9 @@ char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, co
FatalError("String 0x{:X} is invalid. You are probably using an old version of the .lng file.\n", string);
}
return FormatString(buffr, GetStringPtr(string), args, last, case_index);
StringBuilder builder(buffr, last);
FormatString(builder, GetStringPtr(string), args, case_index);
return builder.GetEnd();
}
char *GetString(char *buffr, StringID string, const char *last)
@ -281,6 +289,20 @@ char *GetString(char *buffr, StringID string, const char *last)
return GetStringWithArgs(buffr, string, &_global_string_params, last);
}
/**
* Get a parsed string with most special stringcodes replaced by the string parameters.
* @param builder The builder of the string.
* @param string The ID of the string to parse.
* @param args Arguments for the string.
* @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case.
* @param game_script The string is coming directly from a game script.
*/
void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters *args, uint case_index = 0, bool game_script = false)
{
builder.AddViaStreCallback([&](auto buff, auto last) { return GetStringWithArgs(buff, string, args, last, case_index, game_script); });
}
/**
* Resolve the given StringID into a std::string with all the associated
* DParam lookups and formatting.
@ -418,27 +440,27 @@ static void FormatBytes(StringBuilder &builder, int64 number)
fmt::format_to(builder, NBSP "{}B", iec_prefixes[id]);
}
static char *FormatYmdString(char *buff, TimerGameCalendar::Date date, const char *last, uint case_index)
static void FormatYmdString(StringBuilder &builder, TimerGameCalendar::Date date, uint case_index)
{
TimerGameCalendar::YearMonthDay ymd;
TimerGameCalendar::ConvertDateToYMD(date, &ymd);
int64 args[] = {ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year};
StringParameters tmp_params(args);
return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index);
FormatString(builder, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, case_index);
}
static char *FormatMonthAndYear(char *buff, TimerGameCalendar::Date date, const char *last, uint case_index)
static void FormatMonthAndYear(StringBuilder &builder, TimerGameCalendar::Date date, uint case_index)
{
TimerGameCalendar::YearMonthDay ymd;
TimerGameCalendar::ConvertDateToYMD(date, &ymd);
int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year};
StringParameters tmp_params(args);
return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index);
FormatString(builder, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, case_index);
}
static char *FormatTinyOrISODate(char *buff, TimerGameCalendar::Date date, StringID str, const char *last)
static void FormatTinyOrISODate(StringBuilder &builder, TimerGameCalendar::Date date, StringID str)
{
TimerGameCalendar::YearMonthDay ymd;
TimerGameCalendar::ConvertDateToYMD(date, &ymd);
@ -446,7 +468,7 @@ static char *FormatTinyOrISODate(char *buff, TimerGameCalendar::Date date, Strin
/* Day and month are zero-padded with ZEROFILL_NUM, hence the two 2s. */
int64 args[] = {ymd.day, 2, ymd.month + 1, 2, ymd.year};
StringParameters tmp_params(args);
return FormatString(buff, GetStringPtr(str), &tmp_params, last);
FormatString(builder, GetStringPtr(str), &tmp_params);
}
static void FormatGenericCurrency(StringBuilder &builder, const CurrencySpec *spec, Money number, bool compact)
@ -627,7 +649,7 @@ static int DeterminePluralForm(int64 count, int plural_form)
}
}
static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last)
static const char *ParseStringChoice(const char *b, uint form, StringBuilder &builder)
{
/* <NUM> {Length of each string} {each string} */
uint n = (byte)*b++;
@ -639,7 +661,7 @@ static const char *ParseStringChoice(const char *b, uint form, char **dst, const
pos += len;
}
*dst += seprintf(*dst, last, "%s", b + mypos);
builder += b + mypos;
return b + pos;
}
@ -806,18 +828,26 @@ static std::vector<const char *> _game_script_raw_strings;
/**
* Parse most format codes within a string and write the result to a buffer.
* @param buff The buffer to write the final string to.
* @param builder The string builder to write the final string to.
* @param str_arg The original string with format codes.
* @param args Pointer to extra arguments used by various string codes.
* @param last Pointer to just past the end of the buff array.
* @param dry_run True when the argt array is not yet initialized.
* @param dry_run True when the args' type data is not yet initialized.
*/
static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run)
static void FormatString(StringBuilder &builder, const char *str_arg, StringParameters *args, uint case_index, bool game_script, bool dry_run)
{
uint orig_offset = args->offset;
/* When there is no array with types there is no need to do a dry run. */
if (args->HasTypeInformation() && !dry_run) {
if (!dry_run && args->HasTypeInformation()) {
/*
* FormatString was called without `dry_run` set, however `args` has
* space allocated for type information and thus wants type checks on
* the parameters. So, we need to gather the type information via the
* dry run first, before we can continue formatting the string.
*
* Create a copy of the state of the builder for the dry run, so we do
* not have to reset it after the dry run has completed.
*/
StringBuilder dry_run_builder = builder;
if (UsingNewGRFTextStack()) {
/* Values from the NewGRF text stack are only copied to the normal
* argv array at the time they are encountered. That means that if
@ -826,10 +856,10 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
* pass makes sure the argv array is correctly filled and the second
* pass can reference later values without problems. */
struct TextRefStack *backup = CreateTextRefStackBackup();
FormatString(buff, str_arg, args, last, case_index, game_script, true);
FormatString(dry_run_builder, str_arg, args, case_index, game_script, true);
RestoreTextRefStackBackup(backup);
} else {
FormatString(buff, str_arg, args, last, case_index, game_script, true);
FormatString(dry_run_builder, str_arg, args, case_index, game_script, true);
}
/* We have to restore the original offset here to to read the correct values. */
args->offset = orig_offset;
@ -868,13 +898,13 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (*p != ':' && *p != '\0') {
while (*p != '\0') p++;
str = p;
buff = strecat(buff, "(invalid SCC_ENCODED)", last);
builder += "(invalid SCC_ENCODED)";
break;
}
if (stringid >= TAB_SIZE_GAMESCRIPT) {
while (*p != '\0') p++;
str = p;
buff = strecat(buff, "(invalid StringID)", last);
builder += "(invalid StringID)";
break;
}
@ -922,7 +952,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (param >= TAB_SIZE_GAMESCRIPT) {
while (*p != '\0') p++;
str = p;
buff = strecat(buff, "(invalid sub-StringID)", last);
builder += "(invalid sub-StringID)";
break;
}
param = MakeStringID(TEXT_TAB_GAMESCRIPT_START, param);
@ -942,7 +972,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
/* If we didn't error out, we can actually print the string. */
if (*str != '\0') {
str = p;
buff = GetStringWithArgs(buff, MakeStringID(TEXT_TAB_GAMESCRIPT_START, stringid), &sub_args, last, true);
GetStringWithArgs(builder, MakeStringID(TEXT_TAB_GAMESCRIPT_START, stringid), &sub_args, true);
}
for (i = 0; i < 20; i++) {
@ -981,14 +1011,17 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset));
*p = '\0';
/* Now do the string formatting. */
char buf[256];
/* The gender is stored at the start of the formatted string.
* So to determine the gender after formatting we only need
* enough space for the gender index token, one character
* for the actual gender and one character for '\0'. */
char buf[MAX_CHAR_LENGTH + 1 + 1];
bool old_sgd = _scan_for_gender_data;
_scan_for_gender_data = true;
StringBuilder tmp_builder(buf, lastof(buf));
StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, nullptr);
p = FormatString(buf, input, &tmp_params, lastof(buf));
FormatString(tmp_builder, input, &tmp_params);
_scan_for_gender_data = old_sgd;
*p = '\0';
/* And determine the string. */
const char *s = buf;
@ -996,7 +1029,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
/* Does this string have a gender, if so, set it */
if (c == SCC_GENDER_INDEX) gender = (byte)s[0];
}
str = ParseStringChoice(str, gender, &buff, last);
str = ParseStringChoice(str, gender, builder);
break;
}
@ -1004,8 +1037,8 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
* We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */
case SCC_GENDER_INDEX: // {GENDER 0}
if (_scan_for_gender_data) {
buff += Utf8Encode(buff, SCC_GENDER_INDEX);
*buff++ = *str++;
builder.Utf8Encode(SCC_GENDER_INDEX);
builder += *str++;
} else {
str++;
}
@ -1015,7 +1048,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
int plural_form = *str++; // contains the plural form for this string
uint offset = orig_offset + (byte)*str++;
int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural
str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last);
str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), builder);
break;
}
@ -1049,16 +1082,16 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
}
case SCC_REVISION: // {REV}
buff = strecpy(buff, _openttd_revision, last);
builder += _openttd_revision;
break;
case SCC_RAW_STRING_POINTER: { // {RAW_STRING}
const char *raw_string = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER);
if (game_script && std::find(_game_script_raw_strings.begin(), _game_script_raw_strings.end(), raw_string) == _game_script_raw_strings.end()) {
buff = strecat(buff, "(invalid RAW_STRING parameter)", last);
builder += "(invalid RAW_STRING parameter)";
break;
}
buff = FormatString(buff, raw_string, args, last);
FormatString(builder, raw_string, args);
break;
}
@ -1069,7 +1102,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
* For included strings that consume argument, you should use STRING1, STRING2 etc.
* To debug stuff you can set argv to nullptr and it will tell you */
StringParameters tmp_params(args->GetDataPointer(), args->GetDataLeft(), nullptr);
buff = GetStringWithArgs(buff, string_id, &tmp_params, last, next_substr_case_index, game_script);
GetStringWithArgs(builder, string_id, &tmp_params, next_substr_case_index, game_script);
next_substr_case_index = 0;
break;
}
@ -1086,59 +1119,43 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (game_script && GetStringTab(string_id) != TEXT_TAB_GAMESCRIPT_START) break;
uint size = b - SCC_STRING1 + 1;
if (game_script && size > args->GetDataLeft()) {
buff = strecat(buff, "(too many parameters)", last);
builder += "(too many parameters)";
} else {
StringParameters sub_args(*args, size);
buff = GetStringWithArgs(buff, string_id, &sub_args, last, next_substr_case_index, game_script);
GetStringWithArgs(builder, string_id, &sub_args, next_substr_case_index, game_script);
}
next_substr_case_index = 0;
break;
}
case SCC_COMMA: { // {COMMA}
StringBuilder builder(buff, last);
case SCC_COMMA: // {COMMA}
FormatCommaNumber(builder, args->GetInt64(SCC_COMMA));
buff = builder.GetEnd();
break;
}
case SCC_DECIMAL: { // {DECIMAL}
int64 number = args->GetInt64(SCC_DECIMAL);
int digits = args->GetInt32(SCC_DECIMAL);
StringBuilder builder(buff, last);
FormatCommaNumber(builder, number, digits);
buff = builder.GetEnd();
break;
}
case SCC_NUM: { // {NUM}
StringBuilder builder(buff, last);
case SCC_NUM: // {NUM}
FormatNoCommaNumber(builder, args->GetInt64(SCC_NUM));
buff = builder.GetEnd();
break;
}
case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
int64 num = args->GetInt64();
StringBuilder builder(buff, last);
FormatZerofillNumber(builder, num, args->GetInt64());
buff = builder.GetEnd();
break;
}
case SCC_HEX: { // {HEX}
StringBuilder builder(buff, last);
case SCC_HEX: // {HEX}
FormatHexNumber(builder, (uint64)args->GetInt64(SCC_HEX));
buff = builder.GetEnd();
break;
}
case SCC_BYTES: { // {BYTES}
StringBuilder builder(buff, last);
case SCC_BYTES: // {BYTES}
FormatBytes(builder, args->GetInt64());
buff = builder.GetEnd();
break;
}
case SCC_CARGO_TINY: { // {CARGO_TINY}
/* Tiny description of cargotypes. Layout:
@ -1164,9 +1181,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
}
}
StringBuilder builder(buff, last);
FormatCommaNumber(builder, amount);
buff = builder.GetEnd();
break;
}
@ -1183,7 +1198,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
assert(_settings_game.locale.units_weight < lengthof(_units_weight));
int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last);
FormatString(builder, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params);
break;
}
@ -1191,13 +1206,13 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
assert(_settings_game.locale.units_volume < lengthof(_units_volume));
int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last);
FormatString(builder, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params);
break;
}
default: {
StringParameters tmp_params(*args, 1);
buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
GetStringWithArgs(builder, cargo_str, &tmp_params);
break;
}
}
@ -1211,7 +1226,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
StringID cargo_str = !IsValidCargoID(cargo) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
StringParameters tmp_args(*args, 1);
buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
GetStringWithArgs(builder, cargo_str, &tmp_args);
break;
}
@ -1222,60 +1237,49 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
for (const auto &cs : _sorted_cargo_specs) {
if (!HasBit(cmask, cs->Index())) continue;
if (buff >= last - 2) break; // ',' and ' '
if (builder.Remaining() < 2) break; // ", "
if (first) {
first = false;
} else {
/* Add a comma if this is not the first item */
*buff++ = ',';
*buff++ = ' ';
builder += ", ";
}
buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script);
GetStringWithArgs(builder, cs->name, args, next_substr_case_index, game_script);
}
/* If first is still true then no cargo is accepted */
if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script);
if (first) GetStringWithArgs(builder, STR_JUST_NOTHING, args, next_substr_case_index, game_script);
*buff = '\0';
next_substr_case_index = 0;
/* Make sure we detect any buffer overflow */
assert(buff < last);
break;
}
case SCC_CURRENCY_SHORT: { // {CURRENCY_SHORT}
StringBuilder builder(buff, last);
case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
FormatGenericCurrency(builder, _currency, args->GetInt64(), true);
buff = builder.GetEnd();
break;
}
case SCC_CURRENCY_LONG: { // {CURRENCY_LONG}
StringBuilder builder(buff, last);
case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
FormatGenericCurrency(builder, _currency, args->GetInt64(SCC_CURRENCY_LONG), false);
buff = builder.GetEnd();
break;
}
case SCC_DATE_TINY: // {DATE_TINY}
buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
FormatTinyOrISODate(builder, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY);
break;
case SCC_DATE_SHORT: // {DATE_SHORT}
buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index);
FormatMonthAndYear(builder, args->GetInt32(SCC_DATE_SHORT), next_substr_case_index);
next_substr_case_index = 0;
break;
case SCC_DATE_LONG: // {DATE_LONG}
buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index);
FormatYmdString(builder, args->GetInt32(SCC_DATE_LONG), next_substr_case_index);
next_substr_case_index = 0;
break;
case SCC_DATE_ISO: // {DATE_ISO}
buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last);
FormatTinyOrISODate(builder, args->GetInt32(), STR_FORMAT_DATE_ISO);
break;
case SCC_FORCE: { // {FORCE}
@ -1283,7 +1287,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
const auto &x = _units_force[_settings_game.locale.units_force];
int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last);
FormatString(builder, GetStringPtr(x.s), &tmp_params);
break;
}
@ -1292,7 +1296,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
const auto &x = _units_height[_settings_game.locale.units_height];
int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last);
FormatString(builder, GetStringPtr(x.s), &tmp_params);
break;
}
@ -1301,7 +1305,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
const auto &x = _units_power[_settings_game.locale.units_power];
int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last);
FormatString(builder, GetStringPtr(x.s), &tmp_params);
break;
}
@ -1311,7 +1315,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
const auto &x = _units_power_to_weight[setting];
int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last);
FormatString(builder, GetStringPtr(x.s), &tmp_params);
break;
}
@ -1324,7 +1328,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
const auto &x = _units_velocity[units];
int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(GB(arg, 0, 56), vt), x.decimal_places};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last);
FormatString(builder, GetStringPtr(x.s), &tmp_params);
break;
}
@ -1333,7 +1337,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
const auto &x = _units_volume[_settings_game.locale.units_volume];
int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last);
FormatString(builder, GetStringPtr(x.s), &tmp_params);
break;
}
@ -1342,7 +1346,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
const auto &x = _units_volume[_settings_game.locale.units_volume];
int64 args_array[] = {x.c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG)), x.decimal_places};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(x.l), &tmp_params, last);
FormatString(builder, GetStringPtr(x.l), &tmp_params);
break;
}
@ -1351,7 +1355,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
const auto &x = _units_weight[_settings_game.locale.units_weight];
int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last);
FormatString(builder, GetStringPtr(x.s), &tmp_params);
break;
}
@ -1360,7 +1364,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
const auto &x = _units_weight[_settings_game.locale.units_weight];
int64 args_array[] = {x.c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG)), x.decimal_places};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(x.l), &tmp_params, last);
FormatString(builder, GetStringPtr(x.l), &tmp_params);
break;
}
@ -1371,11 +1375,11 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (!c->name.empty()) {
int64 args_array[] = {(int64)(size_t)c->name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params);
} else {
int64 args_array[] = {c->name_2};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last);
GetStringWithArgs(builder, c->name_1, &tmp_params);
}
break;
}
@ -1387,7 +1391,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (Company::IsValidHumanID(company)) {
int64 args_array[] = {company + 1};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last);
GetStringWithArgs(builder, STR_FORMAT_COMPANY_NUM, &tmp_params);
}
break;
}
@ -1398,7 +1402,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
uint64 args_array[] = {(uint64)args->GetInt32()};
WChar types_array[] = {SCC_STATION_NAME};
StringParameters tmp_params(args_array, 1, types_array);
buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last);
GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params);
break;
}
@ -1406,11 +1410,11 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (!d->name.empty()) {
int64 args_array[] = {(int64)(size_t)d->name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params);
} else {
int64 args_array[] = {d->town->index, d->town_cn + 1};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last);
GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params);
}
break;
}
@ -1423,7 +1427,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (!e->name.empty() && e->IsEnabled()) {
int64 args_array[] = {(int64)(size_t)e->name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params);
break;
}
@ -1439,7 +1443,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
uint64 tmp_dparam[6] = { 0 };
WChar tmp_type[6] = { 0 };
StringParameters tmp_params(tmp_dparam, 6, tmp_type);
buff = GetStringWithArgs(buff, GetGRFStringID(grffile->grfid, 0xD000 + callback), &tmp_params, last);
GetStringWithArgs(builder, GetGRFStringID(grffile->grfid, 0xD000 + callback), &tmp_params);
StopTextRefStackUsage();
break;
@ -1447,7 +1451,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
}
StringParameters tmp_params(nullptr, 0, nullptr);
buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last);
GetStringWithArgs(builder, e->info.string_id, &tmp_params);
break;
}
@ -1458,12 +1462,12 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (!g->name.empty()) {
int64 args_array[] = {(int64)(size_t)g->name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params);
} else {
int64 args_array[] = {g->index};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last);
GetStringWithArgs(builder, STR_FORMAT_GROUP_NAME, &tmp_params);
}
break;
}
@ -1475,18 +1479,18 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
static bool use_cache = true;
if (use_cache) { // Use cached version if first call
AutoRestoreBackup cache_backup(use_cache, false);
buff = strecpy(buff, i->GetCachedName().c_str(), last);
builder += i->GetCachedName();
} else if (_scan_for_gender_data) {
/* Gender is defined by the industry type.
* STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */
StringParameters tmp_params(nullptr, 0, nullptr);
buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index);
FormatString(builder, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, next_substr_case_index);
} else {
/* First print the town name and the industry type name. */
int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name};
StringParameters tmp_params(args_array);
buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index);
FormatString(builder, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, next_substr_case_index);
}
next_substr_case_index = 0;
break;
@ -1499,11 +1503,11 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (!c->president_name.empty()) {
int64 args_array[] = {(int64)(size_t)c->president_name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params);
} else {
int64 args_array[] = {c->president_name_2};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last);
GetStringWithArgs(builder, c->president_name_1, &tmp_params);
}
break;
}
@ -1517,18 +1521,18 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
* be "drawing" an invalid station is in the case of cargo that is
* in transit. */
StringParameters tmp_params(nullptr, 0, nullptr);
buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last);
GetStringWithArgs(builder, STR_UNKNOWN_STATION, &tmp_params);
break;
}
static bool use_cache = true;
if (use_cache) { // Use cached version if first call
AutoRestoreBackup cache_backup(use_cache, false);
buff = strecpy(buff, st->GetCachedName(), last);
builder += st->GetCachedName();
} else if (!st->name.empty()) {
int64 args_array[] = {(int64)(size_t)st->name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params);
} else {
StringID string_id = st->string_id;
if (st->indtype != IT_INVALID) {
@ -1546,7 +1550,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
uint64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index};
WChar types_array[] = {0, SCC_TOWN_NAME, SCC_NUM};
StringParameters tmp_params(args_array, 3, types_array);
buff = GetStringWithArgs(buff, string_id, &tmp_params, last);
GetStringWithArgs(builder, string_id, &tmp_params);
}
break;
}
@ -1558,13 +1562,13 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
static bool use_cache = true;
if (use_cache) { // Use cached version if first call
AutoRestoreBackup cache_backup(use_cache, false);
buff = strecpy(buff, t->GetCachedName().c_str(), last);
builder += t->GetCachedName();
} else if (!t->name.empty()) {
int64 args_array[] = {(int64)(size_t)t->name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params);
} else {
buff = GetTownName(buff, t, last);
builder.AddViaStreCallback([&t](auto buff, auto last) { return GetTownName(buff, t, last); });
}
break;
}
@ -1576,13 +1580,13 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (!wp->name.empty()) {
int64 args_array[] = {(int64)(size_t)wp->name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params);
} else {
int64 args_array[] = {wp->town->index, wp->town_cn + 1};
StringParameters tmp_params(args_array);
StringID string_id = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME);
if (wp->town_cn != 0) string_id++;
buff = GetStringWithArgs(buff, string_id, &tmp_params, last);
GetStringWithArgs(builder, string_id, &tmp_params);
}
break;
}
@ -1594,12 +1598,12 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (!v->name.empty()) {
int64 args_array[] = {(int64)(size_t)v->name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params);
} else if (v->group_id != DEFAULT_GROUP) {
/* The vehicle has no name, but is member of a group, so print group name */
int64 args_array[] = {v->group_id, v->unitnumber};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_VEHICLE_NAME, &tmp_params, last);
GetStringWithArgs(builder, STR_FORMAT_GROUP_VEHICLE_NAME, &tmp_params);
} else {
int64 args_array[] = {v->unitnumber};
StringParameters tmp_params(args_array);
@ -1613,7 +1617,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
case VEH_AIRCRAFT: string_id = STR_SV_AIRCRAFT_NAME; break;
}
buff = GetStringWithArgs(buff, string_id, &tmp_params, last);
GetStringWithArgs(builder, string_id, &tmp_params);
}
break;
}
@ -1625,38 +1629,34 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
if (!si->name.empty()) {
int64 args_array[] = {(int64)(size_t)si->name.c_str()};
StringParameters tmp_params(args_array);
buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last);
GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params);
} else {
StringParameters tmp_params(nullptr, 0, nullptr);
buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last);
GetStringWithArgs(builder, STR_DEFAULT_SIGN_NAME, &tmp_params);
}
break;
}
case SCC_STATION_FEATURES: { // {STATIONFEATURES}
buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last);
StationGetSpecialString(builder, args->GetInt32(SCC_STATION_FEATURES));
break;
}
default:
if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
builder.Utf8Encode(b);
break;
}
}
*buff = '\0';
return buff;
}
static char *StationGetSpecialString(char *buff, int x, const char *last)
static void StationGetSpecialString(StringBuilder &builder, int x)
{
if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
*buff = '\0';
return buff;
if ((x & FACIL_TRAIN) != 0) builder.Utf8Encode(SCC_TRAIN);
if ((x & FACIL_TRUCK_STOP) != 0) builder.Utf8Encode(SCC_LORRY);
if ((x & FACIL_BUS_STOP) != 0) builder.Utf8Encode(SCC_BUS);
if ((x & FACIL_DOCK) != 0) builder.Utf8Encode(SCC_SHIP);
if ((x & FACIL_AIRPORT) != 0) builder.Utf8Encode(SCC_PLANE);
}
static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last)

View File

@ -118,6 +118,16 @@ public:
{
return (ptrdiff_t)(this->last - this->current);
}
/**
* Add a string using the strecpy/strecat-esque calling signature.
* @param function The function to pass the current and last location to,
* that will then return the new current location.
*/
void AddViaStreCallback(std::function<char*(char*, const char*)> function)
{
this->current = function(this->current, this->last);
}
};
#endif /* STRINGS_INTERNAL_H */