From ed51cf117a5ac076baa6189ef66cbb86e30d0932 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 11 May 2023 21:30:58 +0200 Subject: [PATCH 01/35] Add: helper output iterator for formatting strings --- src/CMakeLists.txt | 1 + src/strings_internal.h | 123 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/strings_internal.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e7230c42e6..bc00244f7c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -424,6 +424,7 @@ add_files( stringfilter_type.h strings.cpp strings_func.h + strings_internal.h strings_type.h subsidy.cpp subsidy_base.h diff --git a/src/strings_internal.h b/src/strings_internal.h new file mode 100644 index 0000000000..a77eb0266f --- /dev/null +++ b/src/strings_internal.h @@ -0,0 +1,123 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file strings_interal.h Types and functions related to the internal workings of formatting OpenTTD's strings. */ + +#ifndef STRINGS_INTERNAL_H +#define STRINGS_INTERNAL_H + +/** + * Equivalent to the std::back_insert_iterator in function, with some + * convenience helpers for string concatenation. + * + * The formatter is currently backed by an external char buffer, and has some + * extra functions to ease the migration from char buffers to std::string. + */ +class StringBuilder { + char *current; ///< The current location to add strings + const char *last; ///< The last element of the buffer. + +public: + /* Required type for this to be an output_iterator; mimics std::back_insert_iterator. */ + using value_type = void; + using difference_type = void; + using iterator_category = std::output_iterator_tag; + using pointer = void; + using reference = void; + + /** + * Create the builder of an external buffer. + * @param start The start location to write to. + * @param last The last location to write to. + */ + StringBuilder(char *start, const char *last) : current(start), last(last) {} + + /* Required operators for this to be an output_iterator; mimics std::back_insert_iterator, which has no-ops. */ + StringBuilder &operator++() { return *this; } + StringBuilder operator++(int) { return *this; } + StringBuilder &operator*() { return *this; } + + /** + * Operator to add a character to the end of the buffer. Like the back + * insert iterators this also increases the position of the end of the + * buffer. + * @param value The character to add. + * @return Reference to this inserter. + */ + StringBuilder &operator=(const char value) + { + return this->operator+=(value); + } + + /** + * Operator to add a character to the end of the buffer. + * @param value The character to add. + * @return Reference to this inserter. + */ + StringBuilder &operator+=(const char value) + { + if (this->current != this->last) *this->current++ = value; + return *this; + } + + /** + * Operator to append the given string to the output buffer. + * @param str The string to add. + * @return Reference to this inserter. + */ + StringBuilder &operator+=(const char *str) + { + this->current = strecpy(this->current, str, this->last); + return *this; + } + + /** + * Operator to append the given string to the output buffer. + * @param str The string to add. + * @return Reference to this inserter. + */ + StringBuilder &operator+=(const std::string &str) + { + return this->operator+=(str.c_str()); + } + + /** + * Encode the given Utf8 character into the output buffer. + * @param c The character to encode. + * @return true iff there was enough space and the character got added. + */ + bool Utf8Encode(WChar c) + { + if (this->Remaining() < Utf8CharLen(c)) return false; + + this->current += ::Utf8Encode(this->current, c); + return true; + } + + /** + * Get the pointer to the this->last written element in the buffer. + * This call does '\0' terminate the string, whereas other calls do not + * (necessarily) do this. + * @return The this->current end of the string. + */ + char *GetEnd() + { + *this->current = '\0'; + return this->current; + } + + /** + * Get the remaining number of characters that can be placed. + * @return The number of characters. + */ + ptrdiff_t Remaining() + { + return (ptrdiff_t)(this->last - this->current); + } +}; + +#endif /* STRINGS_INTERNAL_H */ From c384d829fe6d1cf474ca225ca567da855fb509dc Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 11 May 2023 21:34:21 +0200 Subject: [PATCH 02/35] Codechange: let number formatting use StringBuilder --- src/strings.cpp | 143 ++++++++++++++++++++++++++---------------------- 1 file changed, 77 insertions(+), 66 deletions(-) diff --git a/src/strings.cpp b/src/strings.cpp index 9d004f7c83..dfbae283e2 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -43,6 +43,8 @@ #include "table/strings.h" #include "table/control_codes.h" +#include "strings_internal.h" + #include "safeguards.h" std::string _config_language_file; ///< The file (name) stored in the configuration. @@ -315,16 +317,15 @@ void SetDParamStr(uint n, const 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 number, const char *separator, int zerofill = 1, int fractional_digits = 0) { static const int max_digits = 20; uint64 divisor = 10000000000000000000ULL; @@ -332,7 +333,7 @@ static char *FormatNumber(char *buff, int64 number, const char *last, const char int thousands_offset = (max_digits - fractional_digits - 1) % 3; if (number < 0) { - if (buff != last) *buff++ = '-'; + builder += '-'; number = -number; } @@ -342,7 +343,7 @@ static char *FormatNumber(char *buff, int64 number, const char *last, const char 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; @@ -351,48 +352,42 @@ static char *FormatNumber(char *buff, int64 number, const char *last, const char 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 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 number) { - return FormatNumber(buff, number, last, ""); + FormatNumber(builder, number, ""); } -static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last) +static void FormatZerofillNumber(StringBuilder &builder, int64 number, int64 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 number) { - return strecpy(buff, fmt::format("0x{:X}", number).c_str(), last); + fmt::format_to(builder, "0x{:X}", number); } /** * 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 number) { assert(number >= 0); @@ -409,20 +404,18 @@ 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 *FormatYmdString(char *buff, TimerGameCalendar::Date date, const char *last, uint case_index) @@ -456,7 +449,7 @@ static char *FormatTinyOrISODate(char *buff, TimerGameCalendar::Date date, Strin return FormatString(buff, GetStringPtr(str), &tmp_params, last); } -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 */ @@ -467,18 +460,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); + if (!builder.Utf8Encode(SCC_PUSH_COLOUR)) return; + if (!builder.Utf8Encode(SCC_RED)) return; + 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) { @@ -496,21 +487,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; } /** @@ -1109,34 +1096,50 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg break; } - case SCC_COMMA: // {COMMA} - buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last); - break; - - case SCC_DECIMAL: {// {DECIMAL} - int64 number = args->GetInt64(SCC_DECIMAL); - int digits = args->GetInt32(SCC_DECIMAL); - buff = FormatCommaNumber(buff, number, last, digits); + case SCC_COMMA: { // {COMMA} + StringBuilder builder(buff, last); + FormatCommaNumber(builder, args->GetInt64(SCC_COMMA)); + buff = builder.GetEnd(); break; } - case SCC_NUM: // {NUM} - buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last); + case SCC_DECIMAL: { // {DECIMAL} + int64 number = args->GetInt64(SCC_DECIMAL); + int digits = args->GetInt32(SCC_DECIMAL); + StringBuilder builder(buff, last); + FormatCommaNumber(builder, number, digits); + buff = builder.GetEnd(); break; + } + + case SCC_NUM: { // {NUM} + StringBuilder builder(buff, last); + FormatNoCommaNumber(builder, args->GetInt64(SCC_NUM)); + buff = builder.GetEnd(); + break; + } case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM} int64 num = args->GetInt64(); - buff = FormatZerofillNumber(buff, num, args->GetInt64(), last); + StringBuilder builder(buff, last); + FormatZerofillNumber(builder, num, args->GetInt64()); + buff = builder.GetEnd(); break; } - case SCC_HEX: // {HEX} - buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last); + case SCC_HEX: { // {HEX} + StringBuilder builder(buff, last); + FormatHexNumber(builder, (uint64)args->GetInt64(SCC_HEX)); + buff = builder.GetEnd(); break; + } - case SCC_BYTES: // {BYTES} - buff = FormatBytes(buff, args->GetInt64(), last); + case SCC_BYTES: { // {BYTES} + StringBuilder builder(buff, last); + FormatBytes(builder, args->GetInt64()); + buff = builder.GetEnd(); break; + } case SCC_CARGO_TINY: { // {CARGO_TINY} /* Tiny description of cargotypes. Layout: @@ -1162,7 +1165,9 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg } } - buff = FormatCommaNumber(buff, amount, last); + StringBuilder builder(buff, last); + FormatCommaNumber(builder, amount); + buff = builder.GetEnd(); break; } @@ -1242,13 +1247,19 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg break; } - case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT} - buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last); + case SCC_CURRENCY_SHORT: { // {CURRENCY_SHORT} + StringBuilder builder(buff, last); + FormatGenericCurrency(builder, _currency, args->GetInt64(), true); + buff = builder.GetEnd(); break; + } - case SCC_CURRENCY_LONG: // {CURRENCY_LONG} - buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last); + case SCC_CURRENCY_LONG: { // {CURRENCY_LONG} + StringBuilder builder(buff, last); + FormatGenericCurrency(builder, _currency, args->GetInt64(SCC_CURRENCY_LONG), false); + buff = builder.GetEnd(); break; + } case SCC_DATE_TINY: // {DATE_TINY} buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last); From 6b1c38e30323e3cf0c10b42f11eddddea6410fd7 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 4 Jun 2023 13:10:22 +0200 Subject: [PATCH 03/35] Fix 3effb893: mention GSAsyncMode in changelog (#10924) --- src/script/api/game_changelog.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp index 798b7d0289..9253748794 100644 --- a/src/script/api/game_changelog.hpp +++ b/src/script/api/game_changelog.hpp @@ -18,6 +18,7 @@ * This version is not yet released. The following changes are not set in stone yet. * * API additions: + * \li GSAsyncMode * \li GSCompanyMode::IsValid * \li GSCompanyMode::IsDeity * \li GSTown::ROAD_LAYOUT_RANDOM From ac1d04255018c2edbefb31b487a8b6fa5f00c353 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 4 Jun 2023 12:14:56 +0100 Subject: [PATCH 04/35] Remove: obsolete NewGRF text unprinting. (#10884) Co-authored-by: Rubidium --- src/newgrf_text.cpp | 11 +---------- src/newgrf_text.h | 2 +- src/strings.cpp | 3 +-- src/table/control_codes.h | 1 - 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/newgrf_text.cpp b/src/newgrf_text.cpp index 92d2de438c..1809a1239d 100644 --- a/src/newgrf_text.cpp +++ b/src/newgrf_text.cpp @@ -357,11 +357,6 @@ std::string TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, bool allow_n Utf8Encode(d, tmp); break; } - case 0x04: - if (src[0] == '\0') goto string_end; - Utf8Encode(d, SCC_NEWGRF_UNPRINT); - Utf8Encode(d, *src++); - break; case 0x06: Utf8Encode(d, SCC_NEWGRF_PRINT_BYTE_HEX); break; case 0x07: Utf8Encode(d, SCC_NEWGRF_PRINT_WORD_HEX); break; case 0x08: Utf8Encode(d, SCC_NEWGRF_PRINT_DWORD_HEX); break; @@ -846,14 +841,13 @@ void RewindTextRefStack() /** * 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 str the string that we need to write * @param argv the OpenTTD stack of values * @param argv_size space on the stack \a argv * @param modify_argv When true, modify the OpenTTD stack. * @return the string control code to "execute" now */ -uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, uint argv_size, bool modify_argv) +uint RemapNewGRFStringControlCode(uint scc, const char **str, int64 *argv, uint argv_size, bool modify_argv) { switch (scc) { default: break; @@ -939,7 +933,6 @@ 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_PRINT_WORD_CARGO_LONG: case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT: @@ -964,7 +957,6 @@ uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const default: break; case SCC_NEWGRF_PUSH_WORD: - case SCC_NEWGRF_UNPRINT: Utf8Consume(str); break; } @@ -1040,7 +1032,6 @@ uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const case SCC_NEWGRF_DISCARD_WORD: case SCC_NEWGRF_ROTATE_TOP_4_WORDS: case SCC_NEWGRF_PUSH_WORD: - case SCC_NEWGRF_UNPRINT: return 0; } } diff --git a/src/newgrf_text.h b/src/newgrf_text.h index 03d4822052..f177f25686 100644 --- a/src/newgrf_text.h +++ b/src/newgrf_text.h @@ -49,7 +49,7 @@ void RewindTextRefStack(); bool UsingNewGRFTextStack(); struct TextRefStack *CreateTextRefStackBackup(); void RestoreTextRefStackBackup(struct TextRefStack *backup); -uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const char **str, int64 *argv, uint argv_size, bool modify_argv); +uint RemapNewGRFStringControlCode(uint scc, const char **str, int64 *argv, uint argv_size, bool modify_argv); /** Mapping of language data between a NewGRF and OpenTTD. */ struct LanguageMap { diff --git a/src/strings.cpp b/src/strings.cpp index dfbae283e2..968eeb3f3d 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -836,7 +836,6 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg } WChar b = '\0'; uint next_substr_case_index = 0; - char *buf_start = buff; std::stack> str_stack; str_stack.push(str_arg); @@ -850,7 +849,7 @@ 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. */ //todo: should argve be passed here too? - b = RemapNewGRFStringControlCode(b, buf_start, &buff, &str, (int64 *)args->GetDataPointer(), args->GetDataLeft(), dry_run); + b = RemapNewGRFStringControlCode(b, &str, (int64 *)args->GetDataPointer(), args->GetDataLeft(), dry_run); if (b == 0) continue; } diff --git a/src/table/control_codes.h b/src/table/control_codes.h index 02806308eb..390d8185ee 100644 --- a/src/table/control_codes.h +++ b/src/table/control_codes.h @@ -150,7 +150,6 @@ enum StringControlCode { SCC_NEWGRF_PRINT_WORD_CARGO_NAME, ///< 9A 1E: Read 2 bytes from the stack as cargo name SCC_NEWGRF_PRINT_DWORD_FORCE, ///< 9A 21: Read 4 bytes from the stack as unsigned force SCC_NEWGRF_PUSH_WORD, ///< 9A 03: Pushes 2 bytes onto the stack - SCC_NEWGRF_UNPRINT, ///< 9A 04: "Unprints" the given number of bytes from the string SCC_NEWGRF_DISCARD_WORD, ///< 85: Discard the next two bytes SCC_NEWGRF_ROTATE_TOP_4_WORDS, ///< 86: Rotate the top 4 words of the stack (W4 W1 W2 W3) SCC_NEWGRF_LAST = SCC_NEWGRF_ROTATE_TOP_4_WORDS, From 993f90b6a00ce0d5d698d6647c059b521665bf48 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 25 May 2023 18:02:59 +0200 Subject: [PATCH 05/35] Codechange: let GenerateDefaultSaveName return std::string --- src/fileio.cpp | 10 +++++----- src/fileio_func.h | 2 +- src/fios_gui.cpp | 3 +-- src/saveload/saveload.cpp | 22 ++++++++++------------ src/saveload/saveload.h | 2 +- src/screenshot.cpp | 2 +- 6 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/fileio.cpp b/src/fileio.cpp index 990510d545..29927236d5 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -1061,17 +1061,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 7023460878..0ec0ab640c 100644 --- a/src/fileio_func.h +++ b/src/fileio_func.h @@ -23,7 +23,7 @@ void FioCreateDirectory(const std::string &name); 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 2cde115cf6..05b397f2b6 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -299,8 +299,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/saveload/saveload.cpp b/src/saveload/saveload.cpp index b2b626386f..39ea3f7b92 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -3170,17 +3170,16 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, */ void DoAutoOrNetsave(FiosNumberedSaveName &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(); } - Debug(sl, 2, "Autosaving to '{}'", buf); - if (SaveOrLoad(buf, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) { + Debug(sl, 2, "Autosaving to '{}'", filename); + if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) { ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR); } } @@ -3193,11 +3192,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 @@ -3222,8 +3219,9 @@ void GenerateDefaultSaveName(char *buf, const char *last) SetDParam(2, TimerGameCalendar::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/saveload/saveload.h b/src/saveload/saveload.h index 88fcf0602c..f1f29dace2 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -393,7 +393,7 @@ enum SavegameType { extern FileToSaveLoad _file_to_saveload; -void GenerateDefaultSaveName(char *buf, const char *last); +std::string GenerateDefaultSaveName(); void SetSaveLoadError(StringID str); const char *GetSaveLoadErrorString(); SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded = true); diff --git a/src/screenshot.cpp b/src/screenshot.cpp index 8d853c52dc..5de7f98e53 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -674,7 +674,7 @@ static const char *MakeScreenshotName(const char *default_fn, const char *ext, b if (_game_mode == GM_EDITOR || _game_mode == GM_MENU || _local_company == COMPANY_SPECTATOR) { strecpy(_screenshot_name, default_fn, lastof(_screenshot_name)); } else { - GenerateDefaultSaveName(_screenshot_name, lastof(_screenshot_name)); + strecpy(_screenshot_name, GenerateDefaultSaveName().c_str(), lastof(_screenshot_name)); } } From 2dffa7d0c65892b86193c9d4cda6bd8ab4bdf9a3 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 18 May 2023 17:25:35 +0200 Subject: [PATCH 06/35] Codechange: let FormatString use StringBuilder --- src/strings.cpp | 278 ++++++++++++++++++++--------------------- src/strings_internal.h | 10 ++ 2 files changed, 149 insertions(+), 139 deletions(-) diff --git a/src/strings.cpp b/src/strings.cpp index 968eeb3f3d..afce124de7 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -171,11 +171,11 @@ void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num) } } -static char *StationGetSpecialString(char *buff, int x, const char *last); +static void StationGetSpecialString(StringBuilder &builder, int x); static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last); static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last); -static char *FormatString(char *buff, const char *str, StringParameters *args, const char *last, uint case_index = 0, bool game_script = false, bool dry_run = false); +static void FormatString(StringBuilder &builder, const char *str, StringParameters *args, uint case_index = 0, bool game_script = false, bool dry_run = false); struct LanguagePack : public LanguagePackHeader { char data[]; // list of strings @@ -251,14 +251,20 @@ char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, co } break; - case TEXT_TAB_GAMESCRIPT_START: - return FormatString(buffr, GetGameStringPtr(index), args, last, case_index, true); + case TEXT_TAB_GAMESCRIPT_START: { + StringBuilder builder(buffr, last); + FormatString(builder, GetGameStringPtr(index), args, case_index, true); + return builder.GetEnd(); + } case TEXT_TAB_OLD_NEWGRF: NOT_REACHED(); - case TEXT_TAB_NEWGRF_START: - return FormatString(buffr, GetGRFStringPtr(index), args, last, case_index); + case TEXT_TAB_NEWGRF_START: { + StringBuilder builder(buffr, last); + FormatString(builder, GetGRFStringPtr(index), args, case_index); + return builder.GetEnd(); + } default: break; @@ -271,7 +277,9 @@ char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, co FatalError("String 0x{:X} is invalid. You are probably using an old version of the .lng file.\n", string); } - return FormatString(buffr, GetStringPtr(string), args, last, case_index); + StringBuilder builder(buffr, last); + FormatString(builder, GetStringPtr(string), args, case_index); + return builder.GetEnd(); } char *GetString(char *buffr, StringID string, const char *last) @@ -281,6 +289,20 @@ char *GetString(char *buffr, StringID string, const char *last) return GetStringWithArgs(buffr, string, &_global_string_params, last); } +/** + * Get a parsed string with most special stringcodes replaced by the string parameters. + * @param builder The builder of the string. + * @param string The ID of the string to parse. + * @param args Arguments for the string. + * @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case. + * @param game_script The string is coming directly from a game script. + */ +void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters *args, uint case_index = 0, bool game_script = false) +{ + builder.AddViaStreCallback([&](auto buff, auto last) { return GetStringWithArgs(buff, string, args, last, case_index, game_script); }); +} + + /** * Resolve the given StringID into a std::string with all the associated * DParam lookups and formatting. @@ -418,27 +440,27 @@ static void FormatBytes(StringBuilder &builder, int64 number) fmt::format_to(builder, NBSP "{}B", iec_prefixes[id]); } -static char *FormatYmdString(char *buff, TimerGameCalendar::Date date, const char *last, uint case_index) +static void FormatYmdString(StringBuilder &builder, TimerGameCalendar::Date date, uint case_index) { TimerGameCalendar::YearMonthDay ymd; TimerGameCalendar::ConvertDateToYMD(date, &ymd); int64 args[] = {ymd.day + STR_DAY_NUMBER_1ST - 1, STR_MONTH_ABBREV_JAN + ymd.month, ymd.year}; StringParameters tmp_params(args); - return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, last, case_index); + FormatString(builder, GetStringPtr(STR_FORMAT_DATE_LONG), &tmp_params, case_index); } -static char *FormatMonthAndYear(char *buff, TimerGameCalendar::Date date, const char *last, uint case_index) +static void FormatMonthAndYear(StringBuilder &builder, TimerGameCalendar::Date date, uint case_index) { TimerGameCalendar::YearMonthDay ymd; TimerGameCalendar::ConvertDateToYMD(date, &ymd); int64 args[] = {STR_MONTH_JAN + ymd.month, ymd.year}; StringParameters tmp_params(args); - return FormatString(buff, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, last, case_index); + FormatString(builder, GetStringPtr(STR_FORMAT_DATE_SHORT), &tmp_params, case_index); } -static char *FormatTinyOrISODate(char *buff, TimerGameCalendar::Date date, StringID str, const char *last) +static void FormatTinyOrISODate(StringBuilder &builder, TimerGameCalendar::Date date, StringID str) { TimerGameCalendar::YearMonthDay ymd; TimerGameCalendar::ConvertDateToYMD(date, &ymd); @@ -446,7 +468,7 @@ static char *FormatTinyOrISODate(char *buff, TimerGameCalendar::Date date, Strin /* Day and month are zero-padded with ZEROFILL_NUM, hence the two 2s. */ int64 args[] = {ymd.day, 2, ymd.month + 1, 2, ymd.year}; StringParameters tmp_params(args); - return FormatString(buff, GetStringPtr(str), &tmp_params, last); + FormatString(builder, GetStringPtr(str), &tmp_params); } static void FormatGenericCurrency(StringBuilder &builder, const CurrencySpec *spec, Money number, bool compact) @@ -627,7 +649,7 @@ static int DeterminePluralForm(int64 count, int plural_form) } } -static const char *ParseStringChoice(const char *b, uint form, char **dst, const char *last) +static const char *ParseStringChoice(const char *b, uint form, StringBuilder &builder) { /* {Length of each string} {each string} */ uint n = (byte)*b++; @@ -639,7 +661,7 @@ static const char *ParseStringChoice(const char *b, uint form, char **dst, const pos += len; } - *dst += seprintf(*dst, last, "%s", b + mypos); + builder += b + mypos; return b + pos; } @@ -806,18 +828,26 @@ static std::vector _game_script_raw_strings; /** * Parse most format codes within a string and write the result to a buffer. - * @param buff The buffer to write the final string to. + * @param builder The string builder to write the final string to. * @param str_arg The original string with format codes. * @param args Pointer to extra arguments used by various string codes. - * @param last Pointer to just past the end of the buff array. - * @param dry_run True when the argt array is not yet initialized. + * @param dry_run True when the args' type data is not yet initialized. */ -static char *FormatString(char *buff, const char *str_arg, StringParameters *args, const char *last, uint case_index, bool game_script, bool dry_run) +static void FormatString(StringBuilder &builder, const char *str_arg, StringParameters *args, uint case_index, bool game_script, bool dry_run) { uint orig_offset = args->offset; - /* When there is no array with types there is no need to do a dry run. */ - if (args->HasTypeInformation() && !dry_run) { + if (!dry_run && args->HasTypeInformation()) { + /* + * FormatString was called without `dry_run` set, however `args` has + * space allocated for type information and thus wants type checks on + * the parameters. So, we need to gather the type information via the + * dry run first, before we can continue formatting the string. + * + * Create a copy of the state of the builder for the dry run, so we do + * not have to reset it after the dry run has completed. + */ + StringBuilder dry_run_builder = builder; if (UsingNewGRFTextStack()) { /* Values from the NewGRF text stack are only copied to the normal * argv array at the time they are encountered. That means that if @@ -826,10 +856,10 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg * pass makes sure the argv array is correctly filled and the second * pass can reference later values without problems. */ struct TextRefStack *backup = CreateTextRefStackBackup(); - FormatString(buff, str_arg, args, last, case_index, game_script, true); + FormatString(dry_run_builder, str_arg, args, case_index, game_script, true); RestoreTextRefStackBackup(backup); } else { - FormatString(buff, str_arg, args, last, case_index, game_script, true); + FormatString(dry_run_builder, str_arg, args, case_index, game_script, true); } /* We have to restore the original offset here to to read the correct values. */ args->offset = orig_offset; @@ -868,13 +898,13 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (*p != ':' && *p != '\0') { while (*p != '\0') p++; str = p; - buff = strecat(buff, "(invalid SCC_ENCODED)", last); + builder += "(invalid SCC_ENCODED)"; break; } if (stringid >= TAB_SIZE_GAMESCRIPT) { while (*p != '\0') p++; str = p; - buff = strecat(buff, "(invalid StringID)", last); + builder += "(invalid StringID)"; break; } @@ -922,7 +952,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (param >= TAB_SIZE_GAMESCRIPT) { while (*p != '\0') p++; str = p; - buff = strecat(buff, "(invalid sub-StringID)", last); + builder += "(invalid sub-StringID)"; break; } param = MakeStringID(TEXT_TAB_GAMESCRIPT_START, param); @@ -942,7 +972,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg /* If we didn't error out, we can actually print the string. */ if (*str != '\0') { str = p; - buff = GetStringWithArgs(buff, MakeStringID(TEXT_TAB_GAMESCRIPT_START, stringid), &sub_args, last, true); + GetStringWithArgs(builder, MakeStringID(TEXT_TAB_GAMESCRIPT_START, stringid), &sub_args, true); } for (i = 0; i < 20; i++) { @@ -981,14 +1011,17 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg char *p = input + Utf8Encode(input, args->GetTypeAtOffset(offset)); *p = '\0'; - /* Now do the string formatting. */ - char buf[256]; + /* The gender is stored at the start of the formatted string. + * So to determine the gender after formatting we only need + * enough space for the gender index token, one character + * for the actual gender and one character for '\0'. */ + char buf[MAX_CHAR_LENGTH + 1 + 1]; bool old_sgd = _scan_for_gender_data; _scan_for_gender_data = true; + StringBuilder tmp_builder(buf, lastof(buf)); StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, nullptr); - p = FormatString(buf, input, &tmp_params, lastof(buf)); + FormatString(tmp_builder, input, &tmp_params); _scan_for_gender_data = old_sgd; - *p = '\0'; /* And determine the string. */ const char *s = buf; @@ -996,7 +1029,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg /* Does this string have a gender, if so, set it */ if (c == SCC_GENDER_INDEX) gender = (byte)s[0]; } - str = ParseStringChoice(str, gender, &buff, last); + str = ParseStringChoice(str, gender, builder); break; } @@ -1004,8 +1037,8 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg * We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. */ case SCC_GENDER_INDEX: // {GENDER 0} if (_scan_for_gender_data) { - buff += Utf8Encode(buff, SCC_GENDER_INDEX); - *buff++ = *str++; + builder.Utf8Encode(SCC_GENDER_INDEX); + builder += *str++; } else { str++; } @@ -1015,7 +1048,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg int plural_form = *str++; // contains the plural form for this string uint offset = orig_offset + (byte)*str++; int64 v = *args->GetPointerToOffset(offset); // contains the number that determines plural - str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), &buff, last); + str = ParseStringChoice(str, DeterminePluralForm(v, plural_form), builder); break; } @@ -1049,16 +1082,16 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg } case SCC_REVISION: // {REV} - buff = strecpy(buff, _openttd_revision, last); + builder += _openttd_revision; break; case SCC_RAW_STRING_POINTER: { // {RAW_STRING} const char *raw_string = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER); if (game_script && std::find(_game_script_raw_strings.begin(), _game_script_raw_strings.end(), raw_string) == _game_script_raw_strings.end()) { - buff = strecat(buff, "(invalid RAW_STRING parameter)", last); + builder += "(invalid RAW_STRING parameter)"; break; } - buff = FormatString(buff, raw_string, args, last); + FormatString(builder, raw_string, args); break; } @@ -1069,7 +1102,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg * For included strings that consume argument, you should use STRING1, STRING2 etc. * To debug stuff you can set argv to nullptr and it will tell you */ StringParameters tmp_params(args->GetDataPointer(), args->GetDataLeft(), nullptr); - buff = GetStringWithArgs(buff, string_id, &tmp_params, last, next_substr_case_index, game_script); + GetStringWithArgs(builder, string_id, &tmp_params, next_substr_case_index, game_script); next_substr_case_index = 0; break; } @@ -1086,59 +1119,43 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (game_script && GetStringTab(string_id) != TEXT_TAB_GAMESCRIPT_START) break; uint size = b - SCC_STRING1 + 1; if (game_script && size > args->GetDataLeft()) { - buff = strecat(buff, "(too many parameters)", last); + builder += "(too many parameters)"; } else { StringParameters sub_args(*args, size); - buff = GetStringWithArgs(buff, string_id, &sub_args, last, next_substr_case_index, game_script); + GetStringWithArgs(builder, string_id, &sub_args, next_substr_case_index, game_script); } next_substr_case_index = 0; break; } - case SCC_COMMA: { // {COMMA} - StringBuilder builder(buff, last); + case SCC_COMMA: // {COMMA} FormatCommaNumber(builder, args->GetInt64(SCC_COMMA)); - buff = builder.GetEnd(); break; - } case SCC_DECIMAL: { // {DECIMAL} int64 number = args->GetInt64(SCC_DECIMAL); int digits = args->GetInt32(SCC_DECIMAL); - StringBuilder builder(buff, last); FormatCommaNumber(builder, number, digits); - buff = builder.GetEnd(); break; } - case SCC_NUM: { // {NUM} - StringBuilder builder(buff, last); + case SCC_NUM: // {NUM} FormatNoCommaNumber(builder, args->GetInt64(SCC_NUM)); - buff = builder.GetEnd(); break; - } case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM} int64 num = args->GetInt64(); - StringBuilder builder(buff, last); FormatZerofillNumber(builder, num, args->GetInt64()); - buff = builder.GetEnd(); break; } - case SCC_HEX: { // {HEX} - StringBuilder builder(buff, last); + case SCC_HEX: // {HEX} FormatHexNumber(builder, (uint64)args->GetInt64(SCC_HEX)); - buff = builder.GetEnd(); break; - } - case SCC_BYTES: { // {BYTES} - StringBuilder builder(buff, last); + case SCC_BYTES: // {BYTES} FormatBytes(builder, args->GetInt64()); - buff = builder.GetEnd(); break; - } case SCC_CARGO_TINY: { // {CARGO_TINY} /* Tiny description of cargotypes. Layout: @@ -1164,9 +1181,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg } } - StringBuilder builder(buff, last); FormatCommaNumber(builder, amount); - buff = builder.GetEnd(); break; } @@ -1183,7 +1198,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg assert(_settings_game.locale.units_weight < lengthof(_units_weight)); int64 args_array[] = {_units_weight[_settings_game.locale.units_weight].c.ToDisplay(args->GetInt64())}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params, last); + FormatString(builder, GetStringPtr(_units_weight[_settings_game.locale.units_weight].l), &tmp_params); break; } @@ -1191,13 +1206,13 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg assert(_settings_game.locale.units_volume < lengthof(_units_volume)); int64 args_array[] = {_units_volume[_settings_game.locale.units_volume].c.ToDisplay(args->GetInt64())}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params, last); + FormatString(builder, GetStringPtr(_units_volume[_settings_game.locale.units_volume].l), &tmp_params); break; } default: { StringParameters tmp_params(*args, 1); - buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last); + GetStringWithArgs(builder, cargo_str, &tmp_params); break; } } @@ -1211,7 +1226,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg StringID cargo_str = !IsValidCargoID(cargo) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier; StringParameters tmp_args(*args, 1); - buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last); + GetStringWithArgs(builder, cargo_str, &tmp_args); break; } @@ -1222,60 +1237,49 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg for (const auto &cs : _sorted_cargo_specs) { if (!HasBit(cmask, cs->Index())) continue; - if (buff >= last - 2) break; // ',' and ' ' + if (builder.Remaining() < 2) break; // ", " if (first) { first = false; } else { /* Add a comma if this is not the first item */ - *buff++ = ','; - *buff++ = ' '; + builder += ", "; } - buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script); + GetStringWithArgs(builder, cs->name, args, next_substr_case_index, game_script); } /* If first is still true then no cargo is accepted */ - if (first) buff = GetStringWithArgs(buff, STR_JUST_NOTHING, args, last, next_substr_case_index, game_script); + if (first) GetStringWithArgs(builder, STR_JUST_NOTHING, args, next_substr_case_index, game_script); - *buff = '\0'; next_substr_case_index = 0; - - /* Make sure we detect any buffer overflow */ - assert(buff < last); break; } - case SCC_CURRENCY_SHORT: { // {CURRENCY_SHORT} - StringBuilder builder(buff, last); + case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT} FormatGenericCurrency(builder, _currency, args->GetInt64(), true); - buff = builder.GetEnd(); break; - } - case SCC_CURRENCY_LONG: { // {CURRENCY_LONG} - StringBuilder builder(buff, last); + case SCC_CURRENCY_LONG: // {CURRENCY_LONG} FormatGenericCurrency(builder, _currency, args->GetInt64(SCC_CURRENCY_LONG), false); - buff = builder.GetEnd(); break; - } case SCC_DATE_TINY: // {DATE_TINY} - buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last); + FormatTinyOrISODate(builder, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY); break; case SCC_DATE_SHORT: // {DATE_SHORT} - buff = FormatMonthAndYear(buff, args->GetInt32(SCC_DATE_SHORT), last, next_substr_case_index); + FormatMonthAndYear(builder, args->GetInt32(SCC_DATE_SHORT), next_substr_case_index); next_substr_case_index = 0; break; case SCC_DATE_LONG: // {DATE_LONG} - buff = FormatYmdString(buff, args->GetInt32(SCC_DATE_LONG), last, next_substr_case_index); + FormatYmdString(builder, args->GetInt32(SCC_DATE_LONG), next_substr_case_index); next_substr_case_index = 0; break; case SCC_DATE_ISO: // {DATE_ISO} - buff = FormatTinyOrISODate(buff, args->GetInt32(), STR_FORMAT_DATE_ISO, last); + FormatTinyOrISODate(builder, args->GetInt32(), STR_FORMAT_DATE_ISO); break; case SCC_FORCE: { // {FORCE} @@ -1283,7 +1287,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg const auto &x = _units_force[_settings_game.locale.units_force]; int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last); + FormatString(builder, GetStringPtr(x.s), &tmp_params); break; } @@ -1292,7 +1296,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg const auto &x = _units_height[_settings_game.locale.units_height]; int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last); + FormatString(builder, GetStringPtr(x.s), &tmp_params); break; } @@ -1301,7 +1305,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg const auto &x = _units_power[_settings_game.locale.units_power]; int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last); + FormatString(builder, GetStringPtr(x.s), &tmp_params); break; } @@ -1311,7 +1315,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg const auto &x = _units_power_to_weight[setting]; int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last); + FormatString(builder, GetStringPtr(x.s), &tmp_params); break; } @@ -1324,7 +1328,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg const auto &x = _units_velocity[units]; int64 args_array[] = {ConvertKmhishSpeedToDisplaySpeed(GB(arg, 0, 56), vt), x.decimal_places}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last); + FormatString(builder, GetStringPtr(x.s), &tmp_params); break; } @@ -1333,7 +1337,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg const auto &x = _units_volume[_settings_game.locale.units_volume]; int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last); + FormatString(builder, GetStringPtr(x.s), &tmp_params); break; } @@ -1342,7 +1346,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg const auto &x = _units_volume[_settings_game.locale.units_volume]; int64 args_array[] = {x.c.ToDisplay(args->GetInt64(SCC_VOLUME_LONG)), x.decimal_places}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(x.l), &tmp_params, last); + FormatString(builder, GetStringPtr(x.l), &tmp_params); break; } @@ -1351,7 +1355,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg const auto &x = _units_weight[_settings_game.locale.units_weight]; int64 args_array[] = {x.c.ToDisplay(args->GetInt64()), x.decimal_places}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(x.s), &tmp_params, last); + FormatString(builder, GetStringPtr(x.s), &tmp_params); break; } @@ -1360,7 +1364,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg const auto &x = _units_weight[_settings_game.locale.units_weight]; int64 args_array[] = {x.c.ToDisplay(args->GetInt64(SCC_WEIGHT_LONG)), x.decimal_places}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(x.l), &tmp_params, last); + FormatString(builder, GetStringPtr(x.l), &tmp_params); break; } @@ -1371,11 +1375,11 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (!c->name.empty()) { int64 args_array[] = {(int64)(size_t)c->name.c_str()}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params); } else { int64 args_array[] = {c->name_2}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, c->name_1, &tmp_params, last); + GetStringWithArgs(builder, c->name_1, &tmp_params); } break; } @@ -1387,7 +1391,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (Company::IsValidHumanID(company)) { int64 args_array[] = {company + 1}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_FORMAT_COMPANY_NUM, &tmp_params, last); + GetStringWithArgs(builder, STR_FORMAT_COMPANY_NUM, &tmp_params); } break; } @@ -1398,7 +1402,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg uint64 args_array[] = {(uint64)args->GetInt32()}; WChar types_array[] = {SCC_STATION_NAME}; StringParameters tmp_params(args_array, 1, types_array); - buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params, last); + GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_AIRCRAFT, &tmp_params); break; } @@ -1406,11 +1410,11 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (!d->name.empty()) { int64 args_array[] = {(int64)(size_t)d->name.c_str()}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params); } else { int64 args_array[] = {d->town->index, d->town_cn + 1}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params, last); + GetStringWithArgs(builder, STR_FORMAT_DEPOT_NAME_TRAIN + 2 * vt + (d->town_cn == 0 ? 0 : 1), &tmp_params); } break; } @@ -1423,7 +1427,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (!e->name.empty() && e->IsEnabled()) { int64 args_array[] = {(int64)(size_t)e->name.c_str()}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params); break; } @@ -1439,7 +1443,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg uint64 tmp_dparam[6] = { 0 }; WChar tmp_type[6] = { 0 }; StringParameters tmp_params(tmp_dparam, 6, tmp_type); - buff = GetStringWithArgs(buff, GetGRFStringID(grffile->grfid, 0xD000 + callback), &tmp_params, last); + GetStringWithArgs(builder, GetGRFStringID(grffile->grfid, 0xD000 + callback), &tmp_params); StopTextRefStackUsage(); break; @@ -1447,7 +1451,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg } StringParameters tmp_params(nullptr, 0, nullptr); - buff = GetStringWithArgs(buff, e->info.string_id, &tmp_params, last); + GetStringWithArgs(builder, e->info.string_id, &tmp_params); break; } @@ -1458,12 +1462,12 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (!g->name.empty()) { int64 args_array[] = {(int64)(size_t)g->name.c_str()}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params); } else { int64 args_array[] = {g->index}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_NAME, &tmp_params, last); + GetStringWithArgs(builder, STR_FORMAT_GROUP_NAME, &tmp_params); } break; } @@ -1475,18 +1479,18 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg static bool use_cache = true; if (use_cache) { // Use cached version if first call AutoRestoreBackup cache_backup(use_cache, false); - buff = strecpy(buff, i->GetCachedName().c_str(), last); + builder += i->GetCachedName(); } else if (_scan_for_gender_data) { /* Gender is defined by the industry type. * STR_FORMAT_INDUSTRY_NAME may have the town first, so it would result in the gender of the town name */ StringParameters tmp_params(nullptr, 0, nullptr); - buff = FormatString(buff, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, last, next_substr_case_index); + FormatString(builder, GetStringPtr(GetIndustrySpec(i->type)->name), &tmp_params, next_substr_case_index); } else { /* First print the town name and the industry type name. */ int64 args_array[2] = {i->town->index, GetIndustrySpec(i->type)->name}; StringParameters tmp_params(args_array); - buff = FormatString(buff, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, last, next_substr_case_index); + FormatString(builder, GetStringPtr(STR_FORMAT_INDUSTRY_NAME), &tmp_params, next_substr_case_index); } next_substr_case_index = 0; break; @@ -1499,11 +1503,11 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (!c->president_name.empty()) { int64 args_array[] = {(int64)(size_t)c->president_name.c_str()}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params); } else { int64 args_array[] = {c->president_name_2}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, c->president_name_1, &tmp_params, last); + GetStringWithArgs(builder, c->president_name_1, &tmp_params); } break; } @@ -1517,18 +1521,18 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg * be "drawing" an invalid station is in the case of cargo that is * in transit. */ StringParameters tmp_params(nullptr, 0, nullptr); - buff = GetStringWithArgs(buff, STR_UNKNOWN_STATION, &tmp_params, last); + GetStringWithArgs(builder, STR_UNKNOWN_STATION, &tmp_params); break; } static bool use_cache = true; if (use_cache) { // Use cached version if first call AutoRestoreBackup cache_backup(use_cache, false); - buff = strecpy(buff, st->GetCachedName(), last); + builder += st->GetCachedName(); } else if (!st->name.empty()) { int64 args_array[] = {(int64)(size_t)st->name.c_str()}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params); } else { StringID string_id = st->string_id; if (st->indtype != IT_INVALID) { @@ -1546,7 +1550,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg uint64 args_array[] = {STR_TOWN_NAME, st->town->index, st->index}; WChar types_array[] = {0, SCC_TOWN_NAME, SCC_NUM}; StringParameters tmp_params(args_array, 3, types_array); - buff = GetStringWithArgs(buff, string_id, &tmp_params, last); + GetStringWithArgs(builder, string_id, &tmp_params); } break; } @@ -1558,13 +1562,13 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg static bool use_cache = true; if (use_cache) { // Use cached version if first call AutoRestoreBackup cache_backup(use_cache, false); - buff = strecpy(buff, t->GetCachedName().c_str(), last); + builder += t->GetCachedName(); } else if (!t->name.empty()) { int64 args_array[] = {(int64)(size_t)t->name.c_str()}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params); } else { - buff = GetTownName(buff, t, last); + builder.AddViaStreCallback([&t](auto buff, auto last) { return GetTownName(buff, t, last); }); } break; } @@ -1576,13 +1580,13 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (!wp->name.empty()) { int64 args_array[] = {(int64)(size_t)wp->name.c_str()}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params); } else { int64 args_array[] = {wp->town->index, wp->town_cn + 1}; StringParameters tmp_params(args_array); StringID string_id = ((wp->string_id == STR_SV_STNAME_BUOY) ? STR_FORMAT_BUOY_NAME : STR_FORMAT_WAYPOINT_NAME); if (wp->town_cn != 0) string_id++; - buff = GetStringWithArgs(buff, string_id, &tmp_params, last); + GetStringWithArgs(builder, string_id, &tmp_params); } break; } @@ -1594,12 +1598,12 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (!v->name.empty()) { int64 args_array[] = {(int64)(size_t)v->name.c_str()}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params); } else if (v->group_id != DEFAULT_GROUP) { /* The vehicle has no name, but is member of a group, so print group name */ int64 args_array[] = {v->group_id, v->unitnumber}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_FORMAT_GROUP_VEHICLE_NAME, &tmp_params, last); + GetStringWithArgs(builder, STR_FORMAT_GROUP_VEHICLE_NAME, &tmp_params); } else { int64 args_array[] = {v->unitnumber}; StringParameters tmp_params(args_array); @@ -1613,7 +1617,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg case VEH_AIRCRAFT: string_id = STR_SV_AIRCRAFT_NAME; break; } - buff = GetStringWithArgs(buff, string_id, &tmp_params, last); + GetStringWithArgs(builder, string_id, &tmp_params); } break; } @@ -1625,38 +1629,34 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg if (!si->name.empty()) { int64 args_array[] = {(int64)(size_t)si->name.c_str()}; StringParameters tmp_params(args_array); - buff = GetStringWithArgs(buff, STR_JUST_RAW_STRING, &tmp_params, last); + GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params); } else { StringParameters tmp_params(nullptr, 0, nullptr); - buff = GetStringWithArgs(buff, STR_DEFAULT_SIGN_NAME, &tmp_params, last); + GetStringWithArgs(builder, STR_DEFAULT_SIGN_NAME, &tmp_params); } break; } case SCC_STATION_FEATURES: { // {STATIONFEATURES} - buff = StationGetSpecialString(buff, args->GetInt32(SCC_STATION_FEATURES), last); + StationGetSpecialString(builder, args->GetInt32(SCC_STATION_FEATURES)); break; } default: - if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b); + builder.Utf8Encode(b); break; } } - *buff = '\0'; - return buff; } -static char *StationGetSpecialString(char *buff, int x, const char *last) +static void StationGetSpecialString(StringBuilder &builder, int x) { - if ((x & FACIL_TRAIN) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN); - if ((x & FACIL_TRUCK_STOP) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY); - if ((x & FACIL_BUS_STOP) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS); - if ((x & FACIL_DOCK) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP); - if ((x & FACIL_AIRPORT) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE); - *buff = '\0'; - return buff; + if ((x & FACIL_TRAIN) != 0) builder.Utf8Encode(SCC_TRAIN); + if ((x & FACIL_TRUCK_STOP) != 0) builder.Utf8Encode(SCC_LORRY); + if ((x & FACIL_BUS_STOP) != 0) builder.Utf8Encode(SCC_BUS); + if ((x & FACIL_DOCK) != 0) builder.Utf8Encode(SCC_SHIP); + if ((x & FACIL_AIRPORT) != 0) builder.Utf8Encode(SCC_PLANE); } static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last) diff --git a/src/strings_internal.h b/src/strings_internal.h index a77eb0266f..b5cb739d38 100644 --- a/src/strings_internal.h +++ b/src/strings_internal.h @@ -118,6 +118,16 @@ public: { return (ptrdiff_t)(this->last - this->current); } + + /** + * Add a string using the strecpy/strecat-esque calling signature. + * @param function The function to pass the current and last location to, + * that will then return the new current location. + */ + void AddViaStreCallback(std::function function) + { + this->current = function(this->current, this->last); + } }; #endif /* STRINGS_INTERNAL_H */ From 84037d4a5723e2ad52dd8689eb4ebc5e8702a29b Mon Sep 17 00:00:00 2001 From: Rubidium Date: Tue, 30 May 2023 19:42:49 +0200 Subject: [PATCH 07/35] Codechange: use std::string for parameters in the dbg_helpers --- src/misc/array.hpp | 4 +--- src/misc/dbg_helpers.cpp | 16 ++++++++-------- src/misc/dbg_helpers.h | 16 ++++++++-------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/misc/array.hpp b/src/misc/array.hpp index aea58aba42..277fe29a36 100644 --- a/src/misc/array.hpp +++ b/src/misc/array.hpp @@ -108,9 +108,7 @@ public: dmp.WriteValue("num_items", num_items); for (uint i = 0; i < num_items; i++) { const T &item = (*this)[i]; - char name[32]; - seprintf(name, lastof(name), "item[%d]", i); - dmp.WriteStructT(name, &item); + dmp.WriteStructT(fmt::format("item[{}]", i), &item); } } }; diff --git a/src/misc/dbg_helpers.cpp b/src/misc/dbg_helpers.cpp index b197e9a8af..cad18a6eb8 100644 --- a/src/misc/dbg_helpers.cpp +++ b/src/misc/dbg_helpers.cpp @@ -112,30 +112,30 @@ void DumpTarget::WriteIndent() } /** Write 'name = value' with indent and new-line. */ -void DumpTarget::WriteValue(const char *name, int value) +void DumpTarget::WriteValue(const std::string &name, int value) { WriteIndent(); - m_out += std::string(name) + " = " + std::to_string(value) + "\n"; + m_out += name + " = " + std::to_string(value) + "\n"; } /** Write 'name = value' with indent and new-line. */ -void DumpTarget::WriteValue(const char *name, const char *value_str) +void DumpTarget::WriteValue(const std::string &name, const std::string &value_str) { WriteIndent(); - m_out += std::string(name) + " = " + value_str + "\n"; + m_out += name + " = " + value_str + "\n"; } /** Write name & TileIndex to the output. */ -void DumpTarget::WriteTile(const char *name, TileIndex tile) +void DumpTarget::WriteTile(const std::string &name, TileIndex tile) { WriteIndent(); - m_out += std::string(name) + " = " + TileStr(tile) + "\n"; + m_out += name + " = " + TileStr(tile) + "\n"; } /** * Open new structure (one level deeper than the current one) 'name = {\'. */ -void DumpTarget::BeginStruct(size_t type_id, const char *name, const void *ptr) +void DumpTarget::BeginStruct(size_t type_id, const std::string &name, const void *ptr) { /* make composite name */ std::string cur_name = GetCurrentStructName(); @@ -152,7 +152,7 @@ void DumpTarget::BeginStruct(size_t type_id, const char *name, const void *ptr) m_known_names.insert(KNOWN_NAMES::value_type(KnownStructKey(type_id, ptr), cur_name)); WriteIndent(); - m_out += std::string(name) + " = {\n"; + m_out += name + " = {\n"; m_indent++; } diff --git a/src/misc/dbg_helpers.h b/src/misc/dbg_helpers.h index 5317b77d30..8453aa1618 100644 --- a/src/misc/dbg_helpers.h +++ b/src/misc/dbg_helpers.h @@ -130,21 +130,21 @@ struct DumpTarget { void WriteIndent(); - void WriteValue(const char *name, int value); - void WriteValue(const char *name, const char *value_str); - void WriteTile(const char *name, TileIndex t); + void WriteValue(const std::string &name, int value); + void WriteValue(const std::string &name, const std::string &value_str); + void WriteTile(const std::string &name, TileIndex t); /** Dump given enum value (as a number and as named value) */ - template void WriteEnumT(const char *name, E e) + template void WriteEnumT(const std::string &name, E e) { - WriteValue(name, ValueStr(e).c_str()); + WriteValue(name, ValueStr(e)); } - void BeginStruct(size_t type_id, const char *name, const void *ptr); + void BeginStruct(size_t type_id, const std::string &name, const void *ptr); void EndStruct(); /** Dump nested object (or only its name if this instance is already known). */ - template void WriteStructT(const char *name, const S *s) + template void WriteStructT(const std::string &name, const S *s) { static size_t type_id = ++LastTypeId(); @@ -157,7 +157,7 @@ struct DumpTarget { if (FindKnownName(type_id, s, known_as)) { /* We already know this one, no need to dump it. */ std::string known_as_str = std::string("known_as.") + name; - WriteValue(name, known_as_str.c_str()); + WriteValue(name, known_as_str); } else { /* Still unknown, dump it */ BeginStruct(type_id, name, s); From 556b44713e10094f8a1e46a63dfea85653747c20 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Tue, 30 May 2023 22:25:00 +0200 Subject: [PATCH 08/35] Codechange: use std::string for midi filenames --- src/music/midifile.cpp | 20 ++++++++------------ src/music/midifile.hpp | 6 +++--- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/music/midifile.cpp b/src/music/midifile.cpp index 4598569a01..060f5393e4 100644 --- a/src/music/midifile.cpp +++ b/src/music/midifile.cpp @@ -413,7 +413,7 @@ static bool FixupMidiData(MidiFile &target) * @param[out] header filled with data read * @return true if the file could be opened and contained a header with correct format */ -bool MidiFile::ReadSMFHeader(const char *filename, SMFHeader &header) +bool MidiFile::ReadSMFHeader(const std::string &filename, SMFHeader &header) { FILE *file = FioFOpenFile(filename, "rb", Subdirectory::BASESET_DIR); if (!file) return false; @@ -455,7 +455,7 @@ bool MidiFile::ReadSMFHeader(FILE *file, SMFHeader &header) * @param filename name of the file to load * @returns true if loaded was successful */ -bool MidiFile::LoadFile(const char *filename) +bool MidiFile::LoadFile(const std::string &filename) { _midifile_instance = this; @@ -850,7 +850,7 @@ bool MidiFile::LoadSong(const MusicSongInfo &song) { switch (song.filetype) { case MTT_STANDARDMIDI: - return this->LoadFile(song.filename.c_str()); + return this->LoadFile(song.filename); case MTT_MPSMIDI: { size_t songdatalen = 0; @@ -916,7 +916,7 @@ static void WriteVariableLen(FILE *f, uint32 value) * @param filename Name of file to write to * @return True if the file was written to completion */ -bool MidiFile::WriteSMF(const char *filename) +bool MidiFile::WriteSMF(const std::string &filename) { FILE *f = FioFOpenFile(filename, "wb", Subdirectory::NO_DIRECTORY); if (!f) { @@ -1100,7 +1100,7 @@ std::string MidiFile::GetSMFFile(const MusicSongInfo &song) } free(data); - if (midifile.WriteSMF(output_filename.c_str())) { + if (midifile.WriteSMF(output_filename)) { return output_filename; } else { return std::string(); @@ -1124,14 +1124,10 @@ static bool CmdDumpSMF(byte argc, char *argv[]) return false; } - char fnbuf[MAX_PATH] = { 0 }; - if (seprintf(fnbuf, lastof(fnbuf), "%s%s", FiosGetScreenshotDir(), argv[1]) >= (int)lengthof(fnbuf)) { - IConsolePrint(CC_ERROR, "Filename too long."); - return false; - } - IConsolePrint(CC_INFO, "Dumping MIDI to '{}'.", fnbuf); + std::string filename = fmt::format("{}{}", FiosGetScreenshotDir(), argv[1]); + IConsolePrint(CC_INFO, "Dumping MIDI to '{}'.", filename); - if (_midifile_instance->WriteSMF(fnbuf)) { + if (_midifile_instance->WriteSMF(filename)) { IConsolePrint(CC_INFO, "File written successfully."); return true; } else { diff --git a/src/music/midifile.hpp b/src/music/midifile.hpp index 16e6678e27..ccc5a71623 100644 --- a/src/music/midifile.hpp +++ b/src/music/midifile.hpp @@ -35,15 +35,15 @@ struct MidiFile { MidiFile(); ~MidiFile(); - bool LoadFile(const char *filename); + bool LoadFile(const std::string &filename); bool LoadMpsData(const byte *data, size_t length); bool LoadSong(const MusicSongInfo &song); void MoveFrom(MidiFile &other); - bool WriteSMF(const char *filename); + bool WriteSMF(const std::string &filename); static std::string GetSMFFile(const MusicSongInfo &song); - static bool ReadSMFHeader(const char *filename, SMFHeader &header); + static bool ReadSMFHeader(const std::string &filename, SMFHeader &header); static bool ReadSMFHeader(FILE *file, SMFHeader &header); }; From 0e56a73fb8002469b3d819d0ef75226fe312a7fe Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 4 Jun 2023 16:40:17 +0200 Subject: [PATCH 09/35] Fix: disable hardware acceleration when GPU driver crashed the game last attempt (#10928) --- src/driver.cpp | 44 +++++++++++++++++++++++++++++++++++++- src/driver.h | 2 ++ src/lang/english.txt | 1 + src/video/video_driver.cpp | 8 +++++++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/driver.cpp b/src/driver.cpp index ea03604bbe..acb441309a 100644 --- a/src/driver.cpp +++ b/src/driver.cpp @@ -16,8 +16,15 @@ #include "video/video_driver.hpp" #include "string_func.h" #include "table/strings.h" +#include "fileio_func.h" #include +#ifdef _WIN32 +# include +#else +# include +#endif /* _WIN32 */ + #include "safeguards.h" std::string _ini_videodriver; ///< The video driver a stored in the configuration file. @@ -32,6 +39,8 @@ std::string _ini_musicdriver; ///< The music driver a stored in the confi std::string _ini_blitter; ///< The blitter as stored in the configuration file. bool _blitter_autodetected; ///< Was the blitter autodetected or specified by the user? +static const std::string HWACCELERATION_TEST_FILE = "hwaccel.dat"; ///< Filename to test if we crashed last time we tried to use hardware acceleration. + /** * Get a string parameter the list of parameters. * @param parm The parameters. @@ -114,6 +123,27 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t if (type == Driver::DT_VIDEO && !_video_hw_accel && d->UsesHardwareAcceleration()) continue; + if (type == Driver::DT_VIDEO && _video_hw_accel && d->UsesHardwareAcceleration()) { + /* Check if we have already tried this driver in last run. + * If it is here, it most likely means we crashed. So skip + * hardware acceleration. */ + auto filename = FioFindFullPath(BASE_DIR, HWACCELERATION_TEST_FILE); + if (!filename.empty()) { + unlink(filename.c_str()); + + Debug(driver, 1, "Probing {} driver '{}' skipped due to earlier crash", GetDriverTypeName(type), d->name); + + _video_hw_accel = false; + ErrorMessageData msg(STR_VIDEO_DRIVER_ERROR, STR_VIDEO_DRIVER_ERROR_HARDWARE_ACCELERATION_CRASH, true); + ScheduleErrorMessage(msg); + continue; + } + + /* Write empty file to note we are attempting hardware acceleration. */ + auto f = FioFOpenFile(HWACCELERATION_TEST_FILE, "w", BASE_DIR); + FioFCloseFile(f); + } + Driver *oldd = *GetActiveDriver(type); Driver *newd = d->CreateInstance(); *GetActiveDriver(type) = newd; @@ -131,7 +161,7 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t if (type == Driver::DT_VIDEO && _video_hw_accel && d->UsesHardwareAcceleration()) { _video_hw_accel = false; - ErrorMessageData msg(STR_VIDEO_DRIVER_ERROR, STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION); + ErrorMessageData msg(STR_VIDEO_DRIVER_ERROR, STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION, true); ScheduleErrorMessage(msg); } } @@ -177,6 +207,18 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t } } +/** + * Mark the current video driver as operational. + */ +void DriverFactoryBase::MarkVideoDriverOperational() +{ + /* As part of the detection whether the GPU driver crashes the game, + * and as we are operational now, remove the hardware acceleration + * test-file. */ + auto filename = FioFindFullPath(BASE_DIR, HWACCELERATION_TEST_FILE); + if (!filename.empty()) unlink(filename.c_str()); +} + /** * Build a human readable list of available drivers, grouped by type. * @param output_iterator The iterator to write the string to. diff --git a/src/driver.h b/src/driver.h index 80b05b41bd..4683784266 100644 --- a/src/driver.h +++ b/src/driver.h @@ -100,6 +100,8 @@ private: static bool SelectDriverImpl(const std::string &name, Driver::Type type); + static void MarkVideoDriverOperational(); + protected: DriverFactoryBase(Driver::Type type, int priority, const char *name, const char *description); diff --git a/src/lang/english.txt b/src/lang/english.txt index 0c4e06aff6..36248f1edd 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2080,6 +2080,7 @@ STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG :{WHITE}Allocati # Video initalization errors STR_VIDEO_DRIVER_ERROR :{WHITE}Error with video settings... STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION :{WHITE}... no compatible GPU found. Hardware acceleration disabled +STR_VIDEO_DRIVER_ERROR_HARDWARE_ACCELERATION_CRASH :{WHITE}... GPU driver crashed the game. Hardware acceleration disabled # Intro window STR_INTRO_CAPTION :{WHITE}OpenTTD {REV} diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp index 8fe582760f..ad1c8fb808 100644 --- a/src/video/video_driver.cpp +++ b/src/video/video_driver.cpp @@ -12,6 +12,7 @@ #include "../network/network.h" #include "../blitter/factory.hpp" #include "../debug.h" +#include "../driver.h" #include "../fontcache.h" #include "../gfx_func.h" #include "../gfxinit.h" @@ -156,6 +157,13 @@ void VideoDriver::Tick() this->Paint(); this->UnlockVideoBuffer(); + + /* Wait till the first successful drawing tick before marking the driver as operational. */ + static bool first_draw_tick = true; + if (first_draw_tick) { + first_draw_tick = false; + DriverFactoryBase::MarkVideoDriverOperational(); + } } } From 87ccff16b5668d2ee3f11f7ab5240513abbdfdb7 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 25 May 2023 18:23:26 +0200 Subject: [PATCH 10/35] Codechange: use std::string for the screenshot name/path --- src/crashlog.cpp | 2 +- src/screenshot.cpp | 35 +++++++++++++++-------------------- src/screenshot.h | 2 +- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 8f1f05ce44..0d613fdd10 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -417,7 +417,7 @@ bool CrashLog::WriteScreenshot() std::string filename = this->CreateFileName("", false); bool res = MakeScreenshot(SC_CRASHLOG, filename); - if (res) this->screenshot_filename = _full_screenshot_name; + if (res) this->screenshot_filename = _full_screenshot_path; return res; } diff --git a/src/screenshot.cpp b/src/screenshot.cpp index 5de7f98e53..7aef660406 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -40,8 +40,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. /** @@ -668,43 +668,39 @@ 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 { - strecpy(_screenshot_name, GenerateDefaultSaveName().c_str(), lastof(_screenshot_name)); + _screenshot_name = GenerateDefaultSaveName(); } } - size_t len = strlen(_screenshot_name); - /* Handle user-specified filenames ending in # with automatic numbering */ if (StrEndsWith(_screenshot_name, "#")) { generate = true; - len -= 1; - _screenshot_name[len] = '\0'; + _screenshot_name.pop_back(); } + size_t len = _screenshot_name.size(); /* Add extension to screenshot file */ - seprintf(&_screenshot_name[len], lastof(_screenshot_name), ".%s", ext); + _screenshot_name += fmt::format(".{}", 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 = fmt::format("{}{}", screenshot_dir, _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 += fmt::format("#{}.{}", serial, ext); } - return _full_screenshot_name; + return _full_screenshot_path.c_str(); } /** Make a screenshot of the current screen. */ @@ -922,8 +918,7 @@ static bool RealMakeScreenshot(ScreenshotType t, std::string name, uint32 width, SetScreenshotWindowVisibility(false); } - _screenshot_name[0] = '\0'; - if (!name.empty()) strecpy(_screenshot_name, name.c_str(), lastof(_screenshot_name)); + _screenshot_name = name; bool ret; switch (t) { diff --git a/src/screenshot.h b/src/screenshot.h index 6324cab11f..9ce10c0497 100644 --- a/src/screenshot.h +++ b/src/screenshot.h @@ -34,6 +34,6 @@ bool MakeMinimapWorldScreenshot(); 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 */ From ee2d0745e9a87e24387bfc023337acc914e60b71 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 4 Jun 2023 15:57:36 +0100 Subject: [PATCH 11/35] Fix #10502: Refit engine before attaching free wagons. (#10926) Caused by incorrect order of operations when buying a train engine with refit and attaching free wagons. --- src/train.h | 1 + src/train_cmd.cpp | 9 ++------- src/train_cmd.h | 2 +- src/vehicle_cmd.cpp | 7 ++++++- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/train.h b/src/train.h index 36c99c7b95..6f6c9a4861 100644 --- a/src/train.h +++ b/src/train.h @@ -66,6 +66,7 @@ int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, i void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type); bool TrainOnCrossing(TileIndex tile); +void NormalizeTrainVehInDepot(const Train *u); /** Variables that are cached to improve performance and such */ struct TrainCache { diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 6a0243d26c..0a8fe12b7e 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -686,7 +686,7 @@ static CommandCost CmdBuildRailWagon(DoCommandFlag flags, TileIndex tile, const } /** Move all free vehicles in the depot to the train */ -static void NormalizeTrainVehInDepot(const Train *u) +void NormalizeTrainVehInDepot(const Train *u) { for (const Train *v : Train::Iterate()) { if (v->IsFreeWagon() && v->tile == u->tile && @@ -737,11 +737,10 @@ static void AddRearEngineToMultiheadedTrain(Train *v) * @param flags type of operation. * @param tile tile of the depot where rail-vehicle is built. * @param e the engine to build. - * @param free_cars add any free cars to the train. * @param[out] ret the vehicle that has been built. * @return the cost of this operation or an error. */ -CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, bool free_cars, Vehicle **ret) +CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret) { const RailVehicleInfo *rvi = &e->u.rail; @@ -807,10 +806,6 @@ CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engin v->ConsistChanged(CCF_ARRANGE); UpdateTrainGroupID(v); - if (free_cars && !(flags & DC_AUTOREPLACE)) { // check if the cars should be added to the new vehicle - NormalizeTrainVehInDepot(v); - } - CheckConsistencyOfArticulatedVehicle(v); } diff --git a/src/train_cmd.h b/src/train_cmd.h index 3d224717a5..bf365a851c 100644 --- a/src/train_cmd.h +++ b/src/train_cmd.h @@ -14,7 +14,7 @@ #include "engine_type.h" #include "vehicle_type.h" -CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, bool free_cars, Vehicle **ret); +CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret); CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user); CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID dest_veh, bool move_chain); diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index 7f8ac804a9..c23445aa9c 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -135,7 +135,7 @@ std::tuple CmdBuildVehicle(DoC Vehicle *v = nullptr; switch (type) { - case VEH_TRAIN: value.AddCost(CmdBuildRailVehicle(subflags, tile, e, use_free_vehicles, &v)); break; + case VEH_TRAIN: value.AddCost(CmdBuildRailVehicle(subflags, tile, e, &v)); break; case VEH_ROAD: value.AddCost(CmdBuildRoadVehicle(subflags, tile, e, &v)); break; case VEH_SHIP: value.AddCost(CmdBuildShip (subflags, tile, e, &v)); break; case VEH_AIRCRAFT: value.AddCost(CmdBuildAircraft (subflags, tile, e, &v)); break; @@ -172,6 +172,11 @@ std::tuple CmdBuildVehicle(DoC } if (flags & DC_EXEC) { + if (type == VEH_TRAIN && use_free_vehicles && !(flags & DC_AUTOREPLACE)) { + /* Move any free wagons to the new vehicle. */ + NormalizeTrainVehInDepot(Train::From(v)); + } + InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowClassesData(GetWindowClassForVehicleType(type), 0); SetWindowDirty(WC_COMPANY, _current_company); From c43a23cea8c20391c79a17ad5b594291841e6279 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 4 Jun 2023 17:07:18 +0200 Subject: [PATCH 12/35] Fix: crash when not even a single row fits for dropdowns on low resolution screens (#10934) --- src/widgets/dropdown.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index f6e50e5ad2..6ffbfc1bd6 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -385,11 +385,8 @@ void ShowDropDownListAt(Window *w, DropDownList &&list, int selected, int button scroll = true; uint avg_height = height / (uint)list.size(); - /* Check at least there is space for one item. */ - assert(available_height >= avg_height); - - /* Fit the list. */ - uint rows = available_height / avg_height; + /* Fit the list; create at least one row, even if there is no height available. */ + uint rows = std::max(available_height / avg_height, 1); height = rows * avg_height; /* Add space for the scrollbar. */ From 0f3dd9c796c8a5cd83b78e60ec1303089f082a4d Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 4 Jun 2023 17:39:57 +0200 Subject: [PATCH 13/35] Fix: crash when window can't be placed on low resolution screens. (#10932) Co-authored-by: Jonathan G Rennison --- src/core/math_func.hpp | 26 ++++++++++++++++++++++++++ src/misc_gui.cpp | 4 ++-- src/tests/math_func.cpp | 10 ++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/core/math_func.hpp b/src/core/math_func.hpp index 254496a733..a9784dee51 100644 --- a/src/core/math_func.hpp +++ b/src/core/math_func.hpp @@ -83,6 +83,32 @@ static inline T Clamp(const T a, const T min, const T max) return a; } +/** + * Clamp a value between an interval. + * + * This function returns a value which is between the given interval of + * min and max. If the given value is in this interval the value itself + * is returned otherwise the border of the interval is returned, according + * which side of the interval was 'left'. + * + * @note If the min value is greater than the max, return value is the average of the min and max. + * @param a The value to clamp/truncate. + * @param min The minimum of the interval. + * @param max the maximum of the interval. + * @returns A value between min and max which is closest to a. + */ +template +static inline T SoftClamp(const T a, const T min, const T max) +{ + if (min > max) { + using U = std::make_unsigned_t; + return min - (U(min) - max) / 2; + } + if (a <= min) return min; + if (a >= max) return max; + return a; +} + /** * Clamp an integer between an interval. * diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 802d67edd3..69475fdfd4 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -1140,8 +1140,8 @@ struct QueryWindow : public Window { void FindWindowPlacementAndResize(int def_width, int def_height) override { /* Position query window over the calling window, ensuring it's within screen bounds. */ - this->left = Clamp(parent->left + (parent->width / 2) - (this->width / 2), 0, _screen.width - this->width); - this->top = Clamp(parent->top + (parent->height / 2) - (this->height / 2), 0, _screen.height - this->height); + this->left = SoftClamp(parent->left + (parent->width / 2) - (this->width / 2), 0, _screen.width - this->width); + this->top = SoftClamp(parent->top + (parent->height / 2) - (this->height / 2), 0, _screen.height - this->height); this->SetDirty(); } diff --git a/src/tests/math_func.cpp b/src/tests/math_func.cpp index 8db813c105..25abb1838e 100644 --- a/src/tests/math_func.cpp +++ b/src/tests/math_func.cpp @@ -120,3 +120,13 @@ TEST_CASE("ClampTo") /* max uint64_t gets clamped to max int64_t. */ CHECK(std::numeric_limits::max() == ClampTo(std::numeric_limits::max())); } + + +TEST_CASE("SoftClamp") +{ + /* Special behaviour of soft clamp returning the average of min/max when min is higher than max. */ + CHECK(1250 == SoftClamp(0, 1500, 1000)); + int million = 1000 * 1000; + CHECK(1250 * million == SoftClamp(0, 1500 * million, 1000 * million)); + CHECK(0 == SoftClamp(0, 1500 * million, -1500 * million)); +} From 84b53213afa159f052d080f38616a64db7f8cf36 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 26 May 2023 12:45:18 +0100 Subject: [PATCH 14/35] Codechange: Refactor viewport catchment overlay change handling --- src/viewport.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/viewport.cpp b/src/viewport.cpp index 8e5e7ead81..d76ad65475 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -3513,6 +3513,19 @@ void MarkCatchmentTilesDirty() } } +static void SetWindowDirtyForViewportCatchment() +{ + if (_viewport_highlight_station != nullptr) SetWindowDirty(WC_STATION_VIEW, _viewport_highlight_station->index); + if (_viewport_highlight_town != nullptr) SetWindowDirty(WC_TOWN_VIEW, _viewport_highlight_town->index); +} + +static void ClearViewportCatchment() +{ + MarkCatchmentTilesDirty(); + _viewport_highlight_station = nullptr; + _viewport_highlight_town = nullptr; +} + /** * Select or deselect station for coverage area highlight. * Selecting a station will deselect a town. @@ -3521,12 +3534,10 @@ void MarkCatchmentTilesDirty() */ void SetViewportCatchmentStation(const Station *st, bool sel) { - if (_viewport_highlight_station != nullptr) SetWindowDirty(WC_STATION_VIEW, _viewport_highlight_station->index); - if (_viewport_highlight_town != nullptr) SetWindowDirty(WC_TOWN_VIEW, _viewport_highlight_town->index); + SetWindowDirtyForViewportCatchment(); if (sel && _viewport_highlight_station != st) { - MarkCatchmentTilesDirty(); + ClearViewportCatchment(); _viewport_highlight_station = st; - _viewport_highlight_town = nullptr; MarkCatchmentTilesDirty(); } else if (!sel && _viewport_highlight_station == st) { MarkCatchmentTilesDirty(); @@ -3543,10 +3554,9 @@ void SetViewportCatchmentStation(const Station *st, bool sel) */ void SetViewportCatchmentTown(const Town *t, bool sel) { - if (_viewport_highlight_town != nullptr) SetWindowDirty(WC_TOWN_VIEW, _viewport_highlight_town->index); - if (_viewport_highlight_station != nullptr) SetWindowDirty(WC_STATION_VIEW, _viewport_highlight_station->index); + SetWindowDirtyForViewportCatchment(); if (sel && _viewport_highlight_town != t) { - _viewport_highlight_station = nullptr; + ClearViewportCatchment(); _viewport_highlight_town = t; MarkWholeScreenDirty(); } else if (!sel && _viewport_highlight_town == t) { From d7bf6b2c07b764d76c3e7713bef798b32f8a990d Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 26 May 2023 12:45:18 +0100 Subject: [PATCH 15/35] Feature: Highlight waypoint tiles when adjacent or distant joining --- src/rail_gui.cpp | 12 ++++++++++++ src/station_gui.cpp | 44 ++++++++++++++++++++++++++++++++------------ src/station_gui.h | 1 + src/viewport.cpp | 34 +++++++++++++++++++++++++++++++++- src/viewport_func.h | 17 +++++++++++++++++ 5 files changed, 95 insertions(+), 13 deletions(-) diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index cef9403570..17f0a32057 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -422,6 +422,7 @@ struct BuildRailToolbarWindow : Window { void Close() override { if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) SetViewportCatchmentStation(nullptr, true); + if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false); CloseWindowById(WC_SELECT_STATION, 0); this->Window::Close(); @@ -731,6 +732,7 @@ struct BuildRailToolbarWindow : Window { void OnPlaceObjectAbort() override { if (this->IsWidgetLowered(WID_RAT_BUILD_STATION)) SetViewportCatchmentStation(nullptr, true); + if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) SetViewportCatchmentWaypoint(nullptr, true); this->RaiseButtons(); this->DisableWidget(WID_RAT_REMOVE); @@ -757,6 +759,11 @@ struct BuildRailToolbarWindow : Window { return ES_NOT_HANDLED; } + void OnRealtimeTick(uint delta_ms) override + { + if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) CheckRedrawWaypointCoverage(this); + } + static HotkeyList hotkeys; }; @@ -2176,6 +2183,11 @@ struct BuildRailWaypointWindow : PickerWindowBase { this->InvalidateData(); } } + + void OnRealtimeTick(uint delta_ms) override + { + CheckRedrawWaypointCoverage(this); + } }; /* static */ QueryString BuildRailWaypointWindow::editbox(BuildRailWaypointWindow::FILTER_LENGTH * MAX_CHAR_LENGTH, BuildRailWaypointWindow::FILTER_LENGTH); diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 4a8cf9b958..236b8fc7dc 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -80,11 +80,12 @@ int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageTyp * Find stations adjacent to the current tile highlight area, so that existing coverage * area can be drawn. */ -static void FindStationsAroundSelection() +template +void FindStationsAroundSelection() { /* With distant join we don't know which station will be selected, so don't show any */ if (_ctrl_pressed) { - SetViewportCatchmentStation(nullptr, true); + SetViewportCatchmentSpecializedStation(nullptr, true); return; } @@ -95,15 +96,16 @@ static void FindStationsAroundSelection() uint x = TileX(location.tile); uint y = TileY(location.tile); - int max_c = 1; + /* Waypoints can only be built on existing rail tiles, so don't extend area if not highlighting a rail tile. */ + int max_c = T::EXPECTED_FACIL == FACIL_WAYPOINT && !IsTileType(location.tile, MP_RAILWAY) ? 0 : 1; TileArea ta(TileXY(std::max(0, x - max_c), std::max(0, y - max_c)), TileXY(std::min(Map::MaxX(), x + location.w + max_c), std::min(Map::MaxY(), y + location.h + max_c))); - Station *adjacent = nullptr; + T *adjacent = nullptr; /* Direct loop instead of ForAllStationsAroundTiles as we are not interested in catchment area */ for (TileIndex tile : ta) { if (IsTileType(tile, MP_STATION) && GetTileOwner(tile) == _local_company) { - Station *st = Station::GetByTile(tile); + T *st = T::GetByTile(tile); if (st == nullptr) continue; if (adjacent != nullptr && st != adjacent) { /* Multiple nearby, distant join is required. */ @@ -113,7 +115,7 @@ static void FindStationsAroundSelection() adjacent = st; } } - SetViewportCatchmentStation(adjacent, true); + SetViewportCatchmentSpecializedStation(adjacent, true); } /** @@ -135,7 +137,25 @@ void CheckRedrawStationCoverage(const Window *w) w->SetDirty(); if (_settings_client.gui.station_show_coverage && _thd.drawstyle == HT_RECT) { - FindStationsAroundSelection(); + FindStationsAroundSelection(); + } + } +} + +void CheckRedrawWaypointCoverage(const Window *w) +{ + /* Test if ctrl state changed */ + static bool _last_ctrl_pressed; + if (_ctrl_pressed != _last_ctrl_pressed) { + _thd.dirty = 0xff; + _last_ctrl_pressed = _ctrl_pressed; + } + + if (_thd.dirty & 1) { + _thd.dirty &= ~1; + + if (_thd.drawstyle == HT_RECT) { + FindStationsAroundSelection(); } } } @@ -2255,7 +2275,7 @@ struct SelectStationWindow : Window { void Close() override { - if (_settings_client.gui.station_show_coverage) SetViewportCatchmentStation(nullptr, true); + SetViewportCatchmentSpecializedStation(nullptr, true); _thd.freeze = false; this->Window::Close(); @@ -2342,15 +2362,15 @@ struct SelectStationWindow : Window { void OnMouseOver(Point pt, int widget) override { - if (widget != WID_JS_PANEL || T::EXPECTED_FACIL == FACIL_WAYPOINT) { - SetViewportCatchmentStation(nullptr, true); + if (widget != WID_JS_PANEL) { + SetViewportCatchmentSpecializedStation(nullptr, true); return; } /* Show coverage area of station under cursor */ auto it = this->vscroll->GetScrolledItemFromWidget(_stations_nearby_list, pt.y, this, WID_JS_PANEL, WidgetDimensions::scaled.framerect.top); - const Station *st = it == _stations_nearby_list.end() || *it == NEW_STATION ? nullptr : Station::Get(*it); - SetViewportCatchmentStation(st, true); + const T *st = it == _stations_nearby_list.end() || *it == NEW_STATION ? nullptr : T::Get(*it); + SetViewportCatchmentSpecializedStation(st, true); } }; diff --git a/src/station_gui.h b/src/station_gui.h index 503f2f88ab..009bcd7bb9 100644 --- a/src/station_gui.h +++ b/src/station_gui.h @@ -25,6 +25,7 @@ enum StationCoverageType { int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies); void CheckRedrawStationCoverage(const Window *w); +void CheckRedrawWaypointCoverage(const Window *w); using StationPickerCmdProc = std::function; diff --git a/src/viewport.cpp b/src/viewport.cpp index d76ad65475..30f69ba7c7 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -992,7 +992,8 @@ enum TileHighlightType { }; const Station *_viewport_highlight_station; ///< Currently selected station for coverage area highlight -const Town *_viewport_highlight_town; ///< Currently selected town for coverage area highlight +const Waypoint *_viewport_highlight_waypoint; ///< Currently selected waypoint for coverage area highlight +const Town *_viewport_highlight_town; ///< Currently selected town for coverage area highlight /** * Get tile highlight type of coverage area for a given tile. @@ -1005,6 +1006,9 @@ static TileHighlightType GetTileHighlightType(TileIndex t) if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_station->index) return THT_WHITE; if (_viewport_highlight_station->TileIsInCatchment(t)) return THT_BLUE; } + if (_viewport_highlight_waypoint != nullptr) { + if (IsTileType(t, MP_STATION) && GetStationIndex(t) == _viewport_highlight_waypoint->index) return THT_BLUE; + } if (_viewport_highlight_town != nullptr) { if (IsTileType(t, MP_HOUSE)) { @@ -3511,11 +3515,18 @@ void MarkCatchmentTilesDirty() } } } + if (_viewport_highlight_waypoint != nullptr) { + if (!_viewport_highlight_waypoint->IsInUse()) { + _viewport_highlight_waypoint = nullptr; + } + MarkWholeScreenDirty(); + } } static void SetWindowDirtyForViewportCatchment() { if (_viewport_highlight_station != nullptr) SetWindowDirty(WC_STATION_VIEW, _viewport_highlight_station->index); + if (_viewport_highlight_waypoint != nullptr) SetWindowDirty(WC_WAYPOINT_VIEW, _viewport_highlight_waypoint->index); if (_viewport_highlight_town != nullptr) SetWindowDirty(WC_TOWN_VIEW, _viewport_highlight_town->index); } @@ -3523,6 +3534,7 @@ static void ClearViewportCatchment() { MarkCatchmentTilesDirty(); _viewport_highlight_station = nullptr; + _viewport_highlight_waypoint = nullptr; _viewport_highlight_town = nullptr; } @@ -3546,6 +3558,26 @@ void SetViewportCatchmentStation(const Station *st, bool sel) if (_viewport_highlight_station != nullptr) SetWindowDirty(WC_STATION_VIEW, _viewport_highlight_station->index); } +/** + * Select or deselect waypoint for coverage area highlight. + * Selecting a waypoint will deselect a town. + * @param *wp Waypoint in question + * @param sel Select or deselect given waypoint + */ +void SetViewportCatchmentWaypoint(const Waypoint *wp, bool sel) +{ + SetWindowDirtyForViewportCatchment(); + if (sel && _viewport_highlight_waypoint != wp) { + ClearViewportCatchment(); + _viewport_highlight_waypoint = wp; + MarkCatchmentTilesDirty(); + } else if (!sel && _viewport_highlight_waypoint == wp) { + MarkCatchmentTilesDirty(); + _viewport_highlight_waypoint = nullptr; + } + if (_viewport_highlight_waypoint != nullptr) SetWindowDirty(WC_WAYPOINT_VIEW, _viewport_highlight_waypoint->index); +} + /** * Select or deselect town for coverage area highlight. * Selecting a town will deselect a station. diff --git a/src/viewport_func.h b/src/viewport_func.h index 20bf5837c1..651fe44573 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -95,10 +95,27 @@ static inline void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset = Point GetViewportStationMiddle(const Viewport *vp, const Station *st); struct Station; +struct Waypoint; struct Town; void SetViewportCatchmentStation(const Station *st, bool sel); +void SetViewportCatchmentWaypoint(const Waypoint *wp, bool sel); void SetViewportCatchmentTown(const Town *t, bool sel); void MarkCatchmentTilesDirty(); +template +void SetViewportCatchmentSpecializedStation(const T *st, bool sel); + +template<> +inline void SetViewportCatchmentSpecializedStation(const Station *st, bool sel) +{ + SetViewportCatchmentStation(st, sel); +} + +template<> +inline void SetViewportCatchmentSpecializedStation(const Waypoint *st, bool sel) +{ + SetViewportCatchmentWaypoint(st, sel); +} + #endif /* VIEWPORT_FUNC_H */ From ec7f9f63f616a5d81e5d40a27664e681370c94aa Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 26 May 2023 12:45:18 +0100 Subject: [PATCH 16/35] Feature: Add coverage button to waypoint window --- src/waypoint_gui.cpp | 16 +++++++++++++++- src/widgets/waypoint_widget.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/waypoint_gui.cpp b/src/waypoint_gui.cpp index 0d688b49d9..8081984e0c 100644 --- a/src/waypoint_gui.cpp +++ b/src/waypoint_gui.cpp @@ -79,6 +79,7 @@ public: void Close() override { CloseWindowById(GetWindowClassForVehicleType(this->vt), VehicleListIdentifier(VL_STATION_LIST, this->vt, this->owner, this->window_number).Pack(), false); + SetViewportCatchmentWaypoint(Waypoint::Get(this->window_number), false); this->Window::Close(); } @@ -87,6 +88,15 @@ public: if (widget == WID_W_CAPTION) SetDParam(0, this->wp->index); } + void OnPaint() override + { + extern const Waypoint *_viewport_highlight_waypoint; + this->SetWidgetDisabledState(WID_W_CATCHMENT, !this->wp->IsInUse()); + this->SetWidgetLoweredState(WID_W_CATCHMENT, _viewport_highlight_waypoint == this->wp); + + this->DrawWidgets(); + } + void OnClick(Point pt, int widget, int click_count) override { switch (widget) { @@ -106,6 +116,10 @@ public: case WID_W_SHOW_VEHICLES: // show list of vehicles having this waypoint in their orders ShowVehicleListWindow(this->wp->owner, this->vt, this->wp->index); break; + + case WID_W_CATCHMENT: + SetViewportCatchmentWaypoint(Waypoint::Get(this->window_number), !this->IsWidgetLowered(WID_W_CATCHMENT)); + break; } } @@ -162,7 +176,7 @@ static const NWidgetPart _nested_waypoint_view_widgets[] = { EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 0), EndContainer(), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_W_CATCHMENT), SetMinimalSize(50, 12), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_BUTTON_CATCHMENT, STR_TOOLTIP_CATCHMENT), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_W_SHOW_VEHICLES), SetMinimalSize(15, 12), SetDataTip(STR_SHIP, STR_STATION_VIEW_SCHEDULED_SHIPS_TOOLTIP), NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), diff --git a/src/widgets/waypoint_widget.h b/src/widgets/waypoint_widget.h index a5b316034f..bca6082bd1 100644 --- a/src/widgets/waypoint_widget.h +++ b/src/widgets/waypoint_widget.h @@ -17,6 +17,7 @@ enum WaypointWidgets { WID_W_CENTER_VIEW, ///< Center the main view on this waypoint. WID_W_RENAME, ///< Rename this waypoint. WID_W_SHOW_VEHICLES, ///< Show the vehicles visiting this waypoint. + WID_W_CATCHMENT, ///< Coverage button. }; #endif /* WIDGETS_WAYPOINT_WIDGET_H */ From 36aaa9d68389c5d522802b7d66ebf13adf95bffb Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 18 May 2023 18:33:18 +0200 Subject: [PATCH 17/35] Codechange: let GetStringWithArgs use StringBuilder --- src/industry_cmd.cpp | 4 +- src/newgrf_townname.cpp | 14 +++--- src/newgrf_townname.h | 1 - src/station_cmd.cpp | 4 +- src/strings.cpp | 106 ++++++++++++++++++++-------------------- src/strings_func.h | 2 +- src/strings_internal.h | 8 +++ src/townname.cpp | 44 +++++++++++++---- 8 files changed, 106 insertions(+), 77 deletions(-) diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 69baeffeb4..26d25fa2aa 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -2431,11 +2431,9 @@ void Industry::RecomputeProductionMultipliers() void Industry::FillCachedName() const { - char buf[256]; int64 args_array[] = { this->index }; StringParameters tmp_params(args_array); - 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/newgrf_townname.cpp b/src/newgrf_townname.cpp index ce394be77e..a5f67ba3e1 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" @@ -46,7 +47,7 @@ void DelGRFTownName(uint32 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 seed, byte id) { assert(t != nullptr); for (const auto &partlist : t->partlists[id]) { @@ -57,25 +58,22 @@ static char *RandomPart(char *buf, const GRFTownName *t, uint32 seed, byte id, c 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 grfid, uint16 gen, uint32 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; } diff --git a/src/newgrf_townname.h b/src/newgrf_townname.h index df72656d84..ec5356d9ce 100644 --- a/src/newgrf_townname.h +++ b/src/newgrf_townname.h @@ -47,7 +47,6 @@ GRFTownName *AddGRFTownName(uint32 grfid); GRFTownName *GetGRFTownName(uint32 grfid); void DelGRFTownName(uint32 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); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 8310d5703d..ec7d99db7a 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -461,11 +461,9 @@ void UpdateAllStationVirtCoords() void BaseStation::FillCachedName() const { - char buf[MAX_LENGTH_STATION_NAME_CHARS * MAX_CHAR_LENGTH]; int64 args_array[] = { this->index }; StringParameters tmp_params(args_array); - 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/strings.cpp b/src/strings.cpp index afce124de7..3e54ae8120 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -172,8 +172,8 @@ void CopyOutDParam(uint64 *dst, const char **strings, StringID string, int num) } static void StationGetSpecialString(StringBuilder &builder, int x); -static char *GetSpecialTownNameString(char *buff, int ind, uint32 seed, const char *last); -static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, const char *last); +static void GetSpecialTownNameString(StringBuilder &builder, int ind, uint32 seed); +static void GetSpecialNameString(StringBuilder &builder, int ind, StringParameters *args); static void FormatString(StringBuilder &builder, const char *str, StringParameters *args, uint case_index = 0, bool game_script = false, bool dry_run = false); @@ -216,17 +216,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); @@ -234,13 +235,15 @@ 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->GetInt32(), last); + GetSpecialTownNameString(builder, index - 0xC0, args->GetInt32()); + return; } break; case TEXT_TAB_SPECIAL: if (index >= 0xE4 && !game_script) { - return GetSpecialNameString(buffr, index - 0xE4, args, last); + GetSpecialNameString(builder, index - 0xE4, args); + return; } break; @@ -252,18 +255,16 @@ char *GetStringWithArgs(char *buffr, StringID string, StringParameters *args, co break; case TEXT_TAB_GAMESCRIPT_START: { - StringBuilder builder(buffr, last); FormatString(builder, GetGameStringPtr(index), args, case_index, true); - return builder.GetEnd(); + return; } case TEXT_TAB_OLD_NEWGRF: NOT_REACHED(); case TEXT_TAB_NEWGRF_START: { - StringBuilder builder(buffr, last); FormatString(builder, GetGRFStringPtr(index), args, case_index); - return builder.GetEnd(); + return; } default: @@ -272,34 +273,21 @@ 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); } FatalError("String 0x{:X} is invalid. You are probably using an old version of the .lng file.\n", string); } - StringBuilder builder(buffr, last); FormatString(builder, GetStringPtr(string), args, case_index); - return builder.GetEnd(); } char *GetString(char *buffr, StringID string, const char *last) { _global_string_params.ClearTypeInformation(); _global_string_params.offset = 0; - return GetStringWithArgs(buffr, string, &_global_string_params, last); -} - -/** - * Get a parsed string with most special stringcodes replaced by the string parameters. - * @param builder The builder of the string. - * @param string The ID of the string to parse. - * @param args Arguments for the string. - * @param case_index The "case index". This will only be set when FormatString wants to print the string in a different case. - * @param game_script The string is coming directly from a game script. - */ -void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters *args, uint case_index = 0, bool game_script = false) -{ - builder.AddViaStreCallback([&](auto buff, auto last) { return GetStringWithArgs(buff, string, args, last, case_index, game_script); }); + StringBuilder builder(buffr, last); + GetStringWithArgs(builder, string, &_global_string_params); + return builder.GetEnd(); } @@ -316,6 +304,20 @@ std::string GetString(StringID string) return buffer; } +/** + * 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) +{ + char buffer[DRAW_STRING_BUFFER]; + StringBuilder builder(buffer, lastof(buffer)); + GetStringWithArgs(builder, string, args); + return std::string(buffer, builder.GetEnd()); +} + /** * This function is used to "bind" a C string to a OpenTTD dparam slot. * @param n slot of the string @@ -1568,7 +1570,7 @@ static void FormatString(StringBuilder &builder, const char *str_arg, StringPara StringParameters tmp_params(args_array); GetStringWithArgs(builder, STR_JUST_RAW_STRING, &tmp_params); } else { - builder.AddViaStreCallback([&t](auto buff, auto last) { return GetTownName(buff, t, last); }); + GetTownName(builder, t); } break; } @@ -1659,9 +1661,9 @@ static void StationGetSpecialString(StringBuilder &builder, int x) 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 seed) { - return GenerateTownNameString(buff, last, ind, seed); + builder.AddViaStreCallback([&](auto buff, auto last) { return GenerateTownNameString(buff, last, ind, seed); }); } static const char * const _silly_company_names[] = { @@ -1732,7 +1734,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 arg) { const char * const *base; uint num; @@ -1745,13 +1747,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 x) { char initial[] = "?. "; const char * const *base; @@ -1759,12 +1759,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) { @@ -1775,28 +1775,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->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)], last); + builder += _silly_company_names[std::min(args->GetInt32() & 0xFFFF, lengthof(_silly_company_names) - 1)]; + return; case 2: // used for Foobar & Co company names - return GenAndCoName(buff, args->GetInt32(), last); + GenAndCoName(builder, args->GetInt32()); + return; case 3: // President name - return GenPresidentName(buff, args->GetInt32(), last); + GenPresidentName(builder, args->GetInt32()); + return; } /* town name? */ if (IsInsideMM(ind - 6, 0, SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1)) { - buff = GetSpecialTownNameString(buff, ind - 6, args->GetInt32(), last); - return strecpy(buff, " Transport", last); + GetSpecialTownNameString(builder, ind - 6, args->GetInt32()); + builder += " Transport"; + return; } NOT_REACHED(); diff --git a/src/strings_func.h b/src/strings_func.h index 6ea9e46d47..bcaa42ba99 100644 --- a/src/strings_func.h +++ b/src/strings_func.h @@ -172,7 +172,7 @@ extern StringParameters _global_string_params; 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); +std::string GetStringWithArgs(StringID string, StringParameters *args); const char *GetStringPtr(StringID string); uint ConvertKmhishSpeedToDisplaySpeed(uint speed, VehicleType type); diff --git a/src/strings_internal.h b/src/strings_internal.h index b5cb739d38..61a5745e4a 100644 --- a/src/strings_internal.h +++ b/src/strings_internal.h @@ -10,6 +10,8 @@ #ifndef STRINGS_INTERNAL_H #define STRINGS_INTERNAL_H +#include "strings_func.h" + /** * Equivalent to the std::back_insert_iterator in function, with some * convenience helpers for string concatenation. @@ -130,4 +132,10 @@ public: } }; +void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters *args, uint case_index = 0, bool game_script = false); + +/* Do not leak the StringBuilder to everywhere. */ +void GetTownName(StringBuilder &builder, const struct Town *t); +void GRFTownNameGenerate(StringBuilder &builder, uint32 grfid, uint16 gen, uint32 seed); + #endif /* STRINGS_INTERNAL_H */ diff --git a/src/townname.cpp b/src/townname.cpp index fa4449df7f..4ae4be2404 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" @@ -38,6 +39,24 @@ TownNameParams::TownNameParams(const Town *t) : } +/** + * Fills builder with specified town name. + * @param builder The string builder. + * @param par Town name parameters. + * @param townnameparts 'Encoded' town name. + */ +static void GetTownName(StringBuilder &builder, const TownNameParams *par, uint32 townnameparts) +{ + if (par->grfid == 0) { + int64 args_array[1] = { townnameparts }; + StringParameters tmp_params(args_array); + GetStringWithArgs(builder, par->type, &tmp_params); + return; + } + + GRFTownNameGenerate(builder, par->grfid, par->type, townnameparts); +} + /** * Fills buffer with specified town name * @param buff buffer start @@ -48,15 +67,21 @@ TownNameParams::TownNameParams(const Town *t) : */ char *GetTownName(char *buff, const TownNameParams *par, uint32 townnameparts, const char *last) { - if (par->grfid == 0) { - int64 args_array[1] = { townnameparts }; - StringParameters tmp_params(args_array); - return GetStringWithArgs(buff, par->type, &tmp_params, last); - } - - return GRFTownNameGenerate(buff, par->grfid, par->type, townnameparts, last); + StringBuilder builder(buff, last); + GetTownName(builder, par, townnameparts); + return builder.GetEnd(); } +/** + * 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 @@ -67,8 +92,9 @@ char *GetTownName(char *buff, const TownNameParams *par, uint32 townnameparts, c */ char *GetTownName(char *buff, const Town *t, const char *last) { - TownNameParams par(t); - return GetTownName(buff, &par, t->townnameparts, last); + StringBuilder builder(buff, last); + GetTownName(builder, t); + return builder.GetEnd(); } From caa0474d30a172ee3c5994e124fc67300cd41206 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 4 Jun 2023 18:28:58 +0200 Subject: [PATCH 18/35] Fix: crash with tooltip on low resolution screens (#10933) --- src/misc_gui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 69475fdfd4..7495b71398 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -698,9 +698,9 @@ struct TooltipsWindow : public Window /* Correctly position the tooltip position, watch out for window and cursor size * Clamp value to below main toolbar and above statusbar. If tooltip would * go below window, flip it so it is shown above the cursor */ - pt.y = Clamp(_cursor.pos.y + _cursor.total_size.y + _cursor.total_offs.y + 5, scr_top, scr_bot); + pt.y = SoftClamp(_cursor.pos.y + _cursor.total_size.y + _cursor.total_offs.y + 5, scr_top, scr_bot); if (pt.y + sm_height > scr_bot) pt.y = std::min(_cursor.pos.y + _cursor.total_offs.y - 5, scr_bot) - sm_height; - pt.x = sm_width >= _screen.width ? 0 : Clamp(_cursor.pos.x - (sm_width >> 1), 0, _screen.width - sm_width); + pt.x = sm_width >= _screen.width ? 0 : SoftClamp(_cursor.pos.x - (sm_width >> 1), 0, _screen.width - sm_width); return pt; } From 2c667b1773c52f26116d3f906f0ab67b40cccbed Mon Sep 17 00:00:00 2001 From: Bernard Teo Date: Mon, 5 Jun 2023 01:45:18 +0800 Subject: [PATCH 19/35] Doc: Fix spelling error in ScriptTileList::RemoveRectangle (#10937) --- src/script/api/script_tilelist.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script/api/script_tilelist.hpp b/src/script/api/script_tilelist.hpp index 5d2c9d1898..ea584b7111 100644 --- a/src/script/api/script_tilelist.hpp +++ b/src/script/api/script_tilelist.hpp @@ -37,7 +37,7 @@ public: void AddTile(TileIndex tile); /** - * Remove the tiles inside the rectangle between tile_from and tile_to form the list. + * Remove the tiles inside the rectangle between tile_from and tile_to from the list. * @param tile_from One corner of the tiles to remove. * @param tile_to The other corner of the files to remove. * @pre ScriptMap::IsValidTile(tile_from). From 6a519f5d89a2683655a6eeac8d50ce02cb004d4e Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 4 Jun 2023 18:02:46 +0200 Subject: [PATCH 20/35] Cleanup: strecat has no uses anymore --- src/safeguards.h | 2 +- src/string.cpp | 27 --------------------------- src/string_func.h | 1 - src/strings_internal.h | 2 +- 4 files changed, 2 insertions(+), 30 deletions(-) diff --git a/src/safeguards.h b/src/safeguards.h index e3ae5326ac..02da05b918 100644 --- a/src/safeguards.h +++ b/src/safeguards.h @@ -36,7 +36,7 @@ #define strcpy SAFEGUARD_DO_NOT_USE_THIS_METHOD #define strncpy SAFEGUARD_DO_NOT_USE_THIS_METHOD -/* Use strecat instead. */ +/* Use std::string concatenation/fmt::format instead. */ #define strcat SAFEGUARD_DO_NOT_USE_THIS_METHOD #define strncat SAFEGUARD_DO_NOT_USE_THIS_METHOD diff --git a/src/string.cpp b/src/string.cpp index f55ebfbe24..c16ec8c540 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -49,33 +49,6 @@ #include "safeguards.h" #undef vsnprintf -/** - * Appends characters from one string to another. - * - * Appends the source string to the destination string with respect of the - * terminating null-character and and the last pointer to the last element - * in the destination buffer. If the last pointer is set to nullptr no - * boundary check is performed. - * - * @note usage: strecat(dst, src, lastof(dst)); - * @note lastof() applies only to fixed size arrays - * - * @param dst The buffer containing the target string - * @param src The buffer containing the string to append - * @param last The pointer to the last element of the destination buffer - * @return The pointer to the terminating null-character in the destination buffer - */ -char *strecat(char *dst, const char *src, const char *last) -{ - assert(dst <= last); - while (*dst != '\0') { - if (dst == last) return dst; - dst++; - } - - return strecpy(dst, src, last); -} - /** * Copies characters from one buffer to another. diff --git a/src/string_func.h b/src/string_func.h index 914cd064c3..d66a992f09 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -30,7 +30,6 @@ #include "core/span_type.hpp" #include "string_type.h" -char *strecat(char *dst, const char *src, const char *last) NOACCESS(3); char *strecpy(char *dst, const char *src, const char *last) NOACCESS(3); char *stredup(const char *src, const char *last = nullptr) NOACCESS(2); diff --git a/src/strings_internal.h b/src/strings_internal.h index 61a5745e4a..34a93e9cdc 100644 --- a/src/strings_internal.h +++ b/src/strings_internal.h @@ -122,7 +122,7 @@ public: } /** - * Add a string using the strecpy/strecat-esque calling signature. + * Add a string using the strecpy-esque calling signature. * @param function The function to pass the current and last location to, * that will then return the new current location. */ From bfcb027cb9d82b5cab0689986278db1abf4b854f Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sun, 4 Jun 2023 19:55:47 +0200 Subject: [PATCH 21/35] Fix 2dffa7d: fmt::format_to copies the iterator, so some text does not remain during formatting (#10940) --- src/strings.cpp | 15 ++++++++------- src/strings_internal.h | 18 +++++++++--------- src/townname.cpp | 4 ++-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/strings.cpp b/src/strings.cpp index 3e54ae8120..582b02622f 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -285,7 +285,7 @@ char *GetString(char *buffr, StringID string, const char *last) { _global_string_params.ClearTypeInformation(); _global_string_params.offset = 0; - StringBuilder builder(buffr, last); + StringBuilder builder(&buffr, last); GetStringWithArgs(builder, string, &_global_string_params); return builder.GetEnd(); } @@ -313,7 +313,8 @@ std::string GetString(StringID string) std::string GetStringWithArgs(StringID string, StringParameters *args) { char buffer[DRAW_STRING_BUFFER]; - StringBuilder builder(buffer, lastof(buffer)); + char *state = buffer; + StringBuilder builder(&state, lastof(buffer)); GetStringWithArgs(builder, string, args); return std::string(buffer, builder.GetEnd()); } @@ -845,11 +846,10 @@ static void FormatString(StringBuilder &builder, const char *str_arg, StringPara * space allocated for type information and thus wants type checks on * the parameters. So, we need to gather the type information via the * dry run first, before we can continue formatting the string. - * - * Create a copy of the state of the builder for the dry run, so we do - * not have to reset it after the dry run has completed. */ - StringBuilder dry_run_builder = builder; + char buffer[DRAW_STRING_BUFFER]; + char *state = buffer; + StringBuilder dry_run_builder(&state, lastof(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 @@ -1018,9 +1018,10 @@ static void FormatString(StringBuilder &builder, const char *str_arg, StringPara * enough space for the gender index token, one character * for the actual gender and one character for '\0'. */ char buf[MAX_CHAR_LENGTH + 1 + 1]; + char *state = buf; bool old_sgd = _scan_for_gender_data; _scan_for_gender_data = true; - StringBuilder tmp_builder(buf, lastof(buf)); + StringBuilder tmp_builder(&state, lastof(buf)); StringParameters tmp_params(args->GetPointerToOffset(offset), args->num_param - offset, nullptr); FormatString(tmp_builder, input, &tmp_params); _scan_for_gender_data = old_sgd; diff --git a/src/strings_internal.h b/src/strings_internal.h index 34a93e9cdc..df6dacaf3f 100644 --- a/src/strings_internal.h +++ b/src/strings_internal.h @@ -20,7 +20,7 @@ * extra functions to ease the migration from char buffers to std::string. */ class StringBuilder { - char *current; ///< The current location to add strings + char **current; ///< The current location to add strings const char *last; ///< The last element of the buffer. public: @@ -36,7 +36,7 @@ public: * @param start The start location to write to. * @param last The last location to write to. */ - StringBuilder(char *start, const char *last) : current(start), last(last) {} + StringBuilder(char **start, const char *last) : current(start), last(last) {} /* Required operators for this to be an output_iterator; mimics std::back_insert_iterator, which has no-ops. */ StringBuilder &operator++() { return *this; } @@ -62,7 +62,7 @@ public: */ StringBuilder &operator+=(const char value) { - if (this->current != this->last) *this->current++ = value; + if (*this->current != this->last) *(*this->current)++ = value; return *this; } @@ -73,7 +73,7 @@ public: */ StringBuilder &operator+=(const char *str) { - this->current = strecpy(this->current, str, this->last); + *this->current = strecpy(*this->current, str, this->last); return *this; } @@ -96,7 +96,7 @@ public: { if (this->Remaining() < Utf8CharLen(c)) return false; - this->current += ::Utf8Encode(this->current, c); + (*this->current) += ::Utf8Encode(*this->current, c); return true; } @@ -108,8 +108,8 @@ public: */ char *GetEnd() { - *this->current = '\0'; - return this->current; + **this->current = '\0'; + return *this->current; } /** @@ -118,7 +118,7 @@ public: */ ptrdiff_t Remaining() { - return (ptrdiff_t)(this->last - this->current); + return (ptrdiff_t)(this->last - *this->current); } /** @@ -128,7 +128,7 @@ public: */ void AddViaStreCallback(std::function function) { - this->current = function(this->current, this->last); + *this->current = function(*this->current, this->last); } }; diff --git a/src/townname.cpp b/src/townname.cpp index 4ae4be2404..8460430f79 100644 --- a/src/townname.cpp +++ b/src/townname.cpp @@ -67,7 +67,7 @@ static void GetTownName(StringBuilder &builder, const TownNameParams *par, uint3 */ char *GetTownName(char *buff, const TownNameParams *par, uint32 townnameparts, const char *last) { - StringBuilder builder(buff, last); + StringBuilder builder(&buff, last); GetTownName(builder, par, townnameparts); return builder.GetEnd(); } @@ -92,7 +92,7 @@ void GetTownName(StringBuilder &builder, const Town *t) */ char *GetTownName(char *buff, const Town *t, const char *last) { - StringBuilder builder(buff, last); + StringBuilder builder(&buff, last); GetTownName(builder, t); return builder.GetEnd(); } From 83a318edcfb462b306693dfe7b17e3e24bf37af9 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 4 Jun 2023 19:15:24 +0100 Subject: [PATCH 22/35] Fix dec7ff6b0c: Dropdowns would close if their tooltip appeared. (#10939) Solution is to not focus any tooltips, so that the dropdown doesn't lose focus. Tooltips don't accept any input so this does not change their behaviour. --- src/window.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/window.cpp b/src/window.cpp index 927861d742..a1cda1ab4d 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -457,6 +457,9 @@ void SetFocusedWindow(Window *w) { if (_focused_window == w) return; + /* Don't focus a tooltip */ + if (w != nullptr && w->window_class == WC_TOOLTIPS) return; + /* Invalidate focused widget */ if (_focused_window != nullptr) { if (_focused_window->nested_focus != nullptr) _focused_window->nested_focus->SetDirty(_focused_window); From 07add7a96e38f9da6712eaf10bfe5f25c706946e Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 4 Jun 2023 20:38:16 +0200 Subject: [PATCH 23/35] Fix: [Win32] position window in center of workspace of primary display (#10942) --- src/video/win32_v.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 7b06809a55..571ff49ee9 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -213,8 +213,13 @@ bool VideoDriver_Win32Base::MakeWindow(bool full_screen, bool resize) if (this->main_wnd != nullptr) { if (!_window_maximize && resize) SetWindowPos(this->main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE); } else { - int x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2; - int y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2; + /* Center on the workspace of the primary display. */ + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY), &mi); + + int x = (mi.rcWork.right - mi.rcWork.left - w) / 2; + int y = (mi.rcWork.bottom - mi.rcWork.top - h) / 2; char window_title[64]; seprintf(window_title, lastof(window_title), "OpenTTD %s", _openttd_revision); From 820fe8c62110334a48ab21dea68c60f0ae00fa65 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 4 Jun 2023 19:28:18 +0200 Subject: [PATCH 24/35] Codechange: use std::string and concatenation when combining multiple formatted strings --- src/depot_gui.cpp | 9 ++++----- src/error_gui.cpp | 13 ++++++------- src/linkgraph/linkgraph_gui.cpp | 11 +++++------ 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 104368a8f3..58f63dd918 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -875,9 +875,8 @@ struct DepotWindow : Window { } /* Build tooltipstring */ - static char details[1024]; - details[0] = '\0'; - char *pos = details; + static std::string details; + details.clear(); for (CargoID cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) { if (capacity[cargo_type] == 0) continue; @@ -886,13 +885,13 @@ struct DepotWindow : Window { SetDParam(1, loaded[cargo_type]); // {CARGO} #2 SetDParam(2, cargo_type); // {SHORTCARGO} #1 SetDParam(3, capacity[cargo_type]); // {SHORTCARGO} #2 - pos = GetString(pos, STR_DEPOT_VEHICLE_TOOLTIP_CARGO, lastof(details)); + details += GetString(STR_DEPOT_VEHICLE_TOOLTIP_CARGO); } /* Show tooltip window */ uint64 args[2]; args[0] = (whole_chain ? num : v->engine_type); - args[1] = (uint64)(size_t)details; + args[1] = (uint64)(size_t)details.c_str(); GuiShowTooltips(this, whole_chain ? STR_DEPOT_VEHICLE_TOOLTIP_CHAIN : STR_DEPOT_VEHICLE_TOOLTIP, 2, args, TCC_RIGHT_CLICK); return true; diff --git a/src/error_gui.cpp b/src/error_gui.cpp index ad0758fc24..fee7d040c2 100644 --- a/src/error_gui.cpp +++ b/src/error_gui.cpp @@ -414,23 +414,22 @@ 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(); - IConsolePrint(wl == WL_WARNING ? CC_WARNING : CC_ERROR, buf); + IConsolePrint(wl == WL_WARNING ? CC_WARNING : CC_ERROR, message); } bool is_critical = wl == WL_CRITICAL; diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index 1a4265cf1c..e93a0f9d68 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -377,9 +377,8 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond) pt.x - 2 <= std::max(pta.x, ptb.x) && pt.y + 2 >= std::min(pta.y, ptb.y) && pt.y - 2 <= std::max(pta.y, ptb.y)) { - static char buf[1024]; - char *buf_end = buf; - buf[0] = 0; + static std::string tooltip_extension; + tooltip_extension.clear(); /* Fill buf with more information if this is a bidirectional link. */ uint32 back_time = 0; auto k = this->cached_links[j->first].find(i->first); @@ -390,21 +389,21 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond) SetDParam(0, back.cargo); SetDParam(1, back.Usage()); SetDParam(2, back.Usage() * 100 / (back.capacity + 1)); - buf_end = GetString(buf, STR_LINKGRAPH_STATS_TOOLTIP_RETURN_EXTENSION, lastof(buf)); + tooltip_extension = GetString(STR_LINKGRAPH_STATS_TOOLTIP_RETURN_EXTENSION); } } /* Add information about the travel time if known. */ const auto time = link.time ? back_time ? ((link.time + back_time) / 2) : link.time : back_time; if (time > 0) { SetDParam(0, time); - buf_end = GetString(buf_end, STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION, lastof(buf)); + tooltip_extension += GetString(STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION); } SetDParam(0, link.cargo); SetDParam(1, link.Usage()); SetDParam(2, i->first); SetDParam(3, j->first); SetDParam(4, link.Usage() * 100 / (link.capacity + 1)); - SetDParamStr(5, buf); + SetDParamStr(5, tooltip_extension); GuiShowTooltips(this->window, STR_LINKGRAPH_STATS_TOOLTIP, 7, nullptr, close_cond); return true; } From 6a5ab4a9fb565116ab6420156b794418bda42469 Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 4 Jun 2023 18:57:10 +0000 Subject: [PATCH 25/35] Update: Translations from eints french: 3 changes by glx22 --- src/lang/french.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lang/french.txt b/src/lang/french.txt index c33f53a1dd..ce2633feb7 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -2708,8 +2708,10 @@ STR_STATION_BUILD_STATION_CLASS_TOOLTIP :{BLACK}Choisir STR_STATION_BUILD_STATION_TYPE_TOOLTIP :{BLACK}Choisir le type de station à construire STR_STATION_CLASS_DFLT :Par défaut -STR_STATION_CLASS_DFLT_STATION :Station par défaut +STR_STATION_CLASS_DFLT_STATION :Stations par défaut +STR_STATION_CLASS_DFLT_ROADSTOP :Arrêts routiers par défaut STR_STATION_CLASS_WAYP :Points de contrôle +STR_STATION_CLASS_WAYP_WAYPOINT :Points de contrôle par défaut # Signal window STR_BUILD_SIGNAL_CAPTION :{WHITE}Sélection des signaux From 4e39a58c59ac467906479bd54864dd4b079abc67 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 11 May 2023 21:30:58 +0200 Subject: [PATCH 26/35] Codechange: let town name generation use StringBuilder --- src/strings.cpp | 2 +- src/strings_internal.h | 33 ++- src/townname.cpp | 480 +++++++++++++++++++---------------------- src/townname_func.h | 1 - 4 files changed, 244 insertions(+), 272 deletions(-) diff --git a/src/strings.cpp b/src/strings.cpp index 582b02622f..13a55c7898 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1664,7 +1664,7 @@ static void StationGetSpecialString(StringBuilder &builder, int x) static void GetSpecialTownNameString(StringBuilder &builder, int ind, uint32 seed) { - builder.AddViaStreCallback([&](auto buff, auto last) { return GenerateTownNameString(buff, last, ind, seed); }); + GenerateTownNameString(builder, ind, seed); } static const char * const _silly_company_names[] = { diff --git a/src/strings_internal.h b/src/strings_internal.h index df6dacaf3f..a29c6e5486 100644 --- a/src/strings_internal.h +++ b/src/strings_internal.h @@ -20,7 +20,8 @@ * extra functions to ease the migration from char buffers to std::string. */ class StringBuilder { - char **current; ///< The current location to add strings + char **current; ///< The current location to add strings. + char *start; ///< The begin of the string. const char *last; ///< The last element of the buffer. public: @@ -36,7 +37,7 @@ public: * @param start The start location to write to. * @param last The last location to write to. */ - StringBuilder(char **start, const char *last) : current(start), last(last) {} + StringBuilder(char **start, const char *last) : current(start), start(*start), last(last) {} /* Required operators for this to be an output_iterator; mimics std::back_insert_iterator, which has no-ops. */ StringBuilder &operator++() { return *this; } @@ -100,6 +101,15 @@ public: return true; } + /** + * Remove the given amount of characters from the back of the string. + * @param amount The amount of characters to remove. + */ + void RemoveElementsFromBack(size_t amount) + { + *this->current = std::max(this->start, *this->current - amount); + } + /** * Get the pointer to the this->last written element in the buffer. * This call does '\0' terminate the string, whereas other calls do not @@ -122,19 +132,28 @@ public: } /** - * Add a string using the strecpy-esque calling signature. - * @param function The function to pass the current and last location to, - * that will then return the new current location. + * Get the current index in the string. + * @return The index. */ - void AddViaStreCallback(std::function function) + size_t CurrentIndex() { - *this->current = function(*this->current, this->last); + return *this->current - this->start; + } + + /** + * Get the reference to the character at the given index. + * @return The reference to the character. + */ + char &operator[](size_t index) + { + return this->start[index]; } }; void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters *args, uint case_index = 0, bool game_script = false); /* 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 grfid, uint16 gen, uint32 seed); diff --git a/src/townname.cpp b/src/townname.cpp index 8460430f79..945e81639e 100644 --- a/src/townname.cpp +++ b/src/townname.cpp @@ -220,303 +220,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) { @@ -524,41 +503,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, @@ -569,66 +545,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); @@ -735,28 +710,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; @@ -769,189 +741,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[] = { @@ -960,70 +918,66 @@ 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; } @@ -1034,7 +988,7 @@ static char *MakeCatalanTownName(char *buf, const char *last, uint32 seed) * @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); +typedef void TownNameGenerator(StringBuilder &builder, uint32 seed); /** Contains pointer to generator and minimum buffer size (not incl. terminating '\0') */ struct TownNameGeneratorParams { @@ -1070,13 +1024,11 @@ static const TownNameGeneratorParams _town_name_generators[] = { /** * 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 seed) { assert(lang < lengthof(_town_name_generators)); @@ -1087,10 +1039,12 @@ char *GenerateTownNameString(char *buf, const char *last, size_t lang, uint32 se * 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); + if (builder.Remaining() >= par->min) return par->proc(builder, seed); std::string buffer(par->min + 1, '\0'); - par->proc(buffer.data(), buffer.data() + par->min, seed); + char *state = buffer.data(); + StringBuilder buffer_builder(&state, buffer.data() + par->min); + par->proc(buffer_builder, seed); - return strecpy(buf, buffer.c_str(), last); + builder += buffer; } diff --git a/src/townname_func.h b/src/townname_func.h index a3d29e467d..dd2576b790 100644 --- a/src/townname_func.h +++ b/src/townname_func.h @@ -13,7 +13,6 @@ #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); From a4bf45729ac7aa88d3976a4e00955fd256571ea2 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 4 Jun 2023 20:15:57 +0200 Subject: [PATCH 27/35] Change: move string validation (and assignment) to textbuf --- src/misc_gui.cpp | 12 +----------- src/textbuf.cpp | 14 +++++++++++--- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 7495b71398..03dc1e2144 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -959,17 +959,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) { - 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/textbuf.cpp b/src/textbuf.cpp index 151c753d92..7418cdf29a 100644 --- a/src/textbuf.cpp +++ b/src/textbuf.cpp @@ -388,8 +388,7 @@ Textbuf::~Textbuf() */ void Textbuf::Assign(StringID string) { - GetString(this->buf, string, &this->buf[this->max_bytes - 1]); - this->UpdateSize(); + this->Assign(GetString(string)); } /** @@ -398,7 +397,16 @@ void Textbuf::Assign(StringID string) */ void Textbuf::Assign(const std::string_view text) { - strecpy(this->buf, text.data(), &this->buf[this->max_bytes - 1]); + 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'; + } + this->UpdateSize(); } From 6ae6b65edb12527ab0aa5a2379d45aa48c8b7a8d Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 4 Jun 2023 21:54:44 +0100 Subject: [PATCH 28/35] Cleanup: Remove doubled statements. (#10944) --- src/rail_gui.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 17f0a32057..b9b1e58e80 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -1352,8 +1352,6 @@ public: _settings_client.gui.station_numtracks = widget - WID_BRAS_PLATFORM_NUM_BEGIN; _settings_client.gui.station_dragdrop = false; - _settings_client.gui.station_dragdrop = false; - const StationSpec *statspec = _railstation.newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : nullptr; if (statspec != nullptr && HasBit(statspec->disallowed_lengths, _settings_client.gui.station_platlength - 1)) { /* The previously selected number of platforms in invalid */ @@ -1387,8 +1385,6 @@ public: _settings_client.gui.station_platlength = widget - WID_BRAS_PLATFORM_LEN_BEGIN; _settings_client.gui.station_dragdrop = false; - _settings_client.gui.station_dragdrop = false; - const StationSpec *statspec = _railstation.newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : nullptr; if (statspec != nullptr && HasBit(statspec->disallowed_platforms, _settings_client.gui.station_numtracks - 1)) { /* The previously selected number of tracks in invalid */ From c158089eff0441c8e2e91c4e3d2cc6ae9eda1475 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 4 Jun 2023 22:42:27 +0200 Subject: [PATCH 29/35] Codechange: use C++ style methods to combine a Utf8Encoded character and a formatted string --- src/network/network.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 561c1df7fd..3f76ef8413 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -248,7 +248,6 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, default: strid = STR_NETWORK_CHAT_ALL; break; } - char message[1024]; SetDParamStr(0, name); SetDParamStr(1, str); SetDParam(2, data); @@ -258,8 +257,10 @@ 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)); + std::ostringstream stream; + std::ostreambuf_iterator iterator(stream); + Utf8Encode(iterator, _current_text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM); + std::string message = stream.str() + GetString(strid); Debug(desync, 1, "msg: {:08x}; {:02x}; {}", TimerGameCalendar::date, TimerGameCalendar::date_fract, message); IConsolePrint(colour, message); From 15c75e6f45b3ca5e411d3b7595625ddd507d73b1 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 4 Jun 2023 11:14:56 +0200 Subject: [PATCH 30/35] Codechange: use std::array and std::string for high scores --- src/highscore.cpp | 151 ++++++++++++++++++++---------------------- src/highscore.h | 21 +++--- src/highscore_gui.cpp | 8 +-- 3 files changed, 84 insertions(+), 96 deletions(-) diff --git a/src/highscore.cpp b/src/highscore.cpp index e48ed5e01b..bae91cee02 100644 --- a/src/highscore.cpp +++ b/src/highscore.cpp @@ -12,6 +12,7 @@ #include "company_base.h" #include "company_func.h" #include "cheat_func.h" +#include "fileio_func.h" #include "string_func.h" #include "strings_func.h" #include "table/strings.h" @@ -19,7 +20,7 @@ #include "safeguards.h" -HighScore _highscore_table[SP_HIGHSCORE_END][5]; ///< various difficulty-settings; top 5 +HighScoresTable _highscore_table; ///< Table with all the high scores. std::string _highscore_file; ///< The file to store the highscore data in. static const StringID _endgame_perf_titles[] = { @@ -48,31 +49,34 @@ StringID EndGameGetPerformanceTitleFromValue(uint value) return _endgame_perf_titles[value]; } -/** Save the highscore for the company */ -int8 SaveHighScoreValue(const Company *c) +/** + * Save the highscore for the company + * @param c The company to insert. + * @return The index the company got in the high score table, or -1 when it did not end up in the table. + */ +int8_t SaveHighScoreValue(const Company *c) { - HighScore *hs = _highscore_table[SP_CUSTOM]; - uint i; - uint16 score = c->old_economy[0].performance_history; - /* Exclude cheaters from the honour of being in the highscore table */ if (CheatHasBeenUsed()) return -1; - for (i = 0; i < lengthof(_highscore_table[0]); i++) { - /* You are in the TOP5. Move all values one down and save us there */ - if (hs[i].score <= score) { - /* move all elements one down starting from the replaced one */ - memmove(&hs[i + 1], &hs[i], sizeof(HighScore) * (lengthof(_highscore_table[0]) - i - 1)); - SetDParam(0, c->index); - SetDParam(1, c->index); - GetString(hs[i].company, STR_HIGHSCORE_NAME, lastof(hs[i].company)); // get manager/company name string - hs[i].score = score; - hs[i].title = EndGameGetPerformanceTitleFromValue(score); - return i; - } - } + auto &highscores = _highscore_table[SP_CUSTOM]; + uint16 score = c->old_economy[0].performance_history; - return -1; // too bad; we did not make it into the top5 + auto it = std::find_if(highscores.begin(), highscores.end(), [&score](auto &highscore) { return highscore.score <= score; }); + + /* If we cannot find it, our score is not high enough. */ + if (it == highscores.end()) return -1; + + /* Move all elements one down starting from the replaced one */ + std::move_backward(it, highscores.end() - 1, highscores.end()); + + /* Fill the elements. */ + SetDParam(0, c->index); + SetDParam(1, c->index); + it->name = GetString(STR_HIGHSCORE_NAME); // get manager/company name string + it->score = score; + it->title = EndGameGetPerformanceTitleFromValue(score); + return std::distance(highscores.begin(), it); } /** Sort all companies given their performance */ @@ -85,95 +89,82 @@ static bool HighScoreSorter(const Company * const &a, const Company * const &b) * Save the highscores in a network game when it has ended * @return Position of the local company in the highscore list. */ -int8 SaveHighScoreValueNetwork() +int8_t SaveHighScoreValueNetwork() { const Company *cl[MAX_COMPANIES]; - uint count = 0; - int8 company = -1; + size_t count = 0; + int8_t local_company_place = -1; /* Sort all active companies with the highest score first */ for (const Company *c : Company::Iterate()) cl[count++] = c; std::sort(std::begin(cl), std::begin(cl) + count, HighScoreSorter); - { - uint i; + /* Clear the high scores from the previous network game. */ + auto &highscores = _highscore_table[SP_MULTIPLAYER]; + std::fill(highscores.begin(), highscores.end(), HighScore{}); - memset(_highscore_table[SP_MULTIPLAYER], 0, sizeof(_highscore_table[SP_MULTIPLAYER])); + for (size_t i = 0; i < count && i < highscores.size(); i++) { + const Company *c = cl[i]; + auto &highscore = highscores[i]; + SetDParam(0, c->index); + SetDParam(1, c->index); + highscore.name = GetString(STR_HIGHSCORE_NAME); // get manager/company name string + highscore.score = c->old_economy[0].performance_history; + highscore.title = EndGameGetPerformanceTitleFromValue(highscore.score); - /* Copy over Top5 companies */ - for (i = 0; i < lengthof(_highscore_table[SP_MULTIPLAYER]) && i < count; i++) { - HighScore *hs = &_highscore_table[SP_MULTIPLAYER][i]; - - SetDParam(0, cl[i]->index); - SetDParam(1, cl[i]->index); - GetString(hs->company, STR_HIGHSCORE_NAME, lastof(hs->company)); // get manager/company name string - hs->score = cl[i]->old_economy[0].performance_history; - hs->title = EndGameGetPerformanceTitleFromValue(hs->score); - - /* get the ranking of the local company */ - if (cl[i]->index == _local_company) company = i; - } + if (c->index == _local_company) local_company_place = static_cast(i); } - /* Add top5 companies to highscore table */ - return company; + return local_company_place; } /** Save HighScore table to file */ void SaveToHighScore() { - FILE *fp = fopen(_highscore_file.c_str(), "wb"); + std::unique_ptr fp(fopen(_highscore_file.c_str(), "wb")); + if (fp == nullptr) return; - if (fp != nullptr) { - uint i; - HighScore *hs; - - for (i = 0; i < SP_SAVED_HIGHSCORE_END; i++) { - for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) { - /* First character is a command character, so strlen will fail on that */ - byte length = ClampTo(std::min(sizeof(hs->company), StrEmpty(hs->company) ? 0 : strlen(&hs->company[1]) + 1)); - - if (fwrite(&length, sizeof(length), 1, fp) != 1 || // write away string length - fwrite(hs->company, length, 1, fp) > 1 || // Yes... could be 0 bytes too - fwrite(&hs->score, sizeof(hs->score), 1, fp) != 1 || - fwrite(" ", 2, 1, fp) != 1) { // XXX - placeholder for hs->title, not saved anymore; compatibility - Debug(misc, 1, "Could not save highscore."); - i = SP_SAVED_HIGHSCORE_END; - break; - } + /* Does not iterate through the complete array!. */ + for (int i = 0; i < SP_SAVED_HIGHSCORE_END; i++) { + for (HighScore &hs : _highscore_table[i]) { + /* This code is weird and old fashioned to keep compatibility with the old high score files. */ + byte name_length = ClampTo(hs.name.size()); + if (fwrite(&name_length, sizeof(name_length), 1, fp.get()) != 1 || // Write the string length of the name + fwrite(hs.name.data(), name_length, 1, fp.get()) > 1 || // Yes... could be 0 bytes too + fwrite(&hs.score, sizeof(hs.score), 1, fp.get()) != 1 || + fwrite(" ", 2, 1, fp.get()) != 1) { // Used to be hs.title, not saved anymore; compatibility + Debug(misc, 1, "Could not save highscore."); + return; } } - fclose(fp); } } /** Initialize the highscore table to 0 and if any file exists, load in values */ void LoadFromHighScore() { - FILE *fp = fopen(_highscore_file.c_str(), "rb"); + std::fill(_highscore_table.begin(), _highscore_table.end(), HighScores{}); - memset(_highscore_table, 0, sizeof(_highscore_table)); + std::unique_ptr fp(fopen(_highscore_file.c_str(), "rb")); + if (fp == nullptr) return; - if (fp != nullptr) { - uint i; - HighScore *hs; + /* Does not iterate through the complete array!. */ + for (int i = 0; i < SP_SAVED_HIGHSCORE_END; i++) { + for (HighScore &hs : _highscore_table[i]) { + /* This code is weird and old fashioned to keep compatibility with the old high score files. */ + byte name_length; + char buffer[std::numeric_limits::max() + 1]; - for (i = 0; i < SP_SAVED_HIGHSCORE_END; i++) { - for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) { - byte length; - if (fread(&length, sizeof(length), 1, fp) != 1 || - fread(hs->company, std::min(lengthof(hs->company), length), 1, fp) > 1 || // Yes... could be 0 bytes too - fread(&hs->score, sizeof(hs->score), 1, fp) != 1 || - fseek(fp, 2, SEEK_CUR) == -1) { // XXX - placeholder for hs->title, not saved anymore; compatibility - Debug(misc, 1, "Highscore corrupted"); - i = SP_SAVED_HIGHSCORE_END; - break; - } - StrMakeValidInPlace(hs->company, lastof(hs->company), SVS_NONE); - hs->title = EndGameGetPerformanceTitleFromValue(hs->score); + if (fread(&name_length, sizeof(name_length), 1, fp.get()) != 1 || + fread(buffer, name_length, 1, fp.get()) > 1 || // Yes... could be 0 bytes too + fread(&hs.score, sizeof(hs.score), 1, fp.get()) != 1 || + fseek(fp.get(), 2, SEEK_CUR) == -1) { // Used to be hs.title, not saved anymore; compatibility + Debug(misc, 1, "Highscore corrupted"); + return; } + hs.name = StrMakeValid(std::string_view(buffer, name_length)); + hs.title = EndGameGetPerformanceTitleFromValue(hs.score); } - fclose(fp); } } diff --git a/src/highscore.h b/src/highscore.h index 8993e83afa..b1ddd9fa6f 100644 --- a/src/highscore.h +++ b/src/highscore.h @@ -15,23 +15,20 @@ #include "settings_type.h" struct HighScore { - /** - * The name of the company and president. - * The + 5 is for the comma and space or possibly other characters - * that join the two names in this single string and the '\0'. - */ - char company[(MAX_LENGTH_COMPANY_NAME_CHARS + MAX_LENGTH_PRESIDENT_NAME_CHARS + 5) * MAX_CHAR_LENGTH]; - StringID title; ///< NOSAVE, has troubles with changing string-numbers. - uint16 score; ///< The score for this high score. Do NOT change type, will break hs.dat + std::string name; ///< The name of the companyy and president. + StringID title = INVALID_STRING_ID; ///< NOSAVE, has troubles with changing string-numbers. + uint16 score = 0; ///< The score for this high score. Do NOT change type, will break hs.dat }; -extern HighScore _highscore_table[SP_HIGHSCORE_END][5]; +using HighScores = std::array; ///< Record 5 high scores +using HighScoresTable = std::array; ///< Record high score for each of the difficulty levels +extern HighScoresTable _highscore_table; void SaveToHighScore(); void LoadFromHighScore(); -int8 SaveHighScoreValue(const Company *c); -int8 SaveHighScoreValueNetwork(); +int8_t SaveHighScoreValue(const Company *c); +int8_t SaveHighScoreValueNetwork(); StringID EndGameGetPerformanceTitleFromValue(uint value); -void ShowHighscoreTable(int difficulty = SP_CUSTOM, int8 rank = -1); +void ShowHighscoreTable(int difficulty = SP_CUSTOM, int8_t rank = -1); #endif /* HIGHSCORE_H */ diff --git a/src/highscore_gui.cpp b/src/highscore_gui.cpp index 762aac2e11..16d8876bce 100644 --- a/src/highscore_gui.cpp +++ b/src/highscore_gui.cpp @@ -184,7 +184,7 @@ struct HighScoreWindow : EndGameHighScoreBaseWindow { void OnPaint() override { - const HighScore *hs = _highscore_table[this->window_number]; + const auto &hs = _highscore_table[this->window_number]; this->SetupHighScoreEndWindow(); Point pt = this->GetTopLeft(ScaleSpriteTrad(640), ScaleSpriteTrad(480)); @@ -193,14 +193,14 @@ struct HighScoreWindow : EndGameHighScoreBaseWindow { DrawStringMultiLine(pt.x + ScaleSpriteTrad(70), pt.x + ScaleSpriteTrad(570), pt.y, pt.y + ScaleSpriteTrad(140), !_networking ? STR_HIGHSCORE_TOP_COMPANIES_WHO_REACHED : STR_HIGHSCORE_TOP_COMPANIES_NETWORK_GAME, TC_FROMSTRING, SA_CENTER); /* Draw Highscore peepz */ - for (uint8 i = 0; i < lengthof(_highscore_table[0]); i++) { + for (uint8_t i = 0; i < ClampTo(hs.size()); i++) { SetDParam(0, i + 1); DrawString(pt.x + ScaleSpriteTrad(40), pt.x + ScaleSpriteTrad(600), pt.y + ScaleSpriteTrad(140 + i * 55), STR_HIGHSCORE_POSITION); - if (hs[i].company[0] != '\0') { + if (!hs[i].name.empty()) { TextColour colour = (this->rank == i) ? TC_RED : TC_BLACK; // draw new highscore in red - SetDParamStr(0, hs[i].company); + SetDParamStr(0, hs[i].name); DrawString(pt.x + ScaleSpriteTrad(71), pt.x + ScaleSpriteTrad(569), pt.y + ScaleSpriteTrad(140 + i * 55), STR_JUST_BIG_RAW_STRING, colour); SetDParam(0, hs[i].title); SetDParam(1, hs[i].score); From 772729cc7dbb775d38c330afa8058bdab1e296a6 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Mon, 5 Jun 2023 10:27:04 +0200 Subject: [PATCH 31/35] Fix: when syncing width of GUI items, take padding into account (#10915) --- src/company_gui.cpp | 28 +++++++++++++++------------- src/genworld_gui.cpp | 2 +- src/newgrf_gui.cpp | 2 +- src/settings_gui.cpp | 2 +- src/town_gui.cpp | 2 +- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 6a4d6b457b..ce9025af9f 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -1922,17 +1922,17 @@ struct CompanyInfrastructureWindow : Window case WID_CI_RAIL_DESC: { uint lines = 1; // Starts at 1 because a line is also required for the section title - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width); + size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width + padding.width); for (const auto &rt : _sorted_railtypes) { if (HasBit(this->railtypes, rt)) { lines++; - size->width = std::max(size->width, GetStringBoundingBox(GetRailTypeInfo(rt)->strings.name).width + WidgetDimensions::scaled.hsep_indent); + size->width = std::max(size->width, GetStringBoundingBox(GetRailTypeInfo(rt)->strings.name).width + padding.width + WidgetDimensions::scaled.hsep_indent); } } if (this->railtypes != RAILTYPES_NONE) { lines++; - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS).width + WidgetDimensions::scaled.hsep_indent); + size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS).width + padding.width + WidgetDimensions::scaled.hsep_indent); } size->height = std::max(size->height, lines * FONT_HEIGHT_NORMAL); @@ -1943,12 +1943,12 @@ struct CompanyInfrastructureWindow : Window case WID_CI_TRAM_DESC: { uint lines = 1; // Starts at 1 because a line is also required for the section title - size->width = std::max(size->width, GetStringBoundingBox(widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT).width); + size->width = std::max(size->width, GetStringBoundingBox(widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT).width + padding.width); for (const auto &rt : _sorted_roadtypes) { if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) { lines++; - size->width = std::max(size->width, GetStringBoundingBox(GetRoadTypeInfo(rt)->strings.name).width + WidgetDimensions::scaled.hsep_indent); + size->width = std::max(size->width, GetStringBoundingBox(GetRoadTypeInfo(rt)->strings.name).width + padding.width + WidgetDimensions::scaled.hsep_indent); } } @@ -1957,14 +1957,14 @@ struct CompanyInfrastructureWindow : Window } case WID_CI_WATER_DESC: - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT).width); - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS).width + WidgetDimensions::scaled.hsep_indent); + size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT).width + padding.width); + size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS).width + padding.width + WidgetDimensions::scaled.hsep_indent); break; case WID_CI_STATION_DESC: - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT).width); - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS).width + WidgetDimensions::scaled.hsep_indent); - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS).width + WidgetDimensions::scaled.hsep_indent); + size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT).width + padding.width); + size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS).width + padding.width + WidgetDimensions::scaled.hsep_indent); + size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS).width + padding.width + WidgetDimensions::scaled.hsep_indent); break; case WID_CI_RAIL_COUNT: @@ -2378,18 +2378,19 @@ struct CompanyWindow : Window case WID_C_DESC_VEHICLE_COUNTS: SetDParamMaxValue(0, 5000); // Maximum number of vehicles for (uint i = 0; i < lengthof(_company_view_vehicle_count_strings); i++) { - size->width = std::max(size->width, GetStringBoundingBox(_company_view_vehicle_count_strings[i]).width); + size->width = std::max(size->width, GetStringBoundingBox(_company_view_vehicle_count_strings[i]).width + padding.width); } break; case WID_C_DESC_INFRASTRUCTURE_COUNTS: SetDParamMaxValue(0, UINT_MAX); - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL).width); + size->width = GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL).width; size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_WATER).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_STATION).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_NONE).width); + size->width += padding.width; break; case WID_C_VIEW_HQ: @@ -2399,13 +2400,14 @@ struct CompanyWindow : Window case WID_C_GIVE_MONEY: case WID_C_COMPANY_PASSWORD: case WID_C_COMPANY_JOIN: - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_VIEW_HQ_BUTTON).width); + size->width = GetStringBoundingBox(STR_COMPANY_VIEW_VIEW_HQ_BUTTON).width; size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_BUILD_HQ_BUTTON).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_RELOCATE_HQ).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_PASSWORD).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_JOIN).width); + size->width += padding.width; break; case WID_C_HAS_PASSWORD: diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index 962e1a4a56..add156d863 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -1430,7 +1430,7 @@ struct GenerateProgressWindow : public Window { case WID_GP_PROGRESS_TEXT: for (uint i = 0; i < GWP_CLASS_COUNT; i++) { - size->width = std::max(size->width, GetStringBoundingBox(_generation_class_table[i]).width); + size->width = std::max(size->width, GetStringBoundingBox(_generation_class_table[i]).width + padding.width); } size->height = FONT_HEIGHT_NORMAL * 2 + WidgetDimensions::scaled.vsep_normal; break; diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index a2b8af54ee..90626c6c3e 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -2227,7 +2227,7 @@ struct ScanProgressWindow : public Window { SetDParamMaxDigits(1, 4); /* We really don't know the width. We could determine it by scanning the NewGRFs, * but this is the status window for scanning them... */ - size->width = std::max(size->width, GetStringBoundingBox(STR_NEWGRF_SCAN_STATUS).width); + size->width = std::max(size->width, GetStringBoundingBox(STR_NEWGRF_SCAN_STATUS).width + padding.width); size->height = FONT_HEIGHT_NORMAL * 2 + WidgetDimensions::scaled.vsep_normal; break; } diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index d055557b27..c3df6d3c68 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2123,7 +2123,7 @@ struct GameSettingsWindow : Window { }; for (uint i = 0; i < lengthof(setting_types); i++) { SetDParam(0, setting_types[i]); - size->width = std::max(size->width, GetStringBoundingBox(STR_CONFIG_SETTING_TYPE).width); + size->width = std::max(size->width, GetStringBoundingBox(STR_CONFIG_SETTING_TYPE).width + padding.width); } size->height = 2 * FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal + std::max(size->height, GetSettingsTree().GetMaxHelpHeight(size->width)); diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 5b69f8cfa8..cb66491f69 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -263,7 +263,7 @@ public: size->height = (TACT_COUNT + 1) * FONT_HEIGHT_NORMAL + padding.height; size->width = GetStringBoundingBox(STR_LOCAL_AUTHORITY_ACTIONS_TITLE).width; for (uint i = 0; i < TACT_COUNT; i++ ) { - size->width = std::max(size->width, GetStringBoundingBox(STR_LOCAL_AUTHORITY_ACTION_SMALL_ADVERTISING_CAMPAIGN + i).width); + size->width = std::max(size->width, GetStringBoundingBox(STR_LOCAL_AUTHORITY_ACTION_SMALL_ADVERTISING_CAMPAIGN + i).width + padding.width); } size->width += padding.width; break; From 14915526ad64592211eaed70ecaab63d50989847 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 4 Jun 2023 21:46:21 +0200 Subject: [PATCH 32/35] Cleanup: remove stre-style GetString --- src/strings.cpp | 15 +++------------ src/strings_func.h | 1 - 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/strings.cpp b/src/strings.cpp index 13a55c7898..99210bee55 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -281,15 +281,6 @@ void GetStringWithArgs(StringBuilder &builder, StringID string, StringParameters FormatString(builder, GetStringPtr(string), args, case_index); } -char *GetString(char *buffr, StringID string, const char *last) -{ - _global_string_params.ClearTypeInformation(); - _global_string_params.offset = 0; - StringBuilder builder(&buffr, last); - GetStringWithArgs(builder, string, &_global_string_params); - return builder.GetEnd(); -} - /** * Resolve the given StringID into a std::string with all the associated @@ -299,9 +290,9 @@ char *GetString(char *buffr, StringID string, const char *last) */ std::string GetString(StringID string) { - char buffer[DRAW_STRING_BUFFER]; - GetString(buffer, string, lastof(buffer)); - return buffer; + _global_string_params.ClearTypeInformation(); + _global_string_params.offset = 0; + return GetStringWithArgs(string, &_global_string_params); } /** diff --git a/src/strings_func.h b/src/strings_func.h index bcaa42ba99..6d70c2df8c 100644 --- a/src/strings_func.h +++ b/src/strings_func.h @@ -170,7 +170,6 @@ public: }; extern StringParameters _global_string_params; -char *GetString(char *buffr, StringID string, const char *last); std::string GetString(StringID string); std::string GetStringWithArgs(StringID string, StringParameters *args); const char *GetStringPtr(StringID string); From 2dd2b698d25de3862090bbf8a1264308d0224568 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Mon, 5 Jun 2023 00:13:35 +0200 Subject: [PATCH 33/35] Codechange: convert C-style GetTownName API to std::string returning API --- src/town_cmd.cpp | 4 +--- src/town_gui.cpp | 8 +++---- src/townname.cpp | 52 +++++++++++++++++---------------------------- src/townname_func.h | 4 ++-- 4 files changed, 26 insertions(+), 42 deletions(-) diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 6d4b479591..ccdd2fe5ba 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -207,9 +207,7 @@ void Town::InitializeLayout(TownLayout layout) 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.assign(GetTownName(this)); } /** diff --git a/src/town_gui.cpp b/src/town_gui.cpp index cb66491f69..0ddf2b9906 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -1159,8 +1159,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); @@ -1198,9 +1197,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 = Command::Post(errstr, cc, diff --git a/src/townname.cpp b/src/townname.cpp index 945e81639e..e1b6d1364e 100644 --- a/src/townname.cpp +++ b/src/townname.cpp @@ -58,18 +58,18 @@ static void GetTownName(StringBuilder &builder, const TownNameParams *par, uint3 } /** - * 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' + * Get the town name for the given parameters and parts. + * @param par Town name parameters. + * @param townnameparts 'Encoded' town name. + * @return The town name. */ -char *GetTownName(char *buff, const TownNameParams *par, uint32 townnameparts, const char *last) +std::string GetTownName(const TownNameParams *par, uint32 townnameparts) { - StringBuilder builder(&buff, last); + char buffer[DRAW_STRING_BUFFER]; + char *state = buffer; + StringBuilder builder(&state, lastof(buffer)); GetTownName(builder, par, townnameparts); - return builder.GetEnd(); + return std::string(buffer, builder.GetEnd()); } /** @@ -84,17 +84,14 @@ void GetTownName(StringBuilder &builder, const Town *t) } /** - * 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) { - StringBuilder builder(&buff, last); - GetTownName(builder, t); - return builder.GetEnd(); + TownNameParams par(t); + return GetTownName(&par, t->townnameparts); } @@ -107,28 +104,19 @@ char *GetTownName(char *buff, const Town *t, const char *last) */ bool VerifyTownName(uint32 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]; - char buf2[(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. */ - const char *buf = t->name.empty() ? nullptr : t->name.c_str(); - if (buf == nullptr) { - GetTownName(buf2, t, lastof(buf2)); - buf = buf2; - } - if (strcmp(buf1, buf) == 0) return false; + if (name == (t->name.empty() ? GetTownName(t) : t->name)) return false; } } diff --git a/src/townname_func.h b/src/townname_func.h index dd2576b790..026b067fc2 100644 --- a/src/townname_func.h +++ b/src/townname_func.h @@ -13,8 +13,8 @@ #include "core/random_func.hpp" #include "townname_type.h" -char *GetTownName(char *buff, const TownNameParams *par, uint32 townnameparts, const char *last); -char *GetTownName(char *buff, const Town *t, const char *last); +std::string GetTownName(const TownNameParams *par, uint32 townnameparts); +std::string GetTownName(const Town *t); bool VerifyTownName(uint32 r, const TownNameParams *par, TownNames *town_names = nullptr); bool GenerateTownName(Randomizer &randomizer, uint32 *townnameparts, TownNames *town_names = nullptr); From 921f5afc4d10c9e216f044b9e6c97d2f97764a6d Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Mon, 5 Jun 2023 09:24:29 +0200 Subject: [PATCH 34/35] Codechange: Apply suggestions from code review --- src/town_cmd.cpp | 2 +- src/townname.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index ccdd2fe5ba..e12597981e 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -207,7 +207,7 @@ void Town::InitializeLayout(TownLayout layout) void Town::FillCachedName() const { - this->cached_name.assign(GetTownName(this)); + this->cached_name = GetTownName(this); } /** diff --git a/src/townname.cpp b/src/townname.cpp index e1b6d1364e..557ed9c497 100644 --- a/src/townname.cpp +++ b/src/townname.cpp @@ -116,7 +116,11 @@ bool VerifyTownName(uint32 r, const TownNameParams *par, TownNames *town_names) for (const Town *t : Town::Iterate()) { /* We can't just compare the numbers since * several numbers may map to a single name. */ - if (name == (t->name.empty() ? GetTownName(t) : t->name)) return false; + if (t->name.empty()) { + if (name == GetTownName(t)) return false; + } else { + if (name == t->name) return false; + } } } From f814c863896f0b253ff497224a8f3d710dfa42ed Mon Sep 17 00:00:00 2001 From: PeterN Date: Mon, 5 Jun 2023 18:12:30 +0100 Subject: [PATCH 35/35] Codechange: Reorganise hotkey initialisation. (#10951) Hotkeys are now initialized inline, and use std::vector instead of separate static C-arrays and std::string instead of char *. The list end marker is no longer required. --- src/airport_gui.cpp | 36 +++++----- src/build_vehicle_gui.cpp | 10 +-- src/dock_gui.cpp | 52 ++++++-------- src/hotkeys.cpp | 38 +++++----- src/hotkeys.h | 14 ++-- src/main_gui.cpp | 95 +++++++++++------------- src/object_gui.cpp | 35 +++++---- src/order_gui.cpp | 30 ++++---- src/rail_gui.cpp | 100 ++++++++++++-------------- src/road_gui.cpp | 136 ++++++++++++++++------------------- src/script/script_gui.cpp | 78 ++++++++++---------- src/signs_gui.cpp | 34 ++++----- src/terraform_gui.cpp | 97 ++++++++++++------------- src/toolbar_gui.cpp | 147 +++++++++++++++++--------------------- src/vehicle_gui.cpp | 10 +-- 15 files changed, 410 insertions(+), 502 deletions(-) diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp index 2f577357f7..5418f266df 100644 --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -177,28 +177,24 @@ struct BuildAirToolbarWindow : Window { CloseWindowById(WC_SELECT_STATION, 0); } - static HotkeyList hotkeys; -}; + /** + * Handler for global hotkeys of the BuildAirToolbarWindow. + * @param hotkey Hotkey + * @return ES_HANDLED if hotkey was accepted. + */ + static EventState AirportToolbarGlobalHotkeys(int hotkey) + { + if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED; + Window *w = ShowBuildAirToolbar(); + if (w == nullptr) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); + } -/** - * Handler for global hotkeys of the BuildAirToolbarWindow. - * @param hotkey Hotkey - * @return ES_HANDLED if hotkey was accepted. - */ -static EventState AirportToolbarGlobalHotkeys(int hotkey) -{ - if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED; - Window *w = ShowBuildAirToolbar(); - if (w == nullptr) return ES_NOT_HANDLED; - return w->OnHotkey(hotkey); -} - -static Hotkey airtoolbar_hotkeys[] = { - Hotkey('1', "airport", WID_AT_AIRPORT), - Hotkey('2', "demolish", WID_AT_DEMOLISH), - HOTKEY_LIST_END + static inline HotkeyList hotkeys{"airtoolbar", { + Hotkey('1', "airport", WID_AT_AIRPORT), + Hotkey('2', "demolish", WID_AT_DEMOLISH), + }, AirportToolbarGlobalHotkeys}; }; -HotkeyList BuildAirToolbarWindow::hotkeys("airtoolbar", airtoolbar_hotkeys, AirportToolbarGlobalHotkeys); static const NWidgetPart _nested_air_toolbar_widgets[] = { NWidget(NWID_HORIZONTAL), diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index b22218dbe8..a06c6088af 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -1871,15 +1871,11 @@ struct BuildVehicleWindow : Window { return ES_HANDLED; } - static HotkeyList hotkeys; + static inline HotkeyList hotkeys{"buildvehicle", { + Hotkey('F', "focus_filter_box", BVHK_FOCUS_FILTER_BOX), + }}; }; -static Hotkey buildvehicle_hotkeys[] = { - Hotkey('F', "focus_filter_box", BVHK_FOCUS_FILTER_BOX), - HOTKEY_LIST_END -}; -HotkeyList BuildVehicleWindow::hotkeys("buildvehicle", buildvehicle_hotkeys); - static WindowDesc _build_vehicle_desc( WDP_AUTO, "build_vehicle", 240, 268, WC_BUILD_VEHICLE, WC_NONE, diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index 5b71a648cb..6bdaa75d38 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -298,37 +298,31 @@ struct BuildDocksToolbarWindow : Window { VpSetPresizeRange(tile_from, tile_to); } - static HotkeyList hotkeys; + /** + * Handler for global hotkeys of the BuildDocksToolbarWindow. + * @param hotkey Hotkey + * @return ES_HANDLED if hotkey was accepted. + */ + static EventState DockToolbarGlobalHotkeys(int hotkey) + { + if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED; + Window *w = ShowBuildDocksToolbar(); + if (w == nullptr) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); + } + + static inline HotkeyList hotkeys{"dockstoolbar", { + Hotkey('1', "canal", WID_DT_CANAL), + Hotkey('2', "lock", WID_DT_LOCK), + Hotkey('3', "demolish", WID_DT_DEMOLISH), + Hotkey('4', "depot", WID_DT_DEPOT), + Hotkey('5', "dock", WID_DT_STATION), + Hotkey('6', "buoy", WID_DT_BUOY), + Hotkey('7', "river", WID_DT_RIVER), + Hotkey({'B', '8'}, "aqueduct", WID_DT_BUILD_AQUEDUCT), + }, DockToolbarGlobalHotkeys}; }; -/** - * Handler for global hotkeys of the BuildDocksToolbarWindow. - * @param hotkey Hotkey - * @return ES_HANDLED if hotkey was accepted. - */ -static EventState DockToolbarGlobalHotkeys(int hotkey) -{ - if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED; - Window *w = ShowBuildDocksToolbar(); - if (w == nullptr) return ES_NOT_HANDLED; - return w->OnHotkey(hotkey); -} - -const uint16 _dockstoolbar_aqueduct_keys[] = {'B', '8', 0}; - -static Hotkey dockstoolbar_hotkeys[] = { - Hotkey('1', "canal", WID_DT_CANAL), - Hotkey('2', "lock", WID_DT_LOCK), - Hotkey('3', "demolish", WID_DT_DEMOLISH), - Hotkey('4', "depot", WID_DT_DEPOT), - Hotkey('5', "dock", WID_DT_STATION), - Hotkey('6', "buoy", WID_DT_BUOY), - Hotkey('7', "river", WID_DT_RIVER), - Hotkey(_dockstoolbar_aqueduct_keys, "aqueduct", WID_DT_BUILD_AQUEDUCT), - HOTKEY_LIST_END -}; -HotkeyList BuildDocksToolbarWindow::hotkeys("dockstoolbar", dockstoolbar_hotkeys, DockToolbarGlobalHotkeys); - /** * Nested widget parts of docks toolbar, game version. * Position of #WID_DT_RIVER widget has changed. diff --git a/src/hotkeys.cpp b/src/hotkeys.cpp index 7e62cc5481..3cb9d35299 100644 --- a/src/hotkeys.cpp +++ b/src/hotkeys.cpp @@ -149,14 +149,14 @@ static uint16 ParseKeycode(const char *start, const char *end) * @param hotkey The hotkey object to add the keycodes to * @param value The string to parse */ -static void ParseHotkeys(Hotkey *hotkey, const char *value) +static void ParseHotkeys(Hotkey &hotkey, const char *value) { const char *start = value; while (*start != '\0') { const char *end = start; while (*end != '\0' && *end != ',') end++; uint16 keycode = ParseKeycode(start, end); - if (keycode != 0) hotkey->AddKeycode(keycode); + if (keycode != 0) hotkey.AddKeycode(keycode); start = (*end == ',') ? end + 1: end; } } @@ -210,10 +210,10 @@ static std::string KeycodeToString(uint16 keycode) * @param hotkey The keycodes of this hotkey need to be converted to a string. * @return A string representation of all keycodes. */ -std::string SaveKeycodes(const Hotkey *hotkey) +std::string SaveKeycodes(const Hotkey &hotkey) { std::string str; - for (auto keycode : hotkey->keycodes) { + for (auto keycode : hotkey.keycodes) { if (!str.empty()) str += ","; str += KeycodeToString(keycode); } @@ -226,7 +226,7 @@ std::string SaveKeycodes(const Hotkey *hotkey) * @param name The name of this hotkey. * @param num Number of this hotkey, should be unique within the hotkey list. */ -Hotkey::Hotkey(uint16 default_keycode, const char *name, int num) : +Hotkey::Hotkey(uint16 default_keycode, const std::string &name, int num) : name(name), num(num) { @@ -239,14 +239,12 @@ Hotkey::Hotkey(uint16 default_keycode, const char *name, int num) : * @param name The name of this hotkey. * @param num Number of this hotkey, should be unique within the hotkey list. */ -Hotkey::Hotkey(const uint16 *default_keycodes, const char *name, int num) : +Hotkey::Hotkey(const std::vector &default_keycodes, const std::string &name, int num) : name(name), num(num) { - const uint16 *keycode = default_keycodes; - while (*keycode != 0) { - this->AddKeycode(*keycode); - keycode++; + for (uint16 keycode : default_keycodes) { + this->AddKeycode(keycode); } } @@ -260,7 +258,7 @@ void Hotkey::AddKeycode(uint16 keycode) this->keycodes.insert(keycode); } -HotkeyList::HotkeyList(const char *ini_group, Hotkey *items, GlobalHotkeyHandlerFunc global_hotkey_handler) : +HotkeyList::HotkeyList(const std::string &ini_group, const std::vector &items, GlobalHotkeyHandlerFunc global_hotkey_handler) : global_hotkey_handler(global_hotkey_handler), ini_group(ini_group), items(items) { if (_hotkey_lists == nullptr) _hotkey_lists = new std::vector(); @@ -279,10 +277,10 @@ HotkeyList::~HotkeyList() void HotkeyList::Load(IniFile *ini) { IniGroup *group = ini->GetGroup(this->ini_group); - for (Hotkey *hotkey = this->items; hotkey->name != nullptr; ++hotkey) { - IniItem *item = group->GetItem(hotkey->name, false); + for (Hotkey &hotkey : this->items) { + IniItem *item = group->GetItem(hotkey.name, false); if (item != nullptr) { - hotkey->keycodes.clear(); + hotkey.keycodes.clear(); if (item->value.has_value()) ParseHotkeys(hotkey, item->value->c_str()); } } @@ -295,8 +293,8 @@ void HotkeyList::Load(IniFile *ini) void HotkeyList::Save(IniFile *ini) const { IniGroup *group = ini->GetGroup(this->ini_group); - for (const Hotkey *hotkey = this->items; hotkey->name != nullptr; ++hotkey) { - IniItem *item = group->GetItem(hotkey->name, true); + for (const Hotkey &hotkey : this->items) { + IniItem *item = group->GetItem(hotkey.name, true); item->SetValue(SaveKeycodes(hotkey)); } } @@ -309,11 +307,11 @@ void HotkeyList::Save(IniFile *ini) const */ int HotkeyList::CheckMatch(uint16 keycode, bool global_only) const { - for (const Hotkey *list = this->items; list->name != nullptr; ++list) { - auto begin = list->keycodes.begin(); - auto end = list->keycodes.end(); + for (const Hotkey &hotkey : this->items) { + auto begin = hotkey.keycodes.begin(); + auto end = hotkey.keycodes.end(); if (std::find(begin, end, keycode | WKC_GLOBAL_HOTKEY) != end || (!global_only && std::find(begin, end, keycode) != end)) { - return list->num; + return hotkey.num; } } return -1; diff --git a/src/hotkeys.h b/src/hotkeys.h index 781feb2185..8ac94aa911 100644 --- a/src/hotkeys.h +++ b/src/hotkeys.h @@ -19,18 +19,16 @@ * a list of keycodes and a number to help identifying this hotkey. */ struct Hotkey { - Hotkey(uint16 default_keycode, const char *name, int num); - Hotkey(const uint16 *default_keycodes, const char *name, int num); + Hotkey(uint16 default_keycode, const std::string &name, int num); + Hotkey(const std::vector &default_keycodes, const std::string &name, int num); void AddKeycode(uint16 keycode); - const char *name; + const std::string name; int num; std::set keycodes; }; -#define HOTKEY_LIST_END Hotkey((uint16)0, nullptr, -1) - struct IniFile; /** @@ -39,7 +37,7 @@ struct IniFile; struct HotkeyList { typedef EventState (*GlobalHotkeyHandlerFunc)(int hotkey); - HotkeyList(const char *ini_group, Hotkey *items, GlobalHotkeyHandlerFunc global_hotkey_handler = nullptr); + HotkeyList(const std::string &ini_group, const std::vector &items, GlobalHotkeyHandlerFunc global_hotkey_handler = nullptr); ~HotkeyList(); void Load(IniFile *ini); @@ -49,8 +47,8 @@ struct HotkeyList { GlobalHotkeyHandlerFunc global_hotkey_handler; private: - const char *ini_group; - Hotkey *items; + const std::string ini_group; + std::vector items; /** * Dummy private copy constructor to prevent compilers from diff --git a/src/main_gui.cpp b/src/main_gui.cpp index b253ae292f..b7757e87cb 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -461,62 +461,51 @@ struct MainWindow : Window InvalidateWindowData(WC_MAIN_TOOLBAR, 0, data, true); } - static HotkeyList hotkeys; -}; - -const uint16 _ghk_quit_keys[] = {'Q' | WKC_CTRL, 'Q' | WKC_META, 0}; -const uint16 _ghk_abandon_keys[] = {'W' | WKC_CTRL, 'W' | WKC_META, 0}; -const uint16 _ghk_chat_keys[] = {WKC_RETURN, 'T', 0}; -const uint16 _ghk_chat_all_keys[] = {WKC_SHIFT | WKC_RETURN, WKC_SHIFT | 'T', 0}; -const uint16 _ghk_chat_company_keys[] = {WKC_CTRL | WKC_RETURN, WKC_CTRL | 'T', 0}; -const uint16 _ghk_chat_server_keys[] = {WKC_CTRL | WKC_SHIFT | WKC_RETURN, WKC_CTRL | WKC_SHIFT | 'T', 0}; - -static Hotkey global_hotkeys[] = { - Hotkey(_ghk_quit_keys, "quit", GHK_QUIT), - Hotkey(_ghk_abandon_keys, "abandon", GHK_ABANDON), - Hotkey(WKC_BACKQUOTE, "console", GHK_CONSOLE), - Hotkey('B' | WKC_CTRL, "bounding_boxes", GHK_BOUNDING_BOXES), - Hotkey('I' | WKC_CTRL, "dirty_blocks", GHK_DIRTY_BLOCKS), - Hotkey('C', "center", GHK_CENTER), - Hotkey('Z', "center_zoom", GHK_CENTER_ZOOM), - Hotkey(WKC_ESC, "reset_object_to_place", GHK_RESET_OBJECT_TO_PLACE), - Hotkey(WKC_DELETE, "delete_windows", GHK_DELETE_WINDOWS), - Hotkey(WKC_DELETE | WKC_SHIFT, "delete_all_windows", GHK_DELETE_NONVITAL_WINDOWS), - Hotkey(WKC_DELETE | WKC_CTRL, "delete_all_messages", GHK_DELETE_ALL_MESSAGES), - Hotkey('R' | WKC_CTRL, "refresh_screen", GHK_REFRESH_SCREEN), + static inline HotkeyList hotkeys{"global", { + Hotkey({'Q' | WKC_CTRL, 'Q' | WKC_META}, "quit", GHK_QUIT), + Hotkey({'W' | WKC_CTRL, 'W' | WKC_META}, "abandon", GHK_ABANDON), + Hotkey(WKC_BACKQUOTE, "console", GHK_CONSOLE), + Hotkey('B' | WKC_CTRL, "bounding_boxes", GHK_BOUNDING_BOXES), + Hotkey('I' | WKC_CTRL, "dirty_blocks", GHK_DIRTY_BLOCKS), + Hotkey('C', "center", GHK_CENTER), + Hotkey('Z', "center_zoom", GHK_CENTER_ZOOM), + Hotkey(WKC_ESC, "reset_object_to_place", GHK_RESET_OBJECT_TO_PLACE), + Hotkey(WKC_DELETE, "delete_windows", GHK_DELETE_WINDOWS), + Hotkey(WKC_DELETE | WKC_SHIFT, "delete_all_windows", GHK_DELETE_NONVITAL_WINDOWS), + Hotkey(WKC_DELETE | WKC_CTRL, "delete_all_messages", GHK_DELETE_ALL_MESSAGES), + Hotkey('R' | WKC_CTRL, "refresh_screen", GHK_REFRESH_SCREEN), #if defined(_DEBUG) - Hotkey('0' | WKC_ALT, "crash_game", GHK_CRASH), - Hotkey('1' | WKC_ALT, "money", GHK_MONEY), - Hotkey('2' | WKC_ALT, "update_coordinates", GHK_UPDATE_COORDS), + Hotkey('0' | WKC_ALT, "crash_game", GHK_CRASH), + Hotkey('1' | WKC_ALT, "money", GHK_MONEY), + Hotkey('2' | WKC_ALT, "update_coordinates", GHK_UPDATE_COORDS), #endif - Hotkey('1' | WKC_CTRL, "transparency_signs", GHK_TOGGLE_TRANSPARENCY), - Hotkey('2' | WKC_CTRL, "transparency_trees", GHK_TOGGLE_TRANSPARENCY + 1), - Hotkey('3' | WKC_CTRL, "transparency_houses", GHK_TOGGLE_TRANSPARENCY + 2), - Hotkey('4' | WKC_CTRL, "transparency_industries", GHK_TOGGLE_TRANSPARENCY + 3), - Hotkey('5' | WKC_CTRL, "transparency_buildings", GHK_TOGGLE_TRANSPARENCY + 4), - Hotkey('6' | WKC_CTRL, "transparency_bridges", GHK_TOGGLE_TRANSPARENCY + 5), - Hotkey('7' | WKC_CTRL, "transparency_structures", GHK_TOGGLE_TRANSPARENCY + 6), - Hotkey('8' | WKC_CTRL, "transparency_catenary", GHK_TOGGLE_TRANSPARENCY + 7), - Hotkey('9' | WKC_CTRL, "transparency_loading", GHK_TOGGLE_TRANSPARENCY + 8), - Hotkey('1' | WKC_CTRL | WKC_SHIFT, "invisibility_signs", GHK_TOGGLE_INVISIBILITY), - Hotkey('2' | WKC_CTRL | WKC_SHIFT, "invisibility_trees", GHK_TOGGLE_INVISIBILITY + 1), - Hotkey('3' | WKC_CTRL | WKC_SHIFT, "invisibility_houses", GHK_TOGGLE_INVISIBILITY + 2), - Hotkey('4' | WKC_CTRL | WKC_SHIFT, "invisibility_industries", GHK_TOGGLE_INVISIBILITY + 3), - Hotkey('5' | WKC_CTRL | WKC_SHIFT, "invisibility_buildings", GHK_TOGGLE_INVISIBILITY + 4), - Hotkey('6' | WKC_CTRL | WKC_SHIFT, "invisibility_bridges", GHK_TOGGLE_INVISIBILITY + 5), - Hotkey('7' | WKC_CTRL | WKC_SHIFT, "invisibility_structures", GHK_TOGGLE_INVISIBILITY + 6), - Hotkey('8' | WKC_CTRL | WKC_SHIFT, "invisibility_catenary", GHK_TOGGLE_INVISIBILITY + 7), - Hotkey('X' | WKC_CTRL, "transparency_toolbar", GHK_TRANSPARENCY_TOOLBAR), - Hotkey('X', "toggle_transparency", GHK_TRANSPARANCY), - Hotkey(_ghk_chat_keys, "chat", GHK_CHAT), - Hotkey(_ghk_chat_all_keys, "chat_all", GHK_CHAT_ALL), - Hotkey(_ghk_chat_company_keys, "chat_company", GHK_CHAT_COMPANY), - Hotkey(_ghk_chat_server_keys, "chat_server", GHK_CHAT_SERVER), - Hotkey(WKC_SPACE, "close_news", GHK_CLOSE_NEWS), - Hotkey(WKC_SPACE, "close_error", GHK_CLOSE_ERROR), - HOTKEY_LIST_END + Hotkey('1' | WKC_CTRL, "transparency_signs", GHK_TOGGLE_TRANSPARENCY), + Hotkey('2' | WKC_CTRL, "transparency_trees", GHK_TOGGLE_TRANSPARENCY + 1), + Hotkey('3' | WKC_CTRL, "transparency_houses", GHK_TOGGLE_TRANSPARENCY + 2), + Hotkey('4' | WKC_CTRL, "transparency_industries", GHK_TOGGLE_TRANSPARENCY + 3), + Hotkey('5' | WKC_CTRL, "transparency_buildings", GHK_TOGGLE_TRANSPARENCY + 4), + Hotkey('6' | WKC_CTRL, "transparency_bridges", GHK_TOGGLE_TRANSPARENCY + 5), + Hotkey('7' | WKC_CTRL, "transparency_structures", GHK_TOGGLE_TRANSPARENCY + 6), + Hotkey('8' | WKC_CTRL, "transparency_catenary", GHK_TOGGLE_TRANSPARENCY + 7), + Hotkey('9' | WKC_CTRL, "transparency_loading", GHK_TOGGLE_TRANSPARENCY + 8), + Hotkey('1' | WKC_CTRL | WKC_SHIFT, "invisibility_signs", GHK_TOGGLE_INVISIBILITY), + Hotkey('2' | WKC_CTRL | WKC_SHIFT, "invisibility_trees", GHK_TOGGLE_INVISIBILITY + 1), + Hotkey('3' | WKC_CTRL | WKC_SHIFT, "invisibility_houses", GHK_TOGGLE_INVISIBILITY + 2), + Hotkey('4' | WKC_CTRL | WKC_SHIFT, "invisibility_industries", GHK_TOGGLE_INVISIBILITY + 3), + Hotkey('5' | WKC_CTRL | WKC_SHIFT, "invisibility_buildings", GHK_TOGGLE_INVISIBILITY + 4), + Hotkey('6' | WKC_CTRL | WKC_SHIFT, "invisibility_bridges", GHK_TOGGLE_INVISIBILITY + 5), + Hotkey('7' | WKC_CTRL | WKC_SHIFT, "invisibility_structures", GHK_TOGGLE_INVISIBILITY + 6), + Hotkey('8' | WKC_CTRL | WKC_SHIFT, "invisibility_catenary", GHK_TOGGLE_INVISIBILITY + 7), + Hotkey('X' | WKC_CTRL, "transparency_toolbar", GHK_TRANSPARENCY_TOOLBAR), + Hotkey('X', "toggle_transparency", GHK_TRANSPARANCY), + Hotkey({WKC_RETURN, 'T'}, "chat", GHK_CHAT), + Hotkey({WKC_SHIFT | WKC_RETURN, WKC_SHIFT | 'T'}, "chat_all", GHK_CHAT_ALL), + Hotkey({WKC_CTRL | WKC_RETURN, WKC_CTRL | 'T'}, "chat_company", GHK_CHAT_COMPANY), + Hotkey({WKC_CTRL | WKC_SHIFT | WKC_RETURN, WKC_CTRL | WKC_SHIFT | 'T'}, "chat_server", GHK_CHAT_SERVER), + Hotkey(WKC_SPACE, "close_news", GHK_CLOSE_NEWS), + Hotkey(WKC_SPACE, "close_error", GHK_CLOSE_ERROR), + }}; }; -HotkeyList MainWindow::hotkeys("global", global_hotkeys); static WindowDesc _main_window_desc( WDP_MANUAL, nullptr, 0, 0, diff --git a/src/object_gui.cpp b/src/object_gui.cpp index bd461eb350..a6554364ed 100644 --- a/src/object_gui.cpp +++ b/src/object_gui.cpp @@ -637,27 +637,24 @@ public: this->SelectOtherObject(-1); } - static HotkeyList hotkeys; + /** + * Handler for global hotkeys of the BuildObjectWindow. + * @param hotkey Hotkey + * @return ES_HANDLED if hotkey was accepted. + */ + static EventState BuildObjectGlobalHotkeys(int hotkey) + { + if (_game_mode == GM_MENU) return ES_NOT_HANDLED; + Window *w = ShowBuildObjectPicker(); + if (w == nullptr) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); + } + + static inline HotkeyList hotkeys{"buildobject", { + Hotkey('F', "focus_filter_box", BOHK_FOCUS_FILTER_BOX), + }, BuildObjectGlobalHotkeys}; }; -/** - * Handler for global hotkeys of the BuildObjectWindow. - * @param hotkey Hotkey - * @return ES_HANDLED if hotkey was accepted. - */ -static EventState BuildObjectGlobalHotkeys(int hotkey) -{ - if (_game_mode == GM_MENU) return ES_NOT_HANDLED; - Window *w = ShowBuildObjectPicker(); - if (w == nullptr) return ES_NOT_HANDLED; - return w->OnHotkey(hotkey); -} - -static Hotkey buildobject_hotkeys[] = { - Hotkey('F', "focus_filter_box", BOHK_FOCUS_FILTER_BOX), - HOTKEY_LIST_END -}; -HotkeyList BuildObjectWindow::hotkeys("buildobject", buildobject_hotkeys, BuildObjectGlobalHotkeys); Listing BuildObjectWindow::last_sorting = { false, 0 }; Filtering BuildObjectWindow::last_filtering = { false, 0 }; diff --git a/src/order_gui.cpp b/src/order_gui.cpp index a99b520fd7..29be44dc75 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -1546,25 +1546,21 @@ public: this->vscroll->SetCapacityFromWidget(this, WID_O_ORDER_LIST); } - static HotkeyList hotkeys; + static inline HotkeyList hotkeys{"order", { + Hotkey('D', "skip", OHK_SKIP), + Hotkey('F', "delete", OHK_DELETE), + Hotkey('G', "goto", OHK_GOTO), + Hotkey('H', "nonstop", OHK_NONSTOP), + Hotkey('J', "fullload", OHK_FULLLOAD), + Hotkey('K', "unload", OHK_UNLOAD), + Hotkey(0, "nearest_depot", OHK_NEAREST_DEPOT), + Hotkey(0, "always_service", OHK_ALWAYS_SERVICE), + Hotkey(0, "transfer", OHK_TRANSFER), + Hotkey(0, "no_unload", OHK_NO_UNLOAD), + Hotkey(0, "no_load", OHK_NO_LOAD), + }}; }; -static Hotkey order_hotkeys[] = { - Hotkey('D', "skip", OHK_SKIP), - Hotkey('F', "delete", OHK_DELETE), - Hotkey('G', "goto", OHK_GOTO), - Hotkey('H', "nonstop", OHK_NONSTOP), - Hotkey('J', "fullload", OHK_FULLLOAD), - Hotkey('K', "unload", OHK_UNLOAD), - Hotkey((uint16)0, "nearest_depot", OHK_NEAREST_DEPOT), - Hotkey((uint16)0, "always_service", OHK_ALWAYS_SERVICE), - Hotkey((uint16)0, "transfer", OHK_TRANSFER), - Hotkey((uint16)0, "no_unload", OHK_NO_UNLOAD), - Hotkey((uint16)0, "no_load", OHK_NO_LOAD), - HOTKEY_LIST_END -}; -HotkeyList OrdersWindow::hotkeys("order", order_hotkeys); - /** Nested widget definition for "your" train orders. */ static const NWidgetPart _nested_orders_train_widgets[] = { NWidget(NWID_HORIZONTAL), diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index b9b1e58e80..0f99326987 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -764,44 +764,38 @@ struct BuildRailToolbarWindow : Window { if (this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT)) CheckRedrawWaypointCoverage(this); } - static HotkeyList hotkeys; + /** + * Handler for global hotkeys of the BuildRailToolbarWindow. + * @param hotkey Hotkey + * @return ES_HANDLED if hotkey was accepted. + */ + static EventState RailToolbarGlobalHotkeys(int hotkey) + { + if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED; + extern RailType _last_built_railtype; + Window *w = ShowBuildRailToolbar(_last_built_railtype); + if (w == nullptr) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); + } + + static inline HotkeyList hotkeys{"railtoolbar", { + Hotkey('1', "build_ns", WID_RAT_BUILD_NS), + Hotkey('2', "build_x", WID_RAT_BUILD_X), + Hotkey('3', "build_ew", WID_RAT_BUILD_EW), + Hotkey('4', "build_y", WID_RAT_BUILD_Y), + Hotkey({'5', 'A' | WKC_GLOBAL_HOTKEY}, "autorail", WID_RAT_AUTORAIL), + Hotkey('6', "demolish", WID_RAT_DEMOLISH), + Hotkey('7', "depot", WID_RAT_BUILD_DEPOT), + Hotkey('8', "waypoint", WID_RAT_BUILD_WAYPOINT), + Hotkey('9', "station", WID_RAT_BUILD_STATION), + Hotkey('S', "signal", WID_RAT_BUILD_SIGNALS), + Hotkey('B', "bridge", WID_RAT_BUILD_BRIDGE), + Hotkey('T', "tunnel", WID_RAT_BUILD_TUNNEL), + Hotkey('R', "remove", WID_RAT_REMOVE), + Hotkey('C', "convert", WID_RAT_CONVERT_RAIL), + }, RailToolbarGlobalHotkeys}; }; -/** - * Handler for global hotkeys of the BuildRailToolbarWindow. - * @param hotkey Hotkey - * @return ES_HANDLED if hotkey was accepted. - */ -static EventState RailToolbarGlobalHotkeys(int hotkey) -{ - if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED; - extern RailType _last_built_railtype; - Window *w = ShowBuildRailToolbar(_last_built_railtype); - if (w == nullptr) return ES_NOT_HANDLED; - return w->OnHotkey(hotkey); -} - -const uint16 _railtoolbar_autorail_keys[] = {'5', 'A' | WKC_GLOBAL_HOTKEY, 0}; - -static Hotkey railtoolbar_hotkeys[] = { - Hotkey('1', "build_ns", WID_RAT_BUILD_NS), - Hotkey('2', "build_x", WID_RAT_BUILD_X), - Hotkey('3', "build_ew", WID_RAT_BUILD_EW), - Hotkey('4', "build_y", WID_RAT_BUILD_Y), - Hotkey(_railtoolbar_autorail_keys, "autorail", WID_RAT_AUTORAIL), - Hotkey('6', "demolish", WID_RAT_DEMOLISH), - Hotkey('7', "depot", WID_RAT_BUILD_DEPOT), - Hotkey('8', "waypoint", WID_RAT_BUILD_WAYPOINT), - Hotkey('9', "station", WID_RAT_BUILD_STATION), - Hotkey('S', "signal", WID_RAT_BUILD_SIGNALS), - Hotkey('B', "bridge", WID_RAT_BUILD_BRIDGE), - Hotkey('T', "tunnel", WID_RAT_BUILD_TUNNEL), - Hotkey('R', "remove", WID_RAT_REMOVE), - Hotkey('C', "convert", WID_RAT_CONVERT_RAIL), - HOTKEY_LIST_END -}; -HotkeyList BuildRailToolbarWindow::hotkeys("railtoolbar", railtoolbar_hotkeys, RailToolbarGlobalHotkeys); - static const NWidgetPart _nested_build_rail_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), @@ -1502,27 +1496,23 @@ public: this->SetDirty(); }}; - static HotkeyList hotkeys; -}; + /** + * Handler for global hotkeys of the BuildRailStationWindow. + * @param hotkey Hotkey + * @return ES_HANDLED if hotkey was accepted. + */ + static EventState BuildRailStationGlobalHotkeys(int hotkey) + { + if (_game_mode == GM_MENU) return ES_NOT_HANDLED; + Window *w = ShowStationBuilder(FindWindowById(WC_BUILD_TOOLBAR, TRANSPORT_RAIL)); + if (w == nullptr) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); + } -/** - * Handler for global hotkeys of the BuildRailStationWindow. - * @param hotkey Hotkey - * @return ES_HANDLED if hotkey was accepted. - */ -static EventState BuildRailStationGlobalHotkeys(int hotkey) -{ - if (_game_mode == GM_MENU) return ES_NOT_HANDLED; - Window *w = ShowStationBuilder(FindWindowById(WC_BUILD_TOOLBAR, TRANSPORT_RAIL)); - if (w == nullptr) return ES_NOT_HANDLED; - return w->OnHotkey(hotkey); -} - -static Hotkey buildrailstation_hotkeys[] = { - Hotkey('F', "focus_filter_box", BRASHK_FOCUS_FILTER_BOX), - HOTKEY_LIST_END + static inline HotkeyList hotkeys{"buildrailstation", { + Hotkey('F', "focus_filter_box", BRASHK_FOCUS_FILTER_BOX), + }, BuildRailStationGlobalHotkeys}; }; -HotkeyList BuildRailStationWindow::hotkeys("buildrailstation", buildrailstation_hotkeys, BuildRailStationGlobalHotkeys); Listing BuildRailStationWindow::last_sorting = { false, 0 }; Filtering BuildRailStationWindow::last_filtering = { false, 0 }; diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 3f475c8a8f..ef2f2c6896 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -750,82 +750,74 @@ struct BuildRoadToolbarWindow : Window { return ES_NOT_HANDLED; } - static HotkeyList road_hotkeys; - static HotkeyList tram_hotkeys; -}; + /** + * Handler for global hotkeys of the BuildRoadToolbarWindow. + * @param hotkey Hotkey + * @param last_build Last build road type + * @return ES_HANDLED if hotkey was accepted. + */ + static EventState RoadTramToolbarGlobalHotkeys(int hotkey, RoadType last_build, RoadTramType rtt) + { + Window* w = nullptr; + switch (_game_mode) { + case GM_NORMAL: + w = ShowBuildRoadToolbar(last_build); + break; -/** - * Handler for global hotkeys of the BuildRoadToolbarWindow. - * @param hotkey Hotkey - * @param last_build Last build road type - * @return ES_HANDLED if hotkey was accepted. - */ -static EventState RoadTramToolbarGlobalHotkeys(int hotkey, RoadType last_build, RoadTramType rtt) -{ - Window* w = nullptr; - switch (_game_mode) { - case GM_NORMAL: - w = ShowBuildRoadToolbar(last_build); - break; + case GM_EDITOR: + if ((GetRoadTypes(true) & ((rtt == RTT_ROAD) ? ~_roadtypes_type : _roadtypes_type)) == ROADTYPES_NONE) return ES_NOT_HANDLED; + w = ShowBuildRoadScenToolbar(last_build); + break; - case GM_EDITOR: - if ((GetRoadTypes(true) & ((rtt == RTT_ROAD) ? ~_roadtypes_type : _roadtypes_type)) == ROADTYPES_NONE) return ES_NOT_HANDLED; - w = ShowBuildRoadScenToolbar(last_build); - break; + default: + break; + } - default: - break; + if (w == nullptr) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); } - if (w == nullptr) return ES_NOT_HANDLED; - return w->OnHotkey(hotkey); -} + static EventState RoadToolbarGlobalHotkeys(int hotkey) + { + extern RoadType _last_built_roadtype; + return RoadTramToolbarGlobalHotkeys(hotkey, _last_built_roadtype, RTT_ROAD); + } -static EventState RoadToolbarGlobalHotkeys(int hotkey) -{ - extern RoadType _last_built_roadtype; - return RoadTramToolbarGlobalHotkeys(hotkey, _last_built_roadtype, RTT_ROAD); -} + static EventState TramToolbarGlobalHotkeys(int hotkey) + { + extern RoadType _last_built_tramtype; + return RoadTramToolbarGlobalHotkeys(hotkey, _last_built_tramtype, RTT_TRAM); + } -static EventState TramToolbarGlobalHotkeys(int hotkey) -{ - extern RoadType _last_built_tramtype; - return RoadTramToolbarGlobalHotkeys(hotkey, _last_built_tramtype, RTT_TRAM); -} + static inline HotkeyList road_hotkeys{"roadtoolbar", { + Hotkey('1', "build_x", WID_ROT_ROAD_X), + Hotkey('2', "build_y", WID_ROT_ROAD_Y), + Hotkey('3', "autoroad", WID_ROT_AUTOROAD), + Hotkey('4', "demolish", WID_ROT_DEMOLISH), + Hotkey('5', "depot", WID_ROT_DEPOT), + Hotkey('6', "bus_station", WID_ROT_BUS_STATION), + Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION), + Hotkey('8', "oneway", WID_ROT_ONE_WAY), + Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE), + Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL), + Hotkey('R', "remove", WID_ROT_REMOVE), + Hotkey('C', "convert", WID_ROT_CONVERT_ROAD), + }, RoadToolbarGlobalHotkeys}; -static Hotkey roadtoolbar_hotkeys[] = { - Hotkey('1', "build_x", WID_ROT_ROAD_X), - Hotkey('2', "build_y", WID_ROT_ROAD_Y), - Hotkey('3', "autoroad", WID_ROT_AUTOROAD), - Hotkey('4', "demolish", WID_ROT_DEMOLISH), - Hotkey('5', "depot", WID_ROT_DEPOT), - Hotkey('6', "bus_station", WID_ROT_BUS_STATION), - Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION), - Hotkey('8', "oneway", WID_ROT_ONE_WAY), - Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE), - Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL), - Hotkey('R', "remove", WID_ROT_REMOVE), - Hotkey('C', "convert", WID_ROT_CONVERT_ROAD), - HOTKEY_LIST_END + static inline HotkeyList tram_hotkeys{"tramtoolbar", { + Hotkey('1', "build_x", WID_ROT_ROAD_X), + Hotkey('2', "build_y", WID_ROT_ROAD_Y), + Hotkey('3', "autoroad", WID_ROT_AUTOROAD), + Hotkey('4', "demolish", WID_ROT_DEMOLISH), + Hotkey('5', "depot", WID_ROT_DEPOT), + Hotkey('6', "bus_station", WID_ROT_BUS_STATION), + Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION), + Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE), + Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL), + Hotkey('R', "remove", WID_ROT_REMOVE), + Hotkey('C', "convert", WID_ROT_CONVERT_ROAD), + }, TramToolbarGlobalHotkeys}; }; -HotkeyList BuildRoadToolbarWindow::road_hotkeys("roadtoolbar", roadtoolbar_hotkeys, RoadToolbarGlobalHotkeys); - -static Hotkey tramtoolbar_hotkeys[] = { - Hotkey('1', "build_x", WID_ROT_ROAD_X), - Hotkey('2', "build_y", WID_ROT_ROAD_Y), - Hotkey('3', "autoroad", WID_ROT_AUTOROAD), - Hotkey('4', "demolish", WID_ROT_DEMOLISH), - Hotkey('5', "depot", WID_ROT_DEPOT), - Hotkey('6', "bus_station", WID_ROT_BUS_STATION), - Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION), - Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE), - Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL), - Hotkey('R', "remove", WID_ROT_REMOVE), - Hotkey('C', "convert", WID_ROT_CONVERT_ROAD), - HOTKEY_LIST_END -}; -HotkeyList BuildRoadToolbarWindow::tram_hotkeys("tramtoolbar", tramtoolbar_hotkeys, TramToolbarGlobalHotkeys); - static const NWidgetPart _nested_build_road_widgets[] = { NWidget(NWID_HORIZONTAL), @@ -1588,15 +1580,11 @@ public: this->InvalidateData(); }}; - static HotkeyList hotkeys; + static inline HotkeyList hotkeys{"buildroadstop", { + Hotkey('F', "focus_filter_box", BROSHK_FOCUS_FILTER_BOX), + }}; }; -static Hotkey buildroadstop_hotkeys[] = { - Hotkey('F', "focus_filter_box", BROSHK_FOCUS_FILTER_BOX), - HOTKEY_LIST_END -}; -HotkeyList BuildRoadStationWindow::hotkeys("buildroadstop", buildroadstop_hotkeys); - Listing BuildRoadStationWindow::last_sorting = { false, 0 }; Filtering BuildRoadStationWindow::last_filtering = { false, 0 }; diff --git a/src/script/script_gui.cpp b/src/script/script_gui.cpp index a179ddcef9..ef8f5229b8 100644 --- a/src/script/script_gui.cpp +++ b/src/script/script_gui.cpp @@ -1094,7 +1094,43 @@ struct ScriptDebugWindow : public Window { this->vscroll->SetCapacityFromWidget(this, WID_SCRD_LOG_PANEL, WidgetDimensions::scaled.framerect.Vertical()); } - static HotkeyList hotkeys; + /** + * Handler for global hotkeys of the ScriptDebugWindow. + * @param hotkey Hotkey + * @return ES_HANDLED if hotkey was accepted. + */ + static EventState ScriptDebugGlobalHotkeys(int hotkey) + { + if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED; + Window *w = ShowScriptDebugWindow(INVALID_COMPANY); + if (w == nullptr) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); + } + + static inline HotkeyList hotkeys{"aidebug", { + Hotkey('1', "company_1", WID_SCRD_COMPANY_BUTTON_START), + Hotkey('2', "company_2", WID_SCRD_COMPANY_BUTTON_START + 1), + Hotkey('3', "company_3", WID_SCRD_COMPANY_BUTTON_START + 2), + Hotkey('4', "company_4", WID_SCRD_COMPANY_BUTTON_START + 3), + Hotkey('5', "company_5", WID_SCRD_COMPANY_BUTTON_START + 4), + Hotkey('6', "company_6", WID_SCRD_COMPANY_BUTTON_START + 5), + Hotkey('7', "company_7", WID_SCRD_COMPANY_BUTTON_START + 6), + Hotkey('8', "company_8", WID_SCRD_COMPANY_BUTTON_START + 7), + Hotkey('9', "company_9", WID_SCRD_COMPANY_BUTTON_START + 8), + Hotkey(0, "company_10", WID_SCRD_COMPANY_BUTTON_START + 9), + Hotkey(0, "company_11", WID_SCRD_COMPANY_BUTTON_START + 10), + Hotkey(0, "company_12", WID_SCRD_COMPANY_BUTTON_START + 11), + Hotkey(0, "company_13", WID_SCRD_COMPANY_BUTTON_START + 12), + Hotkey(0, "company_14", WID_SCRD_COMPANY_BUTTON_START + 13), + Hotkey(0, "company_15", WID_SCRD_COMPANY_BUTTON_START + 14), + Hotkey('S', "settings", WID_SCRD_SETTINGS), + Hotkey('0', "game_script", WID_SCRD_SCRIPT_GAME), + Hotkey(0, "reload", WID_SCRD_RELOAD_TOGGLE), + Hotkey('B', "break_toggle", WID_SCRD_BREAK_STR_ON_OFF_BTN), + Hotkey('F', "break_string", WID_SCRD_BREAK_STR_EDIT_BOX), + Hotkey('C', "match_case", WID_SCRD_MATCH_CASE_BTN), + Hotkey(WKC_RETURN, "continue", WID_SCRD_CONTINUE_BTN), + }, ScriptDebugGlobalHotkeys}; }; CompanyID ScriptDebugWindow::script_debug_company = INVALID_COMPANY; @@ -1109,46 +1145,6 @@ NWidgetBase *MakeCompanyButtonRowsScriptDebug(int *biggest_index) return MakeCompanyButtonRows(biggest_index, WID_SCRD_COMPANY_BUTTON_START, WID_SCRD_COMPANY_BUTTON_END, COLOUR_GREY, 8, STR_AI_DEBUG_SELECT_AI_TOOLTIP); } -/** - * Handler for global hotkeys of the ScriptDebugWindow. - * @param hotkey Hotkey - * @return ES_HANDLED if hotkey was accepted. - */ -static EventState ScriptDebugGlobalHotkeys(int hotkey) -{ - if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED; - Window *w = ShowScriptDebugWindow(INVALID_COMPANY); - if (w == nullptr) return ES_NOT_HANDLED; - return w->OnHotkey(hotkey); -} - -static Hotkey scriptdebug_hotkeys[] = { - Hotkey('1', "company_1", WID_SCRD_COMPANY_BUTTON_START), - Hotkey('2', "company_2", WID_SCRD_COMPANY_BUTTON_START + 1), - Hotkey('3', "company_3", WID_SCRD_COMPANY_BUTTON_START + 2), - Hotkey('4', "company_4", WID_SCRD_COMPANY_BUTTON_START + 3), - Hotkey('5', "company_5", WID_SCRD_COMPANY_BUTTON_START + 4), - Hotkey('6', "company_6", WID_SCRD_COMPANY_BUTTON_START + 5), - Hotkey('7', "company_7", WID_SCRD_COMPANY_BUTTON_START + 6), - Hotkey('8', "company_8", WID_SCRD_COMPANY_BUTTON_START + 7), - Hotkey('9', "company_9", WID_SCRD_COMPANY_BUTTON_START + 8), - Hotkey((uint16)0, "company_10", WID_SCRD_COMPANY_BUTTON_START + 9), - Hotkey((uint16)0, "company_11", WID_SCRD_COMPANY_BUTTON_START + 10), - Hotkey((uint16)0, "company_12", WID_SCRD_COMPANY_BUTTON_START + 11), - Hotkey((uint16)0, "company_13", WID_SCRD_COMPANY_BUTTON_START + 12), - Hotkey((uint16)0, "company_14", WID_SCRD_COMPANY_BUTTON_START + 13), - Hotkey((uint16)0, "company_15", WID_SCRD_COMPANY_BUTTON_START + 14), - Hotkey('S', "settings", WID_SCRD_SETTINGS), - Hotkey('0', "game_script", WID_SCRD_SCRIPT_GAME), - Hotkey((uint16)0, "reload", WID_SCRD_RELOAD_TOGGLE), - Hotkey('B', "break_toggle", WID_SCRD_BREAK_STR_ON_OFF_BTN), - Hotkey('F', "break_string", WID_SCRD_BREAK_STR_EDIT_BOX), - Hotkey('C', "match_case", WID_SCRD_MATCH_CASE_BTN), - Hotkey(WKC_RETURN, "continue", WID_SCRD_CONTINUE_BTN), - HOTKEY_LIST_END -}; -HotkeyList ScriptDebugWindow::hotkeys("aidebug", scriptdebug_hotkeys, ScriptDebugGlobalHotkeys); - /** Widgets for the Script debug window. */ static const NWidgetPart _nested_script_debug_widgets[] = { NWidget(NWID_HORIZONTAL), diff --git a/src/signs_gui.cpp b/src/signs_gui.cpp index c8a61a41fb..de0d5d1967 100644 --- a/src/signs_gui.cpp +++ b/src/signs_gui.cpp @@ -338,27 +338,23 @@ struct SignListWindow : Window, SignList { } } - static HotkeyList hotkeys; -}; + /** + * Handler for global hotkeys of the SignListWindow. + * @param hotkey Hotkey + * @return ES_HANDLED if hotkey was accepted. + */ + static EventState SignListGlobalHotkeys(int hotkey) + { + if (_game_mode == GM_MENU) return ES_NOT_HANDLED; + Window *w = ShowSignList(); + if (w == nullptr) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); + } -/** - * Handler for global hotkeys of the SignListWindow. - * @param hotkey Hotkey - * @return ES_HANDLED if hotkey was accepted. - */ -static EventState SignListGlobalHotkeys(int hotkey) -{ - if (_game_mode == GM_MENU) return ES_NOT_HANDLED; - Window *w = ShowSignList(); - if (w == nullptr) return ES_NOT_HANDLED; - return w->OnHotkey(hotkey); -} - -static Hotkey signlist_hotkeys[] = { - Hotkey('F', "focus_filter_box", SLHK_FOCUS_FILTER_BOX), - HOTKEY_LIST_END + static inline HotkeyList hotkeys{"signlist", { + Hotkey('F', "focus_filter_box", SLHK_FOCUS_FILTER_BOX), + }, SignListGlobalHotkeys}; }; -HotkeyList SignListWindow::hotkeys("signlist", signlist_hotkeys, SignListGlobalHotkeys); static const NWidgetPart _nested_sign_list_widgets[] = { NWidget(NWID_HORIZONTAL), diff --git a/src/terraform_gui.cpp b/src/terraform_gui.cpp index 2429814518..cf267c25c8 100644 --- a/src/terraform_gui.cpp +++ b/src/terraform_gui.cpp @@ -295,34 +295,30 @@ struct TerraformToolbarWindow : Window { this->RaiseButtons(); } - static HotkeyList hotkeys; -}; + /** + * Handler for global hotkeys of the TerraformToolbarWindow. + * @param hotkey Hotkey + * @return ES_HANDLED if hotkey was accepted. + */ + static EventState TerraformToolbarGlobalHotkeys(int hotkey) + { + if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED; + Window *w = ShowTerraformToolbar(nullptr); + if (w == nullptr) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); + } -/** - * Handler for global hotkeys of the TerraformToolbarWindow. - * @param hotkey Hotkey - * @return ES_HANDLED if hotkey was accepted. - */ -static EventState TerraformToolbarGlobalHotkeys(int hotkey) -{ - if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED; - Window *w = ShowTerraformToolbar(nullptr); - if (w == nullptr) return ES_NOT_HANDLED; - return w->OnHotkey(hotkey); -} - -static Hotkey terraform_hotkeys[] = { - Hotkey('Q' | WKC_GLOBAL_HOTKEY, "lower", WID_TT_LOWER_LAND), - Hotkey('W' | WKC_GLOBAL_HOTKEY, "raise", WID_TT_RAISE_LAND), - Hotkey('E' | WKC_GLOBAL_HOTKEY, "level", WID_TT_LEVEL_LAND), - Hotkey('D' | WKC_GLOBAL_HOTKEY, "dynamite", WID_TT_DEMOLISH), - Hotkey('U', "buyland", WID_TT_BUY_LAND), - Hotkey('I', "trees", WID_TT_PLANT_TREES), - Hotkey('O', "placesign", WID_TT_PLACE_SIGN), - Hotkey('P', "placeobject", WID_TT_PLACE_OBJECT), - HOTKEY_LIST_END + static inline HotkeyList hotkeys{"terraform", { + Hotkey('Q' | WKC_GLOBAL_HOTKEY, "lower", WID_TT_LOWER_LAND), + Hotkey('W' | WKC_GLOBAL_HOTKEY, "raise", WID_TT_RAISE_LAND), + Hotkey('E' | WKC_GLOBAL_HOTKEY, "level", WID_TT_LEVEL_LAND), + Hotkey('D' | WKC_GLOBAL_HOTKEY, "dynamite", WID_TT_DEMOLISH), + Hotkey('U', "buyland", WID_TT_BUY_LAND), + Hotkey('I', "trees", WID_TT_PLANT_TREES), + Hotkey('O', "placesign", WID_TT_PLACE_SIGN), + Hotkey('P', "placeobject", WID_TT_PLACE_OBJECT), + }, TerraformToolbarGlobalHotkeys}; }; -HotkeyList TerraformToolbarWindow::hotkeys("terraform", terraform_hotkeys, TerraformToolbarGlobalHotkeys); static const NWidgetPart _nested_terraform_widgets[] = { NWidget(NWID_HORIZONTAL), @@ -719,35 +715,30 @@ struct ScenarioEditorLandscapeGenerationWindow : Window { this->SetDirty(); } - static HotkeyList hotkeys; + /** + * Handler for global hotkeys of the ScenarioEditorLandscapeGenerationWindow. + * @param hotkey Hotkey + * @return ES_HANDLED if hotkey was accepted. + */ + static EventState TerraformToolbarEditorGlobalHotkeys(int hotkey) + { + if (_game_mode != GM_EDITOR) return ES_NOT_HANDLED; + Window *w = ShowEditorTerraformToolbar(); + if (w == nullptr) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); + } + + static inline HotkeyList hotkeys{"terraform_editor", { + Hotkey('D' | WKC_GLOBAL_HOTKEY, "dynamite", WID_ETT_DEMOLISH), + Hotkey('Q' | WKC_GLOBAL_HOTKEY, "lower", WID_ETT_LOWER_LAND), + Hotkey('W' | WKC_GLOBAL_HOTKEY, "raise", WID_ETT_RAISE_LAND), + Hotkey('E' | WKC_GLOBAL_HOTKEY, "level", WID_ETT_LEVEL_LAND), + Hotkey('R', "rocky", WID_ETT_PLACE_ROCKS), + Hotkey('T', "desert", WID_ETT_PLACE_DESERT), + Hotkey('O', "object", WID_ETT_PLACE_OBJECT), + }, TerraformToolbarEditorGlobalHotkeys}; }; -/** - * Handler for global hotkeys of the ScenarioEditorLandscapeGenerationWindow. - * @param hotkey Hotkey - * @return ES_HANDLED if hotkey was accepted. - */ -static EventState TerraformToolbarEditorGlobalHotkeys(int hotkey) -{ - if (_game_mode != GM_EDITOR) return ES_NOT_HANDLED; - Window *w = ShowEditorTerraformToolbar(); - if (w == nullptr) return ES_NOT_HANDLED; - return w->OnHotkey(hotkey); -} - -static Hotkey terraform_editor_hotkeys[] = { - Hotkey('D' | WKC_GLOBAL_HOTKEY, "dynamite", WID_ETT_DEMOLISH), - Hotkey('Q' | WKC_GLOBAL_HOTKEY, "lower", WID_ETT_LOWER_LAND), - Hotkey('W' | WKC_GLOBAL_HOTKEY, "raise", WID_ETT_RAISE_LAND), - Hotkey('E' | WKC_GLOBAL_HOTKEY, "level", WID_ETT_LEVEL_LAND), - Hotkey('R', "rocky", WID_ETT_PLACE_ROCKS), - Hotkey('T', "desert", WID_ETT_PLACE_DESERT), - Hotkey('O', "object", WID_ETT_PLACE_OBJECT), - HOTKEY_LIST_END -}; - -HotkeyList ScenarioEditorLandscapeGenerationWindow::hotkeys("terraform_editor", terraform_editor_hotkeys, TerraformToolbarEditorGlobalHotkeys); - static WindowDesc _scen_edit_land_gen_desc( WDP_AUTO, "toolbar_landscape_scen", 0, 0, WC_SCEN_LAND_GEN, WC_NONE, diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index 7079266f46..168e110c8d 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -2142,59 +2142,50 @@ struct MainToolbarWindow : Window { HandleZoomMessage(this, GetMainWindow()->viewport, WID_TN_ZOOM_IN, WID_TN_ZOOM_OUT); } - static HotkeyList hotkeys; + static inline HotkeyList hotkeys{"maintoolbar", { + Hotkey({WKC_F1, WKC_PAUSE}, "pause", MTHK_PAUSE), + Hotkey(0, "fastforward", MTHK_FASTFORWARD), + Hotkey(WKC_F2, "settings", MTHK_SETTINGS), + Hotkey(WKC_F3, "saveload", MTHK_SAVEGAME), + Hotkey(0, "load_game", MTHK_LOADGAME), + Hotkey({WKC_F4, 'M'}, "smallmap", MTHK_SMALLMAP), + Hotkey(WKC_F5, "town_list", MTHK_TOWNDIRECTORY), + Hotkey(WKC_F6, "subsidies", MTHK_SUBSIDIES), + Hotkey(WKC_F7, "station_list", MTHK_STATIONS), + Hotkey(WKC_F8, "finances", MTHK_FINANCES), + Hotkey(WKC_F9, "companies", MTHK_COMPANIES), + Hotkey(0, "story_book", MTHK_STORY), + Hotkey(0, "goal_list", MTHK_GOAL), + Hotkey(WKC_F10, "graphs", MTHK_GRAPHS), + Hotkey(WKC_F11, "league", MTHK_LEAGUE), + Hotkey(WKC_F12, "industry_list", MTHK_INDUSTRIES), + Hotkey(WKC_SHIFT | WKC_F1, "train_list", MTHK_TRAIN_LIST), + Hotkey(WKC_SHIFT | WKC_F2, "roadveh_list", MTHK_ROADVEH_LIST), + Hotkey(WKC_SHIFT | WKC_F3, "ship_list", MTHK_SHIP_LIST), + Hotkey(WKC_SHIFT | WKC_F4, "aircraft_list", MTHK_AIRCRAFT_LIST), + Hotkey({WKC_NUM_PLUS, WKC_EQUALS, WKC_SHIFT | WKC_EQUALS, WKC_SHIFT | WKC_F5}, "zoomin", MTHK_ZOOM_IN), + Hotkey({WKC_NUM_MINUS, WKC_MINUS, WKC_SHIFT | WKC_MINUS, WKC_SHIFT | WKC_F6}, "zoomout", MTHK_ZOOM_OUT), + Hotkey(WKC_SHIFT | WKC_F7, "build_rail", MTHK_BUILD_RAIL), + Hotkey(WKC_SHIFT | WKC_F8, "build_road", MTHK_BUILD_ROAD), + Hotkey(0, "build_tram", MTHK_BUILD_TRAM), + Hotkey(WKC_SHIFT | WKC_F9, "build_docks", MTHK_BUILD_DOCKS), + Hotkey(WKC_SHIFT | WKC_F10, "build_airport", MTHK_BUILD_AIRPORT), + Hotkey(WKC_SHIFT | WKC_F11, "build_trees", MTHK_BUILD_TREES), + Hotkey(WKC_SHIFT | WKC_F12, "music", MTHK_MUSIC), + Hotkey(0, "ai_debug", MTHK_SCRIPT_DEBUG), + Hotkey(WKC_CTRL | 'S', "small_screenshot", MTHK_SMALL_SCREENSHOT), + Hotkey(WKC_CTRL | 'P', "zoomedin_screenshot", MTHK_ZOOMEDIN_SCREENSHOT), + Hotkey(WKC_CTRL | 'D', "defaultzoom_screenshot", MTHK_DEFAULTZOOM_SCREENSHOT), + Hotkey(0, "giant_screenshot", MTHK_GIANT_SCREENSHOT), + Hotkey(WKC_CTRL | WKC_ALT | 'C', "cheats", MTHK_CHEATS), + Hotkey('L', "terraform", MTHK_TERRAFORM), + Hotkey('V', "extra_viewport", MTHK_EXTRA_VIEWPORT), + Hotkey(0, "client_list", MTHK_CLIENT_LIST), + Hotkey(0, "sign_list", MTHK_SIGN_LIST), + Hotkey(0, "land_info", MTHK_LANDINFO), + }}; }; -const uint16 _maintoolbar_pause_keys[] = {WKC_F1, WKC_PAUSE, 0}; -const uint16 _maintoolbar_zoomin_keys[] = {WKC_NUM_PLUS, WKC_EQUALS, WKC_SHIFT | WKC_EQUALS, WKC_SHIFT | WKC_F5, 0}; -const uint16 _maintoolbar_zoomout_keys[] = {WKC_NUM_MINUS, WKC_MINUS, WKC_SHIFT | WKC_MINUS, WKC_SHIFT | WKC_F6, 0}; -const uint16 _maintoolbar_smallmap_keys[] = {WKC_F4, 'M', 0}; - -static Hotkey maintoolbar_hotkeys[] = { - Hotkey(_maintoolbar_pause_keys, "pause", MTHK_PAUSE), - Hotkey((uint16)0, "fastforward", MTHK_FASTFORWARD), - Hotkey(WKC_F2, "settings", MTHK_SETTINGS), - Hotkey(WKC_F3, "saveload", MTHK_SAVEGAME), - Hotkey((uint16)0, "load_game", MTHK_LOADGAME), - Hotkey(_maintoolbar_smallmap_keys, "smallmap", MTHK_SMALLMAP), - Hotkey(WKC_F5, "town_list", MTHK_TOWNDIRECTORY), - Hotkey(WKC_F6, "subsidies", MTHK_SUBSIDIES), - Hotkey(WKC_F7, "station_list", MTHK_STATIONS), - Hotkey(WKC_F8, "finances", MTHK_FINANCES), - Hotkey(WKC_F9, "companies", MTHK_COMPANIES), - Hotkey((uint16)0, "story_book", MTHK_STORY), - Hotkey((uint16)0, "goal_list", MTHK_GOAL), - Hotkey(WKC_F10, "graphs", MTHK_GRAPHS), - Hotkey(WKC_F11, "league", MTHK_LEAGUE), - Hotkey(WKC_F12, "industry_list", MTHK_INDUSTRIES), - Hotkey(WKC_SHIFT | WKC_F1, "train_list", MTHK_TRAIN_LIST), - Hotkey(WKC_SHIFT | WKC_F2, "roadveh_list", MTHK_ROADVEH_LIST), - Hotkey(WKC_SHIFT | WKC_F3, "ship_list", MTHK_SHIP_LIST), - Hotkey(WKC_SHIFT | WKC_F4, "aircraft_list", MTHK_AIRCRAFT_LIST), - Hotkey(_maintoolbar_zoomin_keys, "zoomin", MTHK_ZOOM_IN), - Hotkey(_maintoolbar_zoomout_keys, "zoomout", MTHK_ZOOM_OUT), - Hotkey(WKC_SHIFT | WKC_F7, "build_rail", MTHK_BUILD_RAIL), - Hotkey(WKC_SHIFT | WKC_F8, "build_road", MTHK_BUILD_ROAD), - Hotkey((uint16)0, "build_tram", MTHK_BUILD_TRAM), - Hotkey(WKC_SHIFT | WKC_F9, "build_docks", MTHK_BUILD_DOCKS), - Hotkey(WKC_SHIFT | WKC_F10, "build_airport", MTHK_BUILD_AIRPORT), - Hotkey(WKC_SHIFT | WKC_F11, "build_trees", MTHK_BUILD_TREES), - Hotkey(WKC_SHIFT | WKC_F12, "music", MTHK_MUSIC), - Hotkey((uint16)0, "ai_debug", MTHK_SCRIPT_DEBUG), - Hotkey(WKC_CTRL | 'S', "small_screenshot", MTHK_SMALL_SCREENSHOT), - Hotkey(WKC_CTRL | 'P', "zoomedin_screenshot", MTHK_ZOOMEDIN_SCREENSHOT), - Hotkey(WKC_CTRL | 'D', "defaultzoom_screenshot", MTHK_DEFAULTZOOM_SCREENSHOT), - Hotkey((uint16)0, "giant_screenshot", MTHK_GIANT_SCREENSHOT), - Hotkey(WKC_CTRL | WKC_ALT | 'C', "cheats", MTHK_CHEATS), - Hotkey('L', "terraform", MTHK_TERRAFORM), - Hotkey('V', "extra_viewport", MTHK_EXTRA_VIEWPORT), - Hotkey((uint16)0, "client_list", MTHK_CLIENT_LIST), - Hotkey((uint16)0, "sign_list", MTHK_SIGN_LIST), - Hotkey((uint16)0, "land_info", MTHK_LANDINFO), - HOTKEY_LIST_END -}; -HotkeyList MainToolbarWindow::hotkeys("maintoolbar", maintoolbar_hotkeys); - static NWidgetBase *MakeMainToolbar(int *biggest_index) { /** Sprites to use for the different toolbar buttons */ @@ -2526,37 +2517,33 @@ struct ScenarioEditorToolbarWindow : Window { this->SetDirty(); } - static HotkeyList hotkeys; + static inline HotkeyList hotkeys{"scenedit_maintoolbar", { + Hotkey({WKC_F1, WKC_PAUSE}, "pause", MTEHK_PAUSE), + Hotkey(0, "fastforward", MTEHK_FASTFORWARD), + Hotkey(WKC_F2, "settings", MTEHK_SETTINGS), + Hotkey(WKC_F3, "saveload", MTEHK_SAVEGAME), + Hotkey(WKC_F4, "gen_land", MTEHK_GENLAND), + Hotkey(WKC_F5, "gen_town", MTEHK_GENTOWN), + Hotkey(WKC_F6, "gen_industry", MTEHK_GENINDUSTRY), + Hotkey(WKC_F7, "build_road", MTEHK_BUILD_ROAD), + Hotkey(0, "build_tram", MTEHK_BUILD_TRAM), + Hotkey(WKC_F8, "build_docks", MTEHK_BUILD_DOCKS), + Hotkey(WKC_F9, "build_trees", MTEHK_BUILD_TREES), + Hotkey(WKC_F10, "build_sign", MTEHK_SIGN), + Hotkey(WKC_F11, "music", MTEHK_MUSIC), + Hotkey(WKC_F12, "land_info", MTEHK_LANDINFO), + Hotkey(WKC_CTRL | 'S', "small_screenshot", MTEHK_SMALL_SCREENSHOT), + Hotkey(WKC_CTRL | 'P', "zoomedin_screenshot", MTEHK_ZOOMEDIN_SCREENSHOT), + Hotkey(WKC_CTRL | 'D', "defaultzoom_screenshot", MTEHK_DEFAULTZOOM_SCREENSHOT), + Hotkey(0, "giant_screenshot", MTEHK_GIANT_SCREENSHOT), + Hotkey({WKC_NUM_PLUS, WKC_EQUALS, WKC_SHIFT | WKC_EQUALS, WKC_SHIFT | WKC_F5}, "zoomin", MTEHK_ZOOM_IN), + Hotkey({WKC_NUM_MINUS, WKC_MINUS, WKC_SHIFT | WKC_MINUS, WKC_SHIFT | WKC_F6}, "zoomout", MTEHK_ZOOM_OUT), + Hotkey('L', "terraform", MTEHK_TERRAFORM), + Hotkey('M', "smallmap", MTEHK_SMALLMAP), + Hotkey('V', "extra_viewport", MTEHK_EXTRA_VIEWPORT), + }}; }; -static Hotkey scenedit_maintoolbar_hotkeys[] = { - Hotkey(_maintoolbar_pause_keys, "pause", MTEHK_PAUSE), - Hotkey((uint16)0, "fastforward", MTEHK_FASTFORWARD), - Hotkey(WKC_F2, "settings", MTEHK_SETTINGS), - Hotkey(WKC_F3, "saveload", MTEHK_SAVEGAME), - Hotkey(WKC_F4, "gen_land", MTEHK_GENLAND), - Hotkey(WKC_F5, "gen_town", MTEHK_GENTOWN), - Hotkey(WKC_F6, "gen_industry", MTEHK_GENINDUSTRY), - Hotkey(WKC_F7, "build_road", MTEHK_BUILD_ROAD), - Hotkey((uint16)0, "build_tram", MTEHK_BUILD_TRAM), - Hotkey(WKC_F8, "build_docks", MTEHK_BUILD_DOCKS), - Hotkey(WKC_F9, "build_trees", MTEHK_BUILD_TREES), - Hotkey(WKC_F10, "build_sign", MTEHK_SIGN), - Hotkey(WKC_F11, "music", MTEHK_MUSIC), - Hotkey(WKC_F12, "land_info", MTEHK_LANDINFO), - Hotkey(WKC_CTRL | 'S', "small_screenshot", MTEHK_SMALL_SCREENSHOT), - Hotkey(WKC_CTRL | 'P', "zoomedin_screenshot", MTEHK_ZOOMEDIN_SCREENSHOT), - Hotkey(WKC_CTRL | 'D', "defaultzoom_screenshot", MTEHK_DEFAULTZOOM_SCREENSHOT), - Hotkey((uint16)0, "giant_screenshot", MTEHK_GIANT_SCREENSHOT), - Hotkey(_maintoolbar_zoomin_keys, "zoomin", MTEHK_ZOOM_IN), - Hotkey(_maintoolbar_zoomout_keys, "zoomout", MTEHK_ZOOM_OUT), - Hotkey('L', "terraform", MTEHK_TERRAFORM), - Hotkey('M', "smallmap", MTEHK_SMALLMAP), - Hotkey('V', "extra_viewport", MTEHK_EXTRA_VIEWPORT), - HOTKEY_LIST_END -}; -HotkeyList ScenarioEditorToolbarWindow::hotkeys("scenedit_maintoolbar", scenedit_maintoolbar_hotkeys); - static const NWidgetPart _nested_toolb_scen_inner_widgets[] = { NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_PAUSE), SetDataTip(SPR_IMG_PAUSE, STR_TOOLBAR_TOOLTIP_PAUSE_GAME), NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_FAST_FORWARD), SetDataTip(SPR_IMG_FASTFORWARD, STR_TOOLBAR_TOOLTIP_FORWARD), diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 400306d9d4..7bab872700 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -3264,15 +3264,11 @@ public: ::ShowNewGRFInspectWindow(GetGrfSpecFeature(Vehicle::Get(this->window_number)->type), this->window_number); } - static HotkeyList hotkeys; + static inline HotkeyList hotkeys{"vehicleview", { + Hotkey('H', "honk", WID_VV_HONK_HORN), + }}; }; -static Hotkey vehicleview_hotkeys[] = { - Hotkey('H', "honk", WID_VV_HONK_HORN), - HOTKEY_LIST_END -}; -HotkeyList VehicleViewWindow::hotkeys("vehicleview", vehicleview_hotkeys); - /** Vehicle view window descriptor for all vehicles but trains. */ static WindowDesc _vehicle_view_desc( WDP_AUTO, "view_vehicle", 250, 116,