From 5b9d171e63bf1e5dbc93528486a3314176d1cd2c Mon Sep 17 00:00:00 2001
From: Peter Nelson <peter1138@openttd.org>
Date: Tue, 8 Apr 2025 21:19:17 +0100
Subject: [PATCH] Codechange: Use EnumBitSet for StringValidationSettings.
 (#13974)

---
 src/console.cpp                  |  2 +-
 src/network/core/packet.h        |  2 +-
 src/network/network_command.cpp  |  6 +++++-
 src/network/network_content.cpp  |  2 +-
 src/newgrf_gui.cpp               |  4 ++--
 src/news_gui.cpp                 |  2 +-
 src/saveload/saveload.cpp        |  6 +++---
 src/script/api/script_object.hpp |  2 +-
 src/script/api/script_text.cpp   |  2 +-
 src/settings.cpp                 |  2 +-
 src/statusbar_gui.cpp            |  2 +-
 src/string.cpp                   | 10 +++++-----
 src/string_func.h                | 10 +++++-----
 src/string_type.h                | 18 +++++++++---------
 src/textfile_gui.cpp             |  2 +-
 15 files changed, 38 insertions(+), 34 deletions(-)

diff --git a/src/console.cpp b/src/console.cpp
index d09f6fc109..675b8bbcb5 100644
--- a/src/console.cpp
+++ b/src/console.cpp
@@ -103,7 +103,7 @@ void IConsolePrint(TextColour colour_code, const std::string &string)
 
 	/* Create a copy of the string, strip it of colours and invalid
 	 * characters and (when applicable) assign it to the console buffer */
-	std::string str = StrMakeValid(string, SVS_NONE);
+	std::string str = StrMakeValid(string, {});
 
 	if (_network_dedicated) {
 		NetworkAdminConsole("console", str);
diff --git a/src/network/core/packet.h b/src/network/core/packet.h
index 2105bf24a9..d1ffa33c80 100644
--- a/src/network/core/packet.h
+++ b/src/network/core/packet.h
@@ -87,7 +87,7 @@ public:
 	uint64_t Recv_uint64();
 	std::vector<uint8_t> Recv_buffer();
 	size_t Recv_bytes(std::span<uint8_t> span);
-	std::string Recv_string(size_t length, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
+	std::string Recv_string(size_t length, StringValidationSettings settings = StringValidationSetting::ReplaceWithQuestionMark);
 
 	size_t RemainingBytesToTransfer() const;
 
diff --git a/src/network/network_command.cpp b/src/network/network_command.cpp
index e4f65308f4..092ac1811f 100644
--- a/src/network/network_command.cpp
+++ b/src/network/network_command.cpp
@@ -441,7 +441,11 @@ template <class T>
 static inline void SanitizeSingleStringHelper([[maybe_unused]] CommandFlags cmd_flags, T &data)
 {
 	if constexpr (std::is_same_v<std::string, T>) {
-		StrMakeValidInPlace(data, (!_network_server && cmd_flags.Test(CommandFlag::StrCtrl)) ? SVS_ALLOW_CONTROL_CODE | SVS_REPLACE_WITH_QUESTION_MARK : SVS_REPLACE_WITH_QUESTION_MARK);
+		if (!_network_server && cmd_flags.Test(CommandFlag::StrCtrl)) {
+			StrMakeValidInPlace(data, {StringValidationSetting::AllowControlCode, StringValidationSetting::ReplaceWithQuestionMark});
+		} else {
+			StrMakeValidInPlace(data, {StringValidationSetting::ReplaceWithQuestionMark});
+		}
 	}
 }
 
diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp
index ac0f70d683..4a347c6424 100644
--- a/src/network/network_content.cpp
+++ b/src/network/network_content.cpp
@@ -68,7 +68,7 @@ bool ClientNetworkContentSocketHandler::Receive_SERVER_INFO(Packet &p)
 	ci->name        = p.Recv_string(NETWORK_CONTENT_NAME_LENGTH);
 	ci->version     = p.Recv_string(NETWORK_CONTENT_VERSION_LENGTH);
 	ci->url         = p.Recv_string(NETWORK_CONTENT_URL_LENGTH);
-	ci->description = p.Recv_string(NETWORK_CONTENT_DESC_LENGTH, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE);
+	ci->description = p.Recv_string(NETWORK_CONTENT_DESC_LENGTH, {StringValidationSetting::ReplaceWithQuestionMark, StringValidationSetting::AllowNewline});
 
 	ci->unique_id = p.Recv_uint32();
 	p.Recv_bytes(ci->md5sum);
diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp
index c838687d7b..9a36c269d2 100644
--- a/src/newgrf_gui.cpp
+++ b/src/newgrf_gui.cpp
@@ -1398,8 +1398,8 @@ private:
 	/** Sort grfs by name. */
 	static bool NameSorter(const GRFConfig * const &a, const GRFConfig * const &b)
 	{
-		std::string name_a = StrMakeValid(a->GetName(), SVS_NONE); // Make a copy without control codes.
-		std::string name_b = StrMakeValid(b->GetName(), SVS_NONE); // Make a copy without control codes.
+		std::string name_a = StrMakeValid(a->GetName(), {}); // Make a copy without control codes.
+		std::string name_b = StrMakeValid(b->GetName(), {}); // Make a copy without control codes.
 		int i = StrNaturalCompare(name_a, name_b, true); // Sort by name (natural sorting).
 		if (i != 0) return i < 0;
 
diff --git a/src/news_gui.cpp b/src/news_gui.cpp
index 51e51f748f..ab247fb7ae 100644
--- a/src/news_gui.cpp
+++ b/src/news_gui.cpp
@@ -1190,7 +1190,7 @@ void ShowLastNewsMessage()
 static void DrawNewsString(uint left, uint right, int y, TextColour colour, const NewsItem &ni)
 {
 	/* Get the string, replaces newlines with spaces and remove control codes from the string. */
-	std::string message = StrMakeValid(ni.GetStatusText(), SVS_REPLACE_TAB_CR_NL_WITH_SPACE);
+	std::string message = StrMakeValid(ni.GetStatusText(), StringValidationSetting::ReplaceTabCrNlWithSpace);
 
 	/* Truncate and show string; postfixed by '...' if necessary */
 	DrawString(left, right, y, message, colour);
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
index a50c077b31..6b0d349f6b 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -1020,13 +1020,13 @@ static void SlStdString(void *ptr, VarType conv)
 
 			SlReadString(*str, len);
 
-			StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK;
+			StringValidationSettings settings = StringValidationSetting::ReplaceWithQuestionMark;
 			if ((conv & SLF_ALLOW_CONTROL) != 0) {
-				settings = settings | SVS_ALLOW_CONTROL_CODE;
+				settings.Set(StringValidationSetting::AllowControlCode);
 				if (IsSavegameVersionBefore(SLV_ENCODED_STRING_FORMAT)) FixSCCEncoded(*str, IsSavegameVersionBefore(SLV_169));
 			}
 			if ((conv & SLF_ALLOW_NEWLINE) != 0) {
-				settings = settings | SVS_ALLOW_NEWLINE;
+				settings.Set(StringValidationSetting::AllowNewline);
 			}
 			StrMakeValidInPlace(*str, settings);
 		}
diff --git a/src/script/api/script_object.hpp b/src/script/api/script_object.hpp
index 8c3067c448..9a8cf7b226 100644
--- a/src/script/api/script_object.hpp
+++ b/src/script/api/script_object.hpp
@@ -353,7 +353,7 @@ namespace ScriptObjectInternal {
 		if constexpr (std::is_same_v<std::string, T>) {
 			/* The string must be valid, i.e. not contain special codes. Since some
 			 * can be made with GSText, make sure the control codes are removed. */
-			::StrMakeValidInPlace(data, SVS_NONE);
+			::StrMakeValidInPlace(data, {});
 		}
 	}
 
diff --git a/src/script/api/script_text.cpp b/src/script/api/script_text.cpp
index acfbd20861..5cc784a427 100644
--- a/src/script/api/script_text.cpp
+++ b/src/script/api/script_text.cpp
@@ -204,7 +204,7 @@ void ScriptText::ParamCheck::Encode(std::back_insert_iterator<std::string> &outp
 		void operator()(std::string value)
 		{
 			Utf8Encode(this->output, SCC_ENCODED_STRING);
-			StrMakeValidInPlace(value, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE | SVS_REPLACE_TAB_CR_NL_WITH_SPACE);
+			StrMakeValidInPlace(value, {StringValidationSetting::ReplaceWithQuestionMark, StringValidationSetting::AllowNewline, StringValidationSetting::ReplaceTabCrNlWithSpace});
 			fmt::format_to(this->output, "{}", value);
 		}
 
diff --git a/src/settings.cpp b/src/settings.cpp
index 7b99d786f8..9cd14a2da7 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -598,7 +598,7 @@ void StringSettingDesc::MakeValueValid(std::string &str) const
 	 * includes the '\0' termination for network transfer purposes.
 	 * Also ensure the string is valid after chopping of some bytes. */
 	str.erase(this->max_length - 1, std::string::npos);
-	StrMakeValidInPlace(str, SVS_NONE);
+	StrMakeValidInPlace(str, {});
 }
 
 /**
diff --git a/src/statusbar_gui.cpp b/src/statusbar_gui.cpp
index 61aae847af..7944176499 100644
--- a/src/statusbar_gui.cpp
+++ b/src/statusbar_gui.cpp
@@ -39,7 +39,7 @@
 static bool DrawScrollingStatusText(const NewsItem &ni, int scroll_pos, int left, int right, int top, int bottom)
 {
 	/* Replace newlines and the likes with spaces. */
-	std::string message = StrMakeValid(ni.GetStatusText(), SVS_REPLACE_TAB_CR_NL_WITH_SPACE);
+	std::string message = StrMakeValid(ni.GetStatusText(), StringValidationSetting::ReplaceTabCrNlWithSpace);
 
 	DrawPixelInfo tmp_dpi;
 	if (!FillDrawPixelInfo(&tmp_dpi, left, top, right - left, bottom)) return true;
diff --git a/src/string.cpp b/src/string.cpp
index 203a5d6ba7..966a3b6af7 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -159,25 +159,25 @@ static void StrMakeValid(T &dst, const char *str, const char *last, StringValida
 			continue;
 		}
 
-		if ((IsPrintable(c) && (c < SCC_SPRITE_START || c > SCC_SPRITE_END)) || ((settings & SVS_ALLOW_CONTROL_CODE) != 0 && IsSccEncodedCode(c))) {
+		if ((IsPrintable(c) && (c < SCC_SPRITE_START || c > SCC_SPRITE_END)) || (settings.Test(StringValidationSetting::AllowControlCode) && IsSccEncodedCode(c))) {
 			/* Copy the character back. Even if dst is current the same as str
 			 * (i.e. no characters have been changed) this is quicker than
 			 * moving the pointers ahead by len */
 			do {
 				*dst++ = *str++;
 			} while (--len != 0);
-		} else if ((settings & SVS_ALLOW_NEWLINE) != 0 && c == '\n') {
+		} else if (settings.Test(StringValidationSetting::AllowNewline) && c == '\n') {
 			*dst++ = *str++;
 		} else {
-			if ((settings & SVS_ALLOW_NEWLINE) != 0 && c == '\r' && str[1] == '\n') {
+			if (settings.Test(StringValidationSetting::AllowNewline) && c == '\r' && str[1] == '\n') {
 				str += len;
 				continue;
 			}
 			str += len;
-			if ((settings & SVS_REPLACE_TAB_CR_NL_WITH_SPACE) != 0 && (c == '\r' || c == '\n' || c == '\t')) {
+			if (settings.Test(StringValidationSetting::ReplaceTabCrNlWithSpace) && (c == '\r' || c == '\n' || c == '\t')) {
 				/* Replace the tab, carriage return or newline with a space. */
 				*dst++ = ' ';
-			} else if ((settings & SVS_REPLACE_WITH_QUESTION_MARK) != 0) {
+			} else if (settings.Test(StringValidationSetting::ReplaceWithQuestionMark)) {
 				/* Replace the undesirable character with a question mark */
 				*dst++ = '?';
 			}
diff --git a/src/string_func.h b/src/string_func.h
index 231a69527c..4e44b3bb7a 100644
--- a/src/string_func.h
+++ b/src/string_func.h
@@ -21,15 +21,15 @@ void strecpy(std::span<char> dst, std::string_view src);
 
 std::string FormatArrayAsHex(std::span<const uint8_t> data);
 
-void StrMakeValidInPlace(char *str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
-void StrMakeValidInPlace(std::string &str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
+void StrMakeValidInPlace(char *str, StringValidationSettings settings = StringValidationSetting::ReplaceWithQuestionMark);
+void StrMakeValidInPlace(std::string &str, StringValidationSettings settings = StringValidationSetting::ReplaceWithQuestionMark);
 
-[[nodiscard]] std::string StrMakeValid(std::string_view str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
-[[nodiscard]] inline std::string StrMakeValid(const char *str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK)
+[[nodiscard]] std::string StrMakeValid(std::string_view str, StringValidationSettings settings = StringValidationSetting::ReplaceWithQuestionMark);
+[[nodiscard]] inline std::string StrMakeValid(const char *str, StringValidationSettings settings = StringValidationSetting::ReplaceWithQuestionMark)
 {
 	return StrMakeValid(std::string_view(str), settings);
 }
-[[nodiscard]] inline std::string StrMakeValid(std::string &&str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK)
+[[nodiscard]] inline std::string StrMakeValid(std::string &&str, StringValidationSettings settings = StringValidationSetting::ReplaceWithQuestionMark)
 {
 	StrMakeValidInPlace(str, settings);
 	return std::move(str);
diff --git a/src/string_type.h b/src/string_type.h
index 3222e29d46..345a7dcb4e 100644
--- a/src/string_type.h
+++ b/src/string_type.h
@@ -41,19 +41,19 @@ static const char32_t CHAR_TD_RLO = 0x202E; ///< Force the following characters
 static const char32_t CHAR_TD_PDF = 0x202C; ///< Restore the text-direction state to before the last LRE, RLE, LRO or RLO.
 
 /** Settings for the string validation. */
-enum StringValidationSettings : uint8_t {
-	SVS_NONE                       = 0,      ///< Allow nothing and replace nothing.
-	SVS_REPLACE_WITH_QUESTION_MARK = 1 << 0, ///< Replace the unknown/bad bits with question marks.
-	SVS_ALLOW_NEWLINE              = 1 << 1, ///< Allow newlines; replaces '\r\n' with '\n' during processing.
-	SVS_ALLOW_CONTROL_CODE         = 1 << 2, ///< Allow the special control codes.
+enum class StringValidationSetting : uint8_t {
+	ReplaceWithQuestionMark, ///< Replace the unknown/bad bits with question marks.
+	AllowNewline, ///< Allow newlines; replaces '\r\n' with '\n' during processing.
+	AllowControlCode, ///< Allow the special control codes.
 	/**
 	 * Replace tabs ('\t'), carriage returns ('\r') and newlines ('\n') with spaces.
-	 * When #SVS_ALLOW_NEWLINE is set, a '\n' or '\r\n' combination are not replaced with a space. A lone '\r' is replaced with a space.
-	 * When #SVS_REPLACE_WITH_QUESTION_MARK is set, this replacement runs first.
+	 * When #StringValidationSetting::AllowNewline is set, a '\n' or '\r\n' combination are not replaced with a space. A lone '\r' is replaced with a space.
+	 * When #StringValidationSetting::ReplaceWithQuestionMark is set, this replacement runs first.
 	 */
-	SVS_REPLACE_TAB_CR_NL_WITH_SPACE = 1 << 3,
+	ReplaceTabCrNlWithSpace,
 };
-DECLARE_ENUM_AS_BIT_SET(StringValidationSettings)
+
+using StringValidationSettings = EnumBitSet<StringValidationSetting, uint8_t>;
 
 
 /** Type for a list of strings. */
diff --git a/src/textfile_gui.cpp b/src/textfile_gui.cpp
index 3d8bdbcf1a..82e06a02b2 100644
--- a/src/textfile_gui.cpp
+++ b/src/textfile_gui.cpp
@@ -803,7 +803,7 @@ static std::vector<char> Xunzip(std::span<char> input)
  */
 void TextfileWindow::LoadText(std::string_view buf)
 {
-	std::string text = StrMakeValid(buf, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE | SVS_REPLACE_TAB_CR_NL_WITH_SPACE);
+	std::string text = StrMakeValid(buf, {StringValidationSetting::ReplaceWithQuestionMark, StringValidationSetting::AllowNewline, StringValidationSetting::ReplaceTabCrNlWithSpace});
 	this->lines.clear();
 
 	/* Split the string on newlines. */