1
0
Fork 0

Codechange: String parameter encoding for regular strings.

This allows a string and its parameters to be encoded and stored as just one string, instead of juggling with capturing and restoring string parameters.

The advantage of EncodedStrings over raw strings is they use current language and parameter values at the point of decoding.
pull/13528/head
Peter Nelson 2024-12-07 09:28:56 +00:00 committed by Peter Nelson
parent 4010313180
commit 1f21e9dc74
5 changed files with 114 additions and 7 deletions

View File

@ -100,6 +100,7 @@ static bool IsSccEncodedCode(char32_t c)
switch (c) {
case SCC_RECORD_SEPARATOR:
case SCC_ENCODED:
case SCC_ENCODED_INTERNAL:
case SCC_ENCODED_NUMERIC:
case SCC_ENCODED_STRING:
return true;

View File

@ -95,6 +95,65 @@ const StringParameter &StringParameters::GetNextParameterReference()
return param;
}
/**
* Encode a string with no parameters into an encoded string.
* @param str The StringID to format.
* @returns The encoded string.
*/
EncodedString GetEncodedString(StringID str)
{
return GetEncodedStringWithArgs(str, {});
}
/**
* Encode a string with its parameters into an encoded string.
* The encoded string can be stored and decoded later without requiring parameters to be stored separately.
* @param str The StringID to format.
* @param params The parameters of the string.
* @returns The encoded string.
*/
EncodedString GetEncodedStringWithArgs(StringID str, std::span<const StringParameter> params)
{
std::string result;
auto output = std::back_inserter(result);
Utf8Encode(output, SCC_ENCODED_INTERNAL);
fmt::format_to(output, "{:X}", str);
struct visitor {
std::back_insert_iterator<std::string> &output;
void operator()(const std::monostate &) {}
void operator()(const uint64_t &arg)
{
Utf8Encode(output, SCC_ENCODED_NUMERIC);
fmt::format_to(this->output, "{:X}", arg);
}
void operator()(const std::string &value)
{
Utf8Encode(output, SCC_ENCODED_STRING);
fmt::format_to(this->output, "{}", value);
}
};
visitor v{output};
for (const auto &param : params) {
*output = SCC_RECORD_SEPARATOR;
std::visit(v, param.data);
}
return EncodedString{std::move(result)};
}
/**
* Decode the encoded string.
* @returns Decoded raw string.
*/
std::string EncodedString::GetDecodedString() const
{
return GetString(STR_JUST_RAW_STRING, this->string);
}
/**
* Set a string parameter \a v at index \a n in the global string parameter array.
@ -956,10 +1015,11 @@ uint ConvertDisplaySpeedToKmhishSpeed(uint speed, VehicleType type)
/**
* Decodes an encoded string during FormatString.
* @param str The buffer of the encoded string.
* @param game_script Set if decoding a GameScript-encoded string. This affects how string IDs are handled.
* @param builder The string builder to write the string to.
* @returns Updated position position in input buffer.
*/
static const char *DecodeEncodedString(const char *str, StringBuilder &builder)
static const char *DecodeEncodedString(const char *str, bool game_script, StringBuilder &builder)
{
ArrayStringParameters<20> sub_args;
@ -970,7 +1030,7 @@ static const char *DecodeEncodedString(const char *str, StringBuilder &builder)
builder += "(invalid SCC_ENCODED)";
return p;
}
if (id >= TAB_SIZE_GAMESCRIPT) {
if (game_script && id >= TAB_SIZE_GAMESCRIPT) {
while (*p != '\0') p++;
builder += "(invalid StringID)";
return p;
@ -984,6 +1044,12 @@ static const char *DecodeEncodedString(const char *str, StringBuilder &builder)
/* Find end of the parameter. */
for (; *p != '\0' && *p != SCC_RECORD_SEPARATOR; ++p) {}
if (s == p) {
/* This is an empty parameter. */
sub_args.SetParam(i++, std::monostate{});
continue;
}
/* Get the parameter type. */
char32_t parameter_type;
size_t len = Utf8Decode(&parameter_type, s);
@ -1014,13 +1080,13 @@ static const char *DecodeEncodedString(const char *str, StringBuilder &builder)
}
default:
/* Skip unknown parameter. */
i++;
/* Unknown parameter, make it blank. */
sub_args.SetParam(i++, std::monostate{});
break;
}
}
StringID stringid = MakeStringID(TEXT_TAB_GAMESCRIPT_START, id);
StringID stringid = game_script ? MakeStringID(TEXT_TAB_GAMESCRIPT_START, id) : StringID{id.base()};
GetStringWithArgs(builder, stringid, sub_args, true);
return p;
@ -1092,7 +1158,8 @@ static void FormatString(StringBuilder &builder, const char *str_arg, StringPara
args.SetTypeOfNextParameter(b);
switch (b) {
case SCC_ENCODED:
str = DecodeEncodedString(str, builder);
case SCC_ENCODED_INTERNAL:
str = DecodeEncodedString(str, b == SCC_ENCODED, builder);
break;
case SCC_NEWGRF_STRINL: {

View File

@ -135,6 +135,22 @@ std::string GetString(StringID string, Args &&... args)
return GetStringWithArgs(string, params);
}
EncodedString GetEncodedString(StringID str);
EncodedString GetEncodedStringWithArgs(StringID str, std::span<const StringParameter> params);
/**
* Get an encoded string with parameters.
* @param string String ID to encode.
* @param args The parameters to set.
* @return The encoded string.
*/
template <typename... Args>
EncodedString GetEncodedString(StringID string, const Args&... args)
{
auto params = MakeParameters(std::forward<const Args&>(args)...);
return GetEncodedStringWithArgs(string, params);
}
/**
* A searcher for missing glyphs.
*/

View File

@ -92,4 +92,27 @@ struct StringParameter {
inline StringParameter(const ConvertibleThroughBase auto &data) : data(static_cast<uint64_t>(data.base())), type(0) {}
};
/**
* Container for an encoded string, created by GetEncodedString.
*/
class EncodedString {
public:
EncodedString() = default;
auto operator<=>(const EncodedString &) const = default;
std::string GetDecodedString() const;
inline void clear() { this->string.clear(); }
inline bool empty() const { return this->string.empty(); }
private:
std::string string; ///< The encoded string.
/* An EncodedString can only be created by GetEncodedStringWithArgs(). */
explicit EncodedString(std::string &&string) : string(std::move(string)) {}
friend EncodedString GetEncodedStringWithArgs(StringID str, std::span<const StringParameter> params);
};
#endif /* STRINGS_TYPE_H */

View File

@ -25,7 +25,7 @@ enum StringControlCode : uint16_t {
/* All SCC_ENCODED* control codes must have stable ids are they are stored in strings that are saved in savegames. */
SCC_ENCODED = SCC_CONTROL_START, ///< Encoded string marker and sub-string parameter.
SCC_ENCODED_RESERVED, ///< Reserved for future non-GS encoded strings.
SCC_ENCODED_INTERNAL, ///< Encoded text from OpenTTD.
SCC_ENCODED_NUMERIC, ///< Encoded numeric parameter.
SCC_ENCODED_STRING, ///< Encoded string parameter.