mirror of https://github.com/OpenRCT2/OpenRCT2.git
Implement multi-level formatting for any implementation
This commit is contained in:
parent
43842bb668
commit
6c23da4965
|
@ -478,48 +478,18 @@ namespace OpenRCT2
|
|||
}
|
||||
}
|
||||
|
||||
std::pair<std::string_view, uint32_t> FormatNextPart(std::string_view& fmt)
|
||||
{
|
||||
if (fmt.size() > 0)
|
||||
{
|
||||
for (size_t i = 0; i < fmt.size() - 1; i++)
|
||||
{
|
||||
if (fmt[i] == '{' && fmt[i + 1] != '}')
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
// Find end brace
|
||||
for (size_t j = i + 1; j < fmt.size(); j++)
|
||||
{
|
||||
if (fmt[j] == '}')
|
||||
{
|
||||
auto result = fmt.substr(0, j + 1);
|
||||
fmt = fmt.substr(j + 1);
|
||||
return { result, format_get_code(result.substr(1, result.size() - 2)) };
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto result = fmt.substr(0, i);
|
||||
fmt = fmt.substr(i);
|
||||
return { result, 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
auto result = fmt;
|
||||
fmt = {};
|
||||
return { result, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
bool CanFormatToken(FormatToken t)
|
||||
{
|
||||
return t == FORMAT_COMMA1DP16 || (t >= FORMAT_COMMA32 && t <= FORMAT_LENGTH);
|
||||
}
|
||||
|
||||
FmtString GetFmtStringById(rct_string_id id)
|
||||
{
|
||||
auto lang = language_get_string(id);
|
||||
auto fmtc = language_convert_string_to_tokens(lang);
|
||||
return FmtString(std::move(fmtc));
|
||||
}
|
||||
|
||||
template void FormatArgument(std::stringstream&, uint32_t, int32_t);
|
||||
template void FormatArgument(std::stringstream&, uint32_t, const char*);
|
||||
|
||||
|
@ -539,16 +509,28 @@ namespace OpenRCT2
|
|||
}
|
||||
}
|
||||
|
||||
std::string FormatStringAny(const FmtString& fmt, const std::vector<std::any>& args)
|
||||
static void FormatStringAny(
|
||||
std::stringstream& ss, const FmtString& fmt, const std::vector<std::any>& args, size_t& argIndex)
|
||||
{
|
||||
thread_local std::stringstream ss;
|
||||
// Reset the buffer (reported as most efficient way)
|
||||
std::stringstream().swap(ss);
|
||||
|
||||
size_t argIndex = 0;
|
||||
for (const auto& token : fmt)
|
||||
{
|
||||
if (CanFormatToken(token.kind))
|
||||
if (token.kind == FORMAT_STRINGID || token.kind == FORMAT_STRINGID2)
|
||||
{
|
||||
if (argIndex < args.size())
|
||||
{
|
||||
auto arg = args[argIndex++];
|
||||
if (arg.type() == typeid(rct_string_id))
|
||||
{
|
||||
auto subfmt = GetFmtStringById(std::any_cast<rct_string_id>(arg));
|
||||
FormatStringAny(ss, subfmt, args, argIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
argIndex++;
|
||||
}
|
||||
}
|
||||
else if (CanFormatToken(token.kind))
|
||||
{
|
||||
if (argIndex < args.size())
|
||||
{
|
||||
|
@ -561,6 +543,15 @@ namespace OpenRCT2
|
|||
ss << token.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatStringAny(const FmtString& fmt, const std::vector<std::any>& args)
|
||||
{
|
||||
thread_local std::stringstream ss;
|
||||
// Reset the buffer (reported as most efficient way)
|
||||
std::stringstream().swap(ss);
|
||||
size_t argIndex = 0;
|
||||
FormatStringAny(ss, fmt, args, argIndex);
|
||||
return ss.str();
|
||||
}
|
||||
} // namespace OpenRCT2
|
||||
|
|
|
@ -72,8 +72,8 @@ namespace OpenRCT2
|
|||
|
||||
template<typename T> void FormatArgument(std::stringstream& ss, FormatToken token, T arg);
|
||||
|
||||
std::pair<std::string_view, uint32_t> FormatNextPart(std::string_view& fmt);
|
||||
bool CanFormatToken(FormatToken t);
|
||||
FmtString GetFmtStringById(rct_string_id id);
|
||||
|
||||
inline void FormatString(std::stringstream& ss, FmtString::iterator& it)
|
||||
{
|
||||
|
@ -122,18 +122,16 @@ namespace OpenRCT2
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
template<typename... TArgs> static void FormatStringId(std::stringstream& ss, rct_string_id fmt, TArgs&&... argN)
|
||||
template<typename... TArgs> static void FormatStringId(std::stringstream& ss, rct_string_id id, TArgs&&... argN)
|
||||
{
|
||||
auto lang = language_get_string(fmt);
|
||||
auto fmtsz = language_convert_string_to_tokens(lang);
|
||||
FormatString(ss, FmtString(fmtsz), argN...);
|
||||
auto fmt = GetFmtStringById(id);
|
||||
FormatString(ss, fmt, argN...);
|
||||
}
|
||||
|
||||
template<typename... TArgs> std::string FormatStringId(rct_string_id fmt, TArgs&&... argN)
|
||||
template<typename... TArgs> std::string FormatStringId(rct_string_id id, TArgs&&... argN)
|
||||
{
|
||||
auto lang = language_get_string(fmt);
|
||||
auto fmtc = language_convert_string_to_tokens(lang);
|
||||
return FormatString(fmtc, argN...);
|
||||
auto fmt = GetFmtStringById(id);
|
||||
return FormatString(fmt, argN...);
|
||||
}
|
||||
|
||||
std::string FormatStringAny(const FmtString& fmt, const std::vector<std::any>& args);
|
||||
|
|
|
@ -18,7 +18,7 @@ constexpr const rct_string_id STR_VIEWPORT = 0xFFFE;
|
|||
enum
|
||||
{
|
||||
STR_EMPTY = 0,
|
||||
// STR_0001 :{STRINGID} {COMMA16}
|
||||
STR_RIDE_NAME_DEFAULT = 1,
|
||||
STR_RIDE_NAME_SPIRAL_ROLLER_COASTER = 2,
|
||||
STR_RIDE_NAME_STAND_UP_ROLLER_COASTER = 3,
|
||||
STR_RIDE_NAME_SUSPENDED_SWINGING_COASTER = 4,
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <openrct2/OpenRCT2.h>
|
||||
#include <openrct2/config/Config.h>
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include <openrct2/localisation/StringIds.h>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
|
||||
|
@ -263,3 +264,11 @@ TEST_F(FormattingTests, any_string_int_string)
|
|||
"{RED}{STRING} {INT32} has broken down due to '{STRING}'.", { "Twist", 2, "Mechanical failure" });
|
||||
ASSERT_EQ("{RED}Twist 2 has broken down due to 'Mechanical failure'.", actual);
|
||||
}
|
||||
|
||||
TEST_F(FormattingTests, any_two_level_format)
|
||||
{
|
||||
constexpr rct_string_id strDefault = STR_RIDE_NAME_DEFAULT;
|
||||
constexpr rct_string_id strBoatHire = STR_RIDE_NAME_BOAT_HIRE;
|
||||
auto actual = FormatStringAny("Queuing for {STRINGID}", { strDefault, strBoatHire, 2 });
|
||||
ASSERT_EQ("Queuing for Boat Hire 2", actual);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue