Use StringBuilder for GetString/GetStringWithArgs, as per upstream

Update dependent code as required
pull/621/head
Jonathan G Rennison 5 months ago
parent 1b7a5372ec
commit f034714559

@ -73,28 +73,31 @@ inline void IterateCargoPacketDeferredPayments(CargoPacketID index, bool erase_r
} }
} }
void DumpCargoPacketDeferredPaymentStats(char *buffer, const char *last) std::string DumpCargoPacketDeferredPaymentStats()
{ {
Money payments[256][4] = {}; Money payments[256][4] = {};
for (auto &it : _cargo_packet_deferred_payments) { for (auto &it : _cargo_packet_deferred_payments) {
payments[GB(it.first, 24, 8)][GB(it.first, 22, 2)] += it.second; payments[GB(it.first, 24, 8)][GB(it.first, 22, 2)] += it.second;
} }
std::string buffer;
for (uint i = 0; i < 256; i++) { for (uint i = 0; i < 256; i++) {
for (uint j = 0; j < 4; j++) { for (uint j = 0; j < 4; j++) {
if (payments[i][j] != 0) { if (payments[i][j] != 0) {
SetDParam(0, i); SetDParam(0, i);
buffer = GetString(buffer, STR_COMPANY_NAME, last); GetString(StringBuilder(buffer), STR_COMPANY_NAME);
buffer += seprintf(buffer, last, " ("); buffer += " (";
buffer = GetString(buffer, STR_REPLACE_VEHICLE_TRAIN + j, last); GetString(StringBuilder(buffer), STR_REPLACE_VEHICLE_TRAIN + j);
buffer += seprintf(buffer, last, "): "); buffer += "): ";
SetDParam(0, payments[i][j]); SetDParam(0, payments[i][j]);
buffer = GetString(buffer, STR_JUST_CURRENCY_LONG, last); GetString(StringBuilder(buffer), STR_JUST_CURRENCY_LONG);
buffer += seprintf(buffer, last, "\n"); buffer += '\n';
} }
} }
} }
buffer += seprintf(buffer, last, "Deferred payment count: %u\n", (uint) _cargo_packet_deferred_payments.size()); buffer += stdstr_fmt("Deferred payment count: %u\n", (uint) _cargo_packet_deferred_payments.size());
buffer += seprintf(buffer, last, "Total cargo packets: %u\n", (uint)CargoPacket::GetNumItems()); buffer += stdstr_fmt("Total cargo packets: %u\n", (uint)CargoPacket::GetNumItems());
return buffer;
} }
/** /**

@ -1353,34 +1353,26 @@ void CommandCost::UseTextRefStack(const GRFFile *grffile, uint num_registers)
} }
std::string CommandCost::SummaryMessage(StringID cmd_msg) const std::string CommandCost::SummaryMessage(StringID cmd_msg) const
{
char buf[DRAW_STRING_BUFFER];
this->WriteSummaryMessage(buf, lastof(buf), cmd_msg);
return buf;
}
int CommandCost::WriteSummaryMessage(char *buf, char *last, StringID cmd_msg) const
{ {
if (this->Succeeded()) { if (this->Succeeded()) {
return seprintf(buf, last, "Success: cost: " OTTD_PRINTF64, (int64) this->GetCost()); return stdstr_fmt("Success: cost: " OTTD_PRINTF64, (int64) this->GetCost());
} else { } else {
const uint textref_stack_size = this->GetTextRefStackSize(); const uint textref_stack_size = this->GetTextRefStackSize();
if (textref_stack_size > 0) StartTextRefStackUsage(this->GetTextRefStackGRF(), textref_stack_size, this->GetTextRefStack()); if (textref_stack_size > 0) StartTextRefStackUsage(this->GetTextRefStackGRF(), textref_stack_size, this->GetTextRefStack());
char *b = buf; std::string buf = stdstr_fmt("Failed: cost: " OTTD_PRINTF64, (int64) this->GetCost());
b += seprintf(b, last, "Failed: cost: " OTTD_PRINTF64, (int64) this->GetCost());
if (cmd_msg != 0) { if (cmd_msg != 0) {
b += seprintf(b, last, " "); buf += ' ';
b = GetString(b, cmd_msg, last); GetString(StringBuilder(buf), cmd_msg);
} }
if (this->message != INVALID_STRING_ID) { if (this->message != INVALID_STRING_ID) {
b += seprintf(b, last, " "); buf += ' ';
b = GetString(b, this->message, last); GetString(StringBuilder(buf), this->message);
} }
if (textref_stack_size > 0) StopTextRefStackUsage(); if (textref_stack_size > 0) StopTextRefStackUsage();
return b - buf; return buf;
} }
} }

@ -222,15 +222,6 @@ public:
*/ */
std::string SummaryMessage(StringID cmd_msg = 0) const; std::string SummaryMessage(StringID cmd_msg = 0) const;
/**
* Write a string summarising the command result
* @param buf buffer to write to
* @param last last byte in buffer
* @param cmd_msg optional failure string as passed to DoCommand
* @return the number of bytes written
*/
int WriteSummaryMessage(char *buf, char *last, StringID cmd_msg = 0) const;
bool IsSuccessWithMessage() const bool IsSuccessWithMessage() const
{ {
return this->Succeeded() && this->message != INVALID_STRING_ID; return this->Succeeded() && this->message != INVALID_STRING_ID;

@ -380,10 +380,6 @@ CommandCost CheckTileOwnership(TileIndex tile)
*/ */
static void GenerateCompanyName(Company *c) static void GenerateCompanyName(Company *c)
{ {
/* Reserve space for extra unicode character. We need to do this to be able
* to detect too long company name. */
char buffer[(MAX_LENGTH_COMPANY_NAME_CHARS + 1) * MAX_CHAR_LENGTH];
if (c->name_1 != STR_SV_UNNAMED) return; if (c->name_1 != STR_SV_UNNAMED) return;
if (c->last_build_coordinate == 0) return; if (c->last_build_coordinate == 0) return;
@ -391,6 +387,7 @@ static void GenerateCompanyName(Company *c)
StringID str; StringID str;
uint32 strp; uint32 strp;
std::string buffer;
if (t->name.empty() && IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1)) { if (t->name.empty() && IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1)) {
str = t->townnametype - SPECSTR_TOWNNAME_START + SPECSTR_COMPANY_NAME_START; str = t->townnametype - SPECSTR_TOWNNAME_START + SPECSTR_COMPANY_NAME_START;
strp = t->townnameparts; strp = t->townnameparts;
@ -402,7 +399,7 @@ verify_name:;
} }
SetDParam(0, strp); SetDParam(0, strp);
GetString(buffer, str, lastof(buffer)); buffer = GetString(str);
if (Utf8StringLength(buffer) >= MAX_LENGTH_COMPANY_NAME_CHARS) goto bad_town_name; if (Utf8StringLength(buffer) >= MAX_LENGTH_COMPANY_NAME_CHARS) goto bad_town_name;
set_name:; set_name:;
@ -524,18 +521,15 @@ restart:;
/* Reserve space for extra unicode character. We need to do this to be able /* Reserve space for extra unicode character. We need to do this to be able
* to detect too long president name. */ * to detect too long president name. */
char buffer[(MAX_LENGTH_PRESIDENT_NAME_CHARS + 1) * MAX_CHAR_LENGTH];
SetDParam(0, c->index); SetDParam(0, c->index);
GetString(buffer, STR_PRESIDENT_NAME, lastof(buffer)); std::string name = GetString(STR_PRESIDENT_NAME);
if (Utf8StringLength(buffer) >= MAX_LENGTH_PRESIDENT_NAME_CHARS) continue; if (Utf8StringLength(name) >= MAX_LENGTH_PRESIDENT_NAME_CHARS) continue;
for (const Company *cc : Company::Iterate()) { for (const Company *cc : Company::Iterate()) {
if (c != cc) { if (c != cc) {
/* Reserve extra space so even overlength president names can be compared. */
char buffer2[(MAX_LENGTH_PRESIDENT_NAME_CHARS + 1) * MAX_CHAR_LENGTH];
SetDParam(0, cc->index); SetDParam(0, cc->index);
GetString(buffer2, STR_PRESIDENT_NAME, lastof(buffer2)); std::string other_name = GetString(STR_PRESIDENT_NAME);
if (strcmp(buffer2, buffer) == 0) goto restart; if (name == other_name) goto restart;
} }
} }
return; return;

@ -1895,9 +1895,8 @@ DEF_CONSOLE_CMD(ConCompanies)
for (const Company *c : Company::Iterate()) { for (const Company *c : Company::Iterate()) {
/* Grab the company name */ /* Grab the company name */
char company_name[512];
SetDParam(0, c->index); SetDParam(0, c->index);
GetString(company_name, STR_COMPANY_NAME, lastof(company_name)); std::string company_name = GetString(STR_COMPANY_NAME);
const char *password_state = ""; const char *password_state = "";
if (c->is_ai) { if (c->is_ai) {
@ -1906,10 +1905,8 @@ DEF_CONSOLE_CMD(ConCompanies)
password_state = _network_company_states[c->index].password.empty() ? "unprotected" : "protected"; password_state = _network_company_states[c->index].password.empty() ? "unprotected" : "protected";
} }
char colour[512];
GetString(colour, STR_COLOUR_DARK_BLUE + _company_colours[c->index], lastof(colour));
IConsolePrintF(CC_INFO, "#:%d(%s) Company Name: '%s' Year Founded: %d Money: " OTTD_PRINTF64 " Loan: " OTTD_PRINTF64 " Value: " OTTD_PRINTF64 " (T:%d, R:%d, P:%d, S:%d) %s", IConsolePrintF(CC_INFO, "#:%d(%s) Company Name: '%s' Year Founded: %d Money: " OTTD_PRINTF64 " Loan: " OTTD_PRINTF64 " Value: " OTTD_PRINTF64 " (T:%d, R:%d, P:%d, S:%d) %s",
c->index + 1, colour, company_name, c->index + 1, GetStringPtr(STR_COLOUR_DARK_BLUE + _company_colours[c->index]), company_name.c_str(),
c->inaugurated_year, (int64)c->money, (int64)c->current_loan, (int64)CalculateCompanyValue(c), c->inaugurated_year, (int64)c->money, (int64)c->current_loan, (int64)CalculateCompanyValue(c),
c->group_all[VEH_TRAIN].num_vehicle, c->group_all[VEH_TRAIN].num_vehicle,
c->group_all[VEH_ROAD].num_vehicle, c->group_all[VEH_ROAD].num_vehicle,
@ -2076,14 +2073,11 @@ DEF_CONSOLE_CMD(ConCompanyPasswordHashes)
for (const Company *c : Company::Iterate()) { for (const Company *c : Company::Iterate()) {
/* Grab the company name */ /* Grab the company name */
char company_name[512];
SetDParam(0, c->index); SetDParam(0, c->index);
GetString(company_name, STR_COMPANY_NAME, lastof(company_name)); std::string company_name = GetString(STR_COMPANY_NAME);
char colour[512];
GetString(colour, STR_COLOUR_DARK_BLUE + _company_colours[c->index], lastof(colour));
IConsolePrintF(CC_INFO, "#:%d(%s) Company Name: '%s' Hash: '%s'", IConsolePrintF(CC_INFO, "#:%d(%s) Company Name: '%s' Hash: '%s'",
c->index + 1, colour, company_name, _network_company_states[c->index].password.c_str()); c->index + 1, GetStringPtr(STR_COLOUR_DARK_BLUE + _company_colours[c->index]), company_name.c_str(), _network_company_states[c->index].password.c_str());
} }
return true; return true;
@ -2472,10 +2466,8 @@ DEF_CONSOLE_CMD(ConResetBlockedHeliports)
if (!occupied) { if (!occupied) {
st->airport.flags = 0; st->airport.flags = 0;
count++; count++;
char buffer[256];
SetDParam(0, st->index); SetDParam(0, st->index);
GetString(buffer, STR_STATION_NAME, lastof(buffer)); IConsolePrintF(CC_DEFAULT, "Unblocked: %s", GetString(STR_STATION_NAME).c_str());
IConsolePrintF(CC_DEFAULT, "Unblocked: %s", buffer);
} }
} }
@ -2635,10 +2627,8 @@ DEF_CONSOLE_CMD(ConDumpCpdpStats)
return true; return true;
} }
extern void DumpCargoPacketDeferredPaymentStats(char *buffer, const char *last); extern std::string DumpCargoPacketDeferredPaymentStats();
char buffer[32768]; PrintLineByLine(DumpCargoPacketDeferredPaymentStats());
DumpCargoPacketDeferredPaymentStats(buffer, lastof(buffer));
PrintLineByLine(buffer);
return true; return true;
} }

@ -901,7 +901,7 @@ void CrashLog::CloseCrashLogFile()
if (_screen.width < 1 || _screen.height < 1 || _screen.dst_ptr == nullptr) return false; if (_screen.width < 1 || _screen.height < 1 || _screen.dst_ptr == nullptr) return false;
bool res = MakeScreenshot(SC_CRASHLOG, name); bool res = MakeScreenshot(SC_CRASHLOG, name);
if (res) strecpy(filename, _full_screenshot_name, filename_last); if (res) strecpy(filename, _full_screenshot_path.c_str(), filename_last);
return res; return res;
} }

@ -875,20 +875,16 @@ void DeparturesWindow<Twaypoint>::DrawDeparturesListItems(const Rect &r) const
} }
} }
char buf[256] = "";
auto tmp_params = MakeParameters(Waypoint::IsValidID(id) ? STR_WAYPOINT_NAME : STR_STATION_NAME, id, icon_via); auto tmp_params = MakeParameters(Waypoint::IsValidID(id) ? STR_WAYPOINT_NAME : STR_STATION_NAME, id, icon_via);
char *end = GetStringWithArgs(buf, STR_DEPARTURES_VIA_DESCRIPTOR, tmp_params, lastof(buf)); _temp_special_strings[temp_str] = GetStringWithArgs(STR_DEPARTURES_VIA_DESCRIPTOR, tmp_params);
_temp_special_strings[temp_str].assign(buf, end);
}; };
get_single_via_string(0, via); get_single_via_string(0, via);
if (via2 != INVALID_STATION) { if (via2 != INVALID_STATION) {
get_single_via_string(1, via2); get_single_via_string(1, via2);
char buf[512] = "";
auto tmp_params = MakeParameters(SPECSTR_TEMP_START, SPECSTR_TEMP_START + 1); auto tmp_params = MakeParameters(SPECSTR_TEMP_START, SPECSTR_TEMP_START + 1);
char *end = GetStringWithArgs(buf, STR_DEPARTURES_VIA_AND, tmp_params, lastof(buf)); _temp_special_strings[0] = GetStringWithArgs(STR_DEPARTURES_VIA_AND, tmp_params);
_temp_special_strings[0].assign(buf, end);
} }
SetDParam(offset, SPECSTR_TEMP_START); SetDParam(offset, SPECSTR_TEMP_START);
@ -987,11 +983,11 @@ void DeparturesWindow<Twaypoint>::DrawDeparturesListItems(const Rect &r) const
/* RTL languages can be handled in the language file, e.g. by having the following: */ /* RTL languages can be handled in the language file, e.g. by having the following: */
/* STR_DEPARTURES_CALLING_AT_STATION :{STATION}, {RAW_STRING} */ /* STR_DEPARTURES_CALLING_AT_STATION :{STATION}, {RAW_STRING} */
/* STR_DEPARTURES_CALLING_AT_LAST_STATION :{STATION} & {RAW_STRING}*/ /* STR_DEPARTURES_CALLING_AT_LAST_STATION :{STATION} & {RAW_STRING}*/
char buffer[512], scratch[512]; std::string buffer;
if (d->calling_at.size() != 0) { if (d->calling_at.size() != 0) {
SetDParam(0, (d->calling_at[0]).station); SetDParam(0, (d->calling_at[0]).station);
GetString(scratch, STR_DEPARTURES_CALLING_AT_FIRST_STATION, lastof(scratch)); std::string calling_at_buffer = GetString(STR_DEPARTURES_CALLING_AT_FIRST_STATION);
StationID continues_to = INVALID_STATION; StationID continues_to = INVALID_STATION;
@ -1008,29 +1004,25 @@ void DeparturesWindow<Twaypoint>::DrawDeparturesListItems(const Rect &r) const
continues_to = d->calling_at[d->calling_at.size() - 1].station; continues_to = d->calling_at[d->calling_at.size() - 1].station;
break; break;
} }
SetDParamStr(0, scratch); SetDParamStr(0, std::move(calling_at_buffer));
SetDParam(1, s); SetDParam(1, s);
GetString(buffer, STR_DEPARTURES_CALLING_AT_STATION, lastof(buffer)); calling_at_buffer = GetString(STR_DEPARTURES_CALLING_AT_STATION);
strncpy(scratch, buffer, sizeof(scratch));
} }
/* Finally, finish off with " and <station>". */ /* Finally, finish off with " and <station>". */
SetDParamStr(0, scratch); SetDParamStr(0, std::move(calling_at_buffer));
SetDParam(1, d->calling_at[i].station); SetDParam(1, d->calling_at[i].station);
GetString(buffer, STR_DEPARTURES_CALLING_AT_LAST_STATION, lastof(buffer)); calling_at_buffer = GetString(STR_DEPARTURES_CALLING_AT_LAST_STATION);
strncpy(scratch, buffer, sizeof(scratch));
} }
SetDParamStr(1, scratch); SetDParamStr(1, std::move(calling_at_buffer));
if (continues_to == INVALID_STATION) { if (continues_to == INVALID_STATION) {
SetDParam(0, STR_DEPARTURES_CALLING_AT_LIST); SetDParam(0, STR_DEPARTURES_CALLING_AT_LIST);
} else { } else {
SetDParam(0, STR_DEPARTURES_CALLING_AT_LIST_SMART_TERMINUS); SetDParam(0, STR_DEPARTURES_CALLING_AT_LIST_SMART_TERMINUS);
SetDParam(2, continues_to); SetDParam(2, continues_to);
} }
GetString(buffer, size_prefix, lastof(buffer)); buffer = GetString(size_prefix);
} else {
buffer[0] = 0;
} }
int list_width = (GetStringBoundingBox(buffer, _settings_client.gui.departure_larger_font ? FS_NORMAL : FS_SMALL)).width; int list_width = (GetStringBoundingBox(buffer, _settings_client.gui.departure_larger_font ? FS_NORMAL : FS_SMALL)).width;

@ -169,29 +169,27 @@ uint GetTotalCapacityOfArticulatedParts(EngineID engine)
static StringID GetEngineInfoCapacityString(EngineID engine) static StringID GetEngineInfoCapacityString(EngineID engine)
{ {
char buffer[1024];
CargoArray cap = GetCapacityOfArticulatedParts(engine); CargoArray cap = GetCapacityOfArticulatedParts(engine);
if (cap.GetSum<uint>() == 0) { if (cap.GetSum<uint>() == 0) {
/* no cargo at all */ /* no cargo at all */
auto tmp_params = MakeParameters(CT_INVALID, 0); auto tmp_params = MakeParameters(CT_INVALID, 0);
GetStringWithArgs(buffer, STR_JUST_CARGO, tmp_params, lastof(buffer)); _temp_special_strings[1] = GetStringWithArgs(STR_JUST_CARGO, tmp_params);
} else { } else {
char *b = buffer; std::string buffer;
for (uint i = 0; i < NUM_CARGO; i++) { for (uint i = 0; i < NUM_CARGO; i++) {
if (cap[i] == 0) continue; if (cap[i] == 0) continue;
if (b != buffer) { if (!buffer.empty()) {
auto tmp_params = MakeParameters(); auto tmp_params = MakeParameters();
b = GetStringWithArgs(b, STR_COMMA_SEPARATOR, tmp_params, lastof(buffer)); GetStringWithArgs(StringBuilder(buffer), STR_COMMA_SEPARATOR, tmp_params);
} }
auto tmp_params = MakeParameters(i, cap[i]); auto tmp_params = MakeParameters(i, cap[i]);
b = GetStringWithArgs(b, STR_JUST_CARGO, tmp_params, lastof(buffer)); GetStringWithArgs(StringBuilder(buffer), STR_JUST_CARGO, tmp_params);
} }
_temp_special_strings[1] = std::move(buffer);
} }
_temp_special_strings[1].assign(buffer);
return SPECSTR_TEMP_START + 1; return SPECSTR_TEMP_START + 1;
} }

@ -376,25 +376,24 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel
if (wl != WL_INFO) { if (wl != WL_INFO) {
/* Print message to console */ /* Print message to console */
char buf[DRAW_STRING_BUFFER];
if (textref_stack_size > 0) StartTextRefStackUsage(textref_stack_grffile, textref_stack_size, textref_stack); if (textref_stack_size > 0) StartTextRefStackUsage(textref_stack_grffile, textref_stack_size, textref_stack);
char *b = GetString(buf, summary_msg, lastof(buf)); std::string message = GetString(summary_msg);
if (detailed_msg != INVALID_STRING_ID) { if (detailed_msg != INVALID_STRING_ID) {
b += seprintf(b, lastof(buf), " "); message += ' ';
GetString(b, detailed_msg, lastof(buf)); message += GetString(detailed_msg);
} }
if (extra_msg != INVALID_STRING_ID) { if (extra_msg != INVALID_STRING_ID) {
b += seprintf(b, lastof(buf), " "); message += ' ';
GetString(b, extra_msg, lastof(buf)); message += GetString(extra_msg);
} }
if (textref_stack_size > 0) StopTextRefStackUsage(); if (textref_stack_size > 0) StopTextRefStackUsage();
switch (wl) { switch (wl) {
case WL_WARNING: IConsolePrint(CC_WARNING, buf); break; case WL_WARNING: IConsolePrint(CC_WARNING, message.c_str()); break;
default: IConsoleError(buf); break; default: IConsoleError(message.c_str()); break;
} }
} }

@ -1112,17 +1112,17 @@ void DeterminePaths(const char *exe, bool only_local_path)
/** /**
* Sanitizes a filename, i.e. removes all illegal characters from it. * Sanitizes a filename, i.e. removes all illegal characters from it.
* @param filename the "\0" terminated filename * @param filename the filename
*/ */
void SanitizeFilename(char *filename) void SanitizeFilename(std::string &filename)
{ {
for (; *filename != '\0'; filename++) { for (auto &c : filename) {
switch (*filename) { switch (c) {
/* The following characters are not allowed in filenames /* The following characters are not allowed in filenames
* on at least one of the supported operating systems: */ * on at least one of the supported operating systems: */
case ':': case '\\': case '*': case '?': case '/': case ':': case '\\': case '*': case '?': case '/':
case '<': case '>': case '|': case '"': case '<': case '>': case '|': case '"':
*filename = '_'; c = '_';
break; break;
} }
} }

@ -26,7 +26,7 @@ void FioRenameFile(const std::string &oldname, const std::string &newname);
const char *FiosGetScreenshotDir(); const char *FiosGetScreenshotDir();
void SanitizeFilename(char *filename); void SanitizeFilename(std::string &filename);
void AppendPathSeparator(std::string &buf); void AppendPathSeparator(std::string &buf);
void DeterminePaths(const char *exe, bool only_local_path); void DeterminePaths(const char *exe, bool only_local_path);
std::unique_ptr<char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize); std::unique_ptr<char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize);

@ -308,8 +308,7 @@ public:
/** Generate a default save filename. */ /** Generate a default save filename. */
void GenerateFileName() void GenerateFileName()
{ {
GenerateDefaultSaveName(this->filename_editbox.text.buf, &this->filename_editbox.text.buf[this->filename_editbox.text.max_bytes - 1]); this->filename_editbox.text.Assign(GenerateDefaultSaveName());
this->filename_editbox.text.UpdateSize();
} }
SaveLoadWindow(WindowDesc *desc, AbstractFileType abstract_filetype, SaveLoadOperation fop) SaveLoadWindow(WindowDesc *desc, AbstractFileType abstract_filetype, SaveLoadOperation fop)

@ -712,9 +712,7 @@ int DrawString(int left, int right, int top, std::string_view str, TextColour co
*/ */
int DrawString(int left, int right, int top, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize) int DrawString(int left, int right, int top, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
{ {
char buffer[DRAW_STRING_BUFFER]; return DrawString(left, right, top, GetString(str), colour, align, underline, fontsize);
GetString(buffer, str, lastof(buffer));
return DrawString(left, right, top, buffer, colour, align, underline, fontsize);
} }
/** /**
@ -738,9 +736,7 @@ int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
*/ */
int GetStringHeight(StringID str, int maxw) int GetStringHeight(StringID str, int maxw)
{ {
char buffer[DRAW_STRING_BUFFER]; return GetStringHeight(GetString(str), maxw);
GetString(buffer, str, lastof(buffer));
return GetStringHeight(buffer, maxw);
} }
/** /**
@ -751,10 +747,7 @@ int GetStringHeight(StringID str, int maxw)
*/ */
int GetStringLineCount(StringID str, int maxw) int GetStringLineCount(StringID str, int maxw)
{ {
char buffer[DRAW_STRING_BUFFER]; Layouter layout(GetString(str), maxw);
GetString(buffer, str, lastof(buffer));
Layouter layout(buffer, maxw);
return (uint)layout.size(); return (uint)layout.size();
} }
@ -862,9 +855,7 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_vi
*/ */
int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize) int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
{ {
char buffer[DRAW_STRING_BUFFER]; return DrawStringMultiLine(left, right, top, bottom, GetString(str), colour, align, underline, fontsize);
GetString(buffer, str, lastof(buffer));
return DrawStringMultiLine(left, right, top, bottom, buffer, colour, align, underline, fontsize);
} }
/** /**
@ -891,10 +882,7 @@ Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
*/ */
Dimension GetStringBoundingBox(StringID strid, FontSize start_fontsize) Dimension GetStringBoundingBox(StringID strid, FontSize start_fontsize)
{ {
char buffer[DRAW_STRING_BUFFER]; return GetStringBoundingBox(GetString(strid), start_fontsize);
GetString(buffer, strid, lastof(buffer));
return GetStringBoundingBox(buffer, start_fontsize);
} }
/** /**

@ -97,9 +97,6 @@ enum AdjustGUIZoomMode {
}; };
bool AdjustGUIZoom(AdjustGUIZoomMode mode); bool AdjustGUIZoom(AdjustGUIZoomMode mode);
/** Size of the buffer used for drawing strings. */
static const int DRAW_STRING_BUFFER = 2048;
void RedrawScreenRect(int left, int top, int right, int bottom); void RedrawScreenRect(int left, int top, int right, int bottom);
Dimension GetSpriteSize(SpriteID sprid, Point *offset = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI); Dimension GetSpriteSize(SpriteID sprid, Point *offset = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI);

@ -787,20 +787,20 @@ std::string GenerateAutoNameForVehicleGroup(const Vehicle *v)
CargoTypes cargoes = GetVehicleCargoList(v); CargoTypes cargoes = GetVehicleCargoList(v);
char group_name[512]; StringID str;
if (town_from == town_to || town_to == nullptr) { if (town_from == town_to || town_to == nullptr) {
SetDParam(0, town_from->index); SetDParam(0, town_from->index);
SetDParam(1, (cargoes != 0) ? STR_VEHICLE_AUTO_GROUP_CARGO_LIST : STR_EMPTY); SetDParam(1, (cargoes != 0) ? STR_VEHICLE_AUTO_GROUP_CARGO_LIST : STR_EMPTY);
SetDParam(2, cargoes); SetDParam(2, cargoes);
GetString(group_name, STR_VEHICLE_AUTO_GROUP_LOCAL_ROUTE, lastof(group_name)); str = STR_VEHICLE_AUTO_GROUP_LOCAL_ROUTE;
} else { } else {
SetDParam(0, town_from->index); SetDParam(0, town_from->index);
SetDParam(1, town_to->index); SetDParam(1, town_to->index);
SetDParam(2, (cargoes != 0) ? STR_VEHICLE_AUTO_GROUP_CARGO_LIST : STR_EMPTY); SetDParam(2, (cargoes != 0) ? STR_VEHICLE_AUTO_GROUP_CARGO_LIST : STR_EMPTY);
SetDParam(3, cargoes); SetDParam(3, cargoes);
GetString(group_name, STR_VEHICLE_AUTO_GROUP_ROUTE, lastof(group_name)); str = STR_VEHICLE_AUTO_GROUP_ROUTE;
} }
return std::string(group_name); return GetString(str);
} }
/** /**

@ -2596,10 +2596,8 @@ void Industry::RecomputeProductionMultipliers()
void Industry::FillCachedName() const void Industry::FillCachedName() const
{ {
char buf[256];
auto tmp_params = MakeParameters(this->index); auto tmp_params = MakeParameters(this->index);
char *end = GetStringWithArgs(buf, STR_INDUSTRY_NAME, tmp_params, lastof(buf)); this->cached_name = GetStringWithArgs(STR_INDUSTRY_NAME, tmp_params);
this->cached_name.assign(buf, end);
} }
void ClearAllIndustryCachedNames() void ClearAllIndustryCachedNames()

@ -91,7 +91,7 @@ enum CargoSuffixDisplay {
/** Transfer storage of cargo suffix information. */ /** Transfer storage of cargo suffix information. */
struct CargoSuffix { struct CargoSuffix {
CargoSuffixDisplay display; ///< How to display the cargo and text. CargoSuffixDisplay display; ///< How to display the cargo and text.
char text[512]; ///< Cargo suffix text. std::string text; ///< Cargo suffix text.
}; };
extern void GenerateIndustries(); extern void GenerateIndustries();
@ -108,19 +108,19 @@ static void ShowIndustryCargoesWindow(IndustryType id);
*/ */
static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix) static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoSuffix &suffix)
{ {
suffix.text[0] = '\0'; suffix.text.clear();
suffix.display = CSD_CARGO_AMOUNT; suffix.display = CSD_CARGO_AMOUNT;
if (HasBit(indspec->callback_mask, CBM_IND_CARGO_SUFFIX)) { if (HasBit(indspec->callback_mask, CBM_IND_CARGO_SUFFIX)) {
TileIndex t = (cst != CST_FUND) ? ind->location.tile : INVALID_TILE; TileIndex t = (cst != CST_FUND) ? ind->location.tile : INVALID_TILE;
uint16 callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, const_cast<Industry *>(ind), ind_type, t); uint16_t callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, const_cast<Industry *>(ind), ind_type, t);
if (callback == CALLBACK_FAILED) return; if (callback == CALLBACK_FAILED) return;
if (indspec->grf_prop.grffile->grf_version < 8) { if (indspec->grf_prop.grffile->grf_version < 8) {
if (GB(callback, 0, 8) == 0xFF) return; if (GB(callback, 0, 8) == 0xFF) return;
if (callback < 0x400) { if (callback < 0x400) {
StartTextRefStackUsage(indspec->grf_prop.grffile, 6); StartTextRefStackUsage(indspec->grf_prop.grffile, 6);
GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback), lastof(suffix.text)); suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback));
StopTextRefStackUsage(); StopTextRefStackUsage();
suffix.display = CSD_CARGO_AMOUNT_TEXT; suffix.display = CSD_CARGO_AMOUNT_TEXT;
return; return;
@ -136,14 +136,14 @@ static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind,
} }
if (callback < 0x400) { if (callback < 0x400) {
StartTextRefStackUsage(indspec->grf_prop.grffile, 6); StartTextRefStackUsage(indspec->grf_prop.grffile, 6);
GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback), lastof(suffix.text)); suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + callback));
StopTextRefStackUsage(); StopTextRefStackUsage();
suffix.display = CSD_CARGO_AMOUNT_TEXT; suffix.display = CSD_CARGO_AMOUNT_TEXT;
return; return;
} }
if (callback >= 0x800 && callback < 0xC00) { if (callback >= 0x800 && callback < 0xC00) {
StartTextRefStackUsage(indspec->grf_prop.grffile, 6); StartTextRefStackUsage(indspec->grf_prop.grffile, 6);
GetString(suffix.text, GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 - 0x800 + callback), lastof(suffix.text)); suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 - 0x800 + callback));
StopTextRefStackUsage(); StopTextRefStackUsage();
suffix.display = CSD_CARGO_TEXT; suffix.display = CSD_CARGO_TEXT;
return; return;
@ -210,35 +210,26 @@ static inline void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixTy
std::array<IndustryType, NUM_INDUSTRYTYPES> _sorted_industry_types; ///< Industry types sorted by name. std::array<IndustryType, NUM_INDUSTRYTYPES> _sorted_industry_types; ///< Industry types sorted by name.
/** Sort industry types by their name. */
static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
{
static char industry_name[2][64];
const IndustrySpec *indsp1 = GetIndustrySpec(a);
GetString(industry_name[0], indsp1->name, lastof(industry_name[0]));
const IndustrySpec *indsp2 = GetIndustrySpec(b);
GetString(industry_name[1], indsp2->name, lastof(industry_name[1]));
int r = StrNaturalCompare(industry_name[0], industry_name[1]); // Sort by name (natural sorting).
/* If the names are equal, sort by industry type. */
return (r != 0) ? r < 0 : (a < b);
}
/** /**
* Initialize the list of sorted industry types. * Initialize the list of sorted industry types.
*/ */
void SortIndustryTypes() void SortIndustryTypes()
{ {
std::string industry_spec_names[NUM_INDUSTRYTYPES]{};
/* Add each industry type to the list. */ /* Add each industry type to the list. */
for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) { for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
_sorted_industry_types[i] = i; _sorted_industry_types[i] = i;
industry_spec_names[i] = GetString(GetIndustrySpec(i)->name);
} }
/* Sort industry types by name. */ /* Sort industry types by name. */
std::sort(_sorted_industry_types.begin(), _sorted_industry_types.end(), IndustryTypeNameSorter); std::sort(_sorted_industry_types.begin(), _sorted_industry_types.end(), [&](const IndustryType &a, const IndustryType &b) {
int r = StrNaturalCompare(industry_spec_names[a], industry_spec_names[b]); // Sort by name (natural sorting).
/* If the names are equal, sort by industry type. */
return (r != 0) ? r < 0 : (a < b);
});
} }
/** /**
@ -371,33 +362,33 @@ class BuildIndustryWindow : public Window {
std::string MakeCargoListString(const CargoID *cargolist, const CargoSuffix *cargo_suffix, size_t cargolistlen, StringID prefixstr) const std::string MakeCargoListString(const CargoID *cargolist, const CargoSuffix *cargo_suffix, size_t cargolistlen, StringID prefixstr) const
{ {
std::string cargostring; std::string cargostring;
char buf[1024]; size_t firstcargo = cargolistlen;
int numcargo = 0;
int firstcargo = -1;
for (size_t j = 0; j < cargolistlen; j++) { size_t j = 0;
for (; j < cargolistlen; j++) {
if (cargolist[j] == CT_INVALID) continue; if (cargolist[j] == CT_INVALID) continue;
numcargo++; if (firstcargo == cargolistlen) {
if (firstcargo < 0) { firstcargo = j;
firstcargo = (int)j; j++;
continue; break;
} }
SetDParam(0, CargoSpec::Get(cargolist[j])->name);
SetDParamStr(1, cargo_suffix[j].text);
GetString(buf, STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION, lastof(buf));
cargostring += buf;
} }
if (numcargo > 0) { if (firstcargo < cargolistlen) {
SetDParam(0, CargoSpec::Get(cargolist[firstcargo])->name); SetDParam(0, CargoSpec::Get(cargolist[firstcargo])->name);
SetDParamStr(1, cargo_suffix[firstcargo].text); SetDParamStr(1, cargo_suffix[firstcargo].text);
GetString(buf, prefixstr, lastof(buf)); GetString(StringBuilder(cargostring), prefixstr);
cargostring = std::string(buf) + cargostring;
} else { } else {
SetDParam(0, STR_JUST_NOTHING); SetDParam(0, STR_JUST_NOTHING);
SetDParamStr(1, ""); SetDParamStr(1, "");
GetString(buf, prefixstr, lastof(buf)); GetString(StringBuilder(cargostring), prefixstr);
cargostring = std::string(buf); }
for (; j < cargolistlen; j++) {
if (cargolist[j] == CT_INVALID) continue;
SetDParam(0, CargoSpec::Get(cargolist[j])->name);
SetDParamStr(1, cargo_suffix[j].text);
GetString(StringBuilder(cargostring), STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION);
} }
return cargostring; return cargostring;
@ -1563,7 +1554,7 @@ protected:
for (byte j = 0; j < lengthof(i->produced_cargo); j++) { for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
if (i->produced_cargo[j] == CT_INVALID) continue; if (i->produced_cargo[j] == CT_INVALID) continue;
cargos.push_back({ i->produced_cargo[j], i->last_month_production[j], cargo_suffix[j].text, ToPercent8(i->last_month_pct_transported[j]) }); cargos.push_back({ i->produced_cargo[j], i->last_month_production[j], cargo_suffix[j].text.c_str(), ToPercent8(i->last_month_pct_transported[j]) });
} }
switch (static_cast<IndustryDirectoryWindow::SorterType>(this->industries.SortType())) { switch (static_cast<IndustryDirectoryWindow::SorterType>(this->industries.SortType())) {

@ -518,8 +518,8 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond)
pt.y - 2 <= std::max(pta.y, ptb.y) && pt.y - 2 <= std::max(pta.y, ptb.y) &&
check_distance()) { check_distance()) {
char buf[2048]; std::string buf;
char *buf_end = buf; StringBuilder builder(buf);
buf[0] = 0; buf[0] = 0;
auto add_travel_time = [&](uint32 time) { auto add_travel_time = [&](uint32 time) {
@ -527,10 +527,10 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond)
if (_settings_time.time_in_minutes) { if (_settings_time.time_in_minutes) {
SetDParam(0, STR_TIMETABLE_MINUTES); SetDParam(0, STR_TIMETABLE_MINUTES);
SetDParam(1, time / _settings_time.ticks_per_minute); SetDParam(1, time / _settings_time.ticks_per_minute);
buf_end = GetString(buf_end, STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION_GENERAL, lastof(buf)); GetString(builder, STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION_GENERAL);
} else { } else {
SetDParam(0, time / (DAY_TICKS * _settings_game.economy.day_length_factor)); SetDParam(0, time / (DAY_TICKS * _settings_game.economy.day_length_factor));
buf_end = GetString(buf_end, STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION, lastof(buf)); GetString(builder, STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION);
} }
} }
}; };
@ -539,15 +539,15 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond)
if (info_link.usage < info_link.planned) { if (info_link.usage < info_link.planned) {
SetDParam(0, info_link.cargo); SetDParam(0, info_link.cargo);
SetDParam(1, info_link.usage); SetDParam(1, info_link.usage);
buf_end = GetString(buf_end, STR_LINKGRAPH_STATS_TOOLTIP_USAGE, lastof(buf)); GetString(builder, STR_LINKGRAPH_STATS_TOOLTIP_USAGE);
} else if (info_link.planned < info_link.usage) { } else if (info_link.planned < info_link.usage) {
SetDParam(0, info_link.cargo); SetDParam(0, info_link.cargo);
SetDParam(1, info_link.planned); SetDParam(1, info_link.planned);
buf_end = GetString(buf_end, STR_LINKGRAPH_STATS_TOOLTIP_PLANNED, lastof(buf)); GetString(builder, STR_LINKGRAPH_STATS_TOOLTIP_PLANNED);
} }
SetDParam(0, info_link.cargo); SetDParam(0, info_link.cargo);
SetDParam(1, info_link.capacity); SetDParam(1, info_link.capacity);
buf_end = GetString(buf_end, STR_LINKGRAPH_STATS_TOOLTIP_CAPACITY, lastof(buf)); GetString(builder, STR_LINKGRAPH_STATS_TOOLTIP_CAPACITY);
add_travel_time(info_link.time); add_travel_time(info_link.time);
}; };
@ -561,11 +561,11 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond)
if (j->from_id == i->to_id && j->to_id == i->from_id) { if (j->from_id == i->to_id && j->to_id == i->from_id) {
back_time = j->prop.time; back_time = j->prop.time;
if (j->prop.Usage() > 0 || (_ctrl_pressed && j->prop.capacity > 0)) { if (j->prop.Usage() > 0 || (_ctrl_pressed && j->prop.capacity > 0)) {
if (_ctrl_pressed) buf_end = strecat(buf_end, "\n", lastof(buf)); if (_ctrl_pressed) builder += '\n';
SetDParam(0, j->prop.cargo); SetDParam(0, j->prop.cargo);
SetDParam(1, j->prop.Usage()); SetDParam(1, j->prop.Usage());
SetDParam(2, j->prop.Usage() * 100 / (j->prop.capacity + 1)); SetDParam(2, j->prop.Usage() * 100 / (j->prop.capacity + 1));
buf_end = GetString(buf_end, STR_LINKGRAPH_STATS_TOOLTIP_RETURN_EXTENSION, lastof(buf)); GetString(builder, STR_LINKGRAPH_STATS_TOOLTIP_RETURN_EXTENSION);
if (_ctrl_pressed) { if (_ctrl_pressed) {
add_extra_info(j->prop); add_extra_info(j->prop);
} }
@ -580,14 +580,14 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond)
if (_ctrl_pressed) { if (_ctrl_pressed) {
/* Add distance information */ /* Add distance information */
buf_end = strecat(buf_end, "\n\n", lastof(buf)); builder += "\n\n";
TileIndex t0 = Station::Get(i->from_id)->xy; TileIndex t0 = Station::Get(i->from_id)->xy;
TileIndex t1 = Station::Get(i->to_id)->xy; TileIndex t1 = Station::Get(i->to_id)->xy;
uint dx = Delta(TileX(t0), TileX(t1)); uint dx = Delta(TileX(t0), TileX(t1));
uint dy = Delta(TileY(t0), TileY(t1)); uint dy = Delta(TileY(t0), TileY(t1));
SetDParam(0, DistanceManhattan(t0, t1)); SetDParam(0, DistanceManhattan(t0, t1));
SetDParam(1, IntSqrt64(((uint64)dx * (uint64)dx) + ((uint64)dy * (uint64)dy))); // Avoid overflow in DistanceSquare SetDParam(1, IntSqrt64(((uint64)dx * (uint64)dx) + ((uint64)dy * (uint64)dy))); // Avoid overflow in DistanceSquare
buf_end = GetString(buf_end, STR_LINKGRAPH_STATS_TOOLTIP_DISTANCE, lastof(buf)); GetString(builder, STR_LINKGRAPH_STATS_TOOLTIP_DISTANCE);
} }
SetDParam(0, link.cargo); SetDParam(0, link.cargo);
@ -595,7 +595,7 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond)
SetDParam(2, i->from_id); SetDParam(2, i->from_id);
SetDParam(3, i->to_id); SetDParam(3, i->to_id);
SetDParam(4, link.Usage() * 100 / (link.capacity + 1)); SetDParam(4, link.Usage() * 100 / (link.capacity + 1));
SetDParamStr(5, buf); SetDParamStr(5, std::move(buf));
GuiShowTooltips(this->window, STR_LINKGRAPH_STATS_TOOLTIP, close_cond); GuiShowTooltips(this->window, STR_LINKGRAPH_STATS_TOOLTIP, close_cond);
return true; return true;
} }

@ -53,9 +53,8 @@ void CcGiveMoney(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2
if (result.Failed() || !_settings_game.economy.give_money || !_networking) return; if (result.Failed() || !_settings_game.economy.give_money || !_networking) return;
/* Inform the company of the action of one of its clients (controllers). */ /* Inform the company of the action of one of its clients (controllers). */
char msg[64];
SetDParam(0, p1); SetDParam(0, p1);
GetString(msg, STR_COMPANY_NAME, lastof(msg)); std::string msg = GetString(STR_COMPANY_NAME);
/* /*
* bits 31-16: source company * bits 31-16: source company

@ -709,7 +709,7 @@ struct TooltipsWindow : public Window
StringID string_id; ///< String to display as tooltip. StringID string_id; ///< String to display as tooltip.
std::vector<StringParameterBackup> params; ///< The string parameters. std::vector<StringParameterBackup> params; ///< The string parameters.
TooltipCloseCondition close_cond; ///< Condition for closing the window. TooltipCloseCondition close_cond; ///< Condition for closing the window.
char buffer[DRAW_STRING_BUFFER]; ///< Text to draw std::string buffer; ///< Text to draw
int viewport_virtual_left; ///< Owner viewport state: left int viewport_virtual_left; ///< Owner viewport state: left
int viewport_virtual_top; ///< Owner viewport state: top int viewport_virtual_top; ///< Owner viewport state: top
bool delete_next_mouse_loop; ///< Delete window on the next mouse loop bool delete_next_mouse_loop; ///< Delete window on the next mouse loop
@ -721,7 +721,7 @@ struct TooltipsWindow : public Window
CopyOutDParam(this->params, paramcount); CopyOutDParam(this->params, paramcount);
this->close_cond = close_tooltip; this->close_cond = close_tooltip;
this->delete_next_mouse_loop = false; this->delete_next_mouse_loop = false;
if (this->params.size() == 0) GetString(this->buffer, str, lastof(this->buffer)); // Get the text while params are available if (this->params.size() == 0) this->buffer = GetString(str); // Get the text while params are available
if (close_tooltip == TCC_HOVER_VIEWPORT) { if (close_tooltip == TCC_HOVER_VIEWPORT) {
this->viewport_virtual_left = parent->viewport->virtual_left; this->viewport_virtual_left = parent->viewport->virtual_left;
this->viewport_virtual_top = parent->viewport->virtual_top; this->viewport_virtual_top = parent->viewport->virtual_top;
@ -1025,19 +1025,7 @@ struct QueryStringWindow : public Window
QueryStringWindow(StringID str, StringID caption, uint max_bytes, uint max_chars, WindowDesc *desc, Window *parent, CharSetFilter afilter, QueryStringFlags flags) : QueryStringWindow(StringID str, StringID caption, uint max_bytes, uint max_chars, WindowDesc *desc, Window *parent, CharSetFilter afilter, QueryStringFlags flags) :
Window(desc), editbox(max_bytes, max_chars) Window(desc), editbox(max_bytes, max_chars)
{ {
assert(parent != nullptr); this->editbox.text.Assign(str);
char *last_of = &this->editbox.text.buf[this->editbox.text.max_bytes - 1];
GetString(this->editbox.text.buf, str, last_of);
StrMakeValidInPlace(this->editbox.text.buf, last_of, SVS_NONE);
/* Make sure the name isn't too long for the text buffer in the number of
* characters (not bytes). max_chars also counts the '\0' characters. */
while (Utf8StringLength(this->editbox.text.buf) + 1 > this->editbox.text.max_chars) {
*Utf8PrevChar(this->editbox.text.buf + strlen(this->editbox.text.buf)) = '\0';
}
this->editbox.text.UpdateSize();
if ((flags & QSF_ACCEPT_UNCHANGED) == 0) this->editbox.orig = this->editbox.text.buf; if ((flags & QSF_ACCEPT_UNCHANGED) == 0) this->editbox.orig = this->editbox.text.buf;

@ -265,7 +265,6 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send,
{ {
SetDParamStr(0, name); SetDParamStr(0, name);
char message_src[256];
StringID strid; StringID strid;
switch (action) { switch (action) {
case NETWORK_ACTION_SERVER_MESSAGE: case NETWORK_ACTION_SERVER_MESSAGE:
@ -294,8 +293,7 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send,
case NETWORK_ACTION_GIVE_MONEY: { case NETWORK_ACTION_GIVE_MONEY: {
SetDParam(1, data.auxdata >> 16); SetDParam(1, data.auxdata >> 16);
GetString(message_src, STR_NETWORK_MESSAGE_MONEY_GIVE_SRC_DESCRIPTION, lastof(message_src)); SetDParamStr(0, GetString(STR_NETWORK_MESSAGE_MONEY_GIVE_SRC_DESCRIPTION));
SetDParamStr(0, message_src);
extern byte GetCurrentGrfLangID(); extern byte GetCurrentGrfLangID();
byte lang_id = GetCurrentGrfLangID(); byte lang_id = GetCurrentGrfLangID();
@ -317,7 +315,8 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send,
default: strid = STR_NETWORK_CHAT_ALL; break; default: strid = STR_NETWORK_CHAT_ALL; break;
} }
char message[1024]; std::string message;
StringBuilder builder(message);
SetDParamStr(1, str); SetDParamStr(1, str);
SetDParam(2, data.data); SetDParam(2, data.data);
SetDParamStr(3, data_str); SetDParamStr(3, data_str);
@ -326,12 +325,12 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send,
* right-to-left characters depending on the context. As the next text might be an user's name, the * right-to-left characters depending on the context. As the next text might be an user's name, the
* user name's characters will influence the direction of the "***" instead of the language setting * user name's characters will influence the direction of the "***" instead of the language setting
* of the game. Manually set the direction of the "***" by inserting a text-direction marker. */ * of the game. Manually set the direction of the "***" by inserting a text-direction marker. */
char *msg_ptr = message + Utf8Encode(message, _current_text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM); builder.Utf8Encode(_current_text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM);
GetString(msg_ptr, strid, lastof(message)); GetString(builder, strid);
DEBUG(desync, 1, "msg: %s; %s", debug_date_dumper().HexDate(), message); DEBUG(desync, 1, "msg: %s; %s", debug_date_dumper().HexDate(), message.c_str());
IConsolePrintF(colour, "%s", message); IConsolePrintF(colour, "%s", message.c_str());
NetworkAddChatMessage(colour, _settings_client.gui.network_chat_timeout, message); NetworkAddChatMessage(colour, _settings_client.gui.network_chat_timeout, message.c_str());
} }
/* Calculate the frame-lag of a client */ /* Calculate the frame-lag of a client */

@ -33,10 +33,6 @@
#include "../safeguards.h" #include "../safeguards.h"
/** The draw buffer must be able to contain the chat message, client name and the "[All]" message,
* some spaces and possible translations of [All] to other languages. */
static_assert((int)DRAW_STRING_BUFFER >= (int)NETWORK_CHAT_LENGTH + NETWORK_NAME_LENGTH + 40);
/** Spacing between chat lines. */ /** Spacing between chat lines. */
static const uint NETWORK_CHAT_LINE_SPACING = 3; static const uint NETWORK_CHAT_LINE_SPACING = 3;

@ -745,8 +745,7 @@ public:
if (!this->selected->dependencies.empty()) { if (!this->selected->dependencies.empty()) {
/* List dependencies */ /* List dependencies */
char buf[DRAW_STRING_BUFFER] = ""; std::string buf;
char *p = buf;
for (auto &cid : this->selected->dependencies) { for (auto &cid : this->selected->dependencies) {
/* Try to find the dependency */ /* Try to find the dependency */
ConstContentIterator iter = _network_content_client.Begin(); ConstContentIterator iter = _network_content_client.Begin();
@ -754,22 +753,23 @@ public:
const ContentInfo *ci = *iter; const ContentInfo *ci = *iter;
if (ci->id != cid) continue; if (ci->id != cid) continue;
p += seprintf(p, lastof(buf), p == buf ? "%s" : ", %s", (*iter)->name.c_str()); if (!buf.empty()) buf += ", ";
buf += (*iter)->name;
break; break;
} }
} }
SetDParamStr(0, buf); SetDParamStr(0, std::move(buf));
tr.top = DrawStringMultiLine(tr, STR_CONTENT_DETAIL_DEPENDENCIES); tr.top = DrawStringMultiLine(tr, STR_CONTENT_DETAIL_DEPENDENCIES);
} }
if (!this->selected->tags.empty()) { if (!this->selected->tags.empty()) {
/* List all tags */ /* List all tags */
char buf[DRAW_STRING_BUFFER] = ""; std::string buf;
char *p = buf;
for (auto &tag : this->selected->tags) { for (auto &tag : this->selected->tags) {
p += seprintf(p, lastof(buf), p == buf ? "%s" : ", %s", tag.c_str()); if (!buf.empty()) buf += ", ";
buf += tag;
} }
SetDParamStr(0, buf); SetDParamStr(0, std::move(buf));
tr.top = DrawStringMultiLine(tr, STR_CONTENT_DETAIL_TAGS); tr.top = DrawStringMultiLine(tr, STR_CONTENT_DETAIL_TAGS);
} }
@ -778,15 +778,15 @@ public:
ConstContentVector tree; ConstContentVector tree;
_network_content_client.ReverseLookupTreeDependency(tree, this->selected); _network_content_client.ReverseLookupTreeDependency(tree, this->selected);
char buf[DRAW_STRING_BUFFER] = ""; std::string buf;
char *p = buf;
for (const ContentInfo *ci : tree) { for (const ContentInfo *ci : tree) {
if (ci == this->selected || ci->state != ContentInfo::SELECTED) continue; if (ci == this->selected || ci->state != ContentInfo::SELECTED) continue;
p += seprintf(p, lastof(buf), buf == p ? "%s" : ", %s", ci->name.c_str()); if (!buf.empty()) buf += ", ";
buf += ci->name;
} }
if (p != buf) { if (!buf.empty()) {
SetDParamStr(0, buf); SetDParamStr(0, std::move(buf));
tr.top = DrawStringMultiLine(tr, STR_CONTENT_DETAIL_SELECTED_BECAUSE_OF); tr.top = DrawStringMultiLine(tr, STR_CONTENT_DETAIL_SELECTED_BECAUSE_OF);
} }
} }

@ -522,17 +522,14 @@ void ErrorUnknownCallbackResult(uint32 grfid, uint16 cbid, uint16 cb_res)
ShowErrorMessage(STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT, WL_CRITICAL); ShowErrorMessage(STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT, WL_CRITICAL);
} }
/* debug output */
char buffer[512];
SetDParamStr(0, grfconfig->GetName()); SetDParamStr(0, grfconfig->GetName());
GetString(buffer, STR_NEWGRF_BUGGY, lastof(buffer)); std::string buffer = GetString(STR_NEWGRF_BUGGY);
DEBUG(grf, 0, "%s", buffer + 3); DEBUG(grf, 0, "%s", strip_leading_colours(buffer));
SetDParam(1, cbid); SetDParam(1, cbid);
SetDParam(2, cb_res); SetDParam(2, cb_res);
GetString(buffer, STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT, lastof(buffer)); buffer = GetString(STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT);
DEBUG(grf, 0, "%s", buffer + 3); DEBUG(grf, 0, "%s", strip_leading_colours(buffer));
} }
/** /**

@ -542,9 +542,8 @@ struct NewGRFInspectWindow : Window {
if (this->log_console) { if (this->log_console) {
GetFeatureHelper(this->window_number)->SetStringParameters(this->GetFeatureIndex()); GetFeatureHelper(this->window_number)->SetStringParameters(this->GetFeatureIndex());
char buf[1024]; std::string buf = GetString(STR_NEWGRF_INSPECT_CAPTION);
GetString(buf, STR_NEWGRF_INSPECT_CAPTION, lastof(buf)); if (!buf.empty()) DEBUG(misc, 0, "*** %s ***", strip_leading_colours(buf));
DEBUG(misc, 0, "*** %s ***", buf + Utf8EncodedCharLen(buf[0]));
} }
uint index = this->GetFeatureIndex(); uint index = this->GetFeatureIndex();
@ -807,9 +806,7 @@ struct NewGRFInspectWindow : Window {
NOT_REACHED(); NOT_REACHED();
} }
char buffer[64]; this->DrawString(r, i++, " %02x: %s (%s)", nip->prop, GetString(string).c_str(), nip->name);
GetString(buffer, string, lastof(buffer));
this->DrawString(r, i++, " %02x: %s (%s)", nip->prop, buffer, nip->name);
} }
} }

@ -896,13 +896,13 @@ void StopTextRefStackUsage()
/** /**
* FormatString for NewGRF specific "magic" string control codes * FormatString for NewGRF specific "magic" string control codes
* @param scc the string control code that has been read * @param scc the string control code that has been read
* @param buff the buffer we're writing to * @param buffer the buffer we're writing to
* @param str the string that we need to write * @param str the string that we need to write
* @param parameters the OpenTTD string formatting parameters * @param parameters the OpenTTD string formatting parameters
* @param modify_parameters When true, modify the OpenTTD string formatting parameters. * @param modify_parameters When true, modify the OpenTTD string formatting parameters.
* @return the string control code to "execute" now * @return the string control code to "execute" now
*/ */
uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, StringParameters &parameters, bool modify_parameters) uint RemapNewGRFStringControlCode(uint scc, std::string *buffer, const char **str, StringParameters &parameters, bool modify_parameters)
{ {
auto too_many_newgrf_params = [&]() { auto too_many_newgrf_params = [&]() {
const char *buffer = *str; const char *buffer = *str;
@ -1011,7 +1011,7 @@ uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const
case SCC_NEWGRF_ROTATE_TOP_4_WORDS: _newgrf_textrefstack.RotateTop4Words(); break; case SCC_NEWGRF_ROTATE_TOP_4_WORDS: _newgrf_textrefstack.RotateTop4Words(); break;
case SCC_NEWGRF_PUSH_WORD: _newgrf_textrefstack.PushWord(Utf8Consume(str)); break; case SCC_NEWGRF_PUSH_WORD: _newgrf_textrefstack.PushWord(Utf8Consume(str)); break;
case SCC_NEWGRF_UNPRINT: *buff = std::max(*buff - Utf8Consume(str), buf_start); break; case SCC_NEWGRF_UNPRINT: if (buffer != nullptr) buffer->resize(buffer->size() - std::min<size_t>(buffer->size(), Utf8Consume(str))); break;
case SCC_NEWGRF_PRINT_WORD_CARGO_LONG: case SCC_NEWGRF_PRINT_WORD_CARGO_LONG:
case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT: case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT:

@ -54,7 +54,7 @@ struct TextRefStack *CreateTextRefStackBackup();
void RestoreTextRefStackBackup(struct TextRefStack *backup); void RestoreTextRefStackBackup(struct TextRefStack *backup);
class StringParameters; class StringParameters;
uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, StringParameters &parameters, bool modify_parameters); uint RemapNewGRFStringControlCode(uint scc, std::string *buffer, const char **str, StringParameters &parameters, bool modify_parameters);
/** Mapping of language data between a NewGRF and OpenTTD. */ /** Mapping of language data between a NewGRF and OpenTTD. */
struct LanguageMap { struct LanguageMap {

@ -16,6 +16,7 @@
#include "newgrf_townname.h" #include "newgrf_townname.h"
#include "core/alloc_func.hpp" #include "core/alloc_func.hpp"
#include "string_func.h" #include "string_func.h"
#include "strings_internal.h"
#include "table/strings.h" #include "table/strings.h"
@ -24,14 +25,14 @@
static std::vector<GRFTownName> _grf_townnames; static std::vector<GRFTownName> _grf_townnames;
static std::vector<StringID> _grf_townname_names; static std::vector<StringID> _grf_townname_names;
GRFTownName *GetGRFTownName(uint32 grfid) GRFTownName *GetGRFTownName(uint32_t grfid)
{ {
auto found = std::find_if(std::begin(_grf_townnames), std::end(_grf_townnames), [&grfid](const GRFTownName &t){ return t.grfid == grfid; }); auto found = std::find_if(std::begin(_grf_townnames), std::end(_grf_townnames), [&grfid](const GRFTownName &t){ return t.grfid == grfid; });
if (found != std::end(_grf_townnames)) return &*found; if (found != std::end(_grf_townnames)) return &*found;
return nullptr; return nullptr;
} }
GRFTownName *AddGRFTownName(uint32 grfid) GRFTownName *AddGRFTownName(uint32_t grfid)
{ {
GRFTownName *t = GetGRFTownName(grfid); GRFTownName *t = GetGRFTownName(grfid);
if (t == nullptr) { if (t == nullptr) {
@ -41,41 +42,38 @@ GRFTownName *AddGRFTownName(uint32 grfid)
return t; return t;
} }
void DelGRFTownName(uint32 grfid) void DelGRFTownName(uint32_t grfid)
{ {
_grf_townnames.erase(std::find_if(std::begin(_grf_townnames), std::end(_grf_townnames), [&grfid](const GRFTownName &t){ return t.grfid == grfid; })); _grf_townnames.erase(std::find_if(std::begin(_grf_townnames), std::end(_grf_townnames), [&grfid](const GRFTownName &t){ return t.grfid == grfid; }));
} }
static char *RandomPart(char *buf, const GRFTownName *t, uint32 seed, byte id, const char *last) static void RandomPart(StringBuilder builder, const GRFTownName *t, uint32_t seed, byte id)
{ {
assert(t != nullptr); assert(t != nullptr);
for (const auto &partlist : t->partlists[id]) { for (const auto &partlist : t->partlists[id]) {
byte count = partlist.bitcount; byte count = partlist.bitcount;
uint16 maxprob = partlist.maxprob; uint16_t maxprob = partlist.maxprob;
uint32 r = (GB(seed, partlist.bitstart, count) * maxprob) >> count; uint32_t r = (GB(seed, partlist.bitstart, count) * maxprob) >> count;
for (const auto &part : partlist.parts) { for (const auto &part : partlist.parts) {
maxprob -= GB(part.prob, 0, 7); maxprob -= GB(part.prob, 0, 7);
if (maxprob > r) continue; if (maxprob > r) continue;
if (HasBit(part.prob, 7)) { if (HasBit(part.prob, 7)) {
buf = RandomPart(buf, t, seed, part.id, last); RandomPart(builder, t, seed, part.id);
} else { } else {
buf = strecat(buf, part.text.c_str(), last); builder += part.text;
} }
break; break;
} }
} }
return buf;
} }
char *GRFTownNameGenerate(char *buf, uint32 grfid, uint16 gen, uint32 seed, const char *last) void GRFTownNameGenerate(StringBuilder builder, uint32_t grfid, uint16_t gen, uint32_t seed)
{ {
strecpy(buf, "", last);
const GRFTownName *t = GetGRFTownName(grfid); const GRFTownName *t = GetGRFTownName(grfid);
if (t != nullptr) { if (t != nullptr) {
assert(gen < t->styles.size()); assert(gen < t->styles.size());
buf = RandomPart(buf, t, seed, t->styles[gen].id, last); RandomPart(builder, t, seed, t->styles[gen].id);
} }
return buf;
} }
@ -95,7 +93,7 @@ const std::vector<StringID> &GetGRFTownNameList()
return _grf_townname_names; return _grf_townname_names;
} }
StringID GetGRFTownNameName(uint16 gen) StringID GetGRFTownNameName(uint16_t gen)
{ {
return gen < _grf_townname_names.size() ? _grf_townname_names[gen] : STR_UNDEFINED; return gen < _grf_townname_names.size() ? _grf_townname_names[gen] : STR_UNDEFINED;
} }
@ -105,21 +103,21 @@ void CleanUpGRFTownNames()
_grf_townnames.clear(); _grf_townnames.clear();
} }
uint32 GetGRFTownNameId(uint16 gen) uint32_t GetGRFTownNameId(uint16_t gen)
{ {
for (const auto &t : _grf_townnames) { for (const auto &t : _grf_townnames) {
if (gen < t.styles.size()) return t.grfid; if (gen < t.styles.size()) return t.grfid;
gen -= static_cast<uint16>(t.styles.size()); gen -= static_cast<uint16_t>(t.styles.size());
} }
/* Fallback to no NewGRF */ /* Fallback to no NewGRF */
return 0; return 0;
} }
uint16 GetGRFTownNameType(uint16 gen) uint16_t GetGRFTownNameType(uint16_t gen)
{ {
for (const auto &t : _grf_townnames) { for (const auto &t : _grf_townnames) {
if (gen < t.styles.size()) return gen; if (gen < t.styles.size()) return gen;
gen -= static_cast<uint16>(t.styles.size()); gen -= static_cast<uint16_t>(t.styles.size());
} }
/* Fallback to english original */ /* Fallback to english original */
return SPECSTR_TOWNNAME_ENGLISH; return SPECSTR_TOWNNAME_ENGLISH;

@ -25,7 +25,7 @@ struct NamePart {
struct NamePartList { struct NamePartList {
byte bitstart; ///< Start of random seed bits to use. byte bitstart; ///< Start of random seed bits to use.
byte bitcount; ///< Number of bits of random seed to use. byte bitcount; ///< Number of bits of random seed to use.
uint16 maxprob; ///< Total probability of all parts. uint16_t maxprob; ///< Total probability of all parts.
std::vector<NamePart> parts; ///< List of parts to choose from. std::vector<NamePart> parts; ///< List of parts to choose from.
}; };
@ -39,19 +39,18 @@ struct TownNameStyle {
struct GRFTownName { struct GRFTownName {
static const uint MAX_LISTS = 128; ///< Maximum number of town name lists that can be defined per GRF. static const uint MAX_LISTS = 128; ///< Maximum number of town name lists that can be defined per GRF.
uint32 grfid; ///< GRF ID of NewGRF. uint32_t grfid; ///< GRF ID of NewGRF.
std::vector<TownNameStyle> styles; ///< Style names defined by the Town Name NewGRF. std::vector<TownNameStyle> styles; ///< Style names defined by the Town Name NewGRF.
std::vector<NamePartList> partlists[MAX_LISTS]; ///< Lists of town name parts. std::vector<NamePartList> partlists[MAX_LISTS]; ///< Lists of town name parts.
}; };
GRFTownName *AddGRFTownName(uint32 grfid); GRFTownName *AddGRFTownName(uint32_t grfid);
GRFTownName *GetGRFTownName(uint32 grfid); GRFTownName *GetGRFTownName(uint32_t grfid);
void DelGRFTownName(uint32 grfid); void DelGRFTownName(uint32_t grfid);
void CleanUpGRFTownNames(); void CleanUpGRFTownNames();
char *GRFTownNameGenerate(char *buf, uint32 grfid, uint16 gen, uint32 seed, const char *last); uint32_t GetGRFTownNameId(uint16_t gen);
uint32 GetGRFTownNameId(uint16 gen); uint16_t GetGRFTownNameType(uint16_t gen);
uint16 GetGRFTownNameType(uint16 gen); StringID GetGRFTownNameName(uint16_t gen);
StringID GetGRFTownNameName(uint16 gen);
const std::vector<StringID> &GetGRFTownNameList(); const std::vector<StringID> &GetGRFTownNameList();

@ -870,10 +870,11 @@ int openttd_main(int argc, char *argv[])
fprintf(stderr, "Failed to open savegame\n"); fprintf(stderr, "Failed to open savegame\n");
if (_load_check_data.HasErrors()) { if (_load_check_data.HasErrors()) {
InitializeLanguagePacks(); // A language pack is needed for GetString() InitializeLanguagePacks(); // A language pack is needed for GetString()
char buf[256]; std::string buf;
SetDParamStr(0, _load_check_data.error_msg); SetDParamStr(0, _load_check_data.error_msg);
GetString(buf, _load_check_data.error, lastof(buf)); GetString(StringBuilder(buf), _load_check_data.error);
fprintf(stderr, "%s\n", buf); buf += '\n';
fputs(buf.c_str(), stderr);
} }
return ret; return ret;
} }
@ -1508,7 +1509,7 @@ void WriteVehicleInfo(char *&p, const char *last, const Vehicle *u, const Vehicl
p += seprintf(p, last, ": type %i, vehicle %i (%i), company %i, unit number %i, wagon %i, engine: ", p += seprintf(p, last, ": type %i, vehicle %i (%i), company %i, unit number %i, wagon %i, engine: ",
(int)u->type, u->index, v->index, (int)u->owner, v->unitnumber, length); (int)u->type, u->index, v->index, (int)u->owner, v->unitnumber, length);
SetDParam(0, u->engine_type); SetDParam(0, u->engine_type);
p = GetString(p, STR_ENGINE_NAME, last); p = strecpy(p, GetString(STR_ENGINE_NAME).c_str(), last, true);
uint32 grfid = u->GetGRFID(); uint32 grfid = u->GetGRFID();
if (grfid) { if (grfid) {
p += seprintf(p, last, ", GRF: %08X", BSWAP32(grfid)); p += seprintf(p, last, ", GRF: %08X", BSWAP32(grfid));

@ -1063,7 +1063,6 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS + order->GetConditionComparator()); SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS + order->GetConditionComparator());
SetDParam(4, order->GetXData()); SetDParam(4, order->GetXData());
} else if (ocv == OCV_CARGO_WAITING_AMOUNT) { } else if (ocv == OCV_CARGO_WAITING_AMOUNT) {
char buf[512] = "";
ArrayStringParameters<10> tmp_params; ArrayStringParameters<10> tmp_params;
StringID substr; StringID substr;
@ -1089,8 +1088,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
tmp_params.SetParam(7, order->GetConditionValue()); tmp_params.SetParam(7, order->GetConditionValue());
tmp_params.SetParam(8, GB(order->GetXData(), 0, 16)); tmp_params.SetParam(8, GB(order->GetXData(), 0, 16));
} }
char *end = GetStringWithArgs(buf, substr, tmp_params, lastof(buf)); _temp_special_strings[0] = GetStringWithArgs(substr, tmp_params);
_temp_special_strings[0].assign(buf, end);
SetDParam(0, SPECSTR_TEMP_START); SetDParam(0, SPECSTR_TEMP_START);
} else if (ocv == OCV_COUNTER_VALUE) { } else if (ocv == OCV_COUNTER_VALUE) {
if (TraceRestrictCounter::IsValidID(GB(order->GetXData(), 16, 16))) { if (TraceRestrictCounter::IsValidID(GB(order->GetXData(), 16, 16))) {
@ -1117,10 +1115,8 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
if (GB(order->GetXData(), 0, 16) != UINT16_MAX) { if (GB(order->GetXData(), 0, 16) != UINT16_MAX) {
const DispatchSchedule &ds = v->orders->GetDispatchScheduleByIndex(GB(order->GetXData(), 0, 16)); const DispatchSchedule &ds = v->orders->GetDispatchScheduleByIndex(GB(order->GetXData(), 0, 16));
if (ds.ScheduleName().empty()) { if (ds.ScheduleName().empty()) {
char buf[256];
auto tmp_params = MakeParameters(GB(order->GetXData(), 0, 16) + 1); auto tmp_params = MakeParameters(GB(order->GetXData(), 0, 16) + 1);
char *end = GetStringWithArgs(buf, STR_TIMETABLE_ASSIGN_SCHEDULE_ID, tmp_params, lastof(buf)); _temp_special_strings[0] = GetStringWithArgs(STR_TIMETABLE_ASSIGN_SCHEDULE_ID, tmp_params);
_temp_special_strings[0].assign(buf, end);
} else { } else {
_temp_special_strings[0] = ds.ScheduleName(); _temp_special_strings[0] = ds.ScheduleName();
} }

@ -140,7 +140,7 @@ static const StringID _program_sigstate[] = {
}; };
/** Get the string for a condition */ /** Get the string for a condition */
static char *GetConditionString(SignalCondition *cond, char *buf, char *buflast) static std::string GetConditionString(SignalCondition *cond)
{ {
StringID string = INVALID_STRING_ID; StringID string = INVALID_STRING_ID;
if (cond->ConditionCode() == PSC_SLOT_OCC || cond->ConditionCode() == PSC_SLOT_OCC_REM) { if (cond->ConditionCode() == PSC_SLOT_OCC || cond->ConditionCode() == PSC_SLOT_OCC_REM) {
@ -184,7 +184,7 @@ static char *GetConditionString(SignalCondition *cond, char *buf, char *buflast)
} }
} }
} }
return GetString(buf, string, buflast); return GetString(string);
} }
/** /**
@ -200,8 +200,6 @@ static void DrawInstructionString(SignalInstruction *instruction, int y, bool se
{ {
StringID instruction_string = INVALID_STRING_ID; StringID instruction_string = INVALID_STRING_ID;
char condstr[512];
switch (instruction->Opcode()) { switch (instruction->Opcode()) {
case PSO_FIRST: case PSO_FIRST:
instruction_string = STR_PROGSIG_FIRST; instruction_string = STR_PROGSIG_FIRST;
@ -213,8 +211,7 @@ static void DrawInstructionString(SignalInstruction *instruction, int y, bool se
case PSO_IF: { case PSO_IF: {
SignalIf *if_ins = static_cast<SignalIf*>(instruction); SignalIf *if_ins = static_cast<SignalIf*>(instruction);
GetConditionString(if_ins->condition, condstr, lastof(condstr)); SetDParamStr(0, GetConditionString(if_ins->condition));
SetDParamStr(0, condstr);
instruction_string = STR_PROGSIG_IF; instruction_string = STR_PROGSIG_IF;
break; break;
} }

@ -50,7 +50,7 @@ const char *scope_dumper::CompanyInfo(int company_id)
const char *last = lastof(this->buffer); const char *last = lastof(this->buffer);
b += seprintf(b, last, "%d (", company_id); b += seprintf(b, last, "%d (", company_id);
SetDParam(0, company_id); SetDParam(0, company_id);
b = GetString(b, STR_COMPANY_NAME, last); b = strecpy(b, GetString(STR_COMPANY_NAME).c_str(), last);
b += seprintf(b, last, ")"); b += seprintf(b, last, ")");
return buffer; return buffer;
} }
@ -79,7 +79,7 @@ const char *scope_dumper::VehicleInfo(const Vehicle *v)
default: default:
SetDParam(0, v->index); SetDParam(0, v->index);
b = GetString(b, STR_VEHICLE_NAME, last); b = strecpy(b, GetString(STR_VEHICLE_NAME).c_str(), last);
break; break;
} }
if (v->type < VEH_COMPANY_END) { if (v->type < VEH_COMPANY_END) {
@ -95,7 +95,7 @@ const char *scope_dumper::VehicleInfo(const Vehicle *v)
return this->buffer; return this->buffer;
} }
SetDParam(0, v->First()->index); SetDParam(0, v->First()->index);
b = GetString(b, STR_VEHICLE_NAME, last); b = strecpy(b, GetString(STR_VEHICLE_NAME).c_str(), last);
b += seprintf(b, last, ", "); b += seprintf(b, last, ", ");
dump_flags(v->First()); dump_flags(v->First());
b += seprintf(b, last, ")"); b += seprintf(b, last, ")");
@ -116,7 +116,7 @@ const char *scope_dumper::StationInfo(const BaseStation *st)
const bool waypoint = Waypoint::IsExpected(st); const bool waypoint = Waypoint::IsExpected(st);
b += seprintf(b, last, "%s: %u: (", waypoint ? "waypoint" : "station", st->index); b += seprintf(b, last, "%s: %u: (", waypoint ? "waypoint" : "station", st->index);
SetDParam(0, st->index); SetDParam(0, st->index);
b = GetString(b, waypoint ? STR_WAYPOINT_NAME : STR_STATION_NAME, last); b = strecpy(b, GetString(waypoint ? STR_WAYPOINT_NAME : STR_STATION_NAME).c_str(), last);
b += seprintf(b, last, ", c:%d, facil: ", (int) st->owner); b += seprintf(b, last, ", c:%d, facil: ", (int) st->owner);
auto dump_facil = [&](char c, StationFacility flag) { auto dump_facil = [&](char c, StationFacility flag) {
if (st->facilities & flag) b += seprintf(b, last, "%c", c); if (st->facilities & flag) b += seprintf(b, last, "%c", c);

@ -43,8 +43,8 @@ static const char * const HEIGHTMAP_NAME = "heightmap"; ///< Default filename
std::string _screenshot_format_name; ///< Extension of the current screenshot format (corresponds with #_cur_screenshot_format). std::string _screenshot_format_name; ///< Extension of the current screenshot format (corresponds with #_cur_screenshot_format).
uint _num_screenshot_formats; ///< Number of available screenshot formats. uint _num_screenshot_formats; ///< Number of available screenshot formats.
uint _cur_screenshot_format; ///< Index of the currently selected screenshot format in #_screenshot_formats. uint _cur_screenshot_format; ///< Index of the currently selected screenshot format in #_screenshot_formats.
static char _screenshot_name[128]; ///< Filename of the screenshot file. static std::string _screenshot_name; ///< Filename of the screenshot file.
char _full_screenshot_name[MAX_PATH]; ///< Pathname of the screenshot file. std::string _full_screenshot_path; ///< Pathname of the screenshot file.
uint _heightmap_highest_peak; ///< When saving a heightmap, this contains the highest peak on the map. uint _heightmap_highest_peak; ///< When saving a heightmap, this contains the highest peak on the map.
static const char *_screenshot_aux_text_key = nullptr; static const char *_screenshot_aux_text_key = nullptr;
@ -700,47 +700,47 @@ static void LargeWorldCallback(void *userdata, void *buf, uint y, uint pitch, ui
*/ */
static const char *MakeScreenshotName(const char *default_fn, const char *ext, bool crashlog = false) static const char *MakeScreenshotName(const char *default_fn, const char *ext, bool crashlog = false)
{ {
bool generate = StrEmpty(_screenshot_name); bool generate = _screenshot_name.empty();
if (generate) { if (generate) {
if (_game_mode == GM_EDITOR || _game_mode == GM_MENU || _local_company == COMPANY_SPECTATOR) { if (_game_mode == GM_EDITOR || _game_mode == GM_MENU || _local_company == COMPANY_SPECTATOR) {
strecpy(_screenshot_name, default_fn, lastof(_screenshot_name)); _screenshot_name = default_fn;
} else { } else {
GenerateDefaultSaveName(_screenshot_name, lastof(_screenshot_name)); _screenshot_name = GenerateDefaultSaveName();
} }
} }
size_t len = strlen(_screenshot_name); size_t len = _screenshot_name.size();
/* Handle user-specified filenames ending in %d or # with automatic numbering */ /* Handle user-specified filenames ending in %d or # with automatic numbering */
if (len >= 2 && _screenshot_name[len - 2] == '%' && _screenshot_name[len - 1] == 'd') { if (len >= 2 && _screenshot_name[len - 2] == '%' && _screenshot_name[len - 1] == 'd') {
generate = true; generate = true;
len -= 2; _screenshot_name.resize(len - 2);
_screenshot_name[len] = '\0';
} else if (len >= 1 && _screenshot_name[len - 1] == '#') { } else if (len >= 1 && _screenshot_name[len - 1] == '#') {
generate = true; generate = true;
len -= 1; _screenshot_name.resize(len - 1);
_screenshot_name[len] = '\0';
} }
len = _screenshot_name.size();
/* Add extension to screenshot file */ /* Add extension to screenshot file */
seprintf(&_screenshot_name[len], lastof(_screenshot_name), ".%s", ext); _screenshot_name += '.';
_screenshot_name += ext;
const char *screenshot_dir = crashlog ? _personal_dir.c_str() : FiosGetScreenshotDir(); const char *screenshot_dir = crashlog ? _personal_dir.c_str() : FiosGetScreenshotDir();
for (uint serial = 1;; serial++) { for (uint serial = 1;; serial++) {
if (seprintf(_full_screenshot_name, lastof(_full_screenshot_name), "%s%s", screenshot_dir, _screenshot_name) >= (int)lengthof(_full_screenshot_name)) { _full_screenshot_path = screenshot_dir;
/* We need more characters than MAX_PATH -> end with error */ _full_screenshot_path += _screenshot_name;
_full_screenshot_name[0] = '\0';
break;
}
if (!generate) break; // allow overwriting of non-automatic filenames if (!generate) break; // allow overwriting of non-automatic filenames
if (!FileExists(_full_screenshot_name)) break; if (!FileExists(_full_screenshot_path)) break;
/* If file exists try another one with same name, but just with a higher index */ /* If file exists try another one with same name, but just with a higher index */
seprintf(&_screenshot_name[len], lastof(_screenshot_name) - len, "#%u.%s", serial, ext); _screenshot_name.erase(len);
_screenshot_name += stdstr_fmt("#%u.%s", serial, ext);
} }
return _full_screenshot_name; return _full_screenshot_path.c_str();
} }
/** Make a screenshot of the current screen. */ /** Make a screenshot of the current screen. */
@ -983,8 +983,7 @@ static bool RealMakeScreenshot(ScreenshotType t, std::string name, uint32 width,
SetScreenshotWindowHidden(false); SetScreenshotWindowHidden(false);
} }
_screenshot_name[0] = '\0'; _screenshot_name = name;
if (!name.empty()) strecpy(_screenshot_name, name.c_str(), lastof(_screenshot_name));
bool ret; bool ret;
switch (t) { switch (t) {
@ -1332,8 +1331,8 @@ static void IndustryScreenCallback(void *userdata, void *buf, uint y, uint pitch
*/ */
bool MakeMinimapWorldScreenshot(const char *name) bool MakeMinimapWorldScreenshot(const char *name)
{ {
_screenshot_name[0] = '\0'; _screenshot_name.clear();
if (name != nullptr) strecpy(_screenshot_name, name, lastof(_screenshot_name)); if (name != nullptr) _screenshot_name.assign(name);
const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format; const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format;
return sf->proc(MakeScreenshotName(SCREENSHOT_NAME, sf->extension), MinimapScreenCallback, nullptr, MapSizeX(), MapSizeY(), 32, _cur_palette.palette); return sf->proc(MakeScreenshotName(SCREENSHOT_NAME, sf->extension), MinimapScreenCallback, nullptr, MapSizeX(), MapSizeY(), 32, _cur_palette.palette);
@ -1344,8 +1343,8 @@ bool MakeMinimapWorldScreenshot(const char *name)
*/ */
bool MakeTopographyScreenshot(const char *name) bool MakeTopographyScreenshot(const char *name)
{ {
_screenshot_name[0] = '\0'; _screenshot_name.clear();
if (name != nullptr) strecpy(_screenshot_name, name, lastof(_screenshot_name)); if (name != nullptr) _screenshot_name.assign(name);
const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format; const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format;
return sf->proc(MakeScreenshotName(SCREENSHOT_NAME, sf->extension), TopographyScreenCallback, nullptr, MapSizeX(), MapSizeY(), 32, _cur_palette.palette); return sf->proc(MakeScreenshotName(SCREENSHOT_NAME, sf->extension), TopographyScreenCallback, nullptr, MapSizeX(), MapSizeY(), 32, _cur_palette.palette);
@ -1356,8 +1355,8 @@ bool MakeTopographyScreenshot(const char *name)
*/ */
bool MakeIndustryScreenshot(const char *name) bool MakeIndustryScreenshot(const char *name)
{ {
_screenshot_name[0] = '\0'; _screenshot_name.clear();
if (name != nullptr) strecpy(_screenshot_name, name, lastof(_screenshot_name)); if (name != nullptr) _screenshot_name.assign(name);
const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format; const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format;
return sf->proc(MakeScreenshotName(SCREENSHOT_NAME, sf->extension), IndustryScreenCallback, nullptr, MapSizeX(), MapSizeY(), 32, _cur_palette.palette); return sf->proc(MakeScreenshotName(SCREENSHOT_NAME, sf->extension), IndustryScreenCallback, nullptr, MapSizeX(), MapSizeY(), 32, _cur_palette.palette);

@ -45,6 +45,6 @@ inline void ClearScreenshotAuxiliaryText() { SetScreenshotAuxiliaryText(nullptr,
extern std::string _screenshot_format_name; extern std::string _screenshot_format_name;
extern uint _num_screenshot_formats; extern uint _num_screenshot_formats;
extern uint _cur_screenshot_format; extern uint _cur_screenshot_format;
extern char _full_screenshot_name[MAX_PATH]; extern std::string _full_screenshot_path;
#endif /* SCREENSHOT_H */ #endif /* SCREENSHOT_H */

@ -136,7 +136,7 @@ SQInteger ScriptText::AddParam(HSQUIRRELVM vm)
SQInteger ScriptText::_set(HSQUIRRELVM vm) SQInteger ScriptText::_set(HSQUIRRELVM vm)
{ {
int32 k; int32_t k;
if (sq_gettype(vm, 2) == OT_STRING) { if (sq_gettype(vm, 2) == OT_STRING) {
const SQChar *key_string; const SQChar *key_string;
@ -149,7 +149,7 @@ SQInteger ScriptText::_set(HSQUIRRELVM vm)
} else if (sq_gettype(vm, 2) == OT_INTEGER) { } else if (sq_gettype(vm, 2) == OT_INTEGER) {
SQInteger key; SQInteger key;
sq_getinteger(vm, 2, &key); sq_getinteger(vm, 2, &key);
k = (int32)key; k = (int32_t)key;
} else { } else {
return SQ_ERROR; return SQ_ERROR;
} }
@ -161,15 +161,16 @@ SQInteger ScriptText::_set(HSQUIRRELVM vm)
return this->_SetParam(k, vm); return this->_SetParam(k, vm);
} }
const std::string ScriptText::GetEncodedText() std::string ScriptText::GetEncodedText()
{ {
static char buf[1024];
static StringIDList seen_ids; static StringIDList seen_ids;
int param_count = 0; int param_count = 0;
seen_ids.clear(); seen_ids.clear();
this->_GetEncodedText(buf, lastof(buf), param_count, seen_ids); std::string result;
auto output = std::back_inserter(result);
this->_GetEncodedText(output, param_count, seen_ids);
if (param_count > SCRIPT_TEXT_MAX_PARAMETERS) throw Script_FatalError(fmt::format("{}: Too many parameters", GetGameStringName(this->string))); if (param_count > SCRIPT_TEXT_MAX_PARAMETERS) throw Script_FatalError(fmt::format("{}: Too many parameters", GetGameStringName(this->string)));
return buf; return result;
} }
void ScriptText::_TextParamError(std::string msg) void ScriptText::_TextParamError(std::string msg)
@ -181,28 +182,28 @@ void ScriptText::_TextParamError(std::string msg)
} }
} }
char *ScriptText::_GetEncodedText(char *p, char *lastofp, int &param_count, StringIDList &seen_ids) void ScriptText::_GetEncodedText(std::back_insert_iterator<std::string> &output, int &param_count, StringIDList &seen_ids)
{ {
const std::string &name = GetGameStringName(this->string); const std::string &name = GetGameStringName(this->string);
if (std::find(seen_ids.begin(), seen_ids.end(), this->string) != seen_ids.end()) throw Script_FatalError(fmt::format("{}: Circular reference detected", name)); if (std::find(seen_ids.begin(), seen_ids.end(), this->string) != seen_ids.end()) throw Script_FatalError(fmt::format("{}: Circular reference detected", name));
seen_ids.push_back(this->string); seen_ids.push_back(this->string);
p += Utf8Encode(p, SCC_ENCODED); Utf8Encode(output, SCC_ENCODED);
p += seprintf(p, lastofp, "%X", this->string); fmt::format_to(output, "{:X}", this->string);
auto write_param_fallback = [&](int idx) { auto write_param_fallback = [&](int idx) {
if (std::holds_alternative<ScriptTextRef>(this->param[idx])) { if (std::holds_alternative<ScriptTextRef>(this->param[idx])) {
int count = 1; // 1 because the string id is included in consumed parameters int count = 1; // 1 because the string id is included in consumed parameters
p += seprintf(p, lastofp, ":"); fmt::format_to(output, ":");
p = std::get<ScriptTextRef>(this->param[idx])->_GetEncodedText(p, lastofp, count, seen_ids); std::get<ScriptTextRef>(this->param[idx])->_GetEncodedText(output, count, seen_ids);
param_count += count; param_count += count;
} else if (std::holds_alternative<SQInteger>(this->param[idx])) { } else if (std::holds_alternative<SQInteger>(this->param[idx])) {
p += seprintf(p, lastofp, ":" OTTD_PRINTFHEX64, std::get<SQInteger>(this->param[idx])); fmt::format_to(output, ":{:X}", std::get<SQInteger>(this->param[idx]));
param_count++; param_count++;
} else { } else {
/* Fallback value */ /* Fallback value */
p += seprintf(p, lastofp, ":0"); fmt::format_to(output, ":0");
param_count++; param_count++;
} }
}; };
@ -227,7 +228,7 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int &param_count, Stri
write_param_fallback(cur_idx++); write_param_fallback(cur_idx++);
break; break;
} }
p += seprintf(p, lastofp, ":\"%s\"", std::get<std::string>(this->param[cur_idx++]).c_str()); fmt::format_to(output, ":\"{}\"", std::get<std::string>(this->param[cur_idx++]));
param_count++; param_count++;
break; break;
@ -238,8 +239,8 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int &param_count, Stri
break; break;
} }
int count = 1; // 1 because the string id is included in consumed parameters int count = 1; // 1 because the string id is included in consumed parameters
p += seprintf(p, lastofp, ":"); fmt::format_to(output, ":");
p = std::get<ScriptTextRef>(this->param[cur_idx++])->_GetEncodedText(p, lastofp, count, seen_ids); std::get<ScriptTextRef>(this->param[cur_idx++])->_GetEncodedText(output, count, seen_ids);
if (count != cur_param.consumes) { if (count != cur_param.consumes) {
this->_TextParamError(fmt::format("{}: Parameter {} substring consumes {}, but expected {} to be consumed", name, cur_idx, count - 1, cur_param.consumes - 1)); this->_TextParamError(fmt::format("{}: Parameter {} substring consumes {}, but expected {} to be consumed", name, cur_idx, count - 1, cur_param.consumes - 1));
} }
@ -257,7 +258,7 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int &param_count, Stri
write_param_fallback(cur_idx++); write_param_fallback(cur_idx++);
continue; continue;
} }
p += seprintf(p, lastofp, ":" OTTD_PRINTFHEX64, std::get<SQInteger>(this->param[cur_idx++])); fmt::format_to(output, ":{:X}", std::get<SQInteger>(this->param[cur_idx++]));
param_count++; param_count++;
} }
break; break;
@ -267,12 +268,12 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int &param_count, Stri
/* The previous substring added more parameters than expected, means we will consume them but can't properly validate them. */ /* The previous substring added more parameters than expected, means we will consume them but can't properly validate them. */
for (int i = 0; i < cur_param.consumes; i++) { for (int i = 0; i < cur_param.consumes; i++) {
if (prev_idx < prev_count) { if (prev_idx < prev_count) {
ScriptLog::Warning(fmt::format("{}: Parameter {} uses parameter {} from substring {} and cannot be validated", name, param_count + i, prev_idx++, prev_string).c_str()); ScriptLog::Warning(fmt::format("{}: Parameter {} uses parameter {} from substring {} and cannot be validated", name, param_count + i, prev_idx++, prev_string));
} else { } else {
/* No more extra parameters, assume SQInteger are expected. */ /* No more extra parameters, assume SQInteger are expected. */
if (cur_idx >= this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name)); if (cur_idx >= this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name));
if (!std::holds_alternative<SQInteger>(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects an integer", name, param_count + i)); if (!std::holds_alternative<SQInteger>(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects an integer", name, param_count + i));
p += seprintf(p, lastofp, ":" OTTD_PRINTFHEX64, std::get<SQInteger>(this->param[cur_idx++])); fmt::format_to(output, ":{:X}", std::get<SQInteger>(this->param[cur_idx++]));
} }
} }
if (prev_idx == prev_count) { if (prev_idx == prev_count) {
@ -283,18 +284,18 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int &param_count, Stri
switch (cur_param.type) { switch (cur_param.type) {
case StringParam::RAW_STRING: case StringParam::RAW_STRING:
if (!std::holds_alternative<std::string>(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects a raw string", name, param_count)); if (!std::holds_alternative<std::string>(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects a raw string", name, param_count));
p += seprintf(p, lastofp, ":\"%s\"", std::get<std::string>(this->param[cur_idx++]).c_str()); fmt::format_to(output, ":\"{}\"", std::get<std::string>(this->param[cur_idx++]));
break; break;
case StringParam::STRING: { case StringParam::STRING: {
if (!std::holds_alternative<ScriptTextRef>(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects a substring", name, param_count)); if (!std::holds_alternative<ScriptTextRef>(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects a substring", name, param_count));
int count = 0; int count = 0;
p += seprintf(p, lastofp, ":"); fmt::format_to(output, ":");
p = std::get<ScriptTextRef>(this->param[cur_idx++])->_GetEncodedText(p, lastofp, count, seen_ids); std::get<ScriptTextRef>(this->param[cur_idx++])->_GetEncodedText(output, count, seen_ids);
if (++count != cur_param.consumes) { if (++count != cur_param.consumes) {
ScriptLog::Error(fmt::format("{}: Parameter {} substring consumes {}, but expected {} to be consumed", name, param_count, count - 1, cur_param.consumes - 1).c_str()); ScriptLog::Error(fmt::format("{}: Parameter {} substring consumes {}, but expected {} to be consumed", name, param_count, count - 1, cur_param.consumes - 1));
/* Fill missing params if needed. */ /* Fill missing params if needed. */
for (int i = count; i < cur_param.consumes; i++) p += seprintf(p, lastofp, ":0"); for (int i = count; i < cur_param.consumes; i++) fmt::format_to(output, ":0");
/* Disable validation for the extra params if any. */ /* Disable validation for the extra params if any. */
if (count > cur_param.consumes) { if (count > cur_param.consumes) {
prev_string = param_count; prev_string = param_count;
@ -309,7 +310,7 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int &param_count, Stri
if (cur_idx + cur_param.consumes > this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name)); if (cur_idx + cur_param.consumes > this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name));
for (int i = 0; i < cur_param.consumes; i++) { for (int i = 0; i < cur_param.consumes; i++) {
if (!std::holds_alternative<SQInteger>(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects an integer", name, param_count + i)); if (!std::holds_alternative<SQInteger>(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects an integer", name, param_count + i));
p += seprintf(p, lastofp, ":" OTTD_PRINTFHEX64, std::get<SQInteger>(this->param[cur_idx++])); fmt::format_to(output, ":{:X}", std::get<SQInteger>(this->param[cur_idx++]));
} }
} }
} }
@ -323,14 +324,10 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int &param_count, Stri
} }
seen_ids.pop_back(); seen_ids.pop_back();
return p;
} }
const std::string Text::GetDecodedText() const std::string Text::GetDecodedText()
{ {
static char buf[1024];
::SetDParamStr(0, this->GetEncodedText()); ::SetDParamStr(0, this->GetEncodedText());
::GetString(buf, STR_JUST_RAW_STRING, lastof(buf)); return ::GetString(STR_JUST_RAW_STRING);
return buf;
} }

@ -26,7 +26,7 @@ public:
* @return A string. * @return A string.
* @api -all * @api -all
*/ */
virtual const std::string GetEncodedText() = 0; virtual std::string GetEncodedText() = 0;
/** /**
* Convert a #ScriptText into a decoded normal string. * Convert a #ScriptText into a decoded normal string.
@ -44,7 +44,7 @@ class RawText : public Text {
public: public:
RawText(const std::string &text); RawText(const std::string &text);
const std::string GetEncodedText() override { return this->text; } std::string GetEncodedText() override { return this->text; }
private: private:
const std::string text; const std::string text;
}; };
@ -125,7 +125,7 @@ public:
/** /**
* @api -all * @api -all
*/ */
const std::string GetEncodedText() override; std::string GetEncodedText() override;
private: private:
using ScriptTextRef = ScriptObjectRef<ScriptText>; using ScriptTextRef = ScriptObjectRef<ScriptText>;
@ -140,13 +140,11 @@ private:
/** /**
* Internal function for recursive calling this function over multiple * Internal function for recursive calling this function over multiple
* instances, while writing in the same buffer. * instances, while writing in the same buffer.
* @param p The current position in the buffer. * @param output The output to write the encoded text to.
* @param lastofp The last position valid in the buffer.
* @param param_count The number of parameters that are in the string. * @param param_count The number of parameters that are in the string.
* @param seen_ids The list of seen StringID. * @param seen_ids The list of seen StringID.
* @return The new current position in the buffer.
*/ */
char *_GetEncodedText(char *p, char *lastofp, int &param_count, StringIDList &seen_ids); void _GetEncodedText(std::back_insert_iterator<std::string> &output, int &param_count, StringIDList &seen_ids);
/** /**
* Set a parameter, where the value is the first item on the stack. * Set a parameter, where the value is the first item on the stack.

@ -1082,11 +1082,7 @@ struct SettingEntry : BaseSettingEntry {
void SetButtons(byte new_val); void SetButtons(byte new_val);
StringID GetHelpText() const; StringID GetHelpText() const;
struct SetValueDParamsTempData { void SetValueDParams(uint first_param, int32 value) const;
char buffer[512];
};
void SetValueDParams(uint first_param, int32 value, std::unique_ptr<SetValueDParamsTempData> &tempdata) const;
protected: protected:
SettingEntry(const IntSettingDesc *setting); SettingEntry(const IntSettingDesc *setting);
@ -1456,19 +1452,17 @@ static const void *ResolveObject(const GameSettings *settings_ptr, const IntSett
* @param first_param First DParam to use * @param first_param First DParam to use
* @param value Setting value to set params for. * @param value Setting value to set params for.
*/ */
void SettingEntry::SetValueDParams(uint first_param, int32 value, std::unique_ptr<SettingEntry::SetValueDParamsTempData> &tempdata) const void SettingEntry::SetValueDParams(uint first_param, int32 value) const
{ {
if (this->setting->IsBoolSetting()) { if (this->setting->IsBoolSetting()) {
SetDParam(first_param++, value != 0 ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); SetDParam(first_param++, value != 0 ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
} else if (this->setting->flags & SF_DEC1SCALE) { } else if (this->setting->flags & SF_DEC1SCALE) {
tempdata.reset(new SettingEntry::SetValueDParamsTempData());
double scale = std::exp2(((double)value) / 10); double scale = std::exp2(((double)value) / 10);
int log = -std::min(0, (int)std::floor(std::log10(scale)) - 2); int log = -std::min(0, (int)std::floor(std::log10(scale)) - 2);
auto tmp_params = MakeParameters(value, (int64)(scale * std::pow(10.f, (float)log)), log);
GetStringWithArgs(tempdata->buffer, this->setting->str_val, tmp_params, lastof(tempdata->buffer));
SetDParam(first_param++, STR_JUST_RAW_STRING); SetDParam(first_param++, STR_JUST_RAW_STRING);
SetDParamStr(first_param++, tempdata->buffer); auto tmp_params = MakeParameters(value, (int64)(scale * std::pow(10.f, (float)log)), log);
SetDParamStr(first_param++, GetStringWithArgs(this->setting->str_val, tmp_params));
} else { } else {
if ((this->setting->flags & SF_ENUM) != 0) { if ((this->setting->flags & SF_ENUM) != 0) {
StringID str = STR_UNDEFINED; StringID str = STR_UNDEFINED;
@ -1531,8 +1525,7 @@ void SettingEntry::DrawSetting(GameSettings *settings_ptr, int left, int right,
void SettingEntry::DrawSettingString(uint left, uint right, int y, bool highlight, int32 value) const void SettingEntry::DrawSettingString(uint left, uint right, int y, bool highlight, int32 value) const
{ {
std::unique_ptr<SettingEntry::SetValueDParamsTempData> tempdata; this->SetValueDParams(1, value);
this->SetValueDParams(1, value, tempdata);
int edge = DrawString(left, right, y, this->setting->str, highlight ? TC_WHITE : TC_LIGHT_BLUE); int edge = DrawString(left, right, y, this->setting->str, highlight ? TC_WHITE : TC_LIGHT_BLUE);
if (this->setting->flags & SF_GUI_ADVISE_DEFAULT && value != this->setting->def && edge != 0) { if (this->setting->flags & SF_GUI_ADVISE_DEFAULT && value != this->setting->def && edge != 0) {
const Dimension warning_dimensions = GetSpriteSize(SPR_WARNING_SIGN); const Dimension warning_dimensions = GetSpriteSize(SPR_WARNING_SIGN);
@ -1558,8 +1551,7 @@ void CargoDestPerCargoSettingEntry::DrawSettingString(uint left, uint right, int
assert(this->setting->str == STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO); assert(this->setting->str == STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO);
SetDParam(0, CargoSpec::Get(this->cargo)->name); SetDParam(0, CargoSpec::Get(this->cargo)->name);
SetDParam(1, STR_CONFIG_SETTING_VALUE); SetDParam(1, STR_CONFIG_SETTING_VALUE);
std::unique_ptr<SettingEntry::SetValueDParamsTempData> tempdata; this->SetValueDParams(2, value);
this->SetValueDParams(2, value, tempdata);
DrawString(left, right, y, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_PARAM, highlight ? TC_WHITE : TC_LIGHT_BLUE); DrawString(left, right, y, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_PARAM, highlight ? TC_WHITE : TC_LIGHT_BLUE);
} }
@ -2740,8 +2732,7 @@ struct GameSettingsWindow : Window {
DrawString(tr, STR_CONFIG_SETTING_TYPE); DrawString(tr, STR_CONFIG_SETTING_TYPE);
tr.top += GetCharacterHeight(FS_NORMAL); tr.top += GetCharacterHeight(FS_NORMAL);
std::unique_ptr<SettingEntry::SetValueDParamsTempData> tempdata; this->last_clicked->SetValueDParams(0, sd->def);
this->last_clicked->SetValueDParams(0, sd->def, tempdata);
DrawString(tr, STR_CONFIG_SETTING_DEFAULT_VALUE); DrawString(tr, STR_CONFIG_SETTING_DEFAULT_VALUE);
tr.top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; tr.top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;

@ -74,7 +74,6 @@ void DrawShipDetails(const Vehicle *v, const Rect &r)
if (v->Next() != nullptr) { if (v->Next() != nullptr) {
CargoArray max_cargo{}; CargoArray max_cargo{};
StringID subtype_text[NUM_CARGO]; StringID subtype_text[NUM_CARGO];
char capacity[512];
memset(subtype_text, 0, sizeof(subtype_text)); memset(subtype_text, 0, sizeof(subtype_text));
@ -86,23 +85,18 @@ void DrawShipDetails(const Vehicle *v, const Rect &r)
} }
} }
GetString(capacity, STR_VEHICLE_DETAILS_TRAIN_ARTICULATED_RV_CAPACITY, lastof(capacity)); std::string capacity = GetString(STR_VEHICLE_DETAILS_TRAIN_ARTICULATED_RV_CAPACITY);
bool first = true; bool first = true;
for (CargoID i = 0; i < NUM_CARGO; i++) { for (CargoID i = 0; i < NUM_CARGO; i++) {
if (max_cargo[i] > 0) { if (max_cargo[i] > 0) {
char buffer[128]; if (!first) capacity += ", ";
SetDParam(0, i); SetDParam(0, i);
SetDParam(1, max_cargo[i]); SetDParam(1, max_cargo[i]);
GetString(buffer, STR_JUST_CARGO, lastof(buffer)); GetString(StringBuilder(capacity), STR_JUST_CARGO);
if (!first) strecat(capacity, ", ", lastof(capacity));
strecat(capacity, buffer, lastof(capacity));
if (subtype_text[i] != 0) { if (subtype_text[i] != 0) {
GetString(buffer, subtype_text[i], lastof(buffer)); GetString(StringBuilder(capacity), subtype_text[i]);
strecat(capacity, buffer, lastof(capacity));
} }
first = false; first = false;

@ -631,10 +631,8 @@ static void Load_SLXI()
}; };
auto version_error = [](StringID str, const char *feature, int64 p1, int64 p2) { auto version_error = [](StringID str, const char *feature, int64 p1, int64 p2) {
char buf[256];
auto tmp_params = MakeParameters(_sl_xv_version_label.empty() ? STR_EMPTY : STR_GAME_SAVELOAD_FROM_VERSION, _sl_xv_version_label, feature, p1, p2); auto tmp_params = MakeParameters(_sl_xv_version_label.empty() ? STR_EMPTY : STR_GAME_SAVELOAD_FROM_VERSION, _sl_xv_version_label, feature, p1, p2);
GetStringWithArgs(buf, str, tmp_params, lastof(buf)); SlError(STR_JUST_RAW_STRING, GetStringWithArgs(str, tmp_params));
SlError(STR_JUST_RAW_STRING, buf);
}; };
uint32 item_count = SlReadUint32(); uint32 item_count = SlReadUint32();

@ -3147,14 +3147,11 @@ void SetSaveLoadError(StringID str)
} }
/** Get the string representation of the error message */ /** Get the string representation of the error message */
const char *GetSaveLoadErrorString() std::string GetSaveLoadErrorString()
{ {
SetDParam(0, _sl.error_str); SetDParam(0, _sl.error_str);
SetDParamStr(1, _sl.extra_msg); SetDParamStr(1, _sl.extra_msg);
return GetString(_sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED);
static char err_str[512];
GetString(err_str, _sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED, lastof(err_str));
return err_str;
} }
/** Show a gui message when saving has failed */ /** Show a gui message when saving has failed */
@ -3198,7 +3195,7 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded)
* cancelled due to a client disconnecting. */ * cancelled due to a client disconnecting. */
if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) { if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
/* Skip the "colour" character */ /* Skip the "colour" character */
DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); DEBUG(sl, 0, "%s", strip_leading_colours(GetSaveLoadErrorString()));
asfp = SaveFileError; asfp = SaveFileError;
} }
@ -3644,7 +3641,7 @@ SaveOrLoadResult LoadWithFilter(LoadFilter *reader)
ClearSaveLoadState(); ClearSaveLoadState();
/* Skip the "colour" character */ /* Skip the "colour" character */
DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); DEBUG(sl, 0, "%s", strip_leading_colours(GetSaveLoadErrorString()));
return SL_REINIT; return SL_REINIT;
} }
@ -3744,7 +3741,7 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop,
ClearSaveLoadState(); ClearSaveLoadState();
/* Skip the "colour" character */ /* Skip the "colour" character */
if (fop != SLO_CHECK) DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); if (fop != SLO_CHECK) DEBUG(sl, 0, "%s", strip_leading_colours(GetSaveLoadErrorString()));
/* A saver/loader exception!! reinitialize all variables to prevent crash! */ /* A saver/loader exception!! reinitialize all variables to prevent crash! */
return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR; return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR;
@ -3758,23 +3755,22 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop,
*/ */
void DoAutoOrNetsave(FiosNumberedSaveName &counter, bool threaded, FiosNumberedSaveName *lt_counter) void DoAutoOrNetsave(FiosNumberedSaveName &counter, bool threaded, FiosNumberedSaveName *lt_counter)
{ {
char buf[MAX_PATH]; std::string filename;
if (_settings_client.gui.keep_all_autosave) { if (_settings_client.gui.keep_all_autosave) {
GenerateDefaultSaveName(buf, lastof(buf)); filename = GenerateDefaultSaveName() + counter.Extension();
strecat(buf, counter.Extension().c_str(), lastof(buf));
} else { } else {
strecpy(buf, counter.Filename().c_str(), lastof(buf)); filename = counter.Filename();
if (lt_counter != nullptr && counter.GetLastNumber() == 0) { if (lt_counter != nullptr && counter.GetLastNumber() == 0) {
std::string lt_path = lt_counter->FilenameUsingMaxSaves(_settings_client.gui.max_num_lt_autosaves); std::string lt_path = lt_counter->FilenameUsingMaxSaves(_settings_client.gui.max_num_lt_autosaves);
DEBUG(sl, 2, "Renaming autosave '%s' to long-term file '%s'", buf, lt_path.c_str()); DEBUG(sl, 2, "Renaming autosave '%s' to long-term file '%s'", filename.c_str(), lt_path.c_str());
std::string dir = FioFindDirectory(AUTOSAVE_DIR); std::string dir = FioFindDirectory(AUTOSAVE_DIR);
FioRenameFile(dir + buf, dir + lt_path); FioRenameFile(dir + filename, dir + lt_path);
} }
} }
DEBUG(sl, 2, "Autosaving to '%s'", buf); DEBUG(sl, 2, "Autosaving to '%s'", filename.c_str());
if (SaveOrLoad(buf, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, threaded, SMF_ZSTD_OK) != SL_OK) { if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, threaded, SMF_ZSTD_OK) != SL_OK) {
ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR); ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR);
} }
} }
@ -3787,11 +3783,9 @@ void DoExitSave()
} }
/** /**
* Fill the buffer with the default name for a savegame *or* screenshot. * Get the default name for a savegame *or* screenshot.
* @param buf the buffer to write to.
* @param last the last element in the buffer.
*/ */
void GenerateDefaultSaveName(char *buf, const char *last) std::string GenerateDefaultSaveName()
{ {
/* Check if we have a name for this map, which is the name of the first /* Check if we have a name for this map, which is the name of the first
* available company. When there's no company available we'll use * available company. When there's no company available we'll use
@ -3816,8 +3810,9 @@ void GenerateDefaultSaveName(char *buf, const char *last)
SetDParam(2, _date); SetDParam(2, _date);
/* Get the correct string (special string for when there's not company) */ /* Get the correct string (special string for when there's not company) */
GetString(buf, !Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, last); std::string filename = GetString(!Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT);
SanitizeFilename(buf); SanitizeFilename(filename);
return filename;
} }
/** /**

@ -66,9 +66,9 @@ DECLARE_ENUM_AS_BIT_SET(SaveModeFlags);
extern FileToSaveLoad _file_to_saveload; extern FileToSaveLoad _file_to_saveload;
void GenerateDefaultSaveName(char *buf, const char *last); std::string GenerateDefaultSaveName();
void SetSaveLoadError(StringID str); void SetSaveLoadError(StringID str);
const char *GetSaveLoadErrorString(); std::string GetSaveLoadErrorString();
SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded = true, SaveModeFlags flags = SMF_NONE); SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded = true, SaveModeFlags flags = SMF_NONE);
void WaitTillSaved(); void WaitTillSaved();
void ProcessAsyncSaveFinish(); void ProcessAsyncSaveFinish();

@ -553,10 +553,8 @@ void UpdateAllStationVirtCoords()
void BaseStation::FillCachedName() const void BaseStation::FillCachedName() const
{ {
char buf[MAX_LENGTH_STATION_NAME_CHARS * MAX_CHAR_LENGTH];
auto tmp_params = MakeParameters(this->index); auto tmp_params = MakeParameters(this->index);
char *end = GetStringWithArgs(buf, Waypoint::IsExpected(this) ? STR_WAYPOINT_NAME : STR_STATION_NAME, tmp_params, lastof(buf)); this->cached_name = GetStringWithArgs(Waypoint::IsExpected(this) ? STR_WAYPOINT_NAME : STR_STATION_NAME, tmp_params);
this->cached_name.assign(buf, end);
} }
void ClearAllStationCachedNames() void ClearAllStationCachedNames()

@ -2774,12 +2774,11 @@ private:
const CargoSpec *cs; const CargoSpec *cs;
bool newgrf_rating_used; bool newgrf_rating_used;
static const uint RATING_TOOLTIP_LINE_BUFF_SIZE = 512;
static const uint RATING_TOOLTIP_MAX_LINES = 9; static const uint RATING_TOOLTIP_MAX_LINES = 9;
static const uint RATING_TOOLTIP_NEWGRF_INDENT = 20; static const uint RATING_TOOLTIP_NEWGRF_INDENT = 20;
public: public:
char data[RATING_TOOLTIP_MAX_LINES + 1][RATING_TOOLTIP_LINE_BUFF_SIZE] {}; std::string data[RATING_TOOLTIP_MAX_LINES + 1]{};
StationRatingTooltipWindow(Window *parent, const Station *st, const CargoSpec *cs) : Window(&_station_rating_tooltip_desc) StationRatingTooltipWindow(Window *parent, const Station *st, const CargoSpec *cs) : Window(&_station_rating_tooltip_desc)
{ {
@ -2813,7 +2812,7 @@ public:
const GoodsEntry *ge = &this->st->goods[this->cs->Index()]; const GoodsEntry *ge = &this->st->goods[this->cs->Index()];
SetDParam(0, this->cs->name); SetDParam(0, this->cs->name);
GetString(this->data[0], STR_STATION_RATING_TOOLTIP_RATING_DETAILS, lastof(this->data[0])); this->data[0] = GetString(STR_STATION_RATING_TOOLTIP_RATING_DETAILS);
if (!ge->HasRating()) { if (!ge->HasRating()) {
this->data[1][0] = '\0'; this->data[1][0] = '\0';
@ -2831,7 +2830,7 @@ public:
if (_cheats.station_rating.value) { if (_cheats.station_rating.value) {
total_rating = 255; total_rating = 255;
skip = true; skip = true;
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_USING_CHEAT, lastof(this->data[line_nr])); this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_USING_CHEAT);
line_nr++; line_nr++;
} else if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) { } else if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) {
@ -2845,14 +2844,13 @@ public:
SetDParam(0, STR_STATION_RATING_TOOLTIP_NEWGRF_RATING_0 + (new_grf_rating <= 0 ? 0 : 1)); SetDParam(0, STR_STATION_RATING_TOOLTIP_NEWGRF_RATING_0 + (new_grf_rating <= 0 ? 0 : 1));
SetDParam(1, new_grf_rating); SetDParam(1, new_grf_rating);
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_NEWGRF_RATING, lastof(this->data[line_nr])); this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_NEWGRF_RATING);
line_nr++; line_nr++;
const uint last_speed = ge->HasVehicleEverTriedLoading() && ge->IsSupplyAllowed() ? ge->last_speed : 0xFF; const uint last_speed = ge->HasVehicleEverTriedLoading() && ge->IsSupplyAllowed() ? ge->last_speed : 0xFF;
SetDParam(0, std::min<uint>(last_speed, 0xFFu)); SetDParam(0, std::min<uint>(last_speed, 0xFFu));
switch (ge->last_vehicle_type) switch (ge->last_vehicle_type) {
{
case VEH_TRAIN: case VEH_TRAIN:
SetDParam(1, STR_STATION_RATING_TOOLTIP_TRAIN); SetDParam(1, STR_STATION_RATING_TOOLTIP_TRAIN);
break; break;
@ -2869,18 +2867,15 @@ public:
SetDParam(1, STR_STATION_RATING_TOOLTIP_INVALID); SetDParam(1, STR_STATION_RATING_TOOLTIP_INVALID);
break; break;
} }
this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_NEWGRF_SPEED);
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_NEWGRF_SPEED, lastof(this->data[line_nr]));
line_nr++; line_nr++;
SetDParam(0, std::min(ge->max_waiting_cargo, 0xFFFFu)); SetDParam(0, std::min(ge->max_waiting_cargo, 0xFFFFu));
GetString(this->data[line_nr], this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_NEWGRF_WAITUNITS);
STR_STATION_RATING_TOOLTIP_NEWGRF_WAITUNITS,
lastof(this->data[line_nr]));
line_nr++; line_nr++;
SetDParam(0, ge->time_since_pickup * STATION_RATING_TICKS / (DAY_TICKS * _settings_game.economy.day_length_factor)); SetDParam(0, ge->time_since_pickup * STATION_RATING_TICKS / (DAY_TICKS * _settings_game.economy.day_length_factor));
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_NEWGRF_WAITTIME, lastof(this->data[line_nr])); this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_NEWGRF_WAITTIME);
line_nr++; line_nr++;
} }
} }
@ -2906,8 +2901,7 @@ public:
SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY); SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY);
SetDParam(5, rounded_speed_rating); SetDParam(5, rounded_speed_rating);
switch (ge->last_vehicle_type) switch (ge->last_vehicle_type) {
{
case VEH_TRAIN: case VEH_TRAIN:
SetDParam(6, STR_STATION_RATING_TOOLTIP_TRAIN); SetDParam(6, STR_STATION_RATING_TOOLTIP_TRAIN);
break; break;
@ -2924,8 +2918,7 @@ public:
SetDParam(6, STR_STATION_RATING_TOOLTIP_INVALID); SetDParam(6, STR_STATION_RATING_TOOLTIP_INVALID);
break; break;
} }
this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_SPEED);
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_SPEED, lastof(this->data[line_nr]));
line_nr++; line_nr++;
total_rating += speed_rating; total_rating += speed_rating;
@ -2954,11 +2947,7 @@ public:
SetDParam(4, ge->time_since_pickup * STATION_RATING_TICKS / (DAY_TICKS * _settings_game.economy.day_length_factor)); SetDParam(4, ge->time_since_pickup * STATION_RATING_TICKS / (DAY_TICKS * _settings_game.economy.day_length_factor));
SetDParam(5, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY); SetDParam(5, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY);
SetDParam(6, RoundRating(wait_time_rating)); SetDParam(6, RoundRating(wait_time_rating));
GetString(this->data[line_nr], this->data[line_nr] = GetString((ge->last_vehicle_type == VEH_SHIP) ? STR_STATION_RATING_TOOLTIP_WAITTIME_SHIP : STR_STATION_RATING_TOOLTIP_WAITTIME);
(ge->last_vehicle_type == VEH_SHIP) ?
STR_STATION_RATING_TOOLTIP_WAITTIME_SHIP :
STR_STATION_RATING_TOOLTIP_WAITTIME,
lastof(this->data[line_nr]));
line_nr++; line_nr++;
total_rating += wait_time_rating; total_rating += wait_time_rating;
@ -2986,9 +2975,7 @@ public:
SetDParam(3, ge->max_waiting_cargo); SetDParam(3, ge->max_waiting_cargo);
SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY); SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY);
SetDParam(5, RoundRating(cargo_rating)); SetDParam(5, RoundRating(cargo_rating));
GetString(this->data[line_nr], this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_WAITUNITS);
STR_STATION_RATING_TOOLTIP_WAITUNITS,
lastof(this->data[line_nr]));
line_nr++; line_nr++;
total_rating += cargo_rating; total_rating += cargo_rating;
@ -3004,7 +2991,7 @@ public:
SetDParam(2, (statue_rating > 0) ? STR_STATION_RATING_TOOLTIP_STATUE_YES : STR_STATION_RATING_TOOLTIP_STATUE_NO); SetDParam(2, (statue_rating > 0) ? STR_STATION_RATING_TOOLTIP_STATUE_YES : STR_STATION_RATING_TOOLTIP_STATUE_NO);
SetDParam(3, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY); SetDParam(3, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY);
SetDParam(4, (statue_rating > 0) ? 10 : 0); SetDParam(4, (statue_rating > 0) ? 10 : 0);
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_STATUE, lastof(this->data[line_nr])); this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_STATUE);
line_nr++; line_nr++;
total_rating += statue_rating; total_rating += statue_rating;
@ -3030,7 +3017,7 @@ public:
SetDParam(3, ge->last_age); SetDParam(3, ge->last_age);
SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY); SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY);
SetDParam(5, RoundRating(age_rating)); SetDParam(5, RoundRating(age_rating));
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_AGE, lastof(this->data[line_nr])); this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_AGE);
line_nr++; line_nr++;
total_rating += age_rating; total_rating += age_rating;
@ -3041,7 +3028,7 @@ public:
if (detailed) { if (detailed) {
SetDParam(0, ToPercent8(total_rating)); SetDParam(0, ToPercent8(total_rating));
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_TOTAL_RATING, lastof(this->data[line_nr])); this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_TOTAL_RATING);
line_nr++; line_nr++;
} }
@ -3055,7 +3042,7 @@ public:
size->height = WidgetDimensions::scaled.framerect.Vertical() + 2; size->height = WidgetDimensions::scaled.framerect.Vertical() + 2;
for (uint i = 0; i <= RATING_TOOLTIP_MAX_LINES; i++) { for (uint i = 0; i <= RATING_TOOLTIP_MAX_LINES; i++) {
if (StrEmpty(this->data[i])) break; if (this->data[i].empty()) break;
uint width = GetStringBoundingBox(this->data[i]).width + WidgetDimensions::scaled.framerect.Horizontal() + 2; uint width = GetStringBoundingBox(this->data[i]).width + WidgetDimensions::scaled.framerect.Horizontal() + 2;
if (this->newgrf_rating_used && i >= 2 && i <= 4) { if (this->newgrf_rating_used && i >= 2 && i <= 4) {
@ -3084,15 +3071,14 @@ public:
y += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; y += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
for (uint i = 1; i <= RATING_TOOLTIP_MAX_LINES; i++) { for (uint i = 1; i <= RATING_TOOLTIP_MAX_LINES; i++) {
if (StrEmpty(this->data[i])) break; if (this->data[i].empty()) break;
int left = left0, right = right0; int left = left0, right = right0;
if (this->newgrf_rating_used && i >= 2 && i <= 4) { if (this->newgrf_rating_used && i >= 2 && i <= 4) {
if (_current_text_dir == TD_RTL) { if (_current_text_dir == TD_RTL) {
right -= RATING_TOOLTIP_NEWGRF_INDENT; right -= RATING_TOOLTIP_NEWGRF_INDENT;
} } else {
else {
left += RATING_TOOLTIP_NEWGRF_INDENT; left += RATING_TOOLTIP_NEWGRF_INDENT;
} }
} }

@ -547,6 +547,20 @@ void str_strip_colours(char *str)
*dst = '\0'; *dst = '\0';
} }
/** Advances the pointer over any colour codes at the start of the string */
const char *strip_leading_colours(const char *str)
{
char32_t c;
do {
size_t len = Utf8Decode(&c, str);
if (c < SCC_BLUE || c > SCC_BLACK) break;
str += len;
} while (c != '\0');
return str;
}
std::string str_strip_all_scc(const char *str) std::string str_strip_all_scc(const char *str)
{ {
std::string out; std::string out;
@ -849,6 +863,11 @@ size_t Utf8Encode(std::ostreambuf_iterator<char> &buf, WChar c)
return Utf8Encode<std::ostreambuf_iterator<char> &>(buf, c); return Utf8Encode<std::ostreambuf_iterator<char> &>(buf, c);
} }
size_t Utf8Encode(std::back_insert_iterator<std::string> &buf, char32_t c)
{
return Utf8Encode<std::back_insert_iterator<std::string> &>(buf, c);
}
/** /**
* Properly terminate an UTF8 string to some maximum length * Properly terminate an UTF8 string to some maximum length
* @param s string to check if it needs additional trimming * @param s string to check if it needs additional trimming

@ -49,6 +49,14 @@ void StrMakeValidInPlace(char *str, StringValidationSettings settings = SVS_REPL
const char *str_fix_scc_encoded(char *str, const char *last) NOACCESS(2); const char *str_fix_scc_encoded(char *str, const char *last) NOACCESS(2);
void str_strip_colours(char *str); void str_strip_colours(char *str);
const char *strip_leading_colours(const char *str);
inline const char *strip_leading_colours(const std::string &str)
{
return strip_leading_colours(str.c_str());
}
std::string str_strip_all_scc(const char *str); std::string str_strip_all_scc(const char *str);
char *str_replace_wchar(char *str, const char *last, WChar find, WChar replace); char *str_replace_wchar(char *str, const char *last, WChar find, WChar replace);
bool strtolower(char *str); bool strtolower(char *str);
@ -99,25 +107,26 @@ static inline size_t ttd_strnlen(const char *str, size_t maxlen)
return t - str; return t - str;
} }
bool IsValidChar(WChar key, CharSetFilter afilter); bool IsValidChar(char32_t key, CharSetFilter afilter);
size_t Utf8Decode(WChar *c, const char *s); size_t Utf8Decode(char32_t *c, const char *s);
size_t Utf8Encode(char *buf, WChar c); size_t Utf8Encode(char *buf, char32_t c);
size_t Utf8Encode(std::ostreambuf_iterator<char> &buf, WChar c); size_t Utf8Encode(std::ostreambuf_iterator<char> &buf, char32_t c);
size_t Utf8Encode(std::back_insert_iterator<std::string> &buf, char32_t c);
size_t Utf8TrimString(char *s, size_t maxlen); size_t Utf8TrimString(char *s, size_t maxlen);
static inline WChar Utf8Consume(const char **s) static inline char32_t Utf8Consume(const char **s)
{ {
WChar c; char32_t c;
*s += Utf8Decode(&c, *s); *s += Utf8Decode(&c, *s);
return c; return c;
} }
template <class Titr> template <class Titr>
static inline WChar Utf8Consume(Titr &s) static inline char32_t Utf8Consume(Titr &s)
{ {
WChar c; char32_t c;
s += Utf8Decode(&c, &*s); s += Utf8Decode(&c, &*s);
return c; return c;
} }
@ -127,7 +136,7 @@ static inline WChar Utf8Consume(Titr &s)
* @param c Unicode character. * @param c Unicode character.
* @return Length of UTF-8 encoding for character. * @return Length of UTF-8 encoding for character.
*/ */
static inline int8 Utf8CharLen(WChar c) static inline int8_t Utf8CharLen(char32_t c)
{ {
if (c < 0x80) return 1; if (c < 0x80) return 1;
if (c < 0x800) return 2; if (c < 0x800) return 2;
@ -146,7 +155,7 @@ static inline int8 Utf8CharLen(WChar c)
* @param c char to query length of * @param c char to query length of
* @return requested size * @return requested size
*/ */
static inline int8 Utf8EncodedCharLen(char c) static inline int8_t Utf8EncodedCharLen(char c)
{ {
if (GB(c, 3, 5) == 0x1E) return 4; if (GB(c, 3, 5) == 0x1E) return 4;
if (GB(c, 4, 4) == 0x0E) return 3; if (GB(c, 4, 4) == 0x0E) return 3;
@ -214,7 +223,7 @@ static inline bool Utf16IsTrailSurrogate(uint c)
* @param trail Trail surrogate code point. * @param trail Trail surrogate code point.
* @return Decoded Unicode character. * @return Decoded Unicode character.
*/ */
static inline WChar Utf16DecodeSurrogate(uint lead, uint trail) static inline char32_t Utf16DecodeSurrogate(uint lead, uint trail)
{ {
return 0x10000 + (((lead - 0xD800) << 10) | (trail - 0xDC00)); return 0x10000 + (((lead - 0xD800) << 10) | (trail - 0xDC00));
} }
@ -224,7 +233,7 @@ static inline WChar Utf16DecodeSurrogate(uint lead, uint trail)
* @param c Pointer to one or two UTF-16 code points. * @param c Pointer to one or two UTF-16 code points.
* @return Decoded Unicode character. * @return Decoded Unicode character.
*/ */
static inline WChar Utf16DecodeChar(const uint16 *c) static inline char32_t Utf16DecodeChar(const uint16_t *c)
{ {
if (Utf16IsLeadSurrogate(c[0])) { if (Utf16IsLeadSurrogate(c[0])) {
return Utf16DecodeSurrogate(c[0], c[1]); return Utf16DecodeSurrogate(c[0], c[1]);
@ -239,7 +248,7 @@ static inline WChar Utf16DecodeChar(const uint16 *c)
* @return true iff the character is used to influence * @return true iff the character is used to influence
* the text direction. * the text direction.
*/ */
static inline bool IsTextDirectionChar(WChar c) static inline bool IsTextDirectionChar(char32_t c)
{ {
switch (c) { switch (c) {
case CHAR_TD_LRM: case CHAR_TD_LRM:
@ -256,7 +265,7 @@ static inline bool IsTextDirectionChar(WChar c)
} }
} }
static inline bool IsPrintable(WChar c) static inline bool IsPrintable(char32_t c)
{ {
if (c < 0x20) return false; if (c < 0x20) return false;
if (c < 0xE000) return true; if (c < 0xE000) return true;
@ -271,7 +280,7 @@ static inline bool IsPrintable(WChar c)
* @return a boolean value whether 'c' is a whitespace character or not * @return a boolean value whether 'c' is a whitespace character or not
* @see http://www.fileformat.info/info/unicode/category/Zs/list.htm * @see http://www.fileformat.info/info/unicode/category/Zs/list.htm
*/ */
static inline bool IsWhitespace(WChar c) static inline bool IsWhitespace(char32_t c)
{ {
return c == 0x0020 /* SPACE */ || c == 0x3000; /* IDEOGRAPHIC SPACE */ return c == 0x0020 /* SPACE */ || c == 0x3000; /* IDEOGRAPHIC SPACE */
} }

File diff suppressed because it is too large Load Diff

@ -65,9 +65,7 @@ static inline StringID MakeStringID(StringTab tab, uint index)
return (tab << TAB_SIZE_BITS) + index; return (tab << TAB_SIZE_BITS) + index;
} }
char *GetString(char *buffr, StringID string, const char *last);
std::string GetString(StringID string); std::string GetString(StringID string);
char *GetStringWithArgs(char *buffr, StringID string, StringParameters &args, const char *last, uint case_index = 0, bool game_script = false);
const char *GetStringPtr(StringID string); const char *GetStringPtr(StringID string);
uint32 GetStringGRFID(StringID string); uint32 GetStringGRFID(StringID string);

@ -10,7 +10,6 @@
#ifndef STRINGS_INTERNAL_H #ifndef STRINGS_INTERNAL_H
#define STRINGS_INTERNAL_H #define STRINGS_INTERNAL_H
#include "strings_func.h"
#include "string_func.h" #include "string_func.h"
#include "core/span_type.hpp" #include "core/span_type.hpp"
#include "core/strong_typedef_type.hpp" #include "core/strong_typedef_type.hpp"
@ -346,6 +345,21 @@ public:
{ {
return (*this->string)[index]; return (*this->string)[index];
} }
std::string *GetTargetString()
{
return this->string;
}
}; };
void GetStringWithArgs(StringBuilder builder, StringID string, StringParameters &args, uint case_index = 0, bool game_script = false);
std::string GetStringWithArgs(StringID string, StringParameters &args);
void GetString(StringBuilder builder, StringID string);
/* Do not leak the StringBuilder to everywhere. */
void GenerateTownNameString(StringBuilder builder, size_t lang, uint32_t seed);
void GetTownName(StringBuilder builder, const struct Town *t);
void GRFTownNameGenerate(StringBuilder builder, uint32_t grfid, uint16_t gen, uint32_t seed);
#endif /* STRINGS_INTERNAL_H */ #endif /* STRINGS_INTERNAL_H */

@ -493,7 +493,7 @@ class NIHVehicle : public NIHelper {
b = buffer + seprintf(buffer, lastof(buffer), " %s [%d, %d, %d], %u, ", b = buffer + seprintf(buffer, lastof(buffer), " %s [%d, %d, %d], %u, ",
info.id == v->index ? "*" : " ", info.order_count, info.order_ticks, info.cumulative_ticks, info.id); info.id == v->index ? "*" : " ", info.order_count, info.order_ticks, info.cumulative_ticks, info.id);
SetDParam(0, info.id); SetDParam(0, info.id);
b = GetString(b, STR_VEHICLE_NAME, lastof(buffer)); b = strecpy(b, GetString(STR_VEHICLE_NAME).c_str(), lastof(buffer), true);
b += seprintf(b, lastof(buffer), ", lateness: %d", Vehicle::Get(info.id)->lateness_counter); b += seprintf(b, lastof(buffer), ", lateness: %d", Vehicle::Get(info.id)->lateness_counter);
output.print(buffer); output.print(buffer);
} }

@ -48,7 +48,7 @@ bool Textbuf::CanDelChar(bool backspace)
* @param keycode Type of deletion, either WKC_BACKSPACE or WKC_DELETE * @param keycode Type of deletion, either WKC_BACKSPACE or WKC_DELETE
* @return Return true on successful change of Textbuf, or false otherwise * @return Return true on successful change of Textbuf, or false otherwise
*/ */
bool Textbuf::DeleteChar(uint16 keycode) bool Textbuf::DeleteChar(uint16_t keycode)
{ {
bool word = (keycode & WKC_CTRL) != 0; bool word = (keycode & WKC_CTRL) != 0;
@ -60,17 +60,17 @@ bool Textbuf::DeleteChar(uint16 keycode)
if (!CanDelChar(backspace)) return false; if (!CanDelChar(backspace)) return false;
char *s = this->buf + this->caretpos; char *s = this->buf + this->caretpos;
uint16 len = 0; uint16_t len = 0;
if (word) { if (word) {
/* Delete a complete word. */ /* Delete a complete word. */
if (backspace) { if (backspace) {
/* Delete whitespace and word in front of the caret. */ /* Delete whitespace and word in front of the caret. */
len = this->caretpos - (uint16)this->char_iter->Prev(StringIterator::ITER_WORD); len = this->caretpos - (uint16_t)this->char_iter->Prev(StringIterator::ITER_WORD);
s -= len; s -= len;
} else { } else {
/* Delete word and following whitespace following the caret. */ /* Delete word and following whitespace following the caret. */
len = (uint16)this->char_iter->Next(StringIterator::ITER_WORD) - this->caretpos; len = (uint16_t)this->char_iter->Next(StringIterator::ITER_WORD) - this->caretpos;
} }
/* Update character count. */ /* Update character count. */
for (const char *ss = s; ss < s + len; Utf8Consume(&ss)) { for (const char *ss = s; ss < s + len; Utf8Consume(&ss)) {
@ -81,12 +81,12 @@ bool Textbuf::DeleteChar(uint16 keycode)
if (backspace) { if (backspace) {
/* Delete the last code point in front of the caret. */ /* Delete the last code point in front of the caret. */
s = Utf8PrevChar(s); s = Utf8PrevChar(s);
WChar c; char32_t c;
len = (uint16)Utf8Decode(&c, s); len = (uint16_t)Utf8Decode(&c, s);
this->chars--; this->chars--;
} else { } else {
/* Delete the complete character following the caret. */ /* Delete the complete character following the caret. */
len = (uint16)this->char_iter->Next(StringIterator::ITER_CHARACTER) - this->caretpos; len = (uint16_t)this->char_iter->Next(StringIterator::ITER_CHARACTER) - this->caretpos;
/* Update character count. */ /* Update character count. */
for (const char *ss = s; ss < s + len; Utf8Consume(&ss)) { for (const char *ss = s; ss < s + len; Utf8Consume(&ss)) {
this->chars--; this->chars--;
@ -128,9 +128,9 @@ void Textbuf::DeleteAll()
* @param key Character to be inserted * @param key Character to be inserted
* @return Return true on successful change of Textbuf, or false otherwise * @return Return true on successful change of Textbuf, or false otherwise
*/ */
bool Textbuf::InsertChar(WChar key) bool Textbuf::InsertChar(char32_t key)
{ {
uint16 len = (uint16)Utf8CharLen(key); uint16_t len = (uint16_t)Utf8CharLen(key);
if (this->bytes + len <= this->max_bytes && this->chars + 1 <= this->max_chars) { if (this->bytes + len <= this->max_bytes && this->chars + 1 <= this->max_chars) {
memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos); memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos);
Utf8Encode(this->buf + this->caretpos, key); Utf8Encode(this->buf + this->caretpos, key);
@ -160,7 +160,7 @@ bool Textbuf::InsertChar(WChar key)
*/ */
bool Textbuf::InsertString(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end) bool Textbuf::InsertString(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
{ {
uint16 insertpos = (marked && this->marklength != 0) ? this->markpos : this->caretpos; uint16_t insertpos = (marked && this->marklength != 0) ? this->markpos : this->caretpos;
if (insert_location != nullptr) { if (insert_location != nullptr) {
insertpos = insert_location - this->buf; insertpos = insert_location - this->buf;
if (insertpos > this->bytes) return false; if (insertpos > this->bytes) return false;
@ -174,8 +174,8 @@ bool Textbuf::InsertString(const char *str, bool marked, const char *caret, cons
if (str == nullptr) return false; if (str == nullptr) return false;
uint16 bytes = 0, chars = 0; uint16_t bytes = 0, chars = 0;
WChar c; char32_t c;
for (const char *ptr = str; (c = Utf8Consume(&ptr)) != '\0';) { for (const char *ptr = str; (c = Utf8Consume(&ptr)) != '\0';) {
if (!IsValidChar(c, this->afilter)) break; if (!IsValidChar(c, this->afilter)) break;
@ -235,7 +235,7 @@ bool Textbuf::InsertClipboard()
* @param to End of the text to delete. * @param to End of the text to delete.
* @param update Set to true if the internal state should be updated. * @param update Set to true if the internal state should be updated.
*/ */
void Textbuf::DeleteText(uint16 from, uint16 to, bool update) void Textbuf::DeleteText(uint16_t from, uint16_t to, bool update)
{ {
uint c = 0; uint c = 0;
const char *s = this->buf + from; const char *s = this->buf + from;
@ -299,7 +299,7 @@ void Textbuf::UpdateStringIter()
{ {
this->char_iter->SetString(this->buf); this->char_iter->SetString(this->buf);
size_t pos = this->char_iter->SetCurPosition(this->caretpos); size_t pos = this->char_iter->SetCurPosition(this->caretpos);
this->caretpos = pos == StringIterator::END ? 0 : (uint16)pos; this->caretpos = pos == StringIterator::END ? 0 : (uint16_t)pos;
} }
/** Update pixel width of the text. */ /** Update pixel width of the text. */
@ -331,7 +331,7 @@ void Textbuf::UpdateMarkedText()
* @param keycode Direction in which navigation occurs (WKC_CTRL |) WKC_LEFT, (WKC_CTRL |) WKC_RIGHT, WKC_END, WKC_HOME * @param keycode Direction in which navigation occurs (WKC_CTRL |) WKC_LEFT, (WKC_CTRL |) WKC_RIGHT, WKC_END, WKC_HOME
* @return Return true on successful change of Textbuf, or false otherwise * @return Return true on successful change of Textbuf, or false otherwise
*/ */
bool Textbuf::MovePos(uint16 keycode) bool Textbuf::MovePos(uint16_t keycode)
{ {
switch (keycode) { switch (keycode) {
case WKC_LEFT: case WKC_LEFT:
@ -341,7 +341,7 @@ bool Textbuf::MovePos(uint16 keycode)
size_t pos = this->char_iter->Prev(keycode & WKC_CTRL ? StringIterator::ITER_WORD : StringIterator::ITER_CHARACTER); size_t pos = this->char_iter->Prev(keycode & WKC_CTRL ? StringIterator::ITER_WORD : StringIterator::ITER_CHARACTER);
if (pos == StringIterator::END) return true; if (pos == StringIterator::END) return true;
this->caretpos = (uint16)pos; this->caretpos = (uint16_t)pos;
this->UpdateCaretPosition(); this->UpdateCaretPosition();
return true; return true;
} }
@ -353,7 +353,7 @@ bool Textbuf::MovePos(uint16 keycode)
size_t pos = this->char_iter->Next(keycode & WKC_CTRL ? StringIterator::ITER_WORD : StringIterator::ITER_CHARACTER); size_t pos = this->char_iter->Next(keycode & WKC_CTRL ? StringIterator::ITER_WORD : StringIterator::ITER_CHARACTER);
if (pos == StringIterator::END) return true; if (pos == StringIterator::END) return true;
this->caretpos = (uint16)pos; this->caretpos = (uint16_t)pos;
this->UpdateCaretPosition(); this->UpdateCaretPosition();
return true; return true;
} }
@ -383,7 +383,7 @@ bool Textbuf::MovePos(uint16 keycode)
* @param max_bytes maximum size in bytes, including terminating '\0' * @param max_bytes maximum size in bytes, including terminating '\0'
* @param max_chars maximum size in chars, including terminating '\0' * @param max_chars maximum size in chars, including terminating '\0'
*/ */
Textbuf::Textbuf(uint16 max_bytes, uint16 max_chars) Textbuf::Textbuf(uint16_t max_bytes, uint16_t max_chars)
: buf(MallocT<char>(max_bytes)), char_iter(StringIterator::Create()) : buf(MallocT<char>(max_bytes)), char_iter(StringIterator::Create())
{ {
assert(max_bytes != 0); assert(max_bytes != 0);
@ -407,27 +407,26 @@ Textbuf::~Textbuf()
*/ */
void Textbuf::Assign(StringID string) void Textbuf::Assign(StringID string)
{ {
GetString(this->buf, string, &this->buf[this->max_bytes - 1]); this->Assign(GetString(string));
this->UpdateSize();
} }
/** /**
* Copy a string into the textbuffer. * Copy a string into the textbuffer.
* @param text Source. * @param text Source.
*/ */
void Textbuf::Assign(const char *text) void Textbuf::Assign(const std::string_view text)
{ {
strecpy(this->buf, text, &this->buf[this->max_bytes - 1]); const char *last_of = &this->buf[this->max_bytes - 1];
this->UpdateSize(); strecpy(this->buf, text.data(), last_of);
} StrMakeValidInPlace(this->buf, last_of, SVS_NONE);
/* Make sure the name isn't too long for the text buffer in the number of
* characters (not bytes). max_chars also counts the '\0' characters. */
while (Utf8StringLength(this->buf) + 1 > this->max_chars) {
*Utf8PrevChar(this->buf + strlen(this->buf)) = '\0';
}
/** this->UpdateSize();
* Copy a string into the textbuffer.
* @param text Source.
*/
void Textbuf::Assign(const std::string &text)
{
this->Assign(text.c_str());
} }
/** /**
@ -435,10 +434,20 @@ void Textbuf::Assign(const std::string &text)
*/ */
void Textbuf::Print(const char *format, ...) void Textbuf::Print(const char *format, ...)
{ {
const char *last_of = &this->buf[this->max_bytes - 1];
va_list va; va_list va;
va_start(va, format); va_start(va, format);
vseprintf(this->buf, &this->buf[this->max_bytes - 1], format, va); vseprintf(this->buf, last_of, format, va);
va_end(va); va_end(va);
StrMakeValidInPlace(this->buf, last_of, SVS_NONE);
/* Make sure the name isn't too long for the text buffer in the number of
* characters (not bytes). max_chars also counts the '\0' characters. */
while (Utf8StringLength(this->buf) + 1 > this->max_chars) {
*Utf8PrevChar(this->buf + strlen(this->buf)) = '\0';
}
this->UpdateSize(); this->UpdateSize();
} }
@ -454,7 +463,7 @@ void Textbuf::UpdateSize()
this->chars = this->bytes = 1; // terminating zero this->chars = this->bytes = 1; // terminating zero
WChar c; char32_t c;
while ((c = Utf8Consume(&buf)) != '\0') { while ((c = Utf8Consume(&buf)) != '\0') {
this->bytes += Utf8CharLen(c); this->bytes += Utf8CharLen(c);
this->chars++; this->chars++;
@ -486,7 +495,7 @@ bool Textbuf::HandleCaret()
return false; return false;
} }
HandleKeyPressResult Textbuf::HandleKeyPress(WChar key, uint16 keycode) HandleKeyPressResult Textbuf::HandleKeyPress(char32_t key, uint16_t keycode)
{ {
bool edited = false; bool edited = false;

@ -30,37 +30,36 @@ enum HandleKeyPressResult
struct Textbuf { struct Textbuf {
CharSetFilter afilter; ///< Allowed characters CharSetFilter afilter; ///< Allowed characters
char * const buf; ///< buffer in which text is saved char * const buf; ///< buffer in which text is saved
uint16 max_bytes; ///< the maximum size of the buffer in bytes (including terminating '\0') uint16_t max_bytes; ///< the maximum size of the buffer in bytes (including terminating '\0')
uint16 max_chars; ///< the maximum size of the buffer in characters (including terminating '\0') uint16_t max_chars; ///< the maximum size of the buffer in characters (including terminating '\0')
uint16 bytes; ///< the current size of the string in bytes (including terminating '\0') uint16_t bytes; ///< the current size of the string in bytes (including terminating '\0')
uint16 chars; ///< the current size of the string in characters (including terminating '\0') uint16_t chars; ///< the current size of the string in characters (including terminating '\0')
uint16 pixels; ///< the current size of the string in pixels uint16_t pixels; ///< the current size of the string in pixels
bool caret; ///< is the caret ("_") visible or not bool caret; ///< is the caret ("_") visible or not
uint16 caretpos; ///< the current position of the caret in the buffer, in bytes uint16_t caretpos; ///< the current position of the caret in the buffer, in bytes
uint16 caretxoffs; ///< the current position of the caret in pixels uint16_t caretxoffs; ///< the current position of the caret in pixels
uint16 markpos; ///< the start position of the marked area in the buffer, in bytes uint16_t markpos; ///< the start position of the marked area in the buffer, in bytes
uint16 markend; ///< the end position of the marked area in the buffer, in bytes uint16_t markend; ///< the end position of the marked area in the buffer, in bytes
uint16 markxoffs; ///< the start position of the marked area in pixels uint16_t markxoffs; ///< the start position of the marked area in pixels
uint16 marklength; ///< the length of the marked area in pixels uint16_t marklength; ///< the length of the marked area in pixels
explicit Textbuf(uint16 max_bytes, uint16 max_chars = UINT16_MAX); explicit Textbuf(uint16_t max_bytes, uint16_t max_chars = UINT16_MAX);
~Textbuf(); ~Textbuf();
void Assign(StringID string); void Assign(StringID string);
void Assign(const char *text); void Assign(const std::string_view text);
void Assign(const std::string &text);
void CDECL Print(const char *format, ...) WARN_FORMAT(2, 3); void CDECL Print(const char *format, ...) WARN_FORMAT(2, 3);
void DeleteAll(); void DeleteAll();
bool InsertClipboard(); bool InsertClipboard();
bool InsertChar(WChar key); bool InsertChar(char32_t key);
bool InsertString(const char *str, bool marked, const char *caret = nullptr, const char *insert_location = nullptr, const char *replacement_end = nullptr); bool InsertString(const char *str, bool marked, const char *caret = nullptr, const char *insert_location = nullptr, const char *replacement_end = nullptr);
bool DeleteChar(uint16 keycode); bool DeleteChar(uint16_t keycode);
bool MovePos(uint16 keycode); bool MovePos(uint16_t keycode);
HandleKeyPressResult HandleKeyPress(WChar key, uint16 keycode); HandleKeyPressResult HandleKeyPress(char32_t key, uint16_t keycode);
bool HandleCaret(); bool HandleCaret();
void UpdateSize(); void UpdateSize();
@ -74,7 +73,7 @@ private:
bool CanDelChar(bool backspace); bool CanDelChar(bool backspace);
void DeleteText(uint16 from, uint16 to, bool update); void DeleteText(uint16_t from, uint16_t to, bool update);
void UpdateStringIter(); void UpdateStringIter();
void UpdateWidth(); void UpdateWidth();

@ -252,9 +252,7 @@ uint64 Town::LabelParam2() const
void Town::FillCachedName() const void Town::FillCachedName() const
{ {
char buf[MAX_LENGTH_TOWN_NAME_CHARS * MAX_CHAR_LENGTH]; this->cached_name = GetTownName(this);
char *end = GetTownName(buf, this, lastof(buf));
this->cached_name.assign(buf, end);
} }
/** /**

@ -1346,8 +1346,7 @@ public:
if (!this->townnamevalid) { if (!this->townnamevalid) {
this->townname_editbox.text.DeleteAll(); this->townname_editbox.text.DeleteAll();
} else { } else {
GetTownName(this->townname_editbox.text.buf, &this->params, this->townnameparts, &this->townname_editbox.text.buf[this->townname_editbox.text.max_bytes - 1]); this->townname_editbox.text.Assign(GetTownName(&this->params, this->townnameparts));
this->townname_editbox.text.UpdateSize();
} }
UpdateOSKOriginalText(this, WID_TF_TOWN_NAME_EDITBOX); UpdateOSKOriginalText(this, WID_TF_TOWN_NAME_EDITBOX);
@ -1384,9 +1383,8 @@ public:
name = this->townname_editbox.text.buf; name = this->townname_editbox.text.buf;
} else { } else {
/* If user changed the name, send it */ /* If user changed the name, send it */
char buf[MAX_LENGTH_TOWN_NAME_CHARS * MAX_CHAR_LENGTH]; std::string original_name = GetTownName(&this->params, this->townnameparts);
GetTownName(buf, &this->params, this->townnameparts, lastof(buf)); if (original_name != this->townname_editbox.text.buf) name = this->townname_editbox.text.buf;
if (strcmp(buf, this->townname_editbox.text.buf) != 0) name = this->townname_editbox.text.buf;
} }
bool success = DoCommandP(tile, this->town_size | this->city << 2 | this->town_layout << 3 | random << 6, bool success = DoCommandP(tile, this->town_size | this->city << 2 | this->town_layout << 3 | random << 6,
@ -1558,9 +1556,7 @@ public:
const GRFFile *gf = HouseSpec::Get(this->GetHouseAtOffset(house_set, 0))->grf_prop.grffile; const GRFFile *gf = HouseSpec::Get(this->GetHouseAtOffset(house_set, 0))->grf_prop.grffile;
if (gf != nullptr) return GetGRFConfig(gf->grfid)->GetName(); if (gf != nullptr) return GetGRFConfig(gf->grfid)->GetName();
static char name[DRAW_STRING_BUFFER]; return GetStringPtr(STR_BASIC_HOUSE_SET_NAME);
GetString(name, STR_BASIC_HOUSE_SET_NAME, lastof(name));
return name;
} }
/** /**
@ -1812,8 +1808,7 @@ public:
} }
case WID_HP_HOUSE_ACCEPTANCE: { case WID_HP_HOUSE_ACCEPTANCE: {
static char buff[DRAW_STRING_BUFFER] = ""; std::string buff;
char *str = buff;
CargoArray cargo{}; CargoArray cargo{};
CargoTypes dummy = 0; CargoTypes dummy = 0;
AddAcceptedHouseCargo(this->display_house, INVALID_TILE, cargo, &dummy); AddAcceptedHouseCargo(this->display_house, INVALID_TILE, cargo, &dummy);
@ -1823,10 +1818,10 @@ public:
SetDParam(0, cargo[i] < 8 ? STR_HOUSE_BUILD_CARGO_VALUE_EIGHTS : STR_HOUSE_BUILD_CARGO_VALUE_JUST_NAME); SetDParam(0, cargo[i] < 8 ? STR_HOUSE_BUILD_CARGO_VALUE_EIGHTS : STR_HOUSE_BUILD_CARGO_VALUE_JUST_NAME);
SetDParam(1, cargo[i]); SetDParam(1, cargo[i]);
SetDParam(2, CargoSpec::Get(i)->name); SetDParam(2, CargoSpec::Get(i)->name);
str = GetString(str, str == buff ? STR_HOUSE_BUILD_CARGO_FIRST : STR_HOUSE_BUILD_CARGO_SEPARATED, lastof(buff)); GetString(StringBuilder(buff), buff.empty() ? STR_HOUSE_BUILD_CARGO_FIRST : STR_HOUSE_BUILD_CARGO_SEPARATED);
} }
if (str == buff) GetString(buff, STR_JUST_NOTHING, lastof(buff)); if (buff.empty()) GetString(StringBuilder(buff), STR_JUST_NOTHING);
SetDParamStr(0, buff); SetDParamStr(0, std::move(buff));
break; break;
} }

File diff suppressed because it is too large Load Diff

@ -13,10 +13,9 @@
#include "core/random_func.hpp" #include "core/random_func.hpp"
#include "townname_type.h" #include "townname_type.h"
char *GenerateTownNameString(char *buf, const char *last, size_t lang, uint32 seed); std::string GetTownName(const TownNameParams *par, uint32_t townnameparts);
char *GetTownName(char *buff, const TownNameParams *par, uint32 townnameparts, const char *last); std::string GetTownName(const Town *t);
char *GetTownName(char *buff, const Town *t, const char *last); bool VerifyTownName(uint32_t r, const TownNameParams *par, TownNames *town_names = nullptr);
bool VerifyTownName(uint32 r, const TownNameParams *par, TownNames *town_names = nullptr); bool GenerateTownName(Randomizer &randomizer, uint32_t *townnameparts, TownNames *town_names = nullptr);
bool GenerateTownName(Randomizer &randomizer, uint32 *townnameparts, TownNames *town_names = nullptr);
#endif /* TOWNNAME_FUNC_H */ #endif /* TOWNNAME_FUNC_H */

@ -28,8 +28,8 @@ static constexpr uint BUILTIN_TOWNNAME_GENERATOR_COUNT = SPECSTR_TOWNNAME_LAST -
* Speeds things up a bit because these values are computed only once per name generation. * Speeds things up a bit because these values are computed only once per name generation.
*/ */
struct TownNameParams { struct TownNameParams {
uint32 grfid; ///< newgrf ID (0 if not used) uint32_t grfid; ///< newgrf ID (0 if not used)
uint16 type; ///< town name style uint16_t type; ///< town name style
/** /**
* Initializes this struct from language ID * Initializes this struct from language ID

@ -1205,10 +1205,8 @@ static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestric
SetDParam(1, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); SetDParam(1, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY);
} else { } else {
auto insert_warning = [&](uint dparam_index, StringID warning) { auto insert_warning = [&](uint dparam_index, StringID warning) {
char buf[256];
auto tmp_params = MakeParameters(GetDParam(dparam_index)); auto tmp_params = MakeParameters(GetDParam(dparam_index));
char *end = GetStringWithArgs(buf, warning, tmp_params, lastof(buf)); _temp_special_strings[0] = GetStringWithArgs(warning, tmp_params);
_temp_special_strings[0].assign(buf, end);
SetDParam(dparam_index, SPECSTR_TEMP_START); SetDParam(dparam_index, SPECSTR_TEMP_START);
}; };

@ -425,16 +425,11 @@ void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRF
if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE); if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
} }
/* debug output */
char buffer[512];
SetDParamStr(0, grfconfig->GetName()); SetDParamStr(0, grfconfig->GetName());
GetString(buffer, part1, lastof(buffer)); DEBUG(grf, 0, "%s", strip_leading_colours(GetString(part1)));
DEBUG(grf, 0, "%s", buffer + 3);
SetDParam(1, engine); SetDParam(1, engine);
GetString(buffer, part2, lastof(buffer)); DEBUG(grf, 0, "%s", strip_leading_colours(GetString(part2)));
DEBUG(grf, 0, "%s", buffer + 3);
} }
/** /**
@ -4642,7 +4637,7 @@ void DumpVehicleStats(char *buffer, const char *last)
for (auto &it : cstatmap) { for (auto &it : cstatmap) {
buffer += seprintf(buffer, last, "%u: ", (uint) it.first); buffer += seprintf(buffer, last, "%u: ", (uint) it.first);
SetDParam(0, it.first); SetDParam(0, it.first);
buffer = GetString(buffer, STR_COMPANY_NAME, last); buffer = strecpy(buffer, GetString(STR_COMPANY_NAME).c_str(), last, true);
buffer += seprintf(buffer, last, "\n"); buffer += seprintf(buffer, last, "\n");
auto line = [&](vtypestats &vs, const char *type) { auto line = [&](vtypestats &vs, const char *type) {

@ -987,7 +987,6 @@ struct RefitWindow : public Window {
{ {
std::string &name = this->ship_part_names[v->index]; std::string &name = this->ship_part_names[v->index];
if (name.empty()) { if (name.empty()) {
char buffer[128] = "";
const Vehicle *front = v->First(); const Vehicle *front = v->First();
uint offset = 0; uint offset = 0;
for (const Vehicle *u = front; u != v; u = u->Next()) offset++; for (const Vehicle *u = front; u != v; u = u->Next()) offset++;
@ -997,14 +996,11 @@ struct RefitWindow : public Window {
assert(grffile != nullptr); assert(grffile != nullptr);
StartTextRefStackUsage(grffile, 6); StartTextRefStackUsage(grffile, 6);
char *end = GetString(buffer, GetGRFStringID(grffile->grfid, 0xD000 + callback), lastof(buffer)); name = GetString(GetGRFStringID(grffile->grfid, 0xD000 + callback));
StopTextRefStackUsage(); StopTextRefStackUsage();
name.assign(buffer, end - buffer);
} else { } else {
SetDParam(0, offset + 1); SetDParam(0, offset + 1);
char *end = GetString(buffer, STR_REFIT_SHIP_PART, lastof(buffer)); name = GetString(STR_REFIT_SHIP_PART);
name.assign(buffer, end - buffer);
} }
} }
return name; return name;
@ -3201,18 +3197,14 @@ struct VehicleDetailsWindow : Window {
std::vector<TraceRestrictSlotID> slots; std::vector<TraceRestrictSlotID> slots;
TraceRestrictGetVehicleSlots(v->index, slots); TraceRestrictGetVehicleSlots(v->index, slots);
char text_buffer[512];
char *buffer = text_buffer;
const char * const last = lastof(text_buffer);
SetDParam(0, slots.size()); SetDParam(0, slots.size());
buffer = GetString(buffer, STR_TRACE_RESTRICT_SLOT_LIST_HEADER, last); std::string buffer = GetString(STR_TRACE_RESTRICT_SLOT_LIST_HEADER);
for (size_t i = 0; i < slots.size(); i++) { for (size_t i = 0; i < slots.size(); i++) {
if (i != 0) buffer = GetString(buffer, STR_TRACE_RESTRICT_SLOT_LIST_SEPARATOR, last); if (i != 0) GetString(StringBuilder(buffer), STR_TRACE_RESTRICT_SLOT_LIST_SEPARATOR);
buffer = strecpy(buffer, TraceRestrictSlot::Get(slots[i])->name.c_str(), last); buffer += TraceRestrictSlot::Get(slots[i])->name;
} }
SetDParamStr(0, text_buffer); DrawString(tr, buffer);
DrawString(tr, STR_JUST_RAW_STRING);
tr.top += GetCharacterHeight(FS_NORMAL); tr.top += GetCharacterHeight(FS_NORMAL);
} }

@ -1950,15 +1950,16 @@ void ViewportSign::UpdatePosition(ZoomLevel maxzoom, int center, int top, String
this->top = top; this->top = top;
char buffer[DRAW_STRING_BUFFER]; std::string buffer;
GetString(buffer, str, lastof(buffer)); GetString(StringBuilder(buffer), str);
this->width_normal = WidgetDimensions::scaled.fullbevel.left + Align(GetStringBoundingBox(buffer).width, 2) + WidgetDimensions::scaled.fullbevel.right; this->width_normal = WidgetDimensions::scaled.fullbevel.left + Align(GetStringBoundingBox(buffer).width, 2) + WidgetDimensions::scaled.fullbevel.right;
this->center = center; this->center = center;
/* zoomed out version */ /* zoomed out version */
if (str_small != STR_NULL) { if (str_small != STR_NULL) {
GetString(buffer, str_small, lastof(buffer)); buffer.clear();
GetString(StringBuilder(buffer), str_small);
} }
this->width_small = WidgetDimensions::scaled.fullbevel.left + Align(GetStringBoundingBox(buffer, FS_SMALL).width, 2) + WidgetDimensions::scaled.fullbevel.right; this->width_small = WidgetDimensions::scaled.fullbevel.left + Align(GetStringBoundingBox(buffer, FS_SMALL).width, 2) + WidgetDimensions::scaled.fullbevel.right;

Loading…
Cancel
Save