From f034714559c65829dd159c3150e681defdc4b26b Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 5 Jan 2024 21:12:54 +0000 Subject: [PATCH] Use StringBuilder for GetString/GetStringWithArgs, as per upstream Update dependent code as required --- src/cargopacket.cpp | 21 +- src/command.cpp | 22 +- src/command_type.h | 9 - src/company_cmd.cpp | 18 +- src/console_cmds.cpp | 24 +- src/crashlog.cpp | 2 +- src/departures_gui.cpp | 28 +- src/engine_gui.cpp | 14 +- src/error_gui.cpp | 15 +- src/fileio.cpp | 10 +- src/fileio_func.h | 2 +- src/fios_gui.cpp | 3 +- src/gfx.cpp | 22 +- src/gfx_func.h | 3 - src/group_cmd.cpp | 8 +- src/industry_cmd.cpp | 4 +- src/industry_gui.cpp | 75 ++-- src/linkgraph/linkgraph_gui.cpp | 24 +- src/main_gui.cpp | 3 +- src/misc_gui.cpp | 18 +- src/network/network.cpp | 17 +- src/network/network_chat_gui.cpp | 4 - src/network/network_content_gui.cpp | 26 +- src/newgrf_commons.cpp | 11 +- src/newgrf_debug_gui.cpp | 9 +- src/newgrf_text.cpp | 6 +- src/newgrf_text.h | 2 +- src/newgrf_townname.cpp | 34 +- src/newgrf_townname.h | 17 +- src/openttd.cpp | 9 +- src/order_gui.cpp | 8 +- src/programmable_signals_gui.cpp | 9 +- src/scope_info.cpp | 8 +- src/screenshot.cpp | 53 ++- src/screenshot.h | 2 +- src/script/api/script_text.cpp | 57 ++- src/script/api/script_text.hpp | 12 +- src/settings_gui.cpp | 23 +- src/ship_gui.cpp | 14 +- src/sl/extended_ver_sl.cpp | 4 +- src/sl/saveload.cpp | 39 +- src/sl/saveload.h | 4 +- src/station_cmd.cpp | 4 +- src/station_gui.cpp | 50 +-- src/string.cpp | 19 + src/string_func.h | 39 +- src/strings.cpp | 649 ++++++++++++++-------------- src/strings_func.h | 2 - src/strings_internal.h | 16 +- src/table/newgrf_debug_data.h | 2 +- src/textbuf.cpp | 77 ++-- src/textbuf_type.h | 39 +- src/town_cmd.cpp | 4 +- src/town_gui.cpp | 21 +- src/townname.cpp | 622 ++++++++++++-------------- src/townname_func.h | 9 +- src/townname_type.h | 4 +- src/tracerestrict_gui.cpp | 4 +- src/vehicle.cpp | 11 +- src/vehicle_gui.cpp | 20 +- src/viewport.cpp | 7 +- 61 files changed, 1064 insertions(+), 1228 deletions(-) diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index a997c773c3..9f4405888f 100644 --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -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] = {}; for (auto &it : _cargo_packet_deferred_payments) { 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 j = 0; j < 4; j++) { if (payments[i][j] != 0) { SetDParam(0, i); - buffer = GetString(buffer, STR_COMPANY_NAME, last); - buffer += seprintf(buffer, last, " ("); - buffer = GetString(buffer, STR_REPLACE_VEHICLE_TRAIN + j, last); - buffer += seprintf(buffer, last, "): "); + GetString(StringBuilder(buffer), STR_COMPANY_NAME); + buffer += " ("; + GetString(StringBuilder(buffer), STR_REPLACE_VEHICLE_TRAIN + j); + buffer += "): "; SetDParam(0, payments[i][j]); - buffer = GetString(buffer, STR_JUST_CURRENCY_LONG, last); - buffer += seprintf(buffer, last, "\n"); + GetString(StringBuilder(buffer), STR_JUST_CURRENCY_LONG); + buffer += '\n'; } } } - buffer += seprintf(buffer, last, "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("Deferred payment count: %u\n", (uint) _cargo_packet_deferred_payments.size()); + buffer += stdstr_fmt("Total cargo packets: %u\n", (uint)CargoPacket::GetNumItems()); + return buffer; } /** diff --git a/src/command.cpp b/src/command.cpp index 517e63f023..ac43b76bf7 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -1353,34 +1353,26 @@ void CommandCost::UseTextRefStack(const GRFFile *grffile, uint num_registers) } 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()) { - return seprintf(buf, last, "Success: cost: " OTTD_PRINTF64, (int64) this->GetCost()); + return stdstr_fmt("Success: cost: " OTTD_PRINTF64, (int64) this->GetCost()); } else { const uint textref_stack_size = this->GetTextRefStackSize(); if (textref_stack_size > 0) StartTextRefStackUsage(this->GetTextRefStackGRF(), textref_stack_size, this->GetTextRefStack()); - char *b = buf; - b += seprintf(b, last, "Failed: cost: " OTTD_PRINTF64, (int64) this->GetCost()); + std::string buf = stdstr_fmt("Failed: cost: " OTTD_PRINTF64, (int64) this->GetCost()); if (cmd_msg != 0) { - b += seprintf(b, last, " "); - b = GetString(b, cmd_msg, last); + buf += ' '; + GetString(StringBuilder(buf), cmd_msg); } if (this->message != INVALID_STRING_ID) { - b += seprintf(b, last, " "); - b = GetString(b, this->message, last); + buf += ' '; + GetString(StringBuilder(buf), this->message); } if (textref_stack_size > 0) StopTextRefStackUsage(); - return b - buf; + return buf; } } diff --git a/src/command_type.h b/src/command_type.h index f38414483c..bb58057caf 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -222,15 +222,6 @@ public: */ 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 { return this->Succeeded() && this->message != INVALID_STRING_ID; diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index d4248ca275..f03d609bb8 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -380,10 +380,6 @@ CommandCost CheckTileOwnership(TileIndex tile) */ 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->last_build_coordinate == 0) return; @@ -391,6 +387,7 @@ static void GenerateCompanyName(Company *c) StringID str; uint32 strp; + std::string buffer; if (t->name.empty() && IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1)) { str = t->townnametype - SPECSTR_TOWNNAME_START + SPECSTR_COMPANY_NAME_START; strp = t->townnameparts; @@ -402,7 +399,7 @@ verify_name:; } SetDParam(0, strp); - GetString(buffer, str, lastof(buffer)); + buffer = GetString(str); if (Utf8StringLength(buffer) >= MAX_LENGTH_COMPANY_NAME_CHARS) goto bad_town_name; set_name:; @@ -524,18 +521,15 @@ restart:; /* Reserve space for extra unicode character. We need to do this to be able * to detect too long president name. */ - char buffer[(MAX_LENGTH_PRESIDENT_NAME_CHARS + 1) * MAX_CHAR_LENGTH]; SetDParam(0, c->index); - GetString(buffer, STR_PRESIDENT_NAME, lastof(buffer)); - if (Utf8StringLength(buffer) >= MAX_LENGTH_PRESIDENT_NAME_CHARS) continue; + std::string name = GetString(STR_PRESIDENT_NAME); + if (Utf8StringLength(name) >= MAX_LENGTH_PRESIDENT_NAME_CHARS) continue; for (const Company *cc : Company::Iterate()) { 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); - GetString(buffer2, STR_PRESIDENT_NAME, lastof(buffer2)); - if (strcmp(buffer2, buffer) == 0) goto restart; + std::string other_name = GetString(STR_PRESIDENT_NAME); + if (name == other_name) goto restart; } } return; diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 2a58217cdd..700bc62f5c 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1895,9 +1895,8 @@ DEF_CONSOLE_CMD(ConCompanies) for (const Company *c : Company::Iterate()) { /* Grab the company name */ - char company_name[512]; 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 = ""; if (c->is_ai) { @@ -1906,10 +1905,8 @@ DEF_CONSOLE_CMD(ConCompanies) 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", - 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->group_all[VEH_TRAIN].num_vehicle, c->group_all[VEH_ROAD].num_vehicle, @@ -2076,14 +2073,11 @@ DEF_CONSOLE_CMD(ConCompanyPasswordHashes) for (const Company *c : Company::Iterate()) { /* Grab the company name */ - char company_name[512]; 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'", - 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; @@ -2472,10 +2466,8 @@ DEF_CONSOLE_CMD(ConResetBlockedHeliports) if (!occupied) { st->airport.flags = 0; count++; - char buffer[256]; SetDParam(0, st->index); - GetString(buffer, STR_STATION_NAME, lastof(buffer)); - IConsolePrintF(CC_DEFAULT, "Unblocked: %s", buffer); + IConsolePrintF(CC_DEFAULT, "Unblocked: %s", GetString(STR_STATION_NAME).c_str()); } } @@ -2635,10 +2627,8 @@ DEF_CONSOLE_CMD(ConDumpCpdpStats) return true; } - extern void DumpCargoPacketDeferredPaymentStats(char *buffer, const char *last); - char buffer[32768]; - DumpCargoPacketDeferredPaymentStats(buffer, lastof(buffer)); - PrintLineByLine(buffer); + extern std::string DumpCargoPacketDeferredPaymentStats(); + PrintLineByLine(DumpCargoPacketDeferredPaymentStats()); return true; } diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 4015a5d9b1..7c478e10cb 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -901,7 +901,7 @@ void CrashLog::CloseCrashLogFile() if (_screen.width < 1 || _screen.height < 1 || _screen.dst_ptr == nullptr) return false; 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; } diff --git a/src/departures_gui.cpp b/src/departures_gui.cpp index 69335164e6..fa6401421b 100644 --- a/src/departures_gui.cpp +++ b/src/departures_gui.cpp @@ -875,20 +875,16 @@ void DeparturesWindow::DrawDeparturesListItems(const Rect &r) const } } - char buf[256] = ""; 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].assign(buf, end); + _temp_special_strings[temp_str] = GetStringWithArgs(STR_DEPARTURES_VIA_DESCRIPTOR, tmp_params); }; get_single_via_string(0, via); if (via2 != INVALID_STATION) { get_single_via_string(1, via2); - char buf[512] = ""; 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].assign(buf, end); + _temp_special_strings[0] = GetStringWithArgs(STR_DEPARTURES_VIA_AND, tmp_params); } SetDParam(offset, SPECSTR_TEMP_START); @@ -987,11 +983,11 @@ void DeparturesWindow::DrawDeparturesListItems(const Rect &r) const /* 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_LAST_STATION :{STATION} & {RAW_STRING}*/ - char buffer[512], scratch[512]; + std::string buffer; if (d->calling_at.size() != 0) { 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; @@ -1008,29 +1004,25 @@ void DeparturesWindow::DrawDeparturesListItems(const Rect &r) const continues_to = d->calling_at[d->calling_at.size() - 1].station; break; } - SetDParamStr(0, scratch); + SetDParamStr(0, std::move(calling_at_buffer)); SetDParam(1, s); - GetString(buffer, STR_DEPARTURES_CALLING_AT_STATION, lastof(buffer)); - strncpy(scratch, buffer, sizeof(scratch)); + calling_at_buffer = GetString(STR_DEPARTURES_CALLING_AT_STATION); } /* Finally, finish off with " and ". */ - SetDParamStr(0, scratch); + SetDParamStr(0, std::move(calling_at_buffer)); SetDParam(1, d->calling_at[i].station); - GetString(buffer, STR_DEPARTURES_CALLING_AT_LAST_STATION, lastof(buffer)); - strncpy(scratch, buffer, sizeof(scratch)); + calling_at_buffer = GetString(STR_DEPARTURES_CALLING_AT_LAST_STATION); } - SetDParamStr(1, scratch); + SetDParamStr(1, std::move(calling_at_buffer)); if (continues_to == INVALID_STATION) { SetDParam(0, STR_DEPARTURES_CALLING_AT_LIST); } else { SetDParam(0, STR_DEPARTURES_CALLING_AT_LIST_SMART_TERMINUS); SetDParam(2, continues_to); } - GetString(buffer, size_prefix, lastof(buffer)); - } else { - buffer[0] = 0; + buffer = GetString(size_prefix); } int list_width = (GetStringBoundingBox(buffer, _settings_client.gui.departure_larger_font ? FS_NORMAL : FS_SMALL)).width; diff --git a/src/engine_gui.cpp b/src/engine_gui.cpp index 250dfcaaaa..fcecd5f6d5 100644 --- a/src/engine_gui.cpp +++ b/src/engine_gui.cpp @@ -169,29 +169,27 @@ uint GetTotalCapacityOfArticulatedParts(EngineID engine) static StringID GetEngineInfoCapacityString(EngineID engine) { - char buffer[1024]; - CargoArray cap = GetCapacityOfArticulatedParts(engine); if (cap.GetSum() == 0) { /* no cargo at all */ 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 { - char *b = buffer; + std::string buffer; for (uint i = 0; i < NUM_CARGO; i++) { if (cap[i] == 0) continue; - if (b != buffer) { + if (!buffer.empty()) { 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]); - 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; } diff --git a/src/error_gui.cpp b/src/error_gui.cpp index fa6304af1f..0a8ea693b4 100644 --- a/src/error_gui.cpp +++ b/src/error_gui.cpp @@ -376,25 +376,24 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel if (wl != WL_INFO) { /* Print message to console */ - char buf[DRAW_STRING_BUFFER]; 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) { - b += seprintf(b, lastof(buf), " "); - GetString(b, detailed_msg, lastof(buf)); + message += ' '; + message += GetString(detailed_msg); } if (extra_msg != INVALID_STRING_ID) { - b += seprintf(b, lastof(buf), " "); - GetString(b, extra_msg, lastof(buf)); + message += ' '; + message += GetString(extra_msg); } if (textref_stack_size > 0) StopTextRefStackUsage(); switch (wl) { - case WL_WARNING: IConsolePrint(CC_WARNING, buf); break; - default: IConsoleError(buf); break; + case WL_WARNING: IConsolePrint(CC_WARNING, message.c_str()); break; + default: IConsoleError(message.c_str()); break; } } diff --git a/src/fileio.cpp b/src/fileio.cpp index 97cccd9b38..2a4335ba5e 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -1112,17 +1112,17 @@ void DeterminePaths(const char *exe, bool only_local_path) /** * 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++) { - switch (*filename) { + for (auto &c : filename) { + switch (c) { /* The following characters are not allowed in filenames * on at least one of the supported operating systems: */ case ':': case '\\': case '*': case '?': case '/': case '<': case '>': case '|': case '"': - *filename = '_'; + c = '_'; break; } } diff --git a/src/fileio_func.h b/src/fileio_func.h index 3755391aea..0997ad9566 100644 --- a/src/fileio_func.h +++ b/src/fileio_func.h @@ -26,7 +26,7 @@ void FioRenameFile(const std::string &oldname, const std::string &newname); const char *FiosGetScreenshotDir(); -void SanitizeFilename(char *filename); +void SanitizeFilename(std::string &filename); void AppendPathSeparator(std::string &buf); void DeterminePaths(const char *exe, bool only_local_path); std::unique_ptr ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize); diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index a96d178b71..3f481e33ee 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -308,8 +308,7 @@ public: /** Generate a default save filename. */ void GenerateFileName() { - GenerateDefaultSaveName(this->filename_editbox.text.buf, &this->filename_editbox.text.buf[this->filename_editbox.text.max_bytes - 1]); - this->filename_editbox.text.UpdateSize(); + this->filename_editbox.text.Assign(GenerateDefaultSaveName()); } SaveLoadWindow(WindowDesc *desc, AbstractFileType abstract_filetype, SaveLoadOperation fop) diff --git a/src/gfx.cpp b/src/gfx.cpp index e738c81777..9ebe133cec 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -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) { - char buffer[DRAW_STRING_BUFFER]; - GetString(buffer, str, lastof(buffer)); - return DrawString(left, right, top, buffer, colour, align, underline, fontsize); + return DrawString(left, right, top, GetString(str), colour, align, underline, fontsize); } /** @@ -738,9 +736,7 @@ int GetStringHeight(std::string_view str, int maxw, FontSize fontsize) */ int GetStringHeight(StringID str, int maxw) { - char buffer[DRAW_STRING_BUFFER]; - GetString(buffer, str, lastof(buffer)); - return GetStringHeight(buffer, maxw); + return GetStringHeight(GetString(str), maxw); } /** @@ -751,10 +747,7 @@ int GetStringHeight(StringID str, int maxw) */ int GetStringLineCount(StringID str, int maxw) { - char buffer[DRAW_STRING_BUFFER]; - GetString(buffer, str, lastof(buffer)); - - Layouter layout(buffer, maxw); + Layouter layout(GetString(str), maxw); 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) { - char buffer[DRAW_STRING_BUFFER]; - GetString(buffer, str, lastof(buffer)); - return DrawStringMultiLine(left, right, top, bottom, buffer, colour, align, underline, fontsize); + return DrawStringMultiLine(left, right, top, bottom, GetString(str), colour, align, underline, fontsize); } /** @@ -891,10 +882,7 @@ Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize) */ Dimension GetStringBoundingBox(StringID strid, FontSize start_fontsize) { - char buffer[DRAW_STRING_BUFFER]; - - GetString(buffer, strid, lastof(buffer)); - return GetStringBoundingBox(buffer, start_fontsize); + return GetStringBoundingBox(GetString(strid), start_fontsize); } /** diff --git a/src/gfx_func.h b/src/gfx_func.h index 2b5e2e07a7..0a5034d4fa 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -97,9 +97,6 @@ enum AdjustGUIZoomMode { }; 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); Dimension GetSpriteSize(SpriteID sprid, Point *offset = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI); diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index 4efd2ffa27..6947b7d1e5 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -787,20 +787,20 @@ std::string GenerateAutoNameForVehicleGroup(const Vehicle *v) CargoTypes cargoes = GetVehicleCargoList(v); - char group_name[512]; + StringID str; if (town_from == town_to || town_to == nullptr) { SetDParam(0, town_from->index); SetDParam(1, (cargoes != 0) ? STR_VEHICLE_AUTO_GROUP_CARGO_LIST : STR_EMPTY); SetDParam(2, cargoes); - GetString(group_name, STR_VEHICLE_AUTO_GROUP_LOCAL_ROUTE, lastof(group_name)); + str = STR_VEHICLE_AUTO_GROUP_LOCAL_ROUTE; } else { SetDParam(0, town_from->index); SetDParam(1, town_to->index); SetDParam(2, (cargoes != 0) ? STR_VEHICLE_AUTO_GROUP_CARGO_LIST : STR_EMPTY); 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); } /** diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 61beca23ec..8c82dd2062 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -2596,10 +2596,8 @@ void Industry::RecomputeProductionMultipliers() void Industry::FillCachedName() const { - char buf[256]; auto tmp_params = MakeParameters(this->index); - char *end = GetStringWithArgs(buf, STR_INDUSTRY_NAME, tmp_params, lastof(buf)); - this->cached_name.assign(buf, end); + this->cached_name = GetStringWithArgs(STR_INDUSTRY_NAME, tmp_params); } void ClearAllIndustryCachedNames() diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 379ecaba02..a2d46e138c 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -91,7 +91,7 @@ enum CargoSuffixDisplay { /** Transfer storage of cargo suffix information. */ struct CargoSuffix { CargoSuffixDisplay display; ///< How to display the cargo and text. - char text[512]; ///< Cargo suffix text. + std::string text; ///< Cargo suffix text. }; 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) { - suffix.text[0] = '\0'; + suffix.text.clear(); suffix.display = CSD_CARGO_AMOUNT; if (HasBit(indspec->callback_mask, CBM_IND_CARGO_SUFFIX)) { TileIndex t = (cst != CST_FUND) ? ind->location.tile : INVALID_TILE; - uint16 callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, const_cast(ind), ind_type, t); + uint16_t callback = GetIndustryCallback(CBID_INDUSTRY_CARGO_SUFFIX, 0, (cst << 8) | cargo, const_cast(ind), ind_type, t); if (callback == CALLBACK_FAILED) return; if (indspec->grf_prop.grffile->grf_version < 8) { if (GB(callback, 0, 8) == 0xFF) return; if (callback < 0x400) { 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(); suffix.display = CSD_CARGO_AMOUNT_TEXT; return; @@ -136,14 +136,14 @@ static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, } if (callback < 0x400) { 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(); suffix.display = CSD_CARGO_AMOUNT_TEXT; return; } if (callback >= 0x800 && callback < 0xC00) { 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(); suffix.display = CSD_CARGO_TEXT; return; @@ -210,35 +210,26 @@ static inline void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixTy std::array _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. */ void SortIndustryTypes() { + std::string industry_spec_names[NUM_INDUSTRYTYPES]{}; + /* Add each industry type to the list. */ for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) { _sorted_industry_types[i] = i; + industry_spec_names[i] = GetString(GetIndustrySpec(i)->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 cargostring; - char buf[1024]; - int numcargo = 0; - int firstcargo = -1; + size_t firstcargo = cargolistlen; - for (size_t j = 0; j < cargolistlen; j++) { + size_t j = 0; + for (; j < cargolistlen; j++) { if (cargolist[j] == CT_INVALID) continue; - numcargo++; - if (firstcargo < 0) { - firstcargo = (int)j; - continue; + if (firstcargo == cargolistlen) { + firstcargo = j; + j++; + 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); SetDParamStr(1, cargo_suffix[firstcargo].text); - GetString(buf, prefixstr, lastof(buf)); - cargostring = std::string(buf) + cargostring; + GetString(StringBuilder(cargostring), prefixstr); } else { SetDParam(0, STR_JUST_NOTHING); SetDParamStr(1, ""); - GetString(buf, prefixstr, lastof(buf)); - cargostring = std::string(buf); + GetString(StringBuilder(cargostring), prefixstr); + } + + 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; @@ -1563,7 +1554,7 @@ protected: for (byte j = 0; j < lengthof(i->produced_cargo); j++) { 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(this->industries.SortType())) { diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index 8441ff6738..24ba56ba42 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -518,8 +518,8 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond) pt.y - 2 <= std::max(pta.y, ptb.y) && check_distance()) { - char buf[2048]; - char *buf_end = buf; + std::string buf; + StringBuilder builder(buf); buf[0] = 0; auto add_travel_time = [&](uint32 time) { @@ -527,10 +527,10 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond) if (_settings_time.time_in_minutes) { SetDParam(0, STR_TIMETABLE_MINUTES); 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 { 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) { SetDParam(0, info_link.cargo); 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) { SetDParam(0, info_link.cargo); 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(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); }; @@ -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) { back_time = j->prop.time; 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(1, j->prop.Usage()); 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) { add_extra_info(j->prop); } @@ -580,14 +580,14 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond) if (_ctrl_pressed) { /* Add distance information */ - buf_end = strecat(buf_end, "\n\n", lastof(buf)); + builder += "\n\n"; TileIndex t0 = Station::Get(i->from_id)->xy; TileIndex t1 = Station::Get(i->to_id)->xy; uint dx = Delta(TileX(t0), TileX(t1)); uint dy = Delta(TileY(t0), TileY(t1)); SetDParam(0, DistanceManhattan(t0, t1)); 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); @@ -595,7 +595,7 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond) SetDParam(2, i->from_id); SetDParam(3, i->to_id); 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); return true; } diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 9a09ae87c2..3365118312 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -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; /* Inform the company of the action of one of its clients (controllers). */ - char msg[64]; SetDParam(0, p1); - GetString(msg, STR_COMPANY_NAME, lastof(msg)); + std::string msg = GetString(STR_COMPANY_NAME); /* * bits 31-16: source company diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 7e9177e45e..e800f737f4 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -709,7 +709,7 @@ struct TooltipsWindow : public Window StringID string_id; ///< String to display as tooltip. std::vector params; ///< The string parameters. 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_top; ///< Owner viewport state: top bool delete_next_mouse_loop; ///< Delete window on the next mouse loop @@ -721,7 +721,7 @@ struct TooltipsWindow : public Window CopyOutDParam(this->params, paramcount); this->close_cond = close_tooltip; 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) { this->viewport_virtual_left = parent->viewport->virtual_left; 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) : Window(desc), editbox(max_bytes, max_chars) { - assert(parent != nullptr); - - 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(); + this->editbox.text.Assign(str); if ((flags & QSF_ACCEPT_UNCHANGED) == 0) this->editbox.orig = this->editbox.text.buf; diff --git a/src/network/network.cpp b/src/network/network.cpp index 541137a76e..85ab5a4a9f 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -265,7 +265,6 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, { SetDParamStr(0, name); - char message_src[256]; StringID strid; switch (action) { case NETWORK_ACTION_SERVER_MESSAGE: @@ -294,8 +293,7 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, case NETWORK_ACTION_GIVE_MONEY: { SetDParam(1, data.auxdata >> 16); - GetString(message_src, STR_NETWORK_MESSAGE_MONEY_GIVE_SRC_DESCRIPTION, lastof(message_src)); - SetDParamStr(0, message_src); + SetDParamStr(0, GetString(STR_NETWORK_MESSAGE_MONEY_GIVE_SRC_DESCRIPTION)); extern byte 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; } - char message[1024]; + std::string message; + StringBuilder builder(message); SetDParamStr(1, str); SetDParam(2, data.data); 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 * 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. */ - char *msg_ptr = message + Utf8Encode(message, _current_text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM); - GetString(msg_ptr, strid, lastof(message)); + builder.Utf8Encode(_current_text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM); + GetString(builder, strid); - DEBUG(desync, 1, "msg: %s; %s", debug_date_dumper().HexDate(), message); - IConsolePrintF(colour, "%s", message); - NetworkAddChatMessage(colour, _settings_client.gui.network_chat_timeout, message); + DEBUG(desync, 1, "msg: %s; %s", debug_date_dumper().HexDate(), message.c_str()); + IConsolePrintF(colour, "%s", message.c_str()); + NetworkAddChatMessage(colour, _settings_client.gui.network_chat_timeout, message.c_str()); } /* Calculate the frame-lag of a client */ diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index 418f56c373..b07418595e 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -33,10 +33,6 @@ #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. */ static const uint NETWORK_CHAT_LINE_SPACING = 3; diff --git a/src/network/network_content_gui.cpp b/src/network/network_content_gui.cpp index 0e8326a226..f92302a0b4 100644 --- a/src/network/network_content_gui.cpp +++ b/src/network/network_content_gui.cpp @@ -745,8 +745,7 @@ public: if (!this->selected->dependencies.empty()) { /* List dependencies */ - char buf[DRAW_STRING_BUFFER] = ""; - char *p = buf; + std::string buf; for (auto &cid : this->selected->dependencies) { /* Try to find the dependency */ ConstContentIterator iter = _network_content_client.Begin(); @@ -754,22 +753,23 @@ public: const ContentInfo *ci = *iter; 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; } } - SetDParamStr(0, buf); + SetDParamStr(0, std::move(buf)); tr.top = DrawStringMultiLine(tr, STR_CONTENT_DETAIL_DEPENDENCIES); } if (!this->selected->tags.empty()) { /* List all tags */ - char buf[DRAW_STRING_BUFFER] = ""; - char *p = buf; + std::string buf; 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); } @@ -778,15 +778,15 @@ public: ConstContentVector tree; _network_content_client.ReverseLookupTreeDependency(tree, this->selected); - char buf[DRAW_STRING_BUFFER] = ""; - char *p = buf; + std::string buf; for (const ContentInfo *ci : tree) { 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) { - SetDParamStr(0, buf); + if (!buf.empty()) { + SetDParamStr(0, std::move(buf)); tr.top = DrawStringMultiLine(tr, STR_CONTENT_DETAIL_SELECTED_BECAUSE_OF); } } diff --git a/src/newgrf_commons.cpp b/src/newgrf_commons.cpp index ef816bddf3..03fdc41a6f 100644 --- a/src/newgrf_commons.cpp +++ b/src/newgrf_commons.cpp @@ -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); } - /* debug output */ - char buffer[512]; - SetDParamStr(0, grfconfig->GetName()); - GetString(buffer, STR_NEWGRF_BUGGY, lastof(buffer)); - DEBUG(grf, 0, "%s", buffer + 3); + std::string buffer = GetString(STR_NEWGRF_BUGGY); + DEBUG(grf, 0, "%s", strip_leading_colours(buffer)); SetDParam(1, cbid); SetDParam(2, cb_res); - GetString(buffer, STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT, lastof(buffer)); - DEBUG(grf, 0, "%s", buffer + 3); + buffer = GetString(STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT); + DEBUG(grf, 0, "%s", strip_leading_colours(buffer)); } /** diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp index b87ca94eb1..1e7ee98d82 100644 --- a/src/newgrf_debug_gui.cpp +++ b/src/newgrf_debug_gui.cpp @@ -542,9 +542,8 @@ struct NewGRFInspectWindow : Window { if (this->log_console) { GetFeatureHelper(this->window_number)->SetStringParameters(this->GetFeatureIndex()); - char buf[1024]; - GetString(buf, STR_NEWGRF_INSPECT_CAPTION, lastof(buf)); - DEBUG(misc, 0, "*** %s ***", buf + Utf8EncodedCharLen(buf[0])); + std::string buf = GetString(STR_NEWGRF_INSPECT_CAPTION); + if (!buf.empty()) DEBUG(misc, 0, "*** %s ***", strip_leading_colours(buf)); } uint index = this->GetFeatureIndex(); @@ -807,9 +806,7 @@ struct NewGRFInspectWindow : Window { NOT_REACHED(); } - char buffer[64]; - GetString(buffer, string, lastof(buffer)); - this->DrawString(r, i++, " %02x: %s (%s)", nip->prop, buffer, nip->name); + this->DrawString(r, i++, " %02x: %s (%s)", nip->prop, GetString(string).c_str(), nip->name); } } diff --git a/src/newgrf_text.cpp b/src/newgrf_text.cpp index 225e94f242..8bb6e45993 100644 --- a/src/newgrf_text.cpp +++ b/src/newgrf_text.cpp @@ -896,13 +896,13 @@ void StopTextRefStackUsage() /** * FormatString for NewGRF specific "magic" string control codes * @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 parameters the OpenTTD string formatting parameters * @param modify_parameters When true, modify the OpenTTD string formatting parameters. * @return the string control code to "execute" now */ -uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, StringParameters ¶meters, bool modify_parameters) +uint RemapNewGRFStringControlCode(uint scc, std::string *buffer, const char **str, StringParameters ¶meters, bool modify_parameters) { auto too_many_newgrf_params = [&]() { 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_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(buffer->size(), Utf8Consume(str))); break; case SCC_NEWGRF_PRINT_WORD_CARGO_LONG: case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT: diff --git a/src/newgrf_text.h b/src/newgrf_text.h index d867c5ccc8..4252ff5559 100644 --- a/src/newgrf_text.h +++ b/src/newgrf_text.h @@ -54,7 +54,7 @@ struct TextRefStack *CreateTextRefStackBackup(); void RestoreTextRefStackBackup(struct TextRefStack *backup); class StringParameters; -uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, StringParameters ¶meters, bool modify_parameters); +uint RemapNewGRFStringControlCode(uint scc, std::string *buffer, const char **str, StringParameters ¶meters, bool modify_parameters); /** Mapping of language data between a NewGRF and OpenTTD. */ struct LanguageMap { diff --git a/src/newgrf_townname.cpp b/src/newgrf_townname.cpp index 69324f241b..56744f648e 100644 --- a/src/newgrf_townname.cpp +++ b/src/newgrf_townname.cpp @@ -16,6 +16,7 @@ #include "newgrf_townname.h" #include "core/alloc_func.hpp" #include "string_func.h" +#include "strings_internal.h" #include "table/strings.h" @@ -24,14 +25,14 @@ static std::vector _grf_townnames; static std::vector _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; }); if (found != std::end(_grf_townnames)) return &*found; return nullptr; } -GRFTownName *AddGRFTownName(uint32 grfid) +GRFTownName *AddGRFTownName(uint32_t grfid) { GRFTownName *t = GetGRFTownName(grfid); if (t == nullptr) { @@ -41,41 +42,38 @@ GRFTownName *AddGRFTownName(uint32 grfid) 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; })); } -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); for (const auto &partlist : t->partlists[id]) { byte count = partlist.bitcount; - uint16 maxprob = partlist.maxprob; - uint32 r = (GB(seed, partlist.bitstart, count) * maxprob) >> count; + uint16_t maxprob = partlist.maxprob; + uint32_t r = (GB(seed, partlist.bitstart, count) * maxprob) >> count; for (const auto &part : partlist.parts) { maxprob -= GB(part.prob, 0, 7); if (maxprob > r) continue; if (HasBit(part.prob, 7)) { - buf = RandomPart(buf, t, seed, part.id, last); + RandomPart(builder, t, seed, part.id); } else { - buf = strecat(buf, part.text.c_str(), last); + builder += part.text; } 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); if (t != nullptr) { 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 &GetGRFTownNameList() 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; } @@ -105,21 +103,21 @@ void CleanUpGRFTownNames() _grf_townnames.clear(); } -uint32 GetGRFTownNameId(uint16 gen) +uint32_t GetGRFTownNameId(uint16_t gen) { for (const auto &t : _grf_townnames) { if (gen < t.styles.size()) return t.grfid; - gen -= static_cast(t.styles.size()); + gen -= static_cast(t.styles.size()); } /* Fallback to no NewGRF */ return 0; } -uint16 GetGRFTownNameType(uint16 gen) +uint16_t GetGRFTownNameType(uint16_t gen) { for (const auto &t : _grf_townnames) { if (gen < t.styles.size()) return gen; - gen -= static_cast(t.styles.size()); + gen -= static_cast(t.styles.size()); } /* Fallback to english original */ return SPECSTR_TOWNNAME_ENGLISH; diff --git a/src/newgrf_townname.h b/src/newgrf_townname.h index 69b18b8d91..3fe44f0777 100644 --- a/src/newgrf_townname.h +++ b/src/newgrf_townname.h @@ -25,7 +25,7 @@ struct NamePart { struct NamePartList { byte bitstart; ///< Start of random seed bits 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 parts; ///< List of parts to choose from. }; @@ -39,19 +39,18 @@ struct TownNameStyle { struct GRFTownName { 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 styles; ///< Style names defined by the Town Name NewGRF. std::vector partlists[MAX_LISTS]; ///< Lists of town name parts. }; -GRFTownName *AddGRFTownName(uint32 grfid); -GRFTownName *GetGRFTownName(uint32 grfid); -void DelGRFTownName(uint32 grfid); +GRFTownName *AddGRFTownName(uint32_t grfid); +GRFTownName *GetGRFTownName(uint32_t grfid); +void DelGRFTownName(uint32_t grfid); void CleanUpGRFTownNames(); -char *GRFTownNameGenerate(char *buf, uint32 grfid, uint16 gen, uint32 seed, const char *last); -uint32 GetGRFTownNameId(uint16 gen); -uint16 GetGRFTownNameType(uint16 gen); -StringID GetGRFTownNameName(uint16 gen); +uint32_t GetGRFTownNameId(uint16_t gen); +uint16_t GetGRFTownNameType(uint16_t gen); +StringID GetGRFTownNameName(uint16_t gen); const std::vector &GetGRFTownNameList(); diff --git a/src/openttd.cpp b/src/openttd.cpp index 8cc0670b91..3d6899ff30 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -870,10 +870,11 @@ int openttd_main(int argc, char *argv[]) fprintf(stderr, "Failed to open savegame\n"); if (_load_check_data.HasErrors()) { InitializeLanguagePacks(); // A language pack is needed for GetString() - char buf[256]; + std::string buf; SetDParamStr(0, _load_check_data.error_msg); - GetString(buf, _load_check_data.error, lastof(buf)); - fprintf(stderr, "%s\n", buf); + GetString(StringBuilder(buf), _load_check_data.error); + buf += '\n'; + fputs(buf.c_str(), stderr); } 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: ", (int)u->type, u->index, v->index, (int)u->owner, v->unitnumber, length); 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(); if (grfid) { p += seprintf(p, last, ", GRF: %08X", BSWAP32(grfid)); diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 71cf6d5b15..f131400455 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -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(4, order->GetXData()); } else if (ocv == OCV_CARGO_WAITING_AMOUNT) { - char buf[512] = ""; ArrayStringParameters<10> tmp_params; 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(8, GB(order->GetXData(), 0, 16)); } - char *end = GetStringWithArgs(buf, substr, tmp_params, lastof(buf)); - _temp_special_strings[0].assign(buf, end); + _temp_special_strings[0] = GetStringWithArgs(substr, tmp_params); SetDParam(0, SPECSTR_TEMP_START); } else if (ocv == OCV_COUNTER_VALUE) { 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) { const DispatchSchedule &ds = v->orders->GetDispatchScheduleByIndex(GB(order->GetXData(), 0, 16)); if (ds.ScheduleName().empty()) { - char buf[256]; 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].assign(buf, end); + _temp_special_strings[0] = GetStringWithArgs(STR_TIMETABLE_ASSIGN_SCHEDULE_ID, tmp_params); } else { _temp_special_strings[0] = ds.ScheduleName(); } diff --git a/src/programmable_signals_gui.cpp b/src/programmable_signals_gui.cpp index 93ca60d4a7..f3495f6a5a 100644 --- a/src/programmable_signals_gui.cpp +++ b/src/programmable_signals_gui.cpp @@ -140,7 +140,7 @@ static const StringID _program_sigstate[] = { }; /** 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; 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; - char condstr[512]; - switch (instruction->Opcode()) { case PSO_FIRST: instruction_string = STR_PROGSIG_FIRST; @@ -213,8 +211,7 @@ static void DrawInstructionString(SignalInstruction *instruction, int y, bool se case PSO_IF: { SignalIf *if_ins = static_cast(instruction); - GetConditionString(if_ins->condition, condstr, lastof(condstr)); - SetDParamStr(0, condstr); + SetDParamStr(0, GetConditionString(if_ins->condition)); instruction_string = STR_PROGSIG_IF; break; } diff --git a/src/scope_info.cpp b/src/scope_info.cpp index 5aba07ffa7..efc4a75abd 100644 --- a/src/scope_info.cpp +++ b/src/scope_info.cpp @@ -50,7 +50,7 @@ const char *scope_dumper::CompanyInfo(int company_id) const char *last = lastof(this->buffer); b += seprintf(b, last, "%d (", 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, ")"); return buffer; } @@ -79,7 +79,7 @@ const char *scope_dumper::VehicleInfo(const Vehicle *v) default: SetDParam(0, v->index); - b = GetString(b, STR_VEHICLE_NAME, last); + b = strecpy(b, GetString(STR_VEHICLE_NAME).c_str(), last); break; } if (v->type < VEH_COMPANY_END) { @@ -95,7 +95,7 @@ const char *scope_dumper::VehicleInfo(const Vehicle *v) return this->buffer; } 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, ", "); dump_flags(v->First()); b += seprintf(b, last, ")"); @@ -116,7 +116,7 @@ const char *scope_dumper::StationInfo(const BaseStation *st) const bool waypoint = Waypoint::IsExpected(st); b += seprintf(b, last, "%s: %u: (", waypoint ? "waypoint" : "station", 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); auto dump_facil = [&](char c, StationFacility flag) { if (st->facilities & flag) b += seprintf(b, last, "%c", c); diff --git a/src/screenshot.cpp b/src/screenshot.cpp index 00faa2bcfa..d7e4944dc8 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -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). uint _num_screenshot_formats; ///< Number of available 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. -char _full_screenshot_name[MAX_PATH]; ///< Pathname of the screenshot file. +static std::string _screenshot_name; ///< Filename 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. 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) { - bool generate = StrEmpty(_screenshot_name); + bool generate = _screenshot_name.empty(); if (generate) { 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 { - 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 */ if (len >= 2 && _screenshot_name[len - 2] == '%' && _screenshot_name[len - 1] == 'd') { generate = true; - len -= 2; - _screenshot_name[len] = '\0'; + _screenshot_name.resize(len - 2); } else if (len >= 1 && _screenshot_name[len - 1] == '#') { generate = true; - len -= 1; - _screenshot_name[len] = '\0'; + _screenshot_name.resize(len - 1); } + len = _screenshot_name.size(); + /* 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(); 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)) { - /* We need more characters than MAX_PATH -> end with error */ - _full_screenshot_name[0] = '\0'; - break; - } + _full_screenshot_path = screenshot_dir; + _full_screenshot_path += _screenshot_name; + 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 */ - 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. */ @@ -983,8 +983,7 @@ static bool RealMakeScreenshot(ScreenshotType t, std::string name, uint32 width, SetScreenshotWindowHidden(false); } - _screenshot_name[0] = '\0'; - if (!name.empty()) strecpy(_screenshot_name, name.c_str(), lastof(_screenshot_name)); + _screenshot_name = name; bool ret; switch (t) { @@ -1332,8 +1331,8 @@ static void IndustryScreenCallback(void *userdata, void *buf, uint y, uint pitch */ bool MakeMinimapWorldScreenshot(const char *name) { - _screenshot_name[0] = '\0'; - if (name != nullptr) strecpy(_screenshot_name, name, lastof(_screenshot_name)); + _screenshot_name.clear(); + if (name != nullptr) _screenshot_name.assign(name); const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format; 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) { - _screenshot_name[0] = '\0'; - if (name != nullptr) strecpy(_screenshot_name, name, lastof(_screenshot_name)); + _screenshot_name.clear(); + if (name != nullptr) _screenshot_name.assign(name); const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format; 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) { - _screenshot_name[0] = '\0'; - if (name != nullptr) strecpy(_screenshot_name, name, lastof(_screenshot_name)); + _screenshot_name.clear(); + if (name != nullptr) _screenshot_name.assign(name); const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format; return sf->proc(MakeScreenshotName(SCREENSHOT_NAME, sf->extension), IndustryScreenCallback, nullptr, MapSizeX(), MapSizeY(), 32, _cur_palette.palette); diff --git a/src/screenshot.h b/src/screenshot.h index e3221ce7fc..79b7611452 100644 --- a/src/screenshot.h +++ b/src/screenshot.h @@ -45,6 +45,6 @@ inline void ClearScreenshotAuxiliaryText() { SetScreenshotAuxiliaryText(nullptr, extern std::string _screenshot_format_name; extern uint _num_screenshot_formats; extern uint _cur_screenshot_format; -extern char _full_screenshot_name[MAX_PATH]; +extern std::string _full_screenshot_path; #endif /* SCREENSHOT_H */ diff --git a/src/script/api/script_text.cpp b/src/script/api/script_text.cpp index dbac35ea27..05922e4aa9 100644 --- a/src/script/api/script_text.cpp +++ b/src/script/api/script_text.cpp @@ -136,7 +136,7 @@ SQInteger ScriptText::AddParam(HSQUIRRELVM vm) SQInteger ScriptText::_set(HSQUIRRELVM vm) { - int32 k; + int32_t k; if (sq_gettype(vm, 2) == OT_STRING) { const SQChar *key_string; @@ -149,7 +149,7 @@ SQInteger ScriptText::_set(HSQUIRRELVM vm) } else if (sq_gettype(vm, 2) == OT_INTEGER) { SQInteger key; sq_getinteger(vm, 2, &key); - k = (int32)key; + k = (int32_t)key; } else { return SQ_ERROR; } @@ -161,15 +161,16 @@ SQInteger ScriptText::_set(HSQUIRRELVM vm) return this->_SetParam(k, vm); } -const std::string ScriptText::GetEncodedText() +std::string ScriptText::GetEncodedText() { - static char buf[1024]; static StringIDList seen_ids; int param_count = 0; 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))); - return buf; + return result; } void ScriptText::_TextParamError(std::string msg) @@ -181,28 +182,28 @@ void ScriptText::_TextParamError(std::string msg) } } -char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count, StringIDList &seen_ids) +void ScriptText::_GetEncodedText(std::back_insert_iterator &output, int ¶m_count, StringIDList &seen_ids) { 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)); seen_ids.push_back(this->string); - p += Utf8Encode(p, SCC_ENCODED); - p += seprintf(p, lastofp, "%X", this->string); + Utf8Encode(output, SCC_ENCODED); + fmt::format_to(output, "{:X}", this->string); auto write_param_fallback = [&](int idx) { if (std::holds_alternative(this->param[idx])) { int count = 1; // 1 because the string id is included in consumed parameters - p += seprintf(p, lastofp, ":"); - p = std::get(this->param[idx])->_GetEncodedText(p, lastofp, count, seen_ids); + fmt::format_to(output, ":"); + std::get(this->param[idx])->_GetEncodedText(output, count, seen_ids); param_count += count; } else if (std::holds_alternative(this->param[idx])) { - p += seprintf(p, lastofp, ":" OTTD_PRINTFHEX64, std::get(this->param[idx])); + fmt::format_to(output, ":{:X}", std::get(this->param[idx])); param_count++; } else { /* Fallback value */ - p += seprintf(p, lastofp, ":0"); + fmt::format_to(output, ":0"); param_count++; } }; @@ -227,7 +228,7 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count, Stri write_param_fallback(cur_idx++); break; } - p += seprintf(p, lastofp, ":\"%s\"", std::get(this->param[cur_idx++]).c_str()); + fmt::format_to(output, ":\"{}\"", std::get(this->param[cur_idx++])); param_count++; break; @@ -238,8 +239,8 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count, Stri break; } int count = 1; // 1 because the string id is included in consumed parameters - p += seprintf(p, lastofp, ":"); - p = std::get(this->param[cur_idx++])->_GetEncodedText(p, lastofp, count, seen_ids); + fmt::format_to(output, ":"); + std::get(this->param[cur_idx++])->_GetEncodedText(output, count, seen_ids); 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)); } @@ -257,7 +258,7 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count, Stri write_param_fallback(cur_idx++); continue; } - p += seprintf(p, lastofp, ":" OTTD_PRINTFHEX64, std::get(this->param[cur_idx++])); + fmt::format_to(output, ":{:X}", std::get(this->param[cur_idx++])); param_count++; } break; @@ -267,12 +268,12 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count, Stri /* 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++) { 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 { /* No more extra parameters, assume SQInteger are expected. */ if (cur_idx >= this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name)); if (!std::holds_alternative(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(this->param[cur_idx++])); + fmt::format_to(output, ":{:X}", std::get(this->param[cur_idx++])); } } if (prev_idx == prev_count) { @@ -283,18 +284,18 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count, Stri switch (cur_param.type) { case StringParam::RAW_STRING: if (!std::holds_alternative(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects a raw string", name, param_count)); - p += seprintf(p, lastofp, ":\"%s\"", std::get(this->param[cur_idx++]).c_str()); + fmt::format_to(output, ":\"{}\"", std::get(this->param[cur_idx++])); break; case StringParam::STRING: { if (!std::holds_alternative(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects a substring", name, param_count)); int count = 0; - p += seprintf(p, lastofp, ":"); - p = std::get(this->param[cur_idx++])->_GetEncodedText(p, lastofp, count, seen_ids); + fmt::format_to(output, ":"); + std::get(this->param[cur_idx++])->_GetEncodedText(output, count, seen_ids); 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. */ - 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. */ if (count > cur_param.consumes) { prev_string = param_count; @@ -309,7 +310,7 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count, Stri 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++) { if (!std::holds_alternative(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(this->param[cur_idx++])); + fmt::format_to(output, ":{:X}", std::get(this->param[cur_idx++])); } } } @@ -323,14 +324,10 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count, Stri } seen_ids.pop_back(); - - return p; } const std::string Text::GetDecodedText() { - static char buf[1024]; ::SetDParamStr(0, this->GetEncodedText()); - ::GetString(buf, STR_JUST_RAW_STRING, lastof(buf)); - return buf; + return ::GetString(STR_JUST_RAW_STRING); } diff --git a/src/script/api/script_text.hpp b/src/script/api/script_text.hpp index dfc295c72b..8f91e29b39 100644 --- a/src/script/api/script_text.hpp +++ b/src/script/api/script_text.hpp @@ -26,7 +26,7 @@ public: * @return A string. * @api -all */ - virtual const std::string GetEncodedText() = 0; + virtual std::string GetEncodedText() = 0; /** * Convert a #ScriptText into a decoded normal string. @@ -44,7 +44,7 @@ class RawText : public Text { public: RawText(const std::string &text); - const std::string GetEncodedText() override { return this->text; } + std::string GetEncodedText() override { return this->text; } private: const std::string text; }; @@ -125,7 +125,7 @@ public: /** * @api -all */ - const std::string GetEncodedText() override; + std::string GetEncodedText() override; private: using ScriptTextRef = ScriptObjectRef; @@ -140,13 +140,11 @@ private: /** * Internal function for recursive calling this function over multiple * instances, while writing in the same buffer. - * @param p The current position in the buffer. - * @param lastofp The last position valid in the buffer. + * @param output The output to write the encoded text to. * @param param_count The number of parameters that are in the string. * @param seen_ids The list of seen StringID. - * @return The new current position in the buffer. */ - char *_GetEncodedText(char *p, char *lastofp, int ¶m_count, StringIDList &seen_ids); + void _GetEncodedText(std::back_insert_iterator &output, int ¶m_count, StringIDList &seen_ids); /** * Set a parameter, where the value is the first item on the stack. diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 72e8bae6fe..042e432396 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1082,11 +1082,7 @@ struct SettingEntry : BaseSettingEntry { void SetButtons(byte new_val); StringID GetHelpText() const; - struct SetValueDParamsTempData { - char buffer[512]; - }; - - void SetValueDParams(uint first_param, int32 value, std::unique_ptr &tempdata) const; + void SetValueDParams(uint first_param, int32 value) const; protected: 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 value Setting value to set params for. */ -void SettingEntry::SetValueDParams(uint first_param, int32 value, std::unique_ptr &tempdata) const +void SettingEntry::SetValueDParams(uint first_param, int32 value) const { if (this->setting->IsBoolSetting()) { SetDParam(first_param++, value != 0 ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); } else if (this->setting->flags & SF_DEC1SCALE) { - tempdata.reset(new SettingEntry::SetValueDParamsTempData()); double scale = std::exp2(((double)value) / 10); 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); - 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 { if ((this->setting->flags & SF_ENUM) != 0) { 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 { - std::unique_ptr tempdata; - this->SetValueDParams(1, value, tempdata); + this->SetValueDParams(1, value); 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) { 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); SetDParam(0, CargoSpec::Get(this->cargo)->name); SetDParam(1, STR_CONFIG_SETTING_VALUE); - std::unique_ptr tempdata; - this->SetValueDParams(2, value, tempdata); + this->SetValueDParams(2, value); 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); tr.top += GetCharacterHeight(FS_NORMAL); - std::unique_ptr tempdata; - this->last_clicked->SetValueDParams(0, sd->def, tempdata); + this->last_clicked->SetValueDParams(0, sd->def); DrawString(tr, STR_CONFIG_SETTING_DEFAULT_VALUE); tr.top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; diff --git a/src/ship_gui.cpp b/src/ship_gui.cpp index 4e4896dcb0..871967d00d 100644 --- a/src/ship_gui.cpp +++ b/src/ship_gui.cpp @@ -74,7 +74,6 @@ void DrawShipDetails(const Vehicle *v, const Rect &r) if (v->Next() != nullptr) { CargoArray max_cargo{}; StringID subtype_text[NUM_CARGO]; - char capacity[512]; 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; for (CargoID i = 0; i < NUM_CARGO; i++) { if (max_cargo[i] > 0) { - char buffer[128]; - + if (!first) capacity += ", "; SetDParam(0, i); SetDParam(1, max_cargo[i]); - GetString(buffer, STR_JUST_CARGO, lastof(buffer)); - - if (!first) strecat(capacity, ", ", lastof(capacity)); - strecat(capacity, buffer, lastof(capacity)); + GetString(StringBuilder(capacity), STR_JUST_CARGO); if (subtype_text[i] != 0) { - GetString(buffer, subtype_text[i], lastof(buffer)); - strecat(capacity, buffer, lastof(capacity)); + GetString(StringBuilder(capacity), subtype_text[i]); } first = false; diff --git a/src/sl/extended_ver_sl.cpp b/src/sl/extended_ver_sl.cpp index 26ee1be67c..e48ded96b9 100644 --- a/src/sl/extended_ver_sl.cpp +++ b/src/sl/extended_ver_sl.cpp @@ -631,10 +631,8 @@ static void Load_SLXI() }; 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); - GetStringWithArgs(buf, str, tmp_params, lastof(buf)); - SlError(STR_JUST_RAW_STRING, buf); + SlError(STR_JUST_RAW_STRING, GetStringWithArgs(str, tmp_params)); }; uint32 item_count = SlReadUint32(); diff --git a/src/sl/saveload.cpp b/src/sl/saveload.cpp index 780d9e5d56..bda9dcc8ca 100644 --- a/src/sl/saveload.cpp +++ b/src/sl/saveload.cpp @@ -3147,14 +3147,11 @@ void SetSaveLoadError(StringID str) } /** Get the string representation of the error message */ -const char *GetSaveLoadErrorString() +std::string GetSaveLoadErrorString() { SetDParam(0, _sl.error_str); SetDParamStr(1, _sl.extra_msg); - - 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; + return GetString(_sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED); } /** Show a gui message when saving has failed */ @@ -3198,7 +3195,7 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded) * cancelled due to a client disconnecting. */ if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) { /* Skip the "colour" character */ - DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); + DEBUG(sl, 0, "%s", strip_leading_colours(GetSaveLoadErrorString())); asfp = SaveFileError; } @@ -3644,7 +3641,7 @@ SaveOrLoadResult LoadWithFilter(LoadFilter *reader) ClearSaveLoadState(); /* Skip the "colour" character */ - DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3); + DEBUG(sl, 0, "%s", strip_leading_colours(GetSaveLoadErrorString())); return SL_REINIT; } @@ -3744,7 +3741,7 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, ClearSaveLoadState(); /* 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! */ 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) { - char buf[MAX_PATH]; + std::string filename; if (_settings_client.gui.keep_all_autosave) { - GenerateDefaultSaveName(buf, lastof(buf)); - strecat(buf, counter.Extension().c_str(), lastof(buf)); + filename = GenerateDefaultSaveName() + counter.Extension(); } else { - strecpy(buf, counter.Filename().c_str(), lastof(buf)); + filename = counter.Filename(); if (lt_counter != nullptr && counter.GetLastNumber() == 0) { 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); - FioRenameFile(dir + buf, dir + lt_path); + FioRenameFile(dir + filename, dir + lt_path); } } - DEBUG(sl, 2, "Autosaving to '%s'", buf); - if (SaveOrLoad(buf, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, threaded, SMF_ZSTD_OK) != SL_OK) { + DEBUG(sl, 2, "Autosaving to '%s'", filename.c_str()); + 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); } } @@ -3787,11 +3783,9 @@ void DoExitSave() } /** - * Fill the buffer with the default name for a savegame *or* screenshot. - * @param buf the buffer to write to. - * @param last the last element in the buffer. + * Get the default name for a savegame *or* screenshot. */ -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 * 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); /* 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); - SanitizeFilename(buf); + std::string filename = GetString(!Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT); + SanitizeFilename(filename); + return filename; } /** diff --git a/src/sl/saveload.h b/src/sl/saveload.h index 6ffc089014..ee124ed9e2 100644 --- a/src/sl/saveload.h +++ b/src/sl/saveload.h @@ -66,9 +66,9 @@ DECLARE_ENUM_AS_BIT_SET(SaveModeFlags); extern FileToSaveLoad _file_to_saveload; -void GenerateDefaultSaveName(char *buf, const char *last); +std::string GenerateDefaultSaveName(); 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); void WaitTillSaved(); void ProcessAsyncSaveFinish(); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 8f7c3b523a..8dd470aa55 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -553,10 +553,8 @@ void UpdateAllStationVirtCoords() void BaseStation::FillCachedName() const { - char buf[MAX_LENGTH_STATION_NAME_CHARS * MAX_CHAR_LENGTH]; 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.assign(buf, end); + this->cached_name = GetStringWithArgs(Waypoint::IsExpected(this) ? STR_WAYPOINT_NAME : STR_STATION_NAME, tmp_params); } void ClearAllStationCachedNames() diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 0fcbfac687..c92c680038 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -2774,12 +2774,11 @@ private: const CargoSpec *cs; 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_NEWGRF_INDENT = 20; 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) { @@ -2813,7 +2812,7 @@ public: const GoodsEntry *ge = &this->st->goods[this->cs->Index()]; 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()) { this->data[1][0] = '\0'; @@ -2831,7 +2830,7 @@ public: if (_cheats.station_rating.value) { total_rating = 255; 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++; } 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(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++; const uint last_speed = ge->HasVehicleEverTriedLoading() && ge->IsSupplyAllowed() ? ge->last_speed : 0xFF; SetDParam(0, std::min(last_speed, 0xFFu)); - switch (ge->last_vehicle_type) - { + switch (ge->last_vehicle_type) { case VEH_TRAIN: SetDParam(1, STR_STATION_RATING_TOOLTIP_TRAIN); break; @@ -2869,18 +2867,15 @@ public: SetDParam(1, STR_STATION_RATING_TOOLTIP_INVALID); break; } - - GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_NEWGRF_SPEED, lastof(this->data[line_nr])); + this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_NEWGRF_SPEED); line_nr++; SetDParam(0, std::min(ge->max_waiting_cargo, 0xFFFFu)); - GetString(this->data[line_nr], - STR_STATION_RATING_TOOLTIP_NEWGRF_WAITUNITS, - lastof(this->data[line_nr])); + this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_NEWGRF_WAITUNITS); line_nr++; 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++; } } @@ -2906,8 +2901,7 @@ public: SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY); SetDParam(5, rounded_speed_rating); - switch (ge->last_vehicle_type) - { + switch (ge->last_vehicle_type) { case VEH_TRAIN: SetDParam(6, STR_STATION_RATING_TOOLTIP_TRAIN); break; @@ -2924,8 +2918,7 @@ public: SetDParam(6, STR_STATION_RATING_TOOLTIP_INVALID); break; } - - GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_SPEED, lastof(this->data[line_nr])); + this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_SPEED); line_nr++; 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(5, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY); SetDParam(6, RoundRating(wait_time_rating)); - GetString(this->data[line_nr], - (ge->last_vehicle_type == VEH_SHIP) ? - STR_STATION_RATING_TOOLTIP_WAITTIME_SHIP : - STR_STATION_RATING_TOOLTIP_WAITTIME, - lastof(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); line_nr++; total_rating += wait_time_rating; @@ -2986,9 +2975,7 @@ public: SetDParam(3, ge->max_waiting_cargo); SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY); SetDParam(5, RoundRating(cargo_rating)); - GetString(this->data[line_nr], - STR_STATION_RATING_TOOLTIP_WAITUNITS, - lastof(this->data[line_nr])); + this->data[line_nr] = GetString(STR_STATION_RATING_TOOLTIP_WAITUNITS); line_nr++; 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(3, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY); 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++; total_rating += statue_rating; @@ -3030,7 +3017,7 @@ public: SetDParam(3, ge->last_age); SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY); 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++; total_rating += age_rating; @@ -3041,7 +3028,7 @@ public: if (detailed) { 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++; } @@ -3055,7 +3042,7 @@ public: size->height = WidgetDimensions::scaled.framerect.Vertical() + 2; 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; if (this->newgrf_rating_used && i >= 2 && i <= 4) { @@ -3084,15 +3071,14 @@ public: y += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; 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; if (this->newgrf_rating_used && i >= 2 && i <= 4) { if (_current_text_dir == TD_RTL) { right -= RATING_TOOLTIP_NEWGRF_INDENT; - } - else { + } else { left += RATING_TOOLTIP_NEWGRF_INDENT; } } diff --git a/src/string.cpp b/src/string.cpp index 0ce47d1585..685594f8b4 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -547,6 +547,20 @@ void str_strip_colours(char *str) *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 out; @@ -849,6 +863,11 @@ size_t Utf8Encode(std::ostreambuf_iterator &buf, WChar c) return Utf8Encode &>(buf, c); } +size_t Utf8Encode(std::back_insert_iterator &buf, char32_t c) +{ + return Utf8Encode &>(buf, c); +} + /** * Properly terminate an UTF8 string to some maximum length * @param s string to check if it needs additional trimming diff --git a/src/string_func.h b/src/string_func.h index 66c9972813..8130f6db0e 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -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); 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); char *str_replace_wchar(char *str, const char *last, WChar find, WChar replace); bool strtolower(char *str); @@ -99,25 +107,26 @@ static inline size_t ttd_strnlen(const char *str, size_t maxlen) 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 Utf8Encode(char *buf, WChar c); -size_t Utf8Encode(std::ostreambuf_iterator &buf, WChar c); +size_t Utf8Decode(char32_t *c, const char *s); +size_t Utf8Encode(char *buf, char32_t c); +size_t Utf8Encode(std::ostreambuf_iterator &buf, char32_t c); +size_t Utf8Encode(std::back_insert_iterator &buf, char32_t c); 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); return c; } template -static inline WChar Utf8Consume(Titr &s) +static inline char32_t Utf8Consume(Titr &s) { - WChar c; + char32_t c; s += Utf8Decode(&c, &*s); return c; } @@ -127,7 +136,7 @@ static inline WChar Utf8Consume(Titr &s) * @param c Unicode 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 < 0x800) return 2; @@ -146,7 +155,7 @@ static inline int8 Utf8CharLen(WChar c) * @param c char to query length of * @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, 4, 4) == 0x0E) return 3; @@ -214,7 +223,7 @@ static inline bool Utf16IsTrailSurrogate(uint c) * @param trail Trail surrogate code point. * @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)); } @@ -224,7 +233,7 @@ static inline WChar Utf16DecodeSurrogate(uint lead, uint trail) * @param c Pointer to one or two UTF-16 code points. * @return Decoded Unicode character. */ -static inline WChar Utf16DecodeChar(const uint16 *c) +static inline char32_t Utf16DecodeChar(const uint16_t *c) { if (Utf16IsLeadSurrogate(c[0])) { 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 * the text direction. */ -static inline bool IsTextDirectionChar(WChar c) +static inline bool IsTextDirectionChar(char32_t c) { switch (c) { 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 < 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 * @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 */ } diff --git a/src/strings.cpp b/src/strings.cpp index ca6406c36e..954ddfe716 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -32,6 +32,7 @@ #include "smallmap_gui.h" #include "window_func.h" #include "debug.h" +#include "debug_fmt.h" #include "unit_conversion.h" #include "tracerestrict.h" #include "game/game_text.hpp" @@ -41,6 +42,7 @@ #include "core/backup_type.hpp" #include "core/y_combinator.hpp" #include +#include #include #include @@ -73,6 +75,7 @@ void StringParameters::PrepareForNextRun() this->offset = 0; } + /** * Get the next parameter from our parameters. * This updates the offset, so the next time this is called the next parameter @@ -104,7 +107,7 @@ StringParameter *StringParameters::GetNextParameterPointer() * @param min_count Minimum number of digits independent of \a max. * @param size Font of the number */ -void SetDParamMaxValue(size_t n, uint64 max_value, uint min_count, FontSize size) +void SetDParamMaxValue(size_t n, uint64_t max_value, uint min_count, FontSize size) { uint num_digits = 1; while (max_value >= 10) { @@ -180,11 +183,11 @@ bool HaveDParamChanged(const std::vector &backup) return changed; } -static char *StationGetSpecialString(char *buff, StationFacility x, const char *last); -static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last); -static char *GetSpecialNameString(char *buff, int ind, StringParameters &args, const char *last); +static void StationGetSpecialString(StringBuilder builder, StationFacility x); +static void GetSpecialTownNameString(StringBuilder builder, int ind, uint32_t seed); +static void GetSpecialNameString(StringBuilder builder, int ind, StringParameters &args); -static char *FormatString(char *buff, const char *str, StringParameters &args, const char *last, uint case_index = 0, bool game_script = false, bool dry_run = false); +static void FormatString(StringBuilder builder, const char *str, StringParameters &args, uint case_index = 0, bool game_script = false, bool dry_run = false); struct LanguagePack : public LanguagePackHeader { char data[]; // list of strings @@ -227,17 +230,18 @@ const char *GetStringPtr(StringID string) /** * Get a parsed string with most special stringcodes replaced by the string parameters. - * @param buffr Pointer to a string buffer where the formatted string should be written to. - * @param string - * @param args Arguments for the string. - * @param last Pointer just past the end of \a buffr. + * @param builder The builder of the string. + * @param string The ID of the string to parse. + * @param args Arguments for the string. * @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case. * @param game_script The string is coming directly from a game script. - * @return Pointer to the final zero byte of the formatted string. */ -char *GetStringWithArgs(char *buffr, StringID string, StringParameters &args, const char *last, uint case_index, bool game_script) +void GetStringWithArgs(StringBuilder builder, StringID string, StringParameters &args, uint case_index, bool game_script) { - if (string == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, args, last); + if (string == 0) { + GetStringWithArgs(builder, STR_UNDEFINED, args); + return; + } uint index = GetStringIndex(string); StringTab tab = GetStringTab(string); @@ -245,16 +249,19 @@ char *GetStringWithArgs(char *buffr, StringID string, StringParameters &args, co switch (tab) { case TEXT_TAB_TOWN: if (index >= 0xC0 && !game_script) { - return GetSpecialTownNameString(buffr, index - 0xC0, args.GetNextParameter(), last); + GetSpecialTownNameString(builder, index - 0xC0, args.GetNextParameter()); + return; } break; case TEXT_TAB_SPECIAL: if (index >= 0xE4 && !game_script) { - return GetSpecialNameString(buffr, index - 0xE4, args, last); + GetSpecialNameString(builder, index - 0xE4, args); + return; } if (index < lengthof(_temp_special_strings) && !game_script) { - return FormatString(buffr, _temp_special_strings[index].c_str(), args, last, case_index); + FormatString(builder, _temp_special_strings[index].c_str(), args, case_index); + return; } break; @@ -265,14 +272,18 @@ char *GetStringWithArgs(char *buffr, StringID string, StringParameters &args, co } break; - case TEXT_TAB_GAMESCRIPT_START: - return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true); + case TEXT_TAB_GAMESCRIPT_START: { + FormatString(builder, GetGameStringPtr(index), args, case_index, true); + return; + } case TEXT_TAB_OLD_NEWGRF: NOT_REACHED(); - case TEXT_TAB_NEWGRF_START: - return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index); + case TEXT_TAB_NEWGRF_START: { + FormatString(builder, GetGRFStringPtr(index), args, case_index); + return; + } default: break; @@ -280,31 +291,51 @@ char *GetStringWithArgs(char *buffr, StringID string, StringParameters &args, co if (index >= _langpack.langtab_num[tab]) { if (game_script) { - return GetStringWithArgs(buffr, STR_UNDEFINED, args, last); + return GetStringWithArgs(builder, STR_UNDEFINED, args); } error("String 0x%X is invalid. You are probably using an old version of the .lng file.\n", string); } - return FormatString(buffr, GetStringPtr(string), args, last, case_index); + FormatString(builder, GetStringPtr(string), args, case_index); } -char *GetString(char *buffr, StringID string, const char *last) + +/** + * Resolve the given StringID into a std::string with all the associated + * DParam lookups and formatting. + * @param string The unique identifier of the translatable string. + * @return The std::string of the translated string. + */ +std::string GetString(StringID string) { _global_string_params.PrepareForNextRun(); - return GetStringWithArgs(buffr, string, _global_string_params, last); + return GetStringWithArgs(string, _global_string_params); } /** * Resolve the given StringID into a std::string with all the associated * DParam lookups and formatting. + * @param builder the string builder to write to * @param string The unique identifier of the translatable string. - * @return The std::string of the translated string. */ -std::string GetString(StringID string) +void GetString(StringBuilder builder, StringID string) { - char buffer[DRAW_STRING_BUFFER]; - char *last = GetString(buffer, string, lastof(buffer)); - return { buffer, last }; + _global_string_params.PrepareForNextRun(); + GetStringWithArgs(builder, string, _global_string_params); +} + +/** + * Get a parsed string with most special stringcodes replaced by the string parameters. + * @param string The ID of the string to parse. + * @param args Arguments for the string. + * @return The parsed string. + */ +std::string GetStringWithArgs(StringID string, StringParameters &args) +{ + std::string result; + StringBuilder builder(result); + GetStringWithArgs(builder, string, args); + return result; } /** @@ -331,74 +362,69 @@ void SetDParamStr(size_t n, std::string str) /** * Format a number into a string. - * @param buff the buffer to write to + * @param builder the string builder to write to * @param number the number to write down * @param last the last element in the buffer * @param separator the thousands-separator to use * @param zerofill minimum number of digits to print for the integer part. The number will be filled with zeros at the front if necessary. * @param fractional_digits number of fractional digits to display after a decimal separator. The decimal separator is inserted * in front of the \a fractional_digits last digit of \a number. - * @return till where we wrote */ -static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0) +static void FormatNumber(StringBuilder builder, int64_t number, const char *separator, int zerofill = 1, int fractional_digits = 0) { static const int max_digits = 20; - uint64 divisor = 10000000000000000000ULL; + uint64_t divisor = 10000000000000000000ULL; zerofill += fractional_digits; int thousands_offset = (max_digits - fractional_digits - 1) % 3; if (number < 0) { - if (buff != last) *buff++ = '-'; + builder += '-'; number = -number; } - uint64 num = number; - uint64 tot = 0; + uint64_t num = number; + uint64_t tot = 0; for (int i = 0; i < max_digits; i++) { if (i == max_digits - fractional_digits) { const char *decimal_separator = _settings_game.locale.digit_decimal_separator.c_str(); if (StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator; - buff = strecpy(buff, decimal_separator, last); + builder += decimal_separator; } - uint64 quot = 0; + uint64_t quot = 0; if (num >= divisor) { quot = num / divisor; num = num % divisor; } if ((tot |= quot) || i >= max_digits - zerofill) { - if (buff != last) *buff++ = '0' + quot; // quot is a single digit - if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last); + builder += '0' + quot; // quot is a single digit + if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) builder += separator; } divisor /= 10; } - - *buff = '\0'; - - return buff; } -static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0) +static void FormatCommaNumber(StringBuilder builder, int64_t number, int fractional_digits = 0) { const char *separator = _settings_game.locale.digit_group_separator.c_str(); if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator; - return FormatNumber(buff, number, last, separator, 1, fractional_digits); + FormatNumber(builder, number, separator, 1, fractional_digits); } -static char *FormatNoCommaNumber(char *buff, int64 number, const char *last) +static void FormatNoCommaNumber(StringBuilder builder, int64_t number) { - return FormatNumber(buff, number, last, ""); + FormatNumber(builder, number, ""); } -static char *FormatZerofillNumber(char *buff, int64 number, int count, const char *last) +static void FormatZerofillNumber(StringBuilder builder, int64_t number, int count) { - return FormatNumber(buff, number, last, "", count); + FormatNumber(builder, number, "", count); } -static char *FormatHexNumber(char *buff, uint64 number, const char *last) +static void FormatHexNumber(StringBuilder builder, uint64_t number) { - return buff + seprintf(buff, last, "0x" OTTD_PRINTFHEX64, number); + fmt::format_to(builder, "0x{:X}", number); } WChar GetDecimalSeparatorChar() @@ -412,12 +438,10 @@ WChar GetDecimalSeparatorChar() /** * Format a given number as a number of bytes with the SI prefix. - * @param buff the buffer to write to - * @param number the number of bytes to write down - * @param last the last element in the buffer - * @return till where we wrote + * @param builder the string builder to write to + * @param number the number of bytes to write down */ -static char *FormatBytes(char *buff, int64 number, const char *last) +static void FormatBytes(StringBuilder builder, int64_t number) { assert(number >= 0); @@ -434,23 +458,21 @@ static char *FormatBytes(char *buff, int64 number, const char *last) if (number < 1024) { id = 0; - buff += seprintf(buff, last, "%i", (int)number); + fmt::format_to(builder, "{}", number); } else if (number < 1024 * 10) { - buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024); + fmt::format_to(builder, "{}{}{:02}", number / 1024, decimal_separator, (number % 1024) * 100 / 1024); } else if (number < 1024 * 100) { - buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024); + fmt::format_to(builder, "{}{}{:01}", number / 1024, decimal_separator, (number % 1024) * 10 / 1024); } else { assert(number < 1024 * 1024); - buff += seprintf(buff, last, "%i", (int)number / 1024); + fmt::format_to(builder, "{}", number / 1024); } assert(id < lengthof(iec_prefixes)); - buff += seprintf(buff, last, NBSP "%sB", iec_prefixes[id]); - - return buff; + fmt::format_to(builder, NBSP "{}B", iec_prefixes[id]); } -static char *FormatWallClockString(char *buff, DateTicksScaled ticks, const char *last, bool show_date, uint case_index) +static void FormatWallClockString(StringBuilder builder, DateTicksScaled ticks, bool show_date, uint case_index) { TickMinutes minutes = _settings_time.ToTickMinutes(ticks); char hour[3], minute[3]; @@ -458,7 +480,7 @@ static char *FormatWallClockString(char *buff, DateTicksScaled ticks, const char seprintf(minute, lastof(minute), "%02i", minutes.ClockMinute()); if (show_date) { Date date = ScaledDateTicksToDate(ticks); - int64 final_arg; + int64_t final_arg; if (_settings_client.gui.date_with_time == 1) { YearMonthDay ymd = ConvertDateToYMD(date); final_arg = ymd.year; @@ -466,48 +488,48 @@ static char *FormatWallClockString(char *buff, DateTicksScaled ticks, const char final_arg = date.base(); } auto tmp_params = MakeParameters(hour, minute, final_arg); - return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_MINUTES + _settings_client.gui.date_with_time), tmp_params, last, case_index); + FormatString(builder, GetStringPtr(STR_FORMAT_DATE_MINUTES + _settings_client.gui.date_with_time), tmp_params, case_index); } else { auto tmp_params = MakeParameters(hour, minute); - return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_MINUTES), tmp_params, last, case_index); + FormatString(builder, GetStringPtr(STR_FORMAT_DATE_MINUTES), tmp_params, case_index); } } -static char *FormatTimeHHMMString(char *buff, uint time, const char *last, uint case_index) +static void FormatTimeHHMMString(StringBuilder builder, uint time, uint case_index) { char hour[9], minute[3]; seprintf(hour, lastof(hour), "%02i", (int) time / 100); seprintf(minute, lastof(minute), "%02i", (int) time % 100); auto tmp_params = MakeParameters(hour, minute); - return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_MINUTES), tmp_params, last, case_index); + return FormatString(builder, GetStringPtr(STR_FORMAT_DATE_MINUTES), tmp_params, case_index); } -static char *FormatYmdString(char *buff, Date date, const char *last, uint case_index) +static void FormatYmdString(StringBuilder builder, Date date, uint case_index) { YearMonthDay ymd = ConvertDateToYMD(date); auto tmp_params = MakeParameters(ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year); - return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), tmp_params, last, case_index); + FormatString(builder, GetStringPtr(STR_FORMAT_DATE_LONG), tmp_params, case_index); } -static char *FormatMonthAndYear(char *buff, Date date, const char *last, uint case_index) +static void FormatMonthAndYear(StringBuilder builder, Date date, uint case_index) { YearMonthDay ymd = ConvertDateToYMD(date); auto tmp_params = MakeParameters(STR_MONTH_JAN + ymd.month, ymd.year); - return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), tmp_params, last, case_index); + FormatString(builder, GetStringPtr(STR_FORMAT_DATE_SHORT), tmp_params, case_index); } -static char *FormatTinyOrISODate(char *buff, Date date, StringID str, const char *last) +static void FormatTinyOrISODate(StringBuilder builder, Date date, StringID str) { YearMonthDay ymd = ConvertDateToYMD(date); /* Day and month are zero-padded with ZEROFILL_NUM, hence the two 2s. */ auto tmp_params = MakeParameters(ymd.day, 2, ymd.month + 1, 2, ymd.year); - return FormatString(buff, GetStringPtr(str), tmp_params, last); + FormatString(builder, GetStringPtr(str), tmp_params); } -static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last) +static void FormatGenericCurrency(StringBuilder builder, const CurrencySpec *spec, Money number, bool compact) { /* We are going to make number absolute for printing, so * keep this piece of data as we need it later on */ @@ -518,18 +540,16 @@ static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money n /* convert from negative */ if (number < 0) { - if (buff + Utf8CharLen(SCC_PUSH_COLOUR) > last) return buff; - buff += Utf8Encode(buff, SCC_PUSH_COLOUR); - if (buff + Utf8CharLen(SCC_RED) > last) return buff; - buff += Utf8Encode(buff, SCC_RED); - buff = strecpy(buff, "-", last); + builder.Utf8Encode(SCC_PUSH_COLOUR); + builder.Utf8Encode(SCC_RED); + builder += '-'; number = -number; } /* Add prefix part, following symbol_pos specification. * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix). * The only remaining value is 1 (suffix), so everything that is not 1 */ - if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix.c_str(), last); + if (spec->symbol_pos != 1) builder += spec->prefix; /* for huge numbers, compact the number into k or M */ if (compact) { @@ -547,21 +567,17 @@ static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money n const char *separator = _settings_game.locale.digit_group_separator_currency.c_str(); if (StrEmpty(separator)) separator = _currency->separator.c_str(); if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator_currency; - buff = FormatNumber(buff, number, last, separator); - buff = strecpy(buff, multiplier, last); + FormatNumber(builder, number, separator); + builder += multiplier; /* Add suffix part, following symbol_pos specification. * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix). * The only remaining value is 1 (prefix), so everything that is not 0 */ - if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix.c_str(), last); + if (spec->symbol_pos != 0) builder += spec->suffix; if (negative) { - if (buff + Utf8CharLen(SCC_POP_COLOUR) > last) return buff; - buff += Utf8Encode(buff, SCC_POP_COLOUR); - *buff = '\0'; + builder.Utf8Encode(SCC_POP_COLOUR); } - - return buff; } /** @@ -570,10 +586,10 @@ static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money n * @param plural_form The plural form we want an index for. * @return The plural index for the given form. */ -static int DeterminePluralForm(int64 count, int plural_form) +static int DeterminePluralForm(int64_t count, int plural_form) { /* The absolute value determines plurality */ - uint64 n = abs(count); + uint64_t n = abs(count); switch (plural_form) { default: @@ -691,7 +707,7 @@ static int DeterminePluralForm(int64 count, int plural_form) } } -static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last) +static const char *ParseStringChoice(const char *b, uint form, StringBuilder builder) { /* {Length of each string} {each string} */ uint n = (byte)*b++; @@ -703,7 +719,7 @@ static const char *ParseStringChoice(const char *b, uint form, char **dst, const pos += len; } - *dst += seprintf(*dst, last, "%s", b + mypos); + builder += b + mypos; return b + pos; } @@ -717,7 +733,7 @@ struct UnitConversion { * @param round Whether to round the value or not. * @return The converted value. */ - int64 ToDisplay(int64 input, bool round = true) const + int64_t ToDisplay(int64_t input, bool round = true) const { return round ? (int64_t)std::round(input * this->factor) @@ -731,7 +747,7 @@ struct UnitConversion { * @param divider Divide the return value by this. * @return The converted value. */ - int64 FromDisplay(int64 input, bool round = true, int64 divider = 1) const + int64_t FromDisplay(int64_t input, bool round = true, int64_t divider = 1) const { return round ? (int64_t)std::round(input / this->factor / divider) @@ -925,7 +941,7 @@ uint ConvertDisplayPowerToPower(uint power) * @param force the force to convert * @return the converted force. */ -int64 ConvertForceToDisplayForce(int64 force) +int64_t ConvertForceToDisplayForce(int64_t force) { return _units_force[_settings_game.locale.units_force].c.ToDisplay(force); } @@ -935,14 +951,14 @@ int64 ConvertForceToDisplayForce(int64 force) * @param force the force to convert * @return the converted force. */ -int64 ConvertDisplayForceToForce(int64 force) +int64_t ConvertDisplayForceToForce(int64_t force) { return _units_force[_settings_game.locale.units_force].c.FromDisplay(force); } -static void ConvertWeightRatioToDisplay(const Units &unit, int64 ratio, int64 &value, int64 &decimals) +static void ConvertWeightRatioToDisplay(const Units &unit, int64_t ratio, int64_t &value, int64_t &decimals) { - int64 input = ratio; + int64_t input = ratio; decimals = 2; if (_settings_game.locale.units_weight == 2) { input *= 1000; @@ -966,12 +982,12 @@ static uint ConvertDisplayToWeightRatio(const Units &unit, double in) const UnitConversion &weight_conv = _units_weight[_settings_game.locale.units_weight].c; UnitConversion conv = unit.c; conv.factor /= weight_conv.factor; - int64 multiplier = _settings_game.locale.units_weight == 2 ? 1000 : 1; + int64_t multiplier = _settings_game.locale.units_weight == 2 ? 1000 : 1; return conv.FromDisplay(in * 100 * multiplier, true, multiplier); } -static char *FormatUnitWeightRatio(char *buff, const char *last, const Units &unit, int64 raw_value) +static void FormatUnitWeightRatio(StringBuilder builder, const Units &unit, int64_t raw_value) { const char *unit_str = GetStringPtr(unit.s); const char *weight_str = GetStringPtr(_units_weight[_settings_game.locale.units_weight].s); @@ -982,12 +998,11 @@ static char *FormatUnitWeightRatio(char *buff, const char *last, const Units &un str_replace_wchar(insert_pt, lastof(tmp_buffer), SCC_DECIMAL, '/'); str_replace_wchar(insert_pt, lastof(tmp_buffer), 0xA0 /* NBSP */, 0); - int64 value, decimals; + int64_t value, decimals; ConvertWeightRatioToDisplay(unit, raw_value, value, decimals); auto tmp_params = MakeParameters(value, decimals); - buff = FormatString(buff, tmp_buffer, tmp_params, last); - return buff; + FormatString(builder, tmp_buffer, tmp_params); } /** @@ -996,7 +1011,7 @@ static char *FormatUnitWeightRatio(char *buff, const char *last, const Units &un * @param value the output value * @param decimals the output decimal offset */ -void ConvertPowerWeightRatioToDisplay(int64 ratio, int64 &value, int64 &decimals) +void ConvertPowerWeightRatioToDisplay(int64_t ratio, int64_t &value, int64_t &decimals) { ConvertWeightRatioToDisplay(_units_power[_settings_game.locale.units_power], ratio, value, decimals); } @@ -1007,7 +1022,7 @@ void ConvertPowerWeightRatioToDisplay(int64 ratio, int64 &value, int64 &decimals * @param value the output value * @param decimals the output decimal offset */ -void ConvertForceWeightRatioToDisplay(int64 ratio, int64 &value, int64 &decimals) +void ConvertForceWeightRatioToDisplay(int64_t ratio, int64_t &value, int64_t &decimals) { ConvertWeightRatioToDisplay(_units_force[_settings_game.locale.units_force], ratio, value, decimals); } @@ -1064,18 +1079,26 @@ uint ConvertDisplayQuantityToCargoQuantity(CargoID cargo, uint quantity) /** * Parse most format codes within a string and write the result to a buffer. - * @param buff The buffer to write the final string to. + * @param builder The string builder to write the final string to. * @param str_arg The original string with format codes. * @param args Pointer to extra arguments used by various string codes. - * @param last Pointer to just past the end of the buff array. - * @param dry_run True when the argt array is not yet initialized. + * @param dry_run True when the args' type data is not yet initialized. */ -static char *FormatString(char *buff, const char *str_arg, StringParameters &args, const char *last, uint case_index, bool game_script, bool dry_run) +static void FormatString(StringBuilder builder, const char *str_arg, StringParameters &args, uint case_index, bool game_script, bool dry_run) { size_t orig_offset = args.GetOffset(); - /* When there is no array with types there is no need to do a dry run. */ if (!dry_run) { + /* + * This function is normally called with `dry_run` false, then we call this function again + * with `dry_run` being true. The dry run is required for the gender formatting. For the + * gender determination we need to format a sub string to get the gender, but for that we + * need to know as what string control code type the specific parameter is encoded. Since + * gendered words can be before the "parameter" words, this needs to be determined before + * the actual formatting. + */ + std::string buffer; + StringBuilder dry_run_builder(buffer); if (UsingNewGRFTextStack()) { /* Values from the NewGRF text stack are only copied to the normal * argv array at the time they are encountered. That means that if @@ -1084,17 +1107,16 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg * pass makes sure the argv array is correctly filled and the second * pass can reference later values without problems. */ struct TextRefStack *backup = CreateTextRefStackBackup(); - FormatString(buff, str_arg, args, last, case_index, game_script, true); + FormatString(dry_run_builder, str_arg, args, case_index, game_script, true); RestoreTextRefStackBackup(backup); } else { - FormatString(buff, str_arg, args, last, case_index, game_script, true); + FormatString(dry_run_builder, str_arg, args, case_index, game_script, true); } /* We have to restore the original offset here to to read the correct values. */ args.SetOffset(orig_offset); } - WChar b = '\0'; + char32_t b = '\0'; uint next_substr_case_index = 0; - char *buf_start = buff; std::stack> str_stack; str_stack.push(str_arg); @@ -1109,12 +1131,12 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) { /* We need to pass some stuff as it might be modified. */ StringParameters remaining = args.GetRemainingParameters(); - b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, remaining, dry_run); + b = RemapNewGRFStringControlCode(b, builder.GetTargetString(), &str, remaining, dry_run); if (b == 0) continue; } if (b < SCC_CONTROL_START || b > SCC_CONTROL_END) { - if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b); + builder.Utf8Encode(b); continue; } @@ -1124,23 +1146,23 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg ArrayStringParameters<20> sub_args; char *p; - uint32 stringid = std::strtoul(str, &p, 16); + uint32_t stringid = std::strtoul(str, &p, 16); if (*p != ':' && *p != '\0') { while (*p != '\0') p++; str = p; - buff = strecpy(buff, "(invalid SCC_ENCODED)", last); + builder += "(invalid SCC_ENCODED)"; break; } if (stringid >= TAB_SIZE_GAMESCRIPT) { while (*p != '\0') p++; str = p; - buff = strecpy(buff, "(invalid StringID)", last); + builder += "(invalid StringID)"; break; } int i = 0; while (*p != '\0' && i < 20) { - uint64 param; + uint64_t param; const char *s = ++p; /* Find the next value */ @@ -1171,7 +1193,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (*s != '"') { /* Check if we want to look up another string */ - WChar l; + char32_t l; size_t len = Utf8Decode(&l, s); bool lookup = (l == SCC_ENCODED); if (lookup) s += len; @@ -1182,7 +1204,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (param >= TAB_SIZE_GAMESCRIPT) { while (*p != '\0') p++; str = p; - buff = strecpy(buff, "(invalid sub-StringID)", last); + builder += "(invalid sub-StringID)"; break; } param = MakeStringID(TEXT_TAB_GAMESCRIPT_START, param); @@ -1197,7 +1219,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg /* If we didn't error out, we can actually print the string. */ if (*str != '\0') { str = p; - buff = GetStringWithArgs(buff, MakeStringID(TEXT_TAB_GAMESCRIPT_START, stringid), sub_args, last, true); + GetStringWithArgs(builder, MakeStringID(TEXT_TAB_GAMESCRIPT_START, stringid), sub_args, true); } break; } @@ -1229,22 +1251,22 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg char *p = input + Utf8Encode(input, args.GetTypeAtOffset(offset)); *p = '\0'; - /* Now do the string formatting. */ - char buf[256]; + /* The gender is stored at the start of the formatted string. */ bool old_sgd = _scan_for_gender_data; _scan_for_gender_data = true; + std::string buffer; + StringBuilder tmp_builder(buffer); StringParameters tmp_params = args.GetRemainingParameters(offset); - p = FormatString(buf, input, tmp_params, lastof(buf)); + FormatString(tmp_builder, input, tmp_params); _scan_for_gender_data = old_sgd; - *p = '\0'; /* And determine the string. */ - const char *s = buf; - WChar c = Utf8Consume(&s); + const char *s = buffer.c_str(); + char32_t c = Utf8Consume(&s); /* Does this string have a gender, if so, set it */ if (c == SCC_GENDER_INDEX) gender = (byte)s[0]; } - str = ParseStringChoice(str, gender, &buff, last); + str = ParseStringChoice(str, gender, builder); break; } @@ -1252,8 +1274,8 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */ case SCC_GENDER_INDEX: // {GENDER 0} if (_scan_for_gender_data) { - buff += Utf8Encode(buff, SCC_GENDER_INDEX); - *buff++ = *str++; + builder.Utf8Encode(SCC_GENDER_INDEX); + builder += *str++; } else { str++; } @@ -1262,8 +1284,8 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg case SCC_PLURAL_LIST: { // {P} int plural_form = *str++; // contains the plural form for this string size_t offset = orig_offset + (byte)*str++; - int64 v = args.GetParam(offset); // contains the number that determines plural - str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last); + int64_t v = args.GetParam(offset); // contains the number that determines plural + str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), builder); break; } @@ -1297,17 +1319,17 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg } case SCC_REVISION: // {REV} - buff = strecpy(buff, _openttd_revision, last); + builder += _openttd_revision; break; case SCC_RAW_STRING_POINTER: { // {RAW_STRING} const char *raw_string = args.GetNextParameterString(); /* raw_string can be nullptr. */ if (raw_string == nullptr) { - buff = strecpy(buff, "(invalid RAW_STRING parameter)", last); + builder += "(invalid RAW_STRING parameter)"; break; } - buff = FormatString(buff, raw_string, args, last); + FormatString(builder, raw_string, args); break; } @@ -1316,7 +1338,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (game_script && GetStringTab(string_id) != TEXT_TAB_GAMESCRIPT_START) break; /* It's prohibited for the included string to consume any arguments. */ StringParameters tmp_params(args, game_script ? args.GetDataLeft() : 0); - buff = GetStringWithArgs(buff, string_id, tmp_params, last, next_substr_case_index, game_script); + GetStringWithArgs(builder, string_id, tmp_params, next_substr_case_index, game_script); next_substr_case_index = 0; break; } @@ -1333,11 +1355,11 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg StringID string_id = args.GetNextParameter(); if (game_script && GetStringTab(string_id) != TEXT_TAB_GAMESCRIPT_START) break; uint size = b - SCC_STRING1 + 1; - if (size > args.GetDataLeft()) { - buff = strecpy(buff, "(too many parameters)", last); + if (game_script && size > args.GetDataLeft()) { + builder += "(too many parameters)"; } else { StringParameters sub_args(args, game_script ? args.GetDataLeft() : size); - buff = GetStringWithArgs(buff, string_id, sub_args, last, next_substr_case_index, game_script); + GetStringWithArgs(builder, string_id, sub_args, next_substr_case_index, game_script); args.AdvanceOffset(size); } next_substr_case_index = 0; @@ -1345,47 +1367,47 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg } case SCC_COMMA: // {COMMA} - buff = FormatCommaNumber(buff, args.GetNextParameter(), last); + FormatCommaNumber(builder, args.GetNextParameter()); break; - case SCC_DECIMAL: {// {DECIMAL} - int64 number = args.GetNextParameter(); + case SCC_DECIMAL: { // {DECIMAL} + int64_t number = args.GetNextParameter(); int digits = args.GetNextParameter(); - buff = FormatCommaNumber(buff, number, last, digits); + FormatCommaNumber(builder, number, digits); break; } case SCC_DECIMAL1: {// {DECIMAL1} - int64 number = args.GetNextParameter(); - buff = FormatCommaNumber(buff, number, last, 1); + int64_t number = args.GetNextParameter(); + FormatCommaNumber(builder, number, 1); break; } case SCC_NUM: // {NUM} - buff = FormatNoCommaNumber(buff, args.GetNextParameter(), last); + FormatNoCommaNumber(builder, args.GetNextParameter()); break; case SCC_PLUS_NUM: { // {PLUS_NUM} - int64 num = args.GetNextParameter(); - if (num > 0) { - buff += seprintf(buff, last, "+"); + int64_t number = args.GetNextParameter(); + if (number > 0) { + builder += '+'; } - buff = FormatNoCommaNumber(buff, num, last); + FormatNoCommaNumber(builder, number); break; } case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM} - int64 num = args.GetNextParameter(); - buff = FormatZerofillNumber(buff, num, args.GetNextParameter(), last); + int64_t num = args.GetNextParameter(); + FormatZerofillNumber(builder, num, args.GetNextParameter()); break; } case SCC_HEX: // {HEX} - buff = FormatHexNumber(buff, args.GetNextParameter(), last); + FormatHexNumber(builder, args.GetNextParameter()); break; case SCC_BYTES: // {BYTES} - buff = FormatBytes(buff, args.GetNextParameter(), last); + FormatBytes(builder, args.GetNextParameter()); break; case SCC_CARGO_TINY: { // {CARGO_TINY} @@ -1396,23 +1418,23 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (cargo >= CargoSpec::GetArraySize()) break; StringID cargo_str = CargoSpec::Get(cargo)->units_volume; - int64 amount = 0; + int64_t amount = 0; switch (cargo_str) { case STR_TONS: - amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args.GetNextParameter()); + amount = _units_weight[_settings_game.locale.units_weight].c.ToDisplay(args.GetNextParameter()); break; case STR_LITERS: - amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args.GetNextParameter()); + amount = _units_volume[_settings_game.locale.units_volume].c.ToDisplay(args.GetNextParameter()); break; default: { - amount = args.GetNextParameter(); + amount = args.GetNextParameter(); break; } } - buff = FormatCommaNumber(buff, amount, last); + FormatCommaNumber(builder, amount); break; } @@ -1428,22 +1450,22 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg case STR_TONS: { assert(_settings_game.locale.units_weight < lengthof(_units_weight)); const auto &x = _units_weight[_settings_game.locale.units_weight]; - auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); - buff = FormatString(buff, GetStringPtr(x.l), tmp_params, last); + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.l), tmp_params); break; } case STR_LITERS: { assert(_settings_game.locale.units_volume < lengthof(_units_volume)); const auto &x = _units_volume[_settings_game.locale.units_volume]; - auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); - buff = FormatString(buff, GetStringPtr(x.l), tmp_params, last); + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.l), tmp_params); break; } default: { - auto tmp_params = MakeParameters(args.GetNextParameter()); - buff = GetStringWithArgs(buff, cargo_str, tmp_params, last); + auto tmp_params = MakeParameters(args.GetNextParameter()); + GetStringWithArgs(builder, cargo_str, tmp_params); break; } } @@ -1456,8 +1478,8 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break; StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier; - auto tmp_args = MakeParameters(args.GetNextParameter()); - buff = GetStringWithArgs(buff, cargo_str, tmp_args, last); + auto tmp_args = MakeParameters(args.GetNextParameter()); + GetStringWithArgs(builder, cargo_str, tmp_args); break; } @@ -1468,122 +1490,115 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg for (const auto &cs : _sorted_cargo_specs) { if (!HasBit(cmask, cs->Index())) continue; - if (buff >= last - 2) break; // ',' and ' ' - if (first) { first = false; } else { /* Add a comma if this is not the first item */ - *buff++ = ','; - *buff++ = ' '; + builder += ", "; } - buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script); + GetStringWithArgs(builder, cs->name, args, next_substr_case_index, game_script); } /* If first is still true then no cargo is accepted */ - if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script); + if (first) GetStringWithArgs(builder, STR_JUST_NOTHING, args, next_substr_case_index, game_script); - *buff = '\0'; next_substr_case_index = 0; - - /* Make sure we detect any buffer overflow */ - assert(buff < last); break; } case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT} - buff = FormatGenericCurrency(buff, _currency, args.GetNextParameter(), true, last); + FormatGenericCurrency(builder, _currency, args.GetNextParameter(), true); break; case SCC_CURRENCY_LONG: // {CURRENCY_LONG} - buff = FormatGenericCurrency(buff, _currency, args.GetNextParameter(), false, last); + FormatGenericCurrency(builder, _currency, args.GetNextParameter(), false); break; case SCC_DATE_TINY: // {DATE_TINY} - buff = FormatTinyOrISODate(buff, args.GetNextParameter(), STR_FORMAT_DATE_TINY, last); + FormatTinyOrISODate(builder, args.GetNextParameter(), STR_FORMAT_DATE_TINY); break; case SCC_DATE_SHORT: // {DATE_SHORT} - buff = FormatMonthAndYear(buff, args.GetNextParameter(), last, next_substr_case_index); + FormatMonthAndYear(builder, args.GetNextParameter(), next_substr_case_index); next_substr_case_index = 0; break; case SCC_DATE_LONG: // {DATE_LONG} - buff = FormatYmdString(buff, args.GetNextParameter(), last, next_substr_case_index); + FormatYmdString(builder, args.GetNextParameter(), next_substr_case_index); next_substr_case_index = 0; break; case SCC_DATE_WALLCLOCK_LONG: { // {DATE_WALLCLOCK_LONG} if (_settings_time.time_in_minutes) { - buff = FormatWallClockString(buff, args.GetNextParameter(), last, _settings_client.gui.date_with_time, next_substr_case_index); + FormatWallClockString(builder, args.GetNextParameter(), _settings_client.gui.date_with_time, next_substr_case_index); } else { - buff = FormatYmdString(buff, ScaledDateTicksToDate(args.GetNextParameter()), last, next_substr_case_index); + FormatYmdString(builder, ScaledDateTicksToDate(args.GetNextParameter()), next_substr_case_index); } break; } case SCC_DATE_WALLCLOCK_SHORT: { // {DATE_WALLCLOCK_SHORT} if (_settings_time.time_in_minutes) { - buff = FormatWallClockString(buff, args.GetNextParameter(), last, _settings_client.gui.date_with_time, next_substr_case_index); + FormatWallClockString(builder, args.GetNextParameter(), _settings_client.gui.date_with_time, next_substr_case_index); } else { - buff = FormatYmdString(buff, ScaledDateTicksToDate(args.GetNextParameter()), last, next_substr_case_index); + FormatYmdString(builder, ScaledDateTicksToDate(args.GetNextParameter()), next_substr_case_index); } break; } case SCC_DATE_WALLCLOCK_TINY: { // {DATE_WALLCLOCK_TINY} if (_settings_time.time_in_minutes) { - buff = FormatWallClockString(buff, args.GetNextParameter(), last, false, next_substr_case_index); + FormatWallClockString(builder, args.GetNextParameter(), false, next_substr_case_index); } else { - buff = FormatTinyOrISODate(buff, ScaledDateTicksToDate(args.GetNextParameter()), STR_FORMAT_DATE_TINY, last); + FormatTinyOrISODate(builder, ScaledDateTicksToDate(args.GetNextParameter()), STR_FORMAT_DATE_TINY); } break; } case SCC_DATE_WALLCLOCK_ISO: { // {DATE_WALLCLOCK_ISO} if (_settings_time.time_in_minutes) { - buff = FormatWallClockString(buff, args.GetNextParameter(), last, false, next_substr_case_index); + FormatWallClockString(builder, args.GetNextParameter(), false, next_substr_case_index); } else { - buff = FormatTinyOrISODate(buff, ScaledDateTicksToDate(args.GetNextParameter()), STR_FORMAT_DATE_ISO, last); + FormatTinyOrISODate(builder, ScaledDateTicksToDate(args.GetNextParameter()), STR_FORMAT_DATE_ISO); } break; } case SCC_DATE_ISO: // {DATE_ISO} - buff = FormatTinyOrISODate(buff, args.GetNextParameter(), STR_FORMAT_DATE_ISO, last); + FormatTinyOrISODate(builder, args.GetNextParameter(), STR_FORMAT_DATE_ISO); break; case SCC_TIME_HHMM: // {TIME_HHMM} - buff = FormatTimeHHMMString(buff, args.GetNextParameter(), last, next_substr_case_index); + FormatTimeHHMMString(builder, args.GetNextParameter(), next_substr_case_index); break; case SCC_TT_TICKS: // {TT_TICKS} case SCC_TT_TICKS_LONG: // {TT_TICKS_LONG} if (_settings_client.gui.timetable_in_ticks) { - auto tmp_params = MakeParameters(args.GetNextParameter()); - buff = FormatString(buff, GetStringPtr(STR_UNITS_TICKS), tmp_params, last); + auto tmp_params = MakeParameters(args.GetNextParameter()); + FormatString(builder, GetStringPtr(STR_UNITS_TICKS), tmp_params); } else { StringID str = _settings_time.time_in_minutes ? STR_TIMETABLE_MINUTES : STR_UNITS_DAYS; - int64 ticks = args.GetNextParameter(); - int64 ratio = DATE_UNIT_SIZE; - int64 units = ticks / ratio; - int64 leftover = _settings_client.gui.timetable_leftover_ticks ? ticks % ratio : 0; + int64_t ticks = args.GetNextParameter(); + int64_t ratio = DATE_UNIT_SIZE; + int64_t units = ticks / ratio; + int64_t leftover = _settings_client.gui.timetable_leftover_ticks ? ticks % ratio : 0; auto tmp_params = MakeParameters(units); - buff = FormatString(buff, GetStringPtr(str), tmp_params, last); + FormatString(builder, GetStringPtr(str), tmp_params); if (b == SCC_TT_TICKS_LONG && _settings_time.time_in_minutes && units > 59) { - int64 hours = units / 60; - int64 minutes = units % 60; + int64_t hours = units / 60; + int64_t minutes = units % 60; auto tmp_params = MakeParameters( (minutes != 0) ? STR_TIMETABLE_HOURS_MINUTES : STR_TIMETABLE_HOURS, hours, minutes ); - buff = FormatString(buff, GetStringPtr(STR_TIMETABLE_MINUTES_SUFFIX), tmp_params, last); + FormatString(builder, GetStringPtr(STR_TIMETABLE_MINUTES_SUFFIX), tmp_params); } if (leftover != 0) { auto tmp_params = MakeParameters(leftover); - buff = FormatString(buff, GetStringPtr(STR_TIMETABLE_LEFTOVER_TICKS), tmp_params, last); + FormatString(builder, GetStringPtr(STR_TIMETABLE_LEFTOVER_TICKS), tmp_params); } } break; @@ -1591,24 +1606,24 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg case SCC_FORCE: { // {FORCE} assert(_settings_game.locale.units_force < lengthof(_units_force)); const auto &x = _units_force[_settings_game.locale.units_force]; - auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); - buff = FormatString(buff, GetStringPtr(x.s), tmp_params, last); + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.s), tmp_params); break; } case SCC_HEIGHT: { // {HEIGHT} assert(_settings_game.locale.units_height < lengthof(_units_height)); const auto &x = _units_height[_settings_game.locale.units_height]; - auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); - buff = FormatString(buff, GetStringPtr(x.s), tmp_params, last); + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.s), tmp_params); break; } case SCC_POWER: { // {POWER} assert(_settings_game.locale.units_power < lengthof(_units_power)); const auto &x = _units_power[_settings_game.locale.units_power]; - auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); - buff = FormatString(buff, GetStringPtr(x.s), tmp_params, last); + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.s), tmp_params); break; } @@ -1616,52 +1631,52 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg auto setting = _settings_game.locale.units_power * 3u + _settings_game.locale.units_weight; assert(setting < lengthof(_units_power_to_weight)); const auto &x = _units_power_to_weight[setting]; - auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); - buff = FormatString(buff, GetStringPtr(x.s), tmp_params, last); + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.s), tmp_params); break; } case SCC_VELOCITY: { // {VELOCITY} - int64 arg = args.GetNextParameter(); + int64_t arg = args.GetNextParameter(); // Unpack vehicle type from packed argument to get desired units. VehicleType vt = static_cast(GB(arg, 56, 8)); byte units = GetVelocityUnits(vt); assert(units < lengthof(_units_velocity)); const auto &x = _units_velocity[units]; auto tmp_params = MakeParameters(ConvertKmhishSpeedToDisplaySpeed(GB(arg, 0, 56), vt), x.decimal_places); - buff = FormatString(buff, GetStringPtr(x.s), tmp_params, last); + FormatString(builder, GetStringPtr(x.s), tmp_params); break; } case SCC_VOLUME_SHORT: { // {VOLUME_SHORT} assert(_settings_game.locale.units_volume < lengthof(_units_volume)); const auto &x = _units_volume[_settings_game.locale.units_volume]; - auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); - buff = FormatString(buff, GetStringPtr(x.s), tmp_params, last); + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.s), tmp_params); break; } case SCC_VOLUME_LONG: { // {VOLUME_LONG} assert(_settings_game.locale.units_volume < lengthof(_units_volume)); const auto &x = _units_volume[_settings_game.locale.units_volume]; - auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); - buff = FormatString(buff, GetStringPtr(x.l), tmp_params, last); + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.l), tmp_params); break; } case SCC_WEIGHT_SHORT: { // {WEIGHT_SHORT} assert(_settings_game.locale.units_weight < lengthof(_units_weight)); const auto &x = _units_weight[_settings_game.locale.units_weight]; - auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); - buff = FormatString(buff, GetStringPtr(x.s), tmp_params, last); + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.s), tmp_params); break; } case SCC_WEIGHT_LONG: { // {WEIGHT_LONG} assert(_settings_game.locale.units_weight < lengthof(_units_weight)); const auto &x = _units_weight[_settings_game.locale.units_weight]; - auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); - buff = FormatString(buff, GetStringPtr(x.l), tmp_params, last); + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.l), tmp_params); break; } @@ -1669,7 +1684,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg assert(_settings_game.locale.units_power < lengthof(_units_power)); assert(_settings_game.locale.units_weight < lengthof(_units_weight)); - buff = FormatUnitWeightRatio(buff, last, _units_power[_settings_game.locale.units_power], args.GetNextParameter()); + FormatUnitWeightRatio(builder, _units_power[_settings_game.locale.units_power], args.GetNextParameter()); break; } @@ -1677,7 +1692,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg assert(_settings_game.locale.units_force < lengthof(_units_force)); assert(_settings_game.locale.units_weight < lengthof(_units_weight)); - buff = FormatUnitWeightRatio(buff, last, _units_force[_settings_game.locale.units_force], args.GetNextParameter()); + FormatUnitWeightRatio(builder, _units_force[_settings_game.locale.units_force], args.GetNextParameter()); break; } @@ -1686,11 +1701,11 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (c == nullptr) break; if (!c->name.empty()) { - auto tmp_params = MakeParameters(c->name.c_str()); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); + auto tmp_params = MakeParameters(c->name); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); } else { auto tmp_params = MakeParameters(c->name_2); - buff = GetStringWithArgs(buff, c->name_1, tmp_params, last); + GetStringWithArgs(builder, c->name_1, tmp_params); } break; } @@ -1701,7 +1716,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg /* Nothing is added for AI or inactive companies */ if (Company::IsValidHumanID(company)) { auto tmp_params = MakeParameters(company + 1); - buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, tmp_params, last); + GetStringWithArgs(builder, STR_FORMAT_COMPANY_NUM, tmp_params); } break; } @@ -1710,35 +1725,34 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg VehicleType vt = args.GetNextParameter(); if (vt == VEH_AIRCRAFT) { auto tmp_params = MakeParameters(args.GetNextParameter()); - buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, tmp_params, last); + GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_AIRCRAFT, tmp_params); break; } const Depot *d = Depot::Get(args.GetNextParameter()); if (!d->name.empty()) { auto tmp_params = MakeParameters(d->name.c_str()); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); } else { auto tmp_params = MakeParameters(d->town->index, d->town_cn + 1); - buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), tmp_params, last); + GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), tmp_params); } break; } case SCC_ENGINE_NAME: { // {ENGINE} - int64 arg = args.GetNextParameter(); + int64_t arg = args.GetNextParameter(); const Engine *e = Engine::GetIfValid(static_cast(arg)); if (e == nullptr) break; if (!e->name.empty() && e->IsEnabled()) { auto tmp_params = MakeParameters(e->name.c_str()); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); - + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); break; } if (HasBit(e->info.callback_mask, CBM_VEHICLE_NAME)) { - uint16 callback = GetVehicleCallback(CBID_VEHICLE_NAME, static_cast(arg >> 32), 0, e->index, nullptr); + uint16_t callback = GetVehicleCallback(CBID_VEHICLE_NAME, static_cast(arg >> 32), 0, e->index, nullptr); /* Not calling ErrorUnknownCallbackResult due to being inside string processing. */ if (callback != CALLBACK_FAILED && callback < 0x400) { const GRFFile *grffile = e->GetGRF(); @@ -1746,20 +1760,20 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg StartTextRefStackUsage(grffile, 6); ArrayStringParameters<6> tmp_params; - buff = GetStringWithArgs(buff, GetGRFStringID(grffile->grfid, 0xD000 + callback), tmp_params, last); + GetStringWithArgs(builder, GetGRFStringID(grffile->grfid, 0xD000 + callback), tmp_params); StopTextRefStackUsage(); break; } } - auto tmp_params = MakeParameters(); - buff = GetStringWithArgs(buff, e->info.string_id, tmp_params, last); + auto tmp_params = ArrayStringParameters<0>(); + GetStringWithArgs(builder, e->info.string_id, tmp_params); break; } case SCC_GROUP_NAME: { // {GROUP} - uint32 id = args.GetNextParameter(); + uint32_t id = args.GetNextParameter(); bool recurse = _settings_client.gui.show_group_hierarchy_name && (id & GROUP_NAME_HIERARCHY); id &= ~GROUP_NAME_HIERARCHY; const Group *group = Group::GetIfValid(id); @@ -1769,15 +1783,15 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (recurse && g->parent != INVALID_GROUP) { handle_group(Group::Get(g->parent)); auto tmp_params = MakeParameters(); - buff = GetStringWithArgs(buff, STR_HIERARCHY_SEPARATOR, tmp_params, last); + GetStringWithArgs(builder, STR_HIERARCHY_SEPARATOR, tmp_params); } if (!g->name.empty()) { auto tmp_params = MakeParameters(g->name.c_str()); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); } else { auto tmp_params = MakeParameters(g->index); - buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, tmp_params, last); + GetStringWithArgs(builder, STR_FORMAT_GROUP_NAME, tmp_params); } }); handle_group(group); @@ -1791,17 +1805,16 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg static bool use_cache = true; if (use_cache) { // Use cached version if first call AutoRestoreBackup cache_backup(use_cache, false); - buff = strecpy(buff, i->GetCachedName().c_str(), last); + builder += i->GetCachedName(); } else if (_scan_for_gender_data) { /* Gender is defined by the industry type. * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */ - auto tmp_params = MakeParameters(); - buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), tmp_params, last, next_substr_case_index); + auto tmp_params = ArrayStringParameters<0>(); + FormatString(builder, GetStringPtr(GetIndustrySpec(i->type)->name), tmp_params, next_substr_case_index); } else { /* First print the town name and the industry type name. */ auto tmp_params = MakeParameters(i->town->index, GetIndustrySpec(i->type)->name); - - buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), tmp_params, last, next_substr_case_index); + FormatString(builder, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), tmp_params, next_substr_case_index); } next_substr_case_index = 0; break; @@ -1812,11 +1825,11 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (c == nullptr) break; if (!c->president_name.empty()) { - auto tmp_params = MakeParameters(c->president_name.c_str()); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); + auto tmp_params = MakeParameters(c->president_name); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); } else { auto tmp_params = MakeParameters(c->president_name_2); - buff = GetStringWithArgs(buff, c->president_name_1, tmp_params, last); + GetStringWithArgs(builder, c->president_name_1, tmp_params); } break; } @@ -1829,18 +1842,18 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg /* The station doesn't exist anymore. The only place where we might * be "drawing" an invalid station is in the case of cargo that is * in transit. */ - auto tmp_params = MakeParameters(); - buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, tmp_params, last); + auto tmp_params = ArrayStringParameters<0>(); + GetStringWithArgs(builder, STR_UNKNOWN_STATION, tmp_params); break; } static bool use_cache = true; if (use_cache) { // Use cached version if first call AutoRestoreBackup cache_backup(use_cache, false); - buff = strecpy(buff, st->GetCachedName(), last); + builder += st->GetCachedName(); } else if (!st->name.empty()) { auto tmp_params = MakeParameters(st->name.c_str()); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); } else { StringID string_id = st->string_id; if (st->indtype != IT_INVALID) { @@ -1859,7 +1872,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg } auto tmp_params = MakeParameters(STR_TOWN_NAME, st->town->index, st->index); - buff = GetStringWithArgs(buff, string_id, tmp_params, last); + GetStringWithArgs(builder, string_id, tmp_params); } break; } @@ -1871,26 +1884,26 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg static bool use_cache = true; if (use_cache) { // Use cached version if first call AutoRestoreBackup cache_backup(use_cache, false); - buff = strecpy(buff, t->GetCachedName(), last); + builder += t->GetCachedName(); } else if (!t->name.empty()) { auto tmp_params = MakeParameters(t->name.c_str()); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); } else { - buff = GetTownName(buff, t, last); + GetTownName(builder, t); } break; } case SCC_VIEWPORT_TOWN_LABEL1: case SCC_VIEWPORT_TOWN_LABEL2: { // {VIEWPORT_TOWN_LABEL1..2} - int32 t = args.GetNextParameter(); + int32_t t = args.GetNextParameter(); uint64 data = args.GetNextParameter(); bool tiny = (b == SCC_VIEWPORT_TOWN_LABEL2); StringID string_id = STR_VIEWPORT_TOWN_COLOUR; if (!tiny) string_id += GB(data, 40, 2); auto tmp_params = MakeParameters(t, GB(data, 32, 8), GB(data, 0, 32)); - buff = GetStringWithArgs(buff, string_id, tmp_params, last); + GetStringWithArgs(builder, string_id, tmp_params); break; } @@ -1900,19 +1913,19 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (!wp->name.empty()) { auto tmp_params = MakeParameters(wp->name.c_str()); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); } else { auto tmp_params = MakeParameters(wp->town->index, wp->town_cn + 1); StringID string_id = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME); if (wp->town_cn != 0) string_id++; - buff = GetStringWithArgs(buff, string_id, tmp_params, last); + GetStringWithArgs(builder, string_id, tmp_params); } break; } case SCC_VEHICLE_NAME: { // {VEHICLE} - uint32 id = args.GetNextParameter(); - uint8 vehicle_names = _settings_client.gui.vehicle_names; + uint32_t id = args.GetNextParameter(); + uint8_t vehicle_names = _settings_client.gui.vehicle_names; if (id & VEHICLE_NAME_NO_GROUP) { id &= ~VEHICLE_NAME_NO_GROUP; /* Change format from long to traditional */ @@ -1924,17 +1937,17 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (!v->name.empty()) { auto tmp_params = MakeParameters(v->name.c_str()); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); } else if (v->group_id != DEFAULT_GROUP && vehicle_names != 0 && v->type < VEH_COMPANY_END) { /* The vehicle has no name, but is member of a group, so print group name */ - uint32 group_name = v->group_id; + uint32_t group_name = v->group_id; if (_settings_client.gui.show_vehicle_group_hierarchy_name) group_name |= GROUP_NAME_HIERARCHY; if (vehicle_names == 1) { auto tmp_params = MakeParameters(group_name, v->unitnumber); - buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_VEHICLE_NAME, tmp_params, last); + GetStringWithArgs(builder, STR_FORMAT_GROUP_VEHICLE_NAME, tmp_params); } else { auto tmp_params = MakeParameters(group_name, STR_TRADITIONAL_TRAIN_NAME + v->type, v->unitnumber); - buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_VEHICLE_NAME_LONG, tmp_params, last); + GetStringWithArgs(builder, STR_FORMAT_GROUP_VEHICLE_NAME_LONG, tmp_params); } } else { auto tmp_params = MakeParameters(v->unitnumber); @@ -1946,7 +1959,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg string_id = STR_INVALID_VEHICLE; } - buff = GetStringWithArgs(buff, string_id, tmp_params, last); + GetStringWithArgs(builder, string_id, tmp_params); } break; } @@ -1957,40 +1970,38 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg if (!si->name.empty()) { auto tmp_params = MakeParameters(si->name); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); } else { - auto tmp_params = MakeParameters(); - buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, tmp_params, last); + auto tmp_params = ArrayStringParameters<0>(); + GetStringWithArgs(builder, STR_DEFAULT_SIGN_NAME, tmp_params); } break; } case SCC_TR_SLOT_NAME: { // {TRSLOT} - const TraceRestrictSlot *slot = TraceRestrictSlot::GetIfValid(args.GetNextParameter()); + const TraceRestrictSlot *slot = TraceRestrictSlot::GetIfValid(args.GetNextParameter()); if (slot == nullptr) break; auto tmp_params = MakeParameters(slot->name.c_str()); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); break; } case SCC_TR_COUNTER_NAME: { // {TRCOUNTER} - const TraceRestrictCounter *ctr = TraceRestrictCounter::GetIfValid(args.GetNextParameter()); + const TraceRestrictCounter *ctr = TraceRestrictCounter::GetIfValid(args.GetNextParameter()); if (ctr == nullptr) break; auto tmp_params = MakeParameters(ctr->name.c_str()); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, tmp_params); break; } case SCC_STATION_FEATURES: { // {STATIONFEATURES} - buff = StationGetSpecialString(buff, args.GetNextParameter(), last); + StationGetSpecialString(builder, args.GetNextParameter()); break; } - case SCC_COLOUR: {// {COLOUR} - int64 tc = args.GetNextParameter(); - if (tc >= 0 && tc < TC_END) { - buff += Utf8Encode(buff, SCC_BLUE + tc); - } + case SCC_COLOUR: { // {COLOUR} + StringControlCode scc = (StringControlCode)(SCC_BLUE + args.GetNextParameter()); + if (IsInsideMM(scc, SCC_BLUE, SCC_COLOUR)) builder.Utf8Encode(scc); break; } @@ -1999,33 +2010,29 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters &arg break; default: - if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b); + builder.Utf8Encode(b); break; } } catch (std::out_of_range &e) { - DEBUG(misc, 0, "FormatString: %s", e.what()); - buff = strecpy(buff, "(invalid parameter)", last); + Debug(misc, 0, "FormatString: {}", e.what()); + builder += "(invalid parameter)"; } } - *buff = '\0'; - return buff; } -static char *StationGetSpecialString(char *buff, StationFacility x, const char *last) +static void StationGetSpecialString(StringBuilder builder, StationFacility x) { - if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN); - if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY); - if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS); - if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP); - if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE); - *buff = '\0'; - return buff; + if ((x & FACIL_TRAIN) != 0) builder.Utf8Encode(SCC_TRAIN); + if ((x & FACIL_TRUCK_STOP) != 0) builder.Utf8Encode(SCC_LORRY); + if ((x & FACIL_BUS_STOP) != 0) builder.Utf8Encode(SCC_BUS); + if ((x & FACIL_DOCK) != 0) builder.Utf8Encode(SCC_SHIP); + if ((x & FACIL_AIRPORT) != 0) builder.Utf8Encode(SCC_PLANE); } -static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last) +static void GetSpecialTownNameString(StringBuilder builder, int ind, uint32_t seed) { - return GenerateTownNameString(buff, last, ind, seed); + GenerateTownNameString(builder, ind, seed); } static const char * const _silly_company_names[] = { @@ -2096,7 +2103,7 @@ static const char _initial_name_letters[] = { 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W', }; -static char *GenAndCoName(char *buff, uint32 arg, const char *last) +static void GenAndCoName(StringBuilder builder, uint32_t arg) { const char * const *base; uint num; @@ -2109,13 +2116,11 @@ static char *GenAndCoName(char *buff, uint32 arg, const char *last) num = lengthof(_surname_list); } - buff = strecpy(buff, base[num * GB(arg, 16, 8) >> 8], last); - buff = strecpy(buff, " & Co.", last); - - return buff; + builder += base[num * GB(arg, 16, 8) >> 8]; + builder += " & Co."; } -static char *GenPresidentName(char *buff, uint32 x, const char *last) +static void GenPresidentName(StringBuilder builder, uint32_t x) { char initial[] = "?. "; const char * const *base; @@ -2123,12 +2128,12 @@ static char *GenPresidentName(char *buff, uint32 x, const char *last) uint i; initial[0] = _initial_name_letters[sizeof(_initial_name_letters) * GB(x, 0, 8) >> 8]; - buff = strecpy(buff, initial, last); + builder += initial; i = (sizeof(_initial_name_letters) + 35) * GB(x, 8, 8) >> 8; if (i < sizeof(_initial_name_letters)) { initial[0] = _initial_name_letters[i]; - buff = strecpy(buff, initial, last); + builder += initial; } if (_settings_game.game_creation.landscape == LT_TOYLAND) { @@ -2139,28 +2144,30 @@ static char *GenPresidentName(char *buff, uint32 x, const char *last) num = lengthof(_surname_list); } - buff = strecpy(buff, base[num * GB(x, 16, 8) >> 8], last); - - return buff; + builder += base[num * GB(x, 16, 8) >> 8]; } -static char *GetSpecialNameString(char *buff, int ind, StringParameters &args, const char *last) +static void GetSpecialNameString(StringBuilder builder, int ind, StringParameters &args) { switch (ind) { case 1: // not used - return strecpy(buff, _silly_company_names[std::min(args.GetNextParameter() & 0xFFFF, lengthof(_silly_company_names) - 1)], last); + builder += _silly_company_names[std::min(args.GetNextParameter(), lengthof(_silly_company_names) - 1)]; + return; case 2: // used for Foobar & Co company names - return GenAndCoName(buff, args.GetNextParameter(), last); + GenAndCoName(builder, args.GetNextParameter()); + return; case 3: // President name - return GenPresidentName(buff, args.GetNextParameter(), last); + GenPresidentName(builder, args.GetNextParameter()); + return; } /* town name? */ if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) { - buff = GetSpecialTownNameString(buff, ind - 6, args.GetNextParameter(), last); - return strecpy(buff, " Transport", last); + GetSpecialTownNameString(builder, ind - 6, args.GetNextParameter()); + builder += " Transport"; + return; } NOT_REACHED(); @@ -2226,7 +2233,7 @@ bool ReadLanguagePack(const LanguageMetadata *lang) uint count = 0; for (uint i = 0; i < TEXT_TAB_END; i++) { - uint16 num = lang_pack->offsets[i]; + uint16_t num = lang_pack->offsets[i]; if (num > TAB_SIZE) return false; tab_start[i] = count; @@ -2458,7 +2465,7 @@ void InitializeLanguagePacks() chosen_language = (language_fallback != nullptr) ? language_fallback : en_GB_fallback; } - if (!ReadLanguagePack(chosen_language)) usererror("Can't read language pack '%s'", chosen_language->file); + if (!ReadLanguagePack(chosen_language)) UserError("Can't read language pack '{}'", chosen_language->file); } /** @@ -2489,7 +2496,7 @@ bool MissingGlyphSearcher::FindMissingGlyphs() FontSize size = this->DefaultSize(); while (src != text->cend()) { - WChar c = Utf8Consume(src); + char32_t c = Utf8Consume(src); if (c >= SCC_FIRST_FONT && c <= SCC_LAST_FONT) { size = (FontSize)(c - SCC_FIRST_FONT); @@ -2505,7 +2512,7 @@ bool MissingGlyphSearcher::FindMissingGlyphs() default: NOT_REACHED(); } - DEBUG(fontcache, 0, "Font is missing glyphs to display char 0x%X in %s font size", c, size_name.c_str()); + Debug(fontcache, 0, "Font is missing glyphs to display char 0x{:X} in {} font size", (int)c, size_name); return true; } } diff --git a/src/strings_func.h b/src/strings_func.h index 7e2ceb8c68..efcc01fc1c 100644 --- a/src/strings_func.h +++ b/src/strings_func.h @@ -65,9 +65,7 @@ static inline StringID MakeStringID(StringTab tab, uint index) return (tab << TAB_SIZE_BITS) + index; } -char *GetString(char *buffr, StringID string, const char *last); 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); uint32 GetStringGRFID(StringID string); diff --git a/src/strings_internal.h b/src/strings_internal.h index b8f12dd35a..7574536d06 100644 --- a/src/strings_internal.h +++ b/src/strings_internal.h @@ -10,7 +10,6 @@ #ifndef STRINGS_INTERNAL_H #define STRINGS_INTERNAL_H -#include "strings_func.h" #include "string_func.h" #include "core/span_type.hpp" #include "core/strong_typedef_type.hpp" @@ -346,6 +345,21 @@ public: { 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 */ diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 4e3922073b..0eccc0895d 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -493,7 +493,7 @@ class NIHVehicle : public NIHelper { 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); 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); output.print(buffer); } diff --git a/src/textbuf.cpp b/src/textbuf.cpp index 4c2e28166f..78f5550684 100644 --- a/src/textbuf.cpp +++ b/src/textbuf.cpp @@ -48,7 +48,7 @@ bool Textbuf::CanDelChar(bool backspace) * @param keycode Type of deletion, either WKC_BACKSPACE or WKC_DELETE * @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; @@ -60,17 +60,17 @@ bool Textbuf::DeleteChar(uint16 keycode) if (!CanDelChar(backspace)) return false; char *s = this->buf + this->caretpos; - uint16 len = 0; + uint16_t len = 0; if (word) { /* Delete a complete word. */ if (backspace) { /* 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; } else { /* 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. */ for (const char *ss = s; ss < s + len; Utf8Consume(&ss)) { @@ -81,12 +81,12 @@ bool Textbuf::DeleteChar(uint16 keycode) if (backspace) { /* Delete the last code point in front of the caret. */ s = Utf8PrevChar(s); - WChar c; - len = (uint16)Utf8Decode(&c, s); + char32_t c; + len = (uint16_t)Utf8Decode(&c, s); this->chars--; } else { /* 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. */ for (const char *ss = s; ss < s + len; Utf8Consume(&ss)) { this->chars--; @@ -128,9 +128,9 @@ void Textbuf::DeleteAll() * @param key Character to be inserted * @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) { memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos); 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) { - 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) { insertpos = insert_location - this->buf; 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; - uint16 bytes = 0, chars = 0; - WChar c; + uint16_t bytes = 0, chars = 0; + char32_t c; for (const char *ptr = str; (c = Utf8Consume(&ptr)) != '\0';) { if (!IsValidChar(c, this->afilter)) break; @@ -235,7 +235,7 @@ bool Textbuf::InsertClipboard() * @param to End of the text to delete. * @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; const char *s = this->buf + from; @@ -299,7 +299,7 @@ void Textbuf::UpdateStringIter() { this->char_iter->SetString(this->buf); 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. */ @@ -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 * @return Return true on successful change of Textbuf, or false otherwise */ -bool Textbuf::MovePos(uint16 keycode) +bool Textbuf::MovePos(uint16_t keycode) { switch (keycode) { 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); if (pos == StringIterator::END) return true; - this->caretpos = (uint16)pos; + this->caretpos = (uint16_t)pos; this->UpdateCaretPosition(); 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); if (pos == StringIterator::END) return true; - this->caretpos = (uint16)pos; + this->caretpos = (uint16_t)pos; this->UpdateCaretPosition(); return true; } @@ -383,7 +383,7 @@ bool Textbuf::MovePos(uint16 keycode) * @param max_bytes maximum size in bytes, 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(max_bytes)), char_iter(StringIterator::Create()) { assert(max_bytes != 0); @@ -407,27 +407,26 @@ Textbuf::~Textbuf() */ void Textbuf::Assign(StringID string) { - GetString(this->buf, string, &this->buf[this->max_bytes - 1]); - this->UpdateSize(); + this->Assign(GetString(string)); } /** * Copy a string into the textbuffer. * @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]); - this->UpdateSize(); -} + const char *last_of = &this->buf[this->max_bytes - 1]; + 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'; + } -/** - * Copy a string into the textbuffer. - * @param text Source. - */ -void Textbuf::Assign(const std::string &text) -{ - this->Assign(text.c_str()); + this->UpdateSize(); } /** @@ -435,10 +434,20 @@ void Textbuf::Assign(const std::string &text) */ void Textbuf::Print(const char *format, ...) { + const char *last_of = &this->buf[this->max_bytes - 1]; va_list va; 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); + + 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(); } @@ -454,7 +463,7 @@ void Textbuf::UpdateSize() this->chars = this->bytes = 1; // terminating zero - WChar c; + char32_t c; while ((c = Utf8Consume(&buf)) != '\0') { this->bytes += Utf8CharLen(c); this->chars++; @@ -486,7 +495,7 @@ bool Textbuf::HandleCaret() return false; } -HandleKeyPressResult Textbuf::HandleKeyPress(WChar key, uint16 keycode) +HandleKeyPressResult Textbuf::HandleKeyPress(char32_t key, uint16_t keycode) { bool edited = false; diff --git a/src/textbuf_type.h b/src/textbuf_type.h index 2dc78c871e..fea1d38c95 100644 --- a/src/textbuf_type.h +++ b/src/textbuf_type.h @@ -30,37 +30,36 @@ enum HandleKeyPressResult struct Textbuf { CharSetFilter afilter; ///< Allowed characters char * const buf; ///< buffer in which text is saved - uint16 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 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 pixels; ///< the current size of the string in pixels + uint16_t max_bytes; ///< the maximum size of the buffer in bytes (including terminating '\0') + uint16_t max_chars; ///< the maximum size of the buffer in characters (including terminating '\0') + uint16_t bytes; ///< the current size of the string in bytes (including terminating '\0') + uint16_t chars; ///< the current size of the string in characters (including terminating '\0') + uint16_t pixels; ///< the current size of the string in pixels bool caret; ///< is the caret ("_") visible or not - uint16 caretpos; ///< the current position of the caret in the buffer, in bytes - uint16 caretxoffs; ///< the current position of the caret in pixels - uint16 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 markxoffs; ///< the start position of the marked area in pixels - uint16 marklength; ///< the length of the marked area in pixels - - explicit Textbuf(uint16 max_bytes, uint16 max_chars = UINT16_MAX); + uint16_t caretpos; ///< the current position of the caret in the buffer, in bytes + uint16_t caretxoffs; ///< the current position of the caret in pixels + uint16_t markpos; ///< the start 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_t markxoffs; ///< the start position of the marked area in pixels + uint16_t marklength; ///< the length of the marked area in pixels + + explicit Textbuf(uint16_t max_bytes, uint16_t max_chars = UINT16_MAX); ~Textbuf(); void Assign(StringID string); - void Assign(const char *text); - void Assign(const std::string &text); + void Assign(const std::string_view text); void CDECL Print(const char *format, ...) WARN_FORMAT(2, 3); void DeleteAll(); 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 DeleteChar(uint16 keycode); - bool MovePos(uint16 keycode); + bool DeleteChar(uint16_t keycode); + bool MovePos(uint16_t keycode); - HandleKeyPressResult HandleKeyPress(WChar key, uint16 keycode); + HandleKeyPressResult HandleKeyPress(char32_t key, uint16_t keycode); bool HandleCaret(); void UpdateSize(); @@ -74,7 +73,7 @@ private: 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 UpdateWidth(); diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 68b1334e21..b8aad6d5e6 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -252,9 +252,7 @@ uint64 Town::LabelParam2() const void Town::FillCachedName() const { - char buf[MAX_LENGTH_TOWN_NAME_CHARS * MAX_CHAR_LENGTH]; - char *end = GetTownName(buf, this, lastof(buf)); - this->cached_name.assign(buf, end); + this->cached_name = GetTownName(this); } /** diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 0816f242f0..81b7ed7f2d 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -1346,8 +1346,7 @@ public: if (!this->townnamevalid) { this->townname_editbox.text.DeleteAll(); } 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.UpdateSize(); + this->townname_editbox.text.Assign(GetTownName(&this->params, this->townnameparts)); } UpdateOSKOriginalText(this, WID_TF_TOWN_NAME_EDITBOX); @@ -1384,9 +1383,8 @@ public: name = this->townname_editbox.text.buf; } else { /* If user changed the name, send it */ - char buf[MAX_LENGTH_TOWN_NAME_CHARS * MAX_CHAR_LENGTH]; - GetTownName(buf, &this->params, this->townnameparts, lastof(buf)); - if (strcmp(buf, this->townname_editbox.text.buf) != 0) name = this->townname_editbox.text.buf; + std::string original_name = GetTownName(&this->params, this->townnameparts); + if (original_name != this->townname_editbox.text.buf) name = this->townname_editbox.text.buf; } 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; if (gf != nullptr) return GetGRFConfig(gf->grfid)->GetName(); - static char name[DRAW_STRING_BUFFER]; - GetString(name, STR_BASIC_HOUSE_SET_NAME, lastof(name)); - return name; + return GetStringPtr(STR_BASIC_HOUSE_SET_NAME); } /** @@ -1812,8 +1808,7 @@ public: } case WID_HP_HOUSE_ACCEPTANCE: { - static char buff[DRAW_STRING_BUFFER] = ""; - char *str = buff; + std::string buff; CargoArray cargo{}; CargoTypes dummy = 0; 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(1, cargo[i]); 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)); - SetDParamStr(0, buff); + if (buff.empty()) GetString(StringBuilder(buff), STR_JUST_NOTHING); + SetDParamStr(0, std::move(buff)); break; } diff --git a/src/townname.cpp b/src/townname.cpp index 78f566cdbb..672f9a2e6c 100644 --- a/src/townname.cpp +++ b/src/townname.cpp @@ -15,6 +15,7 @@ #include "core/random_func.hpp" #include "genworld.h" #include "gfx_layout.h" +#include "strings_internal.h" #include "table/townname.h" @@ -39,35 +40,56 @@ TownNameParams::TownNameParams(const Town *t) : /** - * Fills buffer with specified town name - * @param buff buffer start - * @param par town name parameters - * @param townnameparts 'encoded' town name - * @param last end of buffer - * @return pointer to terminating '\0' + * Fills builder with specified town name. + * @param builder The string builder. + * @param par Town name parameters. + * @param townnameparts 'Encoded' town name. */ -char *GetTownName(char *buff, const TownNameParams *par, uint32 townnameparts, const char *last) +static void GetTownName(StringBuilder builder, const TownNameParams *par, uint32_t townnameparts) { if (par->grfid == 0) { auto tmp_params = MakeParameters(townnameparts); - return GetStringWithArgs(buff, par->type, tmp_params, last); + GetStringWithArgs(builder, par->type, tmp_params); + return; } - return GRFTownNameGenerate(buff, par->grfid, par->type, townnameparts, last); + GRFTownNameGenerate(builder, par->grfid, par->type, townnameparts); } +/** + * Get the town name for the given parameters and parts. + * @param par Town name parameters. + * @param townnameparts 'Encoded' town name. + * @return The town name. + */ +std::string GetTownName(const TownNameParams *par, uint32_t townnameparts) +{ + std::string result; + StringBuilder builder(result); + GetTownName(builder, par, townnameparts); + return result; +} + +/** + * Fills builder with town's name. + * @param builder String builder. + * @param t The town to get the name from. + */ +void GetTownName(StringBuilder builder, const Town *t) +{ + TownNameParams par(t); + GetTownName(builder, &par, t->townnameparts); +} /** - * Fills buffer with town's name - * @param buff buffer start - * @param t we want to get name of this town - * @param last end of buffer - * @return pointer to terminating '\0' + * Get the name of the given town. + * @param t The town to get the name for. + * @return The town name. */ -char *GetTownName(char *buff, const Town *t, const char *last) +std::string GetTownName(const Town *t) { TownNameParams par(t); - return GetTownName(buff, &par, t->townnameparts, last); + return GetTownName(&par, t->townnameparts); } @@ -78,24 +100,21 @@ char *GetTownName(char *buff, const Town *t, const char *last) * @param town_names if a name is generated, check its uniqueness with the set * @return true iff name is valid and unique */ -bool VerifyTownName(uint32 r, const TownNameParams *par, TownNames *town_names) +bool VerifyTownName(uint32_t r, const TownNameParams *par, TownNames *town_names) { - /* reserve space for extra unicode character and terminating '\0' */ - char buf1[(MAX_LENGTH_TOWN_NAME_CHARS + 1) * MAX_CHAR_LENGTH]; - - GetTownName(buf1, par, r, lastof(buf1)); + std::string name = GetTownName(par, r); /* Check size and width */ - if (Utf8StringLength(buf1) >= MAX_LENGTH_TOWN_NAME_CHARS) return false; + if (Utf8StringLength(name) >= MAX_LENGTH_TOWN_NAME_CHARS) return false; if (town_names != nullptr) { - if (town_names->find(buf1) != town_names->end()) return false; - town_names->insert(buf1); + if (town_names->find(name) != town_names->end()) return false; + town_names->insert(name); } else { for (const Town *t : Town::Iterate()) { /* We can't just compare the numbers since * several numbers may map to a single name. */ - if (strcmp(buf1, t->GetCachedName()) == 0) return false; + if (name == t->GetCachedName()) return false; } } @@ -110,7 +129,7 @@ bool VerifyTownName(uint32 r, const TownNameParams *par, TownNames *town_names) * @param town_names if a name is generated, check its uniqueness with the set * @return true iff a name was generated */ -bool GenerateTownName(Randomizer &randomizer, uint32 *townnameparts, TownNames *town_names) +bool GenerateTownName(Randomizer &randomizer, uint32_t *townnameparts, TownNames *town_names) { TownNameParams par(_settings_game.game_creation.town_name); @@ -124,7 +143,7 @@ bool GenerateTownName(Randomizer &randomizer, uint32 *townnameparts, TownNames * * the other towns may take considerable amount of time (10000 is * too much). */ for (int i = 1000; i != 0; i--) { - uint32 r = randomizer.Next(); + uint32_t r = randomizer.Next(); if (!VerifyTownName(r, &par, town_names)) continue; *townnameparts = r; @@ -143,7 +162,7 @@ bool GenerateTownName(Randomizer &randomizer, uint32 *townnameparts, TownNames * * @param seed seed * @return seed transformed to a number from given range */ -static inline uint32 SeedChance(byte shift_by, int max, uint32 seed) +static inline uint32_t SeedChance(byte shift_by, int max, uint32_t seed) { return (GB(seed, shift_by, 16) * max) >> 16; } @@ -156,7 +175,7 @@ static inline uint32 SeedChance(byte shift_by, int max, uint32 seed) * @param seed seed * @return seed transformed to a number from given range */ -static inline uint32 SeedModChance(byte shift_by, int max, uint32 seed) +static inline uint32_t SeedModChance(byte shift_by, int max, uint32_t seed) { /* This actually gives *MUCH* more even distribution of the values * than SeedChance(), which is absolutely horrible in that. If @@ -179,7 +198,7 @@ static inline uint32 SeedModChance(byte shift_by, int max, uint32 seed) * @param bias minimum value that can be returned * @return seed transformed to a number from given range */ -static inline int32 SeedChanceBias(byte shift_by, int max, uint32 seed, int bias) +static inline int32_t SeedChanceBias(byte shift_by, int max, uint32_t seed, int bias) { return SeedChance(shift_by, max + bias, seed) - bias; } @@ -187,303 +206,282 @@ static inline int32 SeedChanceBias(byte shift_by, int max, uint32 seed, int bias /** * Replaces a string beginning in 'org' with 'rep'. - * @param org string to replace, has to be 4 characters long - * @param rep string to be replaced with, has to be 4 characters long - * @param buf buffer with string + * @param org string to replace, has to be 4 characters long + * @param rep string to be replaced with, has to be 4 characters long + * @param builder string builder of the town name + * @param start the start index within the builder for the town name */ -static void ReplaceWords(const char *org, const char *rep, char *buf) +static void ReplaceWords(const char *org, const char *rep, StringBuilder builder, size_t start) { - assert(strlen(org) == 4 && strlen(rep) == 4 && strlen(buf) >= 4); - if (strncmp(buf, org, 4) == 0) memcpy(buf, rep, 4); // Safe as the string in buf is always more than 4 characters long. + assert(strlen(org) == 4 && strlen(rep) == 4 && builder.CurrentIndex() - start >= 4); + if (strncmp(&builder[start], org, 4) == 0) memcpy(&builder[start], rep, 4); // Safe as the string in buf is always more than 4 characters long. } /** * Replaces english curses and ugly letter combinations by nicer ones. - * @param buf buffer with town name + * @param builder The builder with the town name + * @param start The start index into the builder for the first town name * @param original English (Original) generator was used */ -static void ReplaceEnglishWords(char *buf, bool original) +static void ReplaceEnglishWords(StringBuilder builder, size_t start, bool original) { - ReplaceWords("Cunt", "East", buf); - ReplaceWords("Slag", "Pits", buf); - ReplaceWords("Slut", "Edin", buf); - if (!original) ReplaceWords("Fart", "Boot", buf); // never happens with 'English (Original)' - ReplaceWords("Drar", "Quar", buf); - ReplaceWords("Dreh", "Bash", buf); - ReplaceWords("Frar", "Shor", buf); - ReplaceWords("Grar", "Aber", buf); - ReplaceWords("Brar", "Over", buf); - ReplaceWords("Wrar", original ? "Inve" : "Stan", buf); + ReplaceWords("Cunt", "East", builder, start); + ReplaceWords("Slag", "Pits", builder, start); + ReplaceWords("Slut", "Edin", builder, start); + if (!original) ReplaceWords("Fart", "Boot", builder, start); // never happens with 'English (Original)' + ReplaceWords("Drar", "Quar", builder, start); + ReplaceWords("Dreh", "Bash", builder, start); + ReplaceWords("Frar", "Shor", builder, start); + ReplaceWords("Grar", "Aber", builder, start); + ReplaceWords("Brar", "Over", builder, start); + ReplaceWords("Wrar", original ? "Inve" : "Stan", builder, start); } /** * Generates English (Original) town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeEnglishOriginalTownName(char *buf, const char *last, uint32 seed) +static void MakeEnglishOriginalTownName(StringBuilder builder, uint32_t seed) { - char *orig = buf; + size_t start = builder.CurrentIndex(); /* optional first segment */ int i = SeedChanceBias(0, lengthof(_name_original_english_1), seed, 50); - if (i >= 0) buf = strecpy(buf, _name_original_english_1[i], last); + if (i >= 0) builder += _name_original_english_1[i]; /* mandatory middle segments */ - buf = strecpy(buf, _name_original_english_2[SeedChance(4, lengthof(_name_original_english_2), seed)], last); - buf = strecpy(buf, _name_original_english_3[SeedChance(7, lengthof(_name_original_english_3), seed)], last); - buf = strecpy(buf, _name_original_english_4[SeedChance(10, lengthof(_name_original_english_4), seed)], last); - buf = strecpy(buf, _name_original_english_5[SeedChance(13, lengthof(_name_original_english_5), seed)], last); + builder += _name_original_english_2[SeedChance(4, lengthof(_name_original_english_2), seed)]; + builder += _name_original_english_3[SeedChance(7, lengthof(_name_original_english_3), seed)]; + builder += _name_original_english_4[SeedChance(10, lengthof(_name_original_english_4), seed)]; + builder += _name_original_english_5[SeedChance(13, lengthof(_name_original_english_5), seed)]; /* optional last segment */ i = SeedChanceBias(15, lengthof(_name_original_english_6), seed, 60); - if (i >= 0) buf = strecpy(buf, _name_original_english_6[i], last); + if (i >= 0) builder += _name_original_english_6[i]; /* Ce, Ci => Ke, Ki */ - if (orig[0] == 'C' && (orig[1] == 'e' || orig[1] == 'i')) { - orig[0] = 'K'; + if (builder[start] == 'C' && (builder[start + 1] == 'e' || builder[start + 1] == 'i')) { + builder[start] = 'K'; } - assert(buf - orig >= 4); - ReplaceEnglishWords(orig, true); - - return buf; + assert(builder.CurrentIndex() - start >= 4); + ReplaceEnglishWords(builder, start, true); } /** * Generates English (Additional) town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeEnglishAdditionalTownName(char *buf, const char *last, uint32 seed) +static void MakeEnglishAdditionalTownName(StringBuilder builder, uint32_t seed) { - char *orig = buf; + size_t start = builder.CurrentIndex(); /* optional first segment */ int i = SeedChanceBias(0, lengthof(_name_additional_english_prefix), seed, 50); - if (i >= 0) buf = strecpy(buf, _name_additional_english_prefix[i], last); + if (i >= 0) builder += _name_additional_english_prefix[i]; if (SeedChance(3, 20, seed) >= 14) { - buf = strecpy(buf, _name_additional_english_1a[SeedChance(6, lengthof(_name_additional_english_1a), seed)], last); + builder += _name_additional_english_1a[SeedChance(6, lengthof(_name_additional_english_1a), seed)]; } else { - buf = strecpy(buf, _name_additional_english_1b1[SeedChance(6, lengthof(_name_additional_english_1b1), seed)], last); - buf = strecpy(buf, _name_additional_english_1b2[SeedChance(9, lengthof(_name_additional_english_1b2), seed)], last); + builder += _name_additional_english_1b1[SeedChance(6, lengthof(_name_additional_english_1b1), seed)]; + builder += _name_additional_english_1b2[SeedChance(9, lengthof(_name_additional_english_1b2), seed)]; if (SeedChance(11, 20, seed) >= 4) { - buf = strecpy(buf, _name_additional_english_1b3a[SeedChance(12, lengthof(_name_additional_english_1b3a), seed)], last); + builder += _name_additional_english_1b3a[SeedChance(12, lengthof(_name_additional_english_1b3a), seed)]; } else { - buf = strecpy(buf, _name_additional_english_1b3b[SeedChance(12, lengthof(_name_additional_english_1b3b), seed)], last); + builder += _name_additional_english_1b3b[SeedChance(12, lengthof(_name_additional_english_1b3b), seed)]; } } - buf = strecpy(buf, _name_additional_english_2[SeedChance(14, lengthof(_name_additional_english_2), seed)], last); + builder += _name_additional_english_2[SeedChance(14, lengthof(_name_additional_english_2), seed)]; /* optional last segment */ i = SeedChanceBias(15, lengthof(_name_additional_english_3), seed, 60); - if (i >= 0) buf = strecpy(buf, _name_additional_english_3[i], last); + if (i >= 0) builder += _name_additional_english_3[i]; - assert(buf - orig >= 4); - ReplaceEnglishWords(orig, false); - - return buf; + assert(builder.CurrentIndex() - start >= 4); + ReplaceEnglishWords(builder, start, false); } /** * Generates Austrian town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeAustrianTownName(char *buf, const char *last, uint32 seed) +static void MakeAustrianTownName(StringBuilder builder, uint32_t seed) { /* Bad, Maria, Gross, ... */ int i = SeedChanceBias(0, lengthof(_name_austrian_a1), seed, 15); - if (i >= 0) buf = strecpy(buf, _name_austrian_a1[i], last); + if (i >= 0) builder += _name_austrian_a1[i]; int j = 0; i = SeedChance(4, 6, seed); if (i >= 4) { /* Kaisers-kirchen */ - buf = strecpy(buf, _name_austrian_a2[SeedChance( 7, lengthof(_name_austrian_a2), seed)], last); - buf = strecpy(buf, _name_austrian_a3[SeedChance(13, lengthof(_name_austrian_a3), seed)], last); + builder += _name_austrian_a2[SeedChance( 7, lengthof(_name_austrian_a2), seed)]; + builder += _name_austrian_a3[SeedChance(13, lengthof(_name_austrian_a3), seed)]; } else if (i >= 2) { /* St. Johann */ - buf = strecpy(buf, _name_austrian_a5[SeedChance( 7, lengthof(_name_austrian_a5), seed)], last); - buf = strecpy(buf, _name_austrian_a6[SeedChance( 9, lengthof(_name_austrian_a6), seed)], last); + builder += _name_austrian_a5[SeedChance( 7, lengthof(_name_austrian_a5), seed)]; + builder += _name_austrian_a6[SeedChance( 9, lengthof(_name_austrian_a6), seed)]; j = 1; // More likely to have a " an der " or " am " } else { /* Zell */ - buf = strecpy(buf, _name_austrian_a4[SeedChance( 7, lengthof(_name_austrian_a4), seed)], last); + builder += _name_austrian_a4[SeedChance( 7, lengthof(_name_austrian_a4), seed)]; } i = SeedChance(1, 6, seed); if (i >= 4 - j) { /* an der Donau (rivers) */ - buf = strecpy(buf, _name_austrian_f1[SeedChance(4, lengthof(_name_austrian_f1), seed)], last); - buf = strecpy(buf, _name_austrian_f2[SeedChance(5, lengthof(_name_austrian_f2), seed)], last); + builder += _name_austrian_f1[SeedChance(4, lengthof(_name_austrian_f1), seed)]; + builder += _name_austrian_f2[SeedChance(5, lengthof(_name_austrian_f2), seed)]; } else if (i >= 2 - j) { /* am Dachstein (mountains) */ - buf = strecpy(buf, _name_austrian_b1[SeedChance(4, lengthof(_name_austrian_b1), seed)], last); - buf = strecpy(buf, _name_austrian_b2[SeedChance(5, lengthof(_name_austrian_b2), seed)], last); + builder += _name_austrian_b1[SeedChance(4, lengthof(_name_austrian_b1), seed)]; + builder += _name_austrian_b2[SeedChance(5, lengthof(_name_austrian_b2), seed)]; } - - return buf; } /** * Generates German town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeGermanTownName(char *buf, const char *last, uint32 seed) +static void MakeGermanTownName(StringBuilder builder, uint32_t seed) { uint seed_derivative = SeedChance(7, 28, seed); /* optional prefix */ if (seed_derivative == 12 || seed_derivative == 19) { uint i = SeedChance(2, lengthof(_name_german_pre), seed); - buf = strecpy(buf, _name_german_pre[i], last); + builder += _name_german_pre[i]; } /* mandatory middle segments including option of hardcoded name */ uint i = SeedChance(3, lengthof(_name_german_real) + lengthof(_name_german_1), seed); if (i < lengthof(_name_german_real)) { - buf = strecpy(buf, _name_german_real[i], last); + builder += _name_german_real[i]; } else { - buf = strecpy(buf, _name_german_1[i - lengthof(_name_german_real)], last); + builder += _name_german_1[i - lengthof(_name_german_real)]; i = SeedChance(5, lengthof(_name_german_2), seed); - buf = strecpy(buf, _name_german_2[i], last); + builder += _name_german_2[i]; } /* optional suffix */ if (seed_derivative == 24) { i = SeedChance(9, lengthof(_name_german_4_an_der) + lengthof(_name_german_4_am), seed); if (i < lengthof(_name_german_4_an_der)) { - buf = strecpy(buf, _name_german_3_an_der[0], last); - buf = strecpy(buf, _name_german_4_an_der[i], last); + builder += _name_german_3_an_der[0]; + builder += _name_german_4_an_der[i]; } else { - buf = strecpy(buf, _name_german_3_am[0], last); - buf = strecpy(buf, _name_german_4_am[i - lengthof(_name_german_4_an_der)], last); + builder += _name_german_3_am[0]; + builder += _name_german_4_am[i - lengthof(_name_german_4_an_der)]; } } - - return buf; } /** * Generates Latin-American town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeSpanishTownName(char *buf, const char *last, uint32 seed) +static void MakeSpanishTownName(StringBuilder builder, uint32_t seed) { - return strecpy(buf, _name_spanish_real[SeedChance(0, lengthof(_name_spanish_real), seed)], last); + builder += _name_spanish_real[SeedChance(0, lengthof(_name_spanish_real), seed)]; } /** * Generates French town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeFrenchTownName(char *buf, const char *last, uint32 seed) +static void MakeFrenchTownName(StringBuilder builder, uint32_t seed) { - return strecpy(buf, _name_french_real[SeedChance(0, lengthof(_name_french_real), seed)], last); + builder += _name_french_real[SeedChance(0, lengthof(_name_french_real), seed)]; } /** * Generates Silly town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeSillyTownName(char *buf, const char *last, uint32 seed) +static void MakeSillyTownName(StringBuilder builder, uint32_t seed) { - buf = strecpy(buf, _name_silly_1[SeedChance( 0, lengthof(_name_silly_1), seed)], last); - buf = strecpy(buf, _name_silly_2[SeedChance(16, lengthof(_name_silly_2), seed)], last); - - return buf; + builder += _name_silly_1[SeedChance( 0, lengthof(_name_silly_1), seed)]; + builder += _name_silly_2[SeedChance(16, lengthof(_name_silly_2), seed)]; } /** * Generates Swedish town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeSwedishTownName(char *buf, const char *last, uint32 seed) +static void MakeSwedishTownName(StringBuilder builder, uint32_t seed) { /* optional first segment */ int i = SeedChanceBias(0, lengthof(_name_swedish_1), seed, 50); - if (i >= 0) buf = strecpy(buf, _name_swedish_1[i], last); + if (i >= 0) builder += _name_swedish_1[i]; /* mandatory middle segments including option of hardcoded name */ if (SeedChance(4, 5, seed) >= 3) { - buf = strecpy(buf, _name_swedish_2[SeedChance( 7, lengthof(_name_swedish_2), seed)], last); + builder += _name_swedish_2[SeedChance( 7, lengthof(_name_swedish_2), seed)]; } else { - buf = strecpy(buf, _name_swedish_2a[SeedChance( 7, lengthof(_name_swedish_2a), seed)], last); - buf = strecpy(buf, _name_swedish_2b[SeedChance(10, lengthof(_name_swedish_2b), seed)], last); - buf = strecpy(buf, _name_swedish_2c[SeedChance(13, lengthof(_name_swedish_2c), seed)], last); + builder += _name_swedish_2a[SeedChance( 7, lengthof(_name_swedish_2a), seed)]; + builder += _name_swedish_2b[SeedChance(10, lengthof(_name_swedish_2b), seed)]; + builder += _name_swedish_2c[SeedChance(13, lengthof(_name_swedish_2c), seed)]; } - buf = strecpy(buf, _name_swedish_3[SeedChance(16, lengthof(_name_swedish_3), seed)], last); - - return buf; + builder += _name_swedish_3[SeedChance(16, lengthof(_name_swedish_3), seed)]; } /** * Generates Dutch town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeDutchTownName(char *buf, const char *last, uint32 seed) +static void MakeDutchTownName(StringBuilder builder, uint32_t seed) { /* optional first segment */ int i = SeedChanceBias(0, lengthof(_name_dutch_1), seed, 50); - if (i >= 0) buf = strecpy(buf, _name_dutch_1[i], last); + if (i >= 0) builder += _name_dutch_1[i]; /* mandatory middle segments including option of hardcoded name */ if (SeedChance(6, 9, seed) > 4) { - buf = strecpy(buf, _name_dutch_2[SeedChance( 9, lengthof(_name_dutch_2), seed)], last); + builder += _name_dutch_2[SeedChance( 9, lengthof(_name_dutch_2), seed)]; } else { - buf = strecpy(buf, _name_dutch_3[SeedChance( 9, lengthof(_name_dutch_3), seed)], last); - buf = strecpy(buf, _name_dutch_4[SeedChance(12, lengthof(_name_dutch_4), seed)], last); + builder += _name_dutch_3[SeedChance( 9, lengthof(_name_dutch_3), seed)]; + builder += _name_dutch_4[SeedChance(12, lengthof(_name_dutch_4), seed)]; } - buf = strecpy(buf, _name_dutch_5[SeedChance(15, lengthof(_name_dutch_5), seed)], last); - - return buf; + builder += _name_dutch_5[SeedChance(15, lengthof(_name_dutch_5), seed)]; } /** * Generates Finnish town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeFinnishTownName(char *buf, const char *last, uint32 seed) +static void MakeFinnishTownName(StringBuilder builder, uint32_t seed) { - char *orig = buf; + size_t start = builder.CurrentIndex(); /* Select randomly if town name should consists of one or two parts. */ if (SeedChance(0, 15, seed) >= 10) { - return strecpy(buf, _name_finnish_real[SeedChance(2, lengthof(_name_finnish_real), seed)], last); + builder += _name_finnish_real[SeedChance(2, lengthof(_name_finnish_real), seed)]; + return; } if (SeedChance(0, 15, seed) >= 5) { @@ -491,41 +489,38 @@ static char *MakeFinnishTownName(char *buf, const char *last, uint32 seed) * The reason for not having the contents of _name_finnish_{1,2} in the same table is * that the ones in _name_finnish_2 are not good for this purpose. */ uint sel = SeedChance( 0, lengthof(_name_finnish_1), seed); - buf = strecpy(buf, _name_finnish_1[sel], last); - char *end = buf - 1; - assert(end >= orig); - if (*end == 'i') *end = 'e'; - if (strstr(orig, "a") != nullptr || strstr(orig, "o") != nullptr || strstr(orig, "u") != nullptr || - strstr(orig, "A") != nullptr || strstr(orig, "O") != nullptr || strstr(orig, "U") != nullptr) { - buf = strecpy(buf, "la", last); + builder += _name_finnish_1[sel]; + size_t last = builder.CurrentIndex() - 1; + if (builder[last] == 'i') builder[last] = 'e'; + + std::string_view view(&builder[start], builder.CurrentIndex() - start); + if (view.find_first_of("aouAOU") != std::string_view::npos) { + builder += "la"; } else { - buf = strecpy(buf, u8"l\u00e4", last); + builder += u8"l\u00e4"; } - return buf; + return; } /* A two-part name by combining one of _name_finnish_{1,2} + _name_finnish_3. * Why aren't _name_finnish_{1,2} just one table? See above. */ uint sel = SeedChance(2, lengthof(_name_finnish_1) + lengthof(_name_finnish_2), seed); if (sel >= lengthof(_name_finnish_1)) { - buf = strecpy(buf, _name_finnish_2[sel - lengthof(_name_finnish_1)], last); + builder += _name_finnish_2[sel - lengthof(_name_finnish_1)]; } else { - buf = strecpy(buf, _name_finnish_1[sel], last); + builder += _name_finnish_1[sel]; } - buf = strecpy(buf, _name_finnish_3[SeedChance(10, lengthof(_name_finnish_3), seed)], last); - - return buf; + builder += _name_finnish_3[SeedChance(10, lengthof(_name_finnish_3), seed)]; } /** * Generates Polish town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakePolishTownName(char *buf, const char *last, uint32 seed) +static void MakePolishTownName(StringBuilder builder, uint32_t seed) { /* optional first segment */ uint i = SeedChance(0, @@ -536,66 +531,65 @@ static char *MakePolishTownName(char *buf, const char *last, uint32 seed) if (i < lengthof(_name_polish_2_o)) { - return strecpy(buf, _name_polish_2_o[SeedChance(3, lengthof(_name_polish_2_o), seed)], last); + builder += _name_polish_2_o[SeedChance(3, lengthof(_name_polish_2_o), seed)]; + return; } if (i < lengthof(_name_polish_2_m) + lengthof(_name_polish_2_o)) { if (j < 4) { - buf = strecpy(buf, _name_polish_1_m[SeedChance(5, lengthof(_name_polish_1_m), seed)], last); + builder += _name_polish_1_m[SeedChance(5, lengthof(_name_polish_1_m), seed)]; } - buf = strecpy(buf, _name_polish_2_m[SeedChance(7, lengthof(_name_polish_2_m), seed)], last); + builder += _name_polish_2_m[SeedChance(7, lengthof(_name_polish_2_m), seed)]; if (j >= 4 && j < 16) { - buf = strecpy(buf, _name_polish_3_m[SeedChance(10, lengthof(_name_polish_3_m), seed)], last); + builder += _name_polish_3_m[SeedChance(10, lengthof(_name_polish_3_m), seed)]; } - return buf; + return; } if (i < lengthof(_name_polish_2_f) + lengthof(_name_polish_2_m) + lengthof(_name_polish_2_o)) { if (j < 4) { - buf = strecpy(buf, _name_polish_1_f[SeedChance(5, lengthof(_name_polish_1_f), seed)], last); + builder += _name_polish_1_f[SeedChance(5, lengthof(_name_polish_1_f), seed)]; } - buf = strecpy(buf, _name_polish_2_f[SeedChance(7, lengthof(_name_polish_2_f), seed)], last); + builder += _name_polish_2_f[SeedChance(7, lengthof(_name_polish_2_f), seed)]; if (j >= 4 && j < 16) { - buf = strecpy(buf, _name_polish_3_f[SeedChance(10, lengthof(_name_polish_3_f), seed)], last); + builder += _name_polish_3_f[SeedChance(10, lengthof(_name_polish_3_f), seed)]; } - return buf; + return; } if (j < 4) { - buf = strecpy(buf, _name_polish_1_n[SeedChance(5, lengthof(_name_polish_1_n), seed)], last); + builder += _name_polish_1_n[SeedChance(5, lengthof(_name_polish_1_n), seed)]; } - buf = strecpy(buf, _name_polish_2_n[SeedChance(7, lengthof(_name_polish_2_n), seed)], last); + builder += _name_polish_2_n[SeedChance(7, lengthof(_name_polish_2_n), seed)]; if (j >= 4 && j < 16) { - buf = strecpy(buf, _name_polish_3_n[SeedChance(10, lengthof(_name_polish_3_n), seed)], last); + builder += _name_polish_3_n[SeedChance(10, lengthof(_name_polish_3_n), seed)]; } - return buf; + return; } /** * Generates Czech town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeCzechTownName(char *buf, const char *last, uint32 seed) +static void MakeCzechTownName(StringBuilder builder, uint32_t seed) { /* 1:3 chance to use a real name. */ if (SeedModChance(0, 4, seed) == 0) { - return strecpy(buf, _name_czech_real[SeedModChance(4, lengthof(_name_czech_real), seed)], last); + builder += _name_czech_real[SeedModChance(4, lengthof(_name_czech_real), seed)]; + return; } - [[maybe_unused]] const char *orig = buf; - /* Probability of prefixes/suffixes * 0..11 prefix, 12..13 prefix+suffix, 14..17 suffix, 18..31 nothing */ int prob_tails = SeedModChance(2, 32, seed); @@ -702,28 +696,25 @@ static char *MakeCzechTownName(char *buf, const char *last, uint32 seed) if (do_prefix) { CzechPattern pattern = _name_czech_adj[prefix].pattern; - buf = strecpy(buf, _name_czech_adj[prefix].name, last); + builder += _name_czech_adj[prefix].name; - char *endpos = buf - 1; + size_t endpos = builder.CurrentIndex() - 1; /* Find the first character in a UTF-8 sequence */ - while (GB(*endpos, 6, 2) == 2) endpos--; + while (GB(builder[endpos], 6, 2) == 2) endpos--; + builder.RemoveElementsFromBack(builder.CurrentIndex() - endpos); if (gender == CZG_SMASC && pattern == CZP_PRIVL) { - assert(endpos >= orig + 2); /* -ovX -> -uv */ - *(endpos - 2) = 'u'; - assert(*(endpos - 1) == 'v'); - *endpos = '\0'; + builder[endpos - 2] = 'u'; } else { - assert(endpos >= orig); - endpos = strecpy(endpos, _name_czech_patmod[gender][pattern], last); + builder += _name_czech_patmod[gender][pattern]; } - buf = strecpy(endpos, " ", last); + builder += ' '; } if (dynamic_subst) { - buf = strecpy(buf, _name_czech_subst_stem[stem].name, last); + builder += _name_czech_subst_stem[stem].name; if (postfix < lengthof(_name_czech_subst_postfix)) { const char *poststr = _name_czech_subst_postfix[postfix]; const char *endstr = _name_czech_subst_ending[ending].name; @@ -736,189 +727,175 @@ static char *MakeCzechTownName(char *buf, const char *last, uint32 seed) if (postlen < 2 || postlen > endlen || ((poststr[1] != 'v' || poststr[1] != endstr[1]) && poststr[2] != endstr[1])) { - buf = strecpy(buf, poststr, last); + builder += poststr; /* k-i -> c-i, h-i -> z-i */ if (endstr[0] == 'i') { - switch (*(buf - 1)) { - case 'k': *(buf - 1) = 'c'; break; - case 'h': *(buf - 1) = 'z'; break; + size_t last = builder.CurrentIndex() - 1; + switch (builder[last]) { + case 'k': builder[last] = 'c'; break; + case 'h': builder[last] = 'z'; break; default: break; } } } } - buf = strecpy(buf, _name_czech_subst_ending[ending].name, last); + builder += _name_czech_subst_ending[ending].name; } else { - buf = strecpy(buf, _name_czech_subst_full[stem].name, last); + builder += _name_czech_subst_full[stem].name; } if (do_suffix) { - buf = strecpy(buf, " ", last); - buf = strecpy(buf, _name_czech_suffix[suffix], last); + builder += " "; + builder += _name_czech_suffix[suffix]; } - - return buf; } /** * Generates Romanian town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeRomanianTownName(char *buf, const char *last, uint32 seed) +static void MakeRomanianTownName(StringBuilder builder, uint32_t seed) { - return strecpy(buf, _name_romanian_real[SeedChance(0, lengthof(_name_romanian_real), seed)], last); + builder += _name_romanian_real[SeedChance(0, lengthof(_name_romanian_real), seed)]; } /** * Generates Slovak town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeSlovakTownName(char *buf, const char *last, uint32 seed) +static void MakeSlovakTownName(StringBuilder builder, uint32_t seed) { - return strecpy(buf, _name_slovak_real[SeedChance(0, lengthof(_name_slovak_real), seed)], last); + builder += _name_slovak_real[SeedChance(0, lengthof(_name_slovak_real), seed)]; } /** * Generates Norwegian town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeNorwegianTownName(char *buf, const char *last, uint32 seed) +static void MakeNorwegianTownName(StringBuilder builder, uint32_t seed) { /* Use first 4 bit from seed to decide whether or not this town should * have a real name 3/16 chance. Bit 0-3 */ if (SeedChance(0, 15, seed) < 3) { /* Use 7bit for the realname table index. Bit 4-10 */ - return strecpy(buf, _name_norwegian_real[SeedChance(4, lengthof(_name_norwegian_real), seed)], last); + builder += _name_norwegian_real[SeedChance(4, lengthof(_name_norwegian_real), seed)]; + return; } /* Use 7bit for the first fake part. Bit 4-10 */ - buf = strecpy(buf, _name_norwegian_1[SeedChance(4, lengthof(_name_norwegian_1), seed)], last); + builder += _name_norwegian_1[SeedChance(4, lengthof(_name_norwegian_1), seed)]; /* Use 7bit for the last fake part. Bit 11-17 */ - buf = strecpy(buf, _name_norwegian_2[SeedChance(11, lengthof(_name_norwegian_2), seed)], last); - - return buf; + builder += _name_norwegian_2[SeedChance(11, lengthof(_name_norwegian_2), seed)]; } /** * Generates Hungarian town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeHungarianTownName(char *buf, const char *last, uint32 seed) +static void MakeHungarianTownName(StringBuilder builder, uint32_t seed) { if (SeedChance(12, 15, seed) < 3) { - return strecpy(buf, _name_hungarian_real[SeedChance(0, lengthof(_name_hungarian_real), seed)], last); + builder += _name_hungarian_real[SeedChance(0, lengthof(_name_hungarian_real), seed)]; + return; } /* optional first segment */ uint i = SeedChance(3, lengthof(_name_hungarian_1) * 3, seed); - if (i < lengthof(_name_hungarian_1)) buf = strecpy(buf, _name_hungarian_1[i], last); + if (i < lengthof(_name_hungarian_1)) builder += _name_hungarian_1[i]; /* mandatory middle segments */ - buf = strecpy(buf, _name_hungarian_2[SeedChance(3, lengthof(_name_hungarian_2), seed)], last); - buf = strecpy(buf, _name_hungarian_3[SeedChance(6, lengthof(_name_hungarian_3), seed)], last); + builder += _name_hungarian_2[SeedChance(3, lengthof(_name_hungarian_2), seed)]; + builder += _name_hungarian_3[SeedChance(6, lengthof(_name_hungarian_3), seed)]; /* optional last segment */ i = SeedChance(10, lengthof(_name_hungarian_4) * 3, seed); if (i < lengthof(_name_hungarian_4)) { - buf = strecpy(buf, _name_hungarian_4[i], last); + builder += _name_hungarian_4[i]; } - - return buf; } /** * Generates Swiss town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeSwissTownName(char *buf, const char *last, uint32 seed) +static void MakeSwissTownName(StringBuilder builder, uint32_t seed) { - return strecpy(buf, _name_swiss_real[SeedChance(0, lengthof(_name_swiss_real), seed)], last); + builder += _name_swiss_real[SeedChance(0, lengthof(_name_swiss_real), seed)]; } /** * Generates Danish town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeDanishTownName(char *buf, const char *last, uint32 seed) +static void MakeDanishTownName(StringBuilder builder, uint32_t seed) { /* optional first segment */ int i = SeedChanceBias(0, lengthof(_name_danish_1), seed, 50); - if (i >= 0) buf = strecpy(buf, _name_danish_1[i], last); + if (i >= 0) builder += _name_danish_1[i]; /* middle segments removed as this algorithm seems to create much more realistic names */ - buf = strecpy(buf, _name_danish_2[SeedChance( 7, lengthof(_name_danish_2), seed)], last); - buf = strecpy(buf, _name_danish_3[SeedChance(16, lengthof(_name_danish_3), seed)], last); - - return buf; + builder += _name_danish_2[SeedChance( 7, lengthof(_name_danish_2), seed)]; + builder += _name_danish_3[SeedChance(16, lengthof(_name_danish_3), seed)]; } /** * Generates Turkish town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeTurkishTownName(char *buf, const char *last, uint32 seed) +static void MakeTurkishTownName(StringBuilder builder, uint32_t seed) { uint i = SeedModChance(0, 5, seed); switch (i) { case 0: - buf = strecpy(buf, _name_turkish_prefix[SeedModChance( 2, lengthof(_name_turkish_prefix), seed)], last); + builder += _name_turkish_prefix[SeedModChance( 2, lengthof(_name_turkish_prefix), seed)]; /* middle segment */ - buf = strecpy(buf, _name_turkish_middle[SeedModChance( 4, lengthof(_name_turkish_middle), seed)], last); + builder += _name_turkish_middle[SeedModChance( 4, lengthof(_name_turkish_middle), seed)]; /* optional suffix */ if (SeedModChance(0, 7, seed) == 0) { - buf = strecpy(buf, _name_turkish_suffix[SeedModChance( 10, lengthof(_name_turkish_suffix), seed)], last); + builder += _name_turkish_suffix[SeedModChance( 10, lengthof(_name_turkish_suffix), seed)]; } break; case 1: case 2: - buf = strecpy(buf, _name_turkish_prefix[SeedModChance( 2, lengthof(_name_turkish_prefix), seed)], last); - buf = strecpy(buf, _name_turkish_suffix[SeedModChance( 4, lengthof(_name_turkish_suffix), seed)], last); + builder += _name_turkish_prefix[SeedModChance( 2, lengthof(_name_turkish_prefix), seed)]; + builder += _name_turkish_suffix[SeedModChance( 4, lengthof(_name_turkish_suffix), seed)]; break; default: - buf = strecpy(buf, _name_turkish_real[SeedModChance( 4, lengthof(_name_turkish_real), seed)], last); + builder += _name_turkish_real[SeedModChance( 4, lengthof(_name_turkish_real), seed)]; break; } - - return buf; } /** * Generates Italian town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeItalianTownName(char *buf, const char *last, uint32 seed) +static void MakeItalianTownName(StringBuilder builder, uint32_t seed) { if (SeedModChance(0, 6, seed) == 0) { // real city names - return strecpy(buf, _name_italian_real[SeedModChance(4, lengthof(_name_italian_real), seed)], last); + builder += _name_italian_real[SeedModChance(4, lengthof(_name_italian_real), seed)]; + return; } static const char * const mascul_femin_italian[] = { @@ -927,139 +904,110 @@ static char *MakeItalianTownName(char *buf, const char *last, uint32 seed) }; if (SeedModChance(0, 8, seed) == 0) { // prefix - buf = strecpy(buf, _name_italian_pref[SeedModChance(11, lengthof(_name_italian_pref), seed)], last); + builder += _name_italian_pref[SeedModChance(11, lengthof(_name_italian_pref), seed)]; } uint i = SeedChance(0, 2, seed); if (i == 0) { // masculine form - buf = strecpy(buf, _name_italian_1m[SeedModChance(4, lengthof(_name_italian_1m), seed)], last); + builder += _name_italian_1m[SeedModChance(4, lengthof(_name_italian_1m), seed)]; } else { // feminine form - buf = strecpy(buf, _name_italian_1f[SeedModChance(4, lengthof(_name_italian_1f), seed)], last); + builder += _name_italian_1f[SeedModChance(4, lengthof(_name_italian_1f), seed)]; } if (SeedModChance(3, 3, seed) == 0) { - buf = strecpy(buf, _name_italian_2[SeedModChance(11, lengthof(_name_italian_2), seed)], last); - buf = strecpy(buf, mascul_femin_italian[i], last); + builder += _name_italian_2[SeedModChance(11, lengthof(_name_italian_2), seed)]; + builder += mascul_femin_italian[i]; } else { - buf = strecpy(buf, _name_italian_2i[SeedModChance(16, lengthof(_name_italian_2i), seed)], last); + builder += _name_italian_2i[SeedModChance(16, lengthof(_name_italian_2i), seed)]; } if (SeedModChance(15, 4, seed) == 0) { if (SeedModChance(5, 2, seed) == 0) { // generic suffix - buf = strecpy(buf, _name_italian_3[SeedModChance(4, lengthof(_name_italian_3), seed)], last); + builder += _name_italian_3[SeedModChance(4, lengthof(_name_italian_3), seed)]; } else { // river name suffix - buf = strecpy(buf, _name_italian_river1[SeedModChance(4, lengthof(_name_italian_river1), seed)], last); - buf = strecpy(buf, _name_italian_river2[SeedModChance(16, lengthof(_name_italian_river2), seed)], last); + builder += _name_italian_river1[SeedModChance(4, lengthof(_name_italian_river1), seed)]; + builder += _name_italian_river2[SeedModChance(16, lengthof(_name_italian_river2), seed)]; } } - - return buf; } /** * Generates Catalan town name from given seed. - * @param buf output buffer + * @param builder string builder * @param seed town name seed - * @param last end of buffer */ -static char *MakeCatalanTownName(char *buf, const char *last, uint32 seed) +static void MakeCatalanTownName(StringBuilder builder, uint32_t seed) { if (SeedModChance(0, 3, seed) == 0) { // real city names - return strecpy(buf, _name_catalan_real[SeedModChance(4, lengthof(_name_catalan_real), seed)], last); + builder += _name_catalan_real[SeedModChance(4, lengthof(_name_catalan_real), seed)]; + return; } if (SeedModChance(0, 2, seed) == 0) { // prefix - buf = strecpy(buf, _name_catalan_pref[SeedModChance(11, lengthof(_name_catalan_pref), seed)], last); + builder += _name_catalan_pref[SeedModChance(11, lengthof(_name_catalan_pref), seed)]; } uint i = SeedChance(0, 2, seed); if (i == 0) { // masculine form - buf = strecpy(buf, _name_catalan_1m[SeedModChance(4, lengthof(_name_catalan_1m), seed)], last); - buf = strecpy(buf, _name_catalan_2m[SeedModChance(11, lengthof(_name_catalan_2m), seed)], last); + builder += _name_catalan_1m[SeedModChance(4, lengthof(_name_catalan_1m), seed)]; + builder += _name_catalan_2m[SeedModChance(11, lengthof(_name_catalan_2m), seed)]; } else { // feminine form - buf = strecpy(buf, _name_catalan_1f[SeedModChance(4, lengthof(_name_catalan_1f), seed)], last); - buf = strecpy(buf, _name_catalan_2f[SeedModChance(11, lengthof(_name_catalan_2f), seed)], last); + builder += _name_catalan_1f[SeedModChance(4, lengthof(_name_catalan_1f), seed)]; + builder += _name_catalan_2f[SeedModChance(11, lengthof(_name_catalan_2f), seed)]; } if (SeedModChance(15, 5, seed) == 0) { if (SeedModChance(5, 2, seed) == 0) { // generic suffix - buf = strecpy(buf, _name_catalan_3[SeedModChance(4, lengthof(_name_catalan_3), seed)], last); + builder += _name_catalan_3[SeedModChance(4, lengthof(_name_catalan_3), seed)]; } else { // river name suffix - buf = strecpy(buf, _name_catalan_river1[SeedModChance(4, lengthof(_name_catalan_river1), seed)], last); + builder += _name_catalan_river1[SeedModChance(4, lengthof(_name_catalan_river1), seed)]; } } - - return buf; } /** * Type for all town name generator functions. - * @param buf The buffer to write the name to. - * @param last The last element of the buffer. + * @param builder The builder to write the name to. * @param seed The seed of the town name. - * @return The end of the filled buffer. */ -typedef char *TownNameGenerator(char *buf, const char *last, uint32 seed); - -/** Contains pointer to generator and minimum buffer size (not incl. terminating '\0') */ -struct TownNameGeneratorParams { - byte min; ///< minimum number of characters that need to be printed for generator to work correctly - TownNameGenerator *proc; ///< generator itself -}; +typedef void TownNameGenerator(StringBuilder builder, uint32_t seed); /** Town name generators */ -static const TownNameGeneratorParams _town_name_generators[] = { - { 4, MakeEnglishOriginalTownName}, // replaces first 4 characters of name - { 0, MakeFrenchTownName}, - { 0, MakeGermanTownName}, - { 4, MakeEnglishAdditionalTownName}, // replaces first 4 characters of name - { 0, MakeSpanishTownName}, - { 0, MakeSillyTownName}, - { 0, MakeSwedishTownName}, - { 0, MakeDutchTownName}, - { 8, MakeFinnishTownName}, // _name_finnish_1 - { 0, MakePolishTownName}, - { 0, MakeSlovakTownName}, - { 0, MakeNorwegianTownName}, - { 0, MakeHungarianTownName}, - { 0, MakeAustrianTownName}, - { 0, MakeRomanianTownName}, - { 28, MakeCzechTownName}, // _name_czech_adj + _name_czech_patmod + 1 + _name_czech_subst_stem + _name_czech_subst_postfix - { 0, MakeSwissTownName}, - { 0, MakeDanishTownName}, - { 0, MakeTurkishTownName}, - { 0, MakeItalianTownName}, - { 0, MakeCatalanTownName}, +static TownNameGenerator *_town_name_generators[] = { + MakeEnglishOriginalTownName, // replaces first 4 characters of name + MakeFrenchTownName, + MakeGermanTownName, + MakeEnglishAdditionalTownName, // replaces first 4 characters of name + MakeSpanishTownName, + MakeSillyTownName, + MakeSwedishTownName, + MakeDutchTownName, + MakeFinnishTownName, // _name_finnish_1 + MakePolishTownName, + MakeSlovakTownName, + MakeNorwegianTownName, + MakeHungarianTownName, + MakeAustrianTownName, + MakeRomanianTownName, + MakeCzechTownName, // _name_czech_adj + _name_czech_patmod + 1 + _name_czech_subst_stem + _name_czech_subst_postfix + MakeSwissTownName, + MakeDanishTownName, + MakeTurkishTownName, + MakeItalianTownName, + MakeCatalanTownName, }; /** * Generates town name from given seed. - * @param buf output buffer - * @param last end of buffer - * @param lang town name language - * @param seed generation seed - * @return last character ('/0') + * @param builder string builder to write to + * @param lang town name language + * @param seed generation seed */ -char *GenerateTownNameString(char *buf, const char *last, size_t lang, uint32 seed) +void GenerateTownNameString(StringBuilder builder, size_t lang, uint32_t seed) { assert(lang < lengthof(_town_name_generators)); - - /* Some generators need at least 9 bytes in buffer. English generators need 5 for - * string replacing, others use constructions like strlen(buf)-3 and so on. - * Finnish generator needs to fit all strings from _name_finnish_1. - * Czech generator needs to fit almost whole town name... - * These would break. Using another temporary buffer results in ~40% slower code, - * so use it only when really needed. */ - const TownNameGeneratorParams *par = &_town_name_generators[lang]; - if (last >= buf + par->min) return par->proc(buf, last, seed); - - IGNORE_UNINITIALIZED_WARNING_START - char *buffer = AllocaM(char, par->min + 1); - par->proc(buffer, buffer + par->min, seed); - IGNORE_UNINITIALIZED_WARNING_STOP - - return strecpy(buf, buffer, last); + return _town_name_generators[lang](builder, seed); } diff --git a/src/townname_func.h b/src/townname_func.h index a3d29e467d..b3c0a601b5 100644 --- a/src/townname_func.h +++ b/src/townname_func.h @@ -13,10 +13,9 @@ #include "core/random_func.hpp" #include "townname_type.h" -char *GenerateTownNameString(char *buf, const char *last, size_t lang, uint32 seed); -char *GetTownName(char *buff, const TownNameParams *par, uint32 townnameparts, const char *last); -char *GetTownName(char *buff, const Town *t, const char *last); -bool VerifyTownName(uint32 r, const TownNameParams *par, TownNames *town_names = nullptr); -bool GenerateTownName(Randomizer &randomizer, uint32 *townnameparts, TownNames *town_names = nullptr); +std::string GetTownName(const TownNameParams *par, uint32_t townnameparts); +std::string GetTownName(const Town *t); +bool VerifyTownName(uint32_t r, const TownNameParams *par, TownNames *town_names = nullptr); +bool GenerateTownName(Randomizer &randomizer, uint32_t *townnameparts, TownNames *town_names = nullptr); #endif /* TOWNNAME_FUNC_H */ diff --git a/src/townname_type.h b/src/townname_type.h index 811c8a118c..0562bc6803 100644 --- a/src/townname_type.h +++ b/src/townname_type.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. */ struct TownNameParams { - uint32 grfid; ///< newgrf ID (0 if not used) - uint16 type; ///< town name style + uint32_t grfid; ///< newgrf ID (0 if not used) + uint16_t type; ///< town name style /** * Initializes this struct from language ID diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index cc72213c10..a7487567b0 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -1205,10 +1205,8 @@ static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestric SetDParam(1, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); } else { auto insert_warning = [&](uint dparam_index, StringID warning) { - char buf[256]; auto tmp_params = MakeParameters(GetDParam(dparam_index)); - char *end = GetStringWithArgs(buf, warning, tmp_params, lastof(buf)); - _temp_special_strings[0].assign(buf, end); + _temp_special_strings[0] = GetStringWithArgs(warning, tmp_params); SetDParam(dparam_index, SPECSTR_TEMP_START); }; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 50fa98b305..c390727da8 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -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); } - /* debug output */ - char buffer[512]; - SetDParamStr(0, grfconfig->GetName()); - GetString(buffer, part1, lastof(buffer)); - DEBUG(grf, 0, "%s", buffer + 3); + DEBUG(grf, 0, "%s", strip_leading_colours(GetString(part1))); SetDParam(1, engine); - GetString(buffer, part2, lastof(buffer)); - DEBUG(grf, 0, "%s", buffer + 3); + DEBUG(grf, 0, "%s", strip_leading_colours(GetString(part2))); } /** @@ -4642,7 +4637,7 @@ void DumpVehicleStats(char *buffer, const char *last) for (auto &it : cstatmap) { buffer += seprintf(buffer, last, "%u: ", (uint) 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"); auto line = [&](vtypestats &vs, const char *type) { diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 8181efe4d9..fe910167e3 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -987,7 +987,6 @@ struct RefitWindow : public Window { { std::string &name = this->ship_part_names[v->index]; if (name.empty()) { - char buffer[128] = ""; const Vehicle *front = v->First(); uint offset = 0; for (const Vehicle *u = front; u != v; u = u->Next()) offset++; @@ -997,14 +996,11 @@ struct RefitWindow : public Window { assert(grffile != nullptr); StartTextRefStackUsage(grffile, 6); - char *end = GetString(buffer, GetGRFStringID(grffile->grfid, 0xD000 + callback), lastof(buffer)); + name = GetString(GetGRFStringID(grffile->grfid, 0xD000 + callback)); StopTextRefStackUsage(); - - name.assign(buffer, end - buffer); } else { SetDParam(0, offset + 1); - char *end = GetString(buffer, STR_REFIT_SHIP_PART, lastof(buffer)); - name.assign(buffer, end - buffer); + name = GetString(STR_REFIT_SHIP_PART); } } return name; @@ -3201,18 +3197,14 @@ struct VehicleDetailsWindow : Window { std::vector slots; TraceRestrictGetVehicleSlots(v->index, slots); - char text_buffer[512]; - char *buffer = text_buffer; - const char * const last = lastof(text_buffer); 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++) { - if (i != 0) buffer = GetString(buffer, STR_TRACE_RESTRICT_SLOT_LIST_SEPARATOR, last); - buffer = strecpy(buffer, TraceRestrictSlot::Get(slots[i])->name.c_str(), last); + if (i != 0) GetString(StringBuilder(buffer), STR_TRACE_RESTRICT_SLOT_LIST_SEPARATOR); + buffer += TraceRestrictSlot::Get(slots[i])->name; } - SetDParamStr(0, text_buffer); - DrawString(tr, STR_JUST_RAW_STRING); + DrawString(tr, buffer); tr.top += GetCharacterHeight(FS_NORMAL); } diff --git a/src/viewport.cpp b/src/viewport.cpp index 01518edc6c..124a020c0a 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -1950,15 +1950,16 @@ void ViewportSign::UpdatePosition(ZoomLevel maxzoom, int center, int top, String 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->center = center; /* zoomed out version */ 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;