diff --git a/Doxyfile b/Doxyfile index c1aa43e8..5f4c4e2d 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1588,7 +1588,7 @@ INCLUDE_FILE_PATTERNS = # undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = +PREDEFINED = BUILDING_DOXYGEN # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/Sources/Plasma/Apps/plClient/winmain.cpp b/Sources/Plasma/Apps/plClient/winmain.cpp index f0dfcdfe..0a742bf5 100644 --- a/Sources/Plasma/Apps/plClient/winmain.cpp +++ b/Sources/Plasma/Apps/plClient/winmain.cpp @@ -1053,7 +1053,7 @@ BOOL CALLBACK UruLoginDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM // When general.ini gets expanded, this will need to find a proper home somewhere. { plFileName gipath = plFileName::Join(plFileSystem::GetInitPath(), "general.ini"); - plString ini_str = plString::Format("App.SetLanguage %s\n", plLocalization::GetLanguageName(new_language)); + plString ini_str = plFormat("App.SetLanguage {}\n", plLocalization::GetLanguageName(new_language)); hsStream* gini = plEncryptedStream::OpenEncryptedFileWrite(gipath); gini->WriteString(ini_str); gini->Close(); diff --git a/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp index 45ff9f9a..cb54af35 100644 --- a/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp +++ b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp @@ -354,7 +354,7 @@ static void IGotFileServIPs(ENetError result, void* param, const wchar_t* addr) static void IEapSucksErrorProc(ENetProtocol protocol, ENetError error) { if (s_errorProc) { - plString msg = plString::Format("Protocol: %S", NetProtocolToString(protocol)); + plString msg = plFormat("Protocol: {}", NetProtocolToString(protocol)); s_errorProc(error, msg); } } diff --git a/Sources/Plasma/Apps/plUruLauncher/winmain.cpp b/Sources/Plasma/Apps/plUruLauncher/winmain.cpp index 10cd64e1..9c686b7b 100644 --- a/Sources/Plasma/Apps/plUruLauncher/winmain.cpp +++ b/Sources/Plasma/Apps/plUruLauncher/winmain.cpp @@ -185,7 +185,7 @@ static void PumpMessages() static void IOnDownloadBegin(const plFileName& file) { - plString msg = plString::Format("Downloading... %s", file.AsString().c_str()); + plString msg = plFormat("Downloading... {}", file); SetDlgItemTextW(s_dialog, IDC_TEXT, msg.ToWchar()); } @@ -195,8 +195,8 @@ static void IOnProgressTick(uint64_t curBytes, uint64_t totalBytes, const plStri IShowMarquee(false); // DL size - plString size = plString::Format("%s / %s", plFileSystem::ConvertFileSize(curBytes).c_str(), - plFileSystem::ConvertFileSize(totalBytes).c_str()); + plString size = plFormat("{} / {}", plFileSystem::ConvertFileSize(curBytes), + plFileSystem::ConvertFileSize(totalBytes)); SetDlgItemTextW(s_dialog, IDC_DLSIZE, size.ToWchar()); // DL speed @@ -243,7 +243,7 @@ static handleptr_t ICreateProcess(const plFileName& exe, const plString& args) si.cb = sizeof(si); // Create wchar things and stuff :/ - plString cmd = plString::Format("%s %s", exe.AsString().c_str(), args.c_str()); + plString cmd = plFormat("{} {}", exe, args); plStringBuffer file = exe.AsString().ToWchar(); plStringBuffer params = cmd.ToWchar(); @@ -296,7 +296,7 @@ static handleptr_t ICreateProcess(const plFileName& exe, const plString& args) static bool IInstallRedist(const plFileName& exe) { - ISetDownloadStatus(plString::Format("Installing... %s", exe.AsString().c_str())); + ISetDownloadStatus(plFormat("Installing... {}", exe)); Sleep(2500); // let's Sleep for a bit so the user can see that we're doing something before the UAC dialog pops up! // Try to guess some arguments... Unfortunately, the file manifest format is fairly immutable. @@ -350,7 +350,7 @@ static void IOnNetError(ENetError result, const plString& msg) if (s_taskbar) s_taskbar->SetProgressState(s_dialog, TBPF_ERROR); - s_error = plString::Format("Error: %S\r\n%s", NetErrorAsString(result), msg.c_str()); + s_error = plFormat("Error: {}\r\n{}", NetErrorAsString(result), msg); IQuit(PLASMA_PHAILURE); } @@ -394,7 +394,7 @@ int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdL // Ensure there is only ever one patcher running... if (IsPatcherRunning()) { - plString text = plString::Format("%s is already running", plProduct::LongName().c_str()); + plString text = plFormat("{} is already running", plProduct::LongName()); IShowErrorDialog(text.ToWchar()); return PLASMA_OK; } diff --git a/Sources/Plasma/CoreLib/CMakeLists.txt b/Sources/Plasma/CoreLib/CMakeLists.txt index d2f65fb5..be0bf861 100644 --- a/Sources/Plasma/CoreLib/CMakeLists.txt +++ b/Sources/Plasma/CoreLib/CMakeLists.txt @@ -51,6 +51,7 @@ set(CoreLib_SOURCES hsWide.cpp pcSmallRect.cpp plFileSystem.cpp + plFormat.cpp plGeneric.cpp plLoadMask.cpp plProduct.cpp @@ -96,6 +97,7 @@ set(CoreLib_HEADERS hsWindows.h pcSmallRect.h plFileSystem.h + plFormat.h plGeneric.h plLoadMask.h plProduct.h diff --git a/Sources/Plasma/CoreLib/HeadSpin.cpp b/Sources/Plasma/CoreLib/HeadSpin.cpp index ab25f06d..580b6d6c 100644 --- a/Sources/Plasma/CoreLib/HeadSpin.cpp +++ b/Sources/Plasma/CoreLib/HeadSpin.cpp @@ -49,7 +49,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #pragma hdrstop #include "hsTemplates.h" -#include "plString.h" +#include "plFormat.h" /////////////////////////////////////////////////////////////////////////// @@ -535,7 +535,7 @@ std::vector DisplaySystemVersion() if ( osvi.dwMajorVersion <= 4 ) { - versionStrs.push_back(plString::Format("version %d.%d %s (Build %d)\n", + versionStrs.push_back(plFormat("version {}.{} {} (Build {})\n", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.szCSDVersion, @@ -543,7 +543,7 @@ std::vector DisplaySystemVersion() } else { - versionStrs.push_back(plString::Format("%s (Build %d)\n", + versionStrs.push_back(plFormat("{} (Build {})\n", osvi.szCSDVersion, osvi.dwBuildNumber & 0xFFFF)); } diff --git a/Sources/Plasma/CoreLib/hsStream.cpp b/Sources/Plasma/CoreLib/hsStream.cpp index 45e0d00f..f93d04dc 100644 --- a/Sources/Plasma/CoreLib/hsStream.cpp +++ b/Sources/Plasma/CoreLib/hsStream.cpp @@ -204,8 +204,8 @@ plString hsStream::ReadSafeWStringLong() uint32_t hsStream::WriteSafeString(const plString &string) { int len = string.GetSize(); - hsAssert(len<0xf000, plString::Format("string len of %d is too long for WriteSafeString %s, use WriteSafeStringLong", - len, string.c_str()).c_str() ); + hsAssert(len<0xf000, plFormat("string len of {} is too long for WriteSafeString {}, use WriteSafeStringLong", + len, string).c_str() ); WriteLE16(len | 0xf000); if (len > 0) @@ -226,7 +226,7 @@ uint32_t hsStream::WriteSafeWString(const plString &string) { plStringBuffer wbuff = string.ToUtf16(); uint32_t len = wbuff.GetSize(); - hsAssert(len<0xf000, plString::Format("string len of %d is too long for WriteSafeWString, use WriteSafeWStringLong", + hsAssert(len<0xf000, plFormat("string len of {} is too long for WriteSafeWString, use WriteSafeWStringLong", len).c_str() ); WriteLE16(len | 0xf000); diff --git a/Sources/Plasma/CoreLib/plFileSystem.cpp b/Sources/Plasma/CoreLib/plFileSystem.cpp index eb395efa..8d223d11 100644 --- a/Sources/Plasma/CoreLib/plFileSystem.cpp +++ b/Sources/Plasma/CoreLib/plFileSystem.cpp @@ -187,10 +187,8 @@ plFileName plFileName::Join(const plFileName &base, const plFileName &path) char last = base.fName.CharAt(base.GetSize() - 1); char first = path.fName.CharAt(0); if (last != '/' && last != '\\') { - if (first != '/' && first != '\\') { - return plString::Format("%s" PATH_SEPARATOR_STR "%s", - base.fName.c_str(), path.fName.c_str()); - } + if (first != '/' && first != '\\') + return plFormat("{}" PATH_SEPARATOR_STR "{}", base, path); return base.fName + path.fName; } else if (first != '/' && first != '\\') { return base.fName + path.fName; @@ -199,6 +197,11 @@ plFileName plFileName::Join(const plFileName &base, const plFileName &path) return base.fName + path.fName.Substr(1); } +PL_FORMAT_IMPL(const plFileName &) +{ + return PL_FORMAT_FORWARD(format, value.AsString()); +} + /* plFileInfo */ plFileInfo::plFileInfo(const plFileName &filename) @@ -544,7 +547,7 @@ plString plFileSystem::ConvertFileSize(uint64_t size) { const char* labels[] = { "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" }; if (size < 1024) - return plString::Format("%i B"); + return plFormat("{} B", size); uint64_t last_div = size; for (size_t i = 0; i < arrsize(labels); ++i) { @@ -561,5 +564,5 @@ plString plFileSystem::ConvertFileSize(uint64_t size) } // this should never happen - return plString::Format("%i %s", last_div, labels[arrsize(labels) - 1]); + return plFormat("{} {}", last_div, labels[arrsize(labels) - 1]); } diff --git a/Sources/Plasma/CoreLib/plFileSystem.h b/Sources/Plasma/CoreLib/plFileSystem.h index 07f3d767..8fe89b4f 100644 --- a/Sources/Plasma/CoreLib/plFileSystem.h +++ b/Sources/Plasma/CoreLib/plFileSystem.h @@ -47,6 +47,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include #include +#include "plFormat.h" + #if HS_BUILD_FOR_WIN32 # define PATH_SEPARATOR '\\' # define PATH_SEPARATOR_STR "\\" @@ -225,6 +227,10 @@ inline plFileName operator+(const char *left, const plFileName &right) { return left + right.AsString(); } +// Shortcut for use in plFormat +PL_FORMAT_TYPE(const plFileName &) + + /** Structure to get information about a file by name. * \sa plFileName */ diff --git a/Sources/Plasma/CoreLib/plFormat.cpp b/Sources/Plasma/CoreLib/plFormat.cpp new file mode 100644 index 00000000..eee0fd88 --- /dev/null +++ b/Sources/Plasma/CoreLib/plFormat.cpp @@ -0,0 +1,492 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program 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, either version 3 of the License, or +(at your option) any later version. + +This program 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 this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com +or by snail mail at: +Cyan Worlds, Inc. +14617 N Newport Hwy +Mead, WA 99021 + +*==LICENSE==*/ + +#include "plFormat.h" + +#include "HeadSpin.h" +#include +#include + +#define BADCHAR_REPLACEMENT (0xFFFDul) + +namespace plFormat_Private +{ + static const char *_scanNextFormat(_IFormatDataObject &data) + { + hsAssert(data.fFormatStr, "Passed a null format string!"); + + const char *ptr = data.fFormatStr; + while (*ptr) { + if (*ptr == '{') + return ptr; + ++ptr; + } + + return ptr; + } + + static void _fetchPrefixChunk(_IFormatDataObject &data) + { + do { + const char *next = _scanNextFormat(data); + if (*next && *(next + 1) == '{') { + // Escaped '{' + data.fOutput.push_back(plStringBuffer(data.fFormatStr, 1 + next - data.fFormatStr)); + data.fFormatStr = next + 2; + continue; + } + + if (next != data.fFormatStr) + data.fOutput.push_back(plStringBuffer(data.fFormatStr, next - data.fFormatStr)); + data.fFormatStr = next; + } while (0); + } + + FormatSpec _FetchNextFormat(_IFormatDataObject &data) + { + _fetchPrefixChunk(data); + hsAssert(*data.fFormatStr == '{', "Too many actual parameters for format string"); + + FormatSpec spec; + const char *ptr = data.fFormatStr; + for ( ;; ) { + ++ptr; + + switch (*ptr) { + case 0: + hsAssert(0, "Unterminated format specifier."); + abort(); + case '}': + // Done with format spec + data.fFormatStr = ptr + 1; + return spec; + break; + + case '<': + spec.fAlignment = kAlignLeft; + break; + case '>': + spec.fAlignment = kAlignRight; + break; + case '_': + spec.fPadChar = *(ptr + 1); + hsAssert(spec.fPadChar, "Unterminated format specifier"); + ++ptr; + break; + case 'x': + spec.fDigitClass = kDigitHex; + break; + case 'X': + spec.fDigitClass = kDigitHexUpper; + break; + case '+': + spec.fDigitClass = kDigitDecAlwaysSigned; + break; + case 'd': + if (spec.fDigitClass != kDigitDecAlwaysSigned) + spec.fDigitClass = kDigitDec; + break; + case 'o': + spec.fDigitClass = kDigitOct; + break; + case 'b': + spec.fDigitClass = kDigitBin; + break; + case 'c': + spec.fDigitClass = kDigitChar; + break; + case 'f': + spec.fFloatClass = kFloatFixed; + break; + case 'e': + spec.fFloatClass = kFloatExp; + break; + case 'E': + spec.fFloatClass = kFloatExpUpper; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + char *end = nullptr; + spec.fPrecisionLeft = strtol(ptr, &end, 10); + ptr = end - 1; + } + break; + case '.': + { + hsAssert(*(ptr + 1), "Unterminated format specifier"); + char *end = nullptr; + spec.fPrecisionRight = strtol(ptr + 1, &end, 10); + ptr = end - 1; + } + break; + default: + hsAssert(0, "Unexpected character in format string"); + break; + } + } + } + + plString _IFormat(plFormat_Private::_IFormatDataObject &data) + { + _fetchPrefixChunk(data); + hsAssert(*data.fFormatStr == 0, "Not enough actual parameters for format string"); + + size_t outsize = 0; + for (const plStringBuffer &buf : data.fOutput) + outsize += buf.GetSize(); + + plStringBuffer outbuf; + char *out_ptr = outbuf.CreateWritableBuffer(outsize); + for (const plStringBuffer &buf : data.fOutput) { + memcpy(out_ptr, buf.GetData(), buf.GetSize()); + out_ptr += buf.GetSize(); + } + *out_ptr = 0; + + return outbuf; + } +} + +template +void _IFormatNumeric_Impl(char *output_end, _IType value, int radix, bool upperCase = false) +{ + if (value == 0) { + *(output_end - 1) = '0'; + return; + } + + while (value) + { + int digit = (value % radix); + value /= radix; + --output_end; + + if (digit < 10) + *output_end = '0' + digit; + else if (upperCase) + *output_end = 'A' + digit - 10; + else + *output_end = 'a' + digit - 10; + } +} + +template +static plStringBuffer _formatNumeric(const plFormat_Private::FormatSpec &format, + _IType value, int radix, bool upperCase = false) +{ + char pad = format.fPadChar ? format.fPadChar : ' '; + + size_t max = 0; + _IType temp = value; + while (temp) { + ++max; + temp /= radix; + } + + if (max == 0) + max = 1; + + plStringBuffer buffer; + if (format.fPrecisionLeft > max) { + char *output = buffer.CreateWritableBuffer(format.fPrecisionLeft); + memset(output, pad, format.fPrecisionLeft); + if (format.fAlignment == plFormat_Private::kAlignLeft) { + _IFormatNumeric_Impl<_IType>(output + max, value, radix, upperCase); + } else { + _IFormatNumeric_Impl<_IType>(output + format.fPrecisionLeft, + value, radix, upperCase); + } + output[format.fPrecisionLeft] = 0; + } else { + char *output = buffer.CreateWritableBuffer(max); + _IFormatNumeric_Impl<_IType>(output + max, value, radix, upperCase); + output[max] = 0; + } + + return buffer; +} + +// Currently, only decimal formatting supports rendering negative numbers +template +static plStringBuffer _formatDecimal(const plFormat_Private::FormatSpec &format, _IType value) +{ + char pad = format.fPadChar ? format.fPadChar : ' '; + _IType abs = (value < 0) ? -value : value; + + size_t max = 0; + _IType temp = abs; + while (temp) { + ++max; + temp /= 10; + } + + if (max == 0) + max = 1; + + if (value < 0 || format.fDigitClass == plFormat_Private::kDigitDecAlwaysSigned) + ++max; + + plStringBuffer buffer; + char *output; + if (format.fPrecisionLeft > max) { + output = buffer.CreateWritableBuffer(format.fPrecisionLeft); + memset(output, pad, format.fPrecisionLeft); + if (format.fAlignment == plFormat_Private::kAlignLeft) + _IFormatNumeric_Impl<_IType>(output + max, abs, 10); + else + _IFormatNumeric_Impl<_IType>(output + format.fPrecisionLeft, abs, 10); + output[format.fPrecisionLeft] = 0; + } else { + output = buffer.CreateWritableBuffer(max); + _IFormatNumeric_Impl<_IType>(output + max, abs, 10); + output[max] = 0; + } + + int signPos = format.fPrecisionLeft - static_cast(max); + if (signPos < 0) + signPos = 0; + + if (value < 0) + output[signPos] = '-'; + else if (format.fDigitClass == plFormat_Private::kDigitDecAlwaysSigned) + output[signPos] = '+'; + + return buffer; +} + +static plStringBuffer _formatChar(const plFormat_Private::FormatSpec &format, int ch) +{ + hsAssert(format.fPrecisionLeft == 0 && format.fPadChar == 0, + "Char formatting does not currently support padding"); + + // Don't need to nul-terminate this, since plStringBuffer's constructor fixes it + char utf8[4]; + size_t max; + + // Yanked from plString + if (ch > 0x10FFFF) { + hsAssert(0, "Unicode character out of range"); + + // Character out of range; Use U+FFFD instead for release builds + max = 3; + utf8[0] = 0xE0 | ((BADCHAR_REPLACEMENT >> 12) & 0x0F); + utf8[1] = 0x80 | ((BADCHAR_REPLACEMENT >> 6) & 0x3F); + utf8[2] = 0x80 | ((BADCHAR_REPLACEMENT ) & 0x3F); + } else if (ch > 0xFFFF) { + max = 4; + utf8[0] = 0xF0 | ((ch >> 18) & 0x07); + utf8[1] = 0x80 | ((ch >> 12) & 0x3F); + utf8[2] = 0x80 | ((ch >> 6) & 0x3F); + utf8[3] = 0x80 | ((ch ) & 0x3F); + } else if (ch > 0x7FF) { + max = 3; + utf8[0] = 0xE0 | ((ch >> 12) & 0x0F); + utf8[1] = 0x80 | ((ch >> 6) & 0x3F); + utf8[2] = 0x80 | ((ch ) & 0x3F); + } else if (ch > 0x7F) { + max = 2; + utf8[0] = 0xC0 | ((ch >> 6) & 0x1F); + utf8[1] = 0x80 | ((ch ) & 0x3F); + } else { + max = 1; + utf8[0] = (char)ch; + } + + return plStringBuffer(utf8, max); +} + +#define _PL_FORMAT_IMPL_INT_TYPE(_stype, _utype) \ + PL_FORMAT_IMPL(_stype) \ + { \ + /* Note: The use of unsigned here is not a typo -- we only format decimal + values with a sign, so we can convert everything else to unsigned. */ \ + switch (format.fDigitClass) { \ + case plFormat_Private::kDigitBin: \ + return _formatNumeric<_utype>(format, value, 2); \ + case plFormat_Private::kDigitOct: \ + return _formatNumeric<_utype>(format, value, 8); \ + case plFormat_Private::kDigitHex: \ + return _formatNumeric<_utype>(format, value, 16, false); \ + case plFormat_Private::kDigitHexUpper: \ + return _formatNumeric<_utype>(format, value, 16, true); \ + case plFormat_Private::kDigitDec: \ + case plFormat_Private::kDigitDecAlwaysSigned: \ + case plFormat_Private::kDigitDefault: \ + return _formatDecimal<_stype>(format, value); \ + case plFormat_Private::kDigitChar: \ + return _formatChar(format, value); \ + } \ + \ + hsAssert(0, "Unexpected digit class"); \ + return plStringBuffer(); \ + } \ + \ + PL_FORMAT_IMPL(_utype) \ + { \ + switch (format.fDigitClass) { \ + case plFormat_Private::kDigitBin: \ + return _formatNumeric<_utype>(format, value, 2); \ + case plFormat_Private::kDigitOct: \ + return _formatNumeric<_utype>(format, value, 8); \ + case plFormat_Private::kDigitHex: \ + return _formatNumeric<_utype>(format, value, 16, false); \ + case plFormat_Private::kDigitHexUpper: \ + return _formatNumeric<_utype>(format, value, 16, true); \ + case plFormat_Private::kDigitDec: \ + case plFormat_Private::kDigitDecAlwaysSigned: \ + case plFormat_Private::kDigitDefault: \ + return _formatDecimal<_utype>(format, value); \ + case plFormat_Private::kDigitChar: \ + return _formatChar(format, value); \ + } \ + \ + hsAssert(0, "Unexpected digit class"); \ + return plStringBuffer(); \ + } + +_PL_FORMAT_IMPL_INT_TYPE(signed char, unsigned char) +_PL_FORMAT_IMPL_INT_TYPE(short, unsigned short) +_PL_FORMAT_IMPL_INT_TYPE(int, unsigned) +_PL_FORMAT_IMPL_INT_TYPE(long, unsigned long) +_PL_FORMAT_IMPL_INT_TYPE(int64_t, uint64_t) + +PL_FORMAT_IMPL(char) +{ + /* Note: The use of unsigned here is not a typo -- we only format decimal + values with a sign, so we can convert everything else to unsigned. */ + switch (format.fDigitClass) { + case plFormat_Private::kDigitBin: + return _formatNumeric(format, value, 2); + case plFormat_Private::kDigitOct: + return _formatNumeric(format, value, 8); + case plFormat_Private::kDigitHex: + return _formatNumeric(format, value, 16, false); + case plFormat_Private::kDigitHexUpper: + return _formatNumeric(format, value, 16, true); + case plFormat_Private::kDigitDec: + case plFormat_Private::kDigitDecAlwaysSigned: + return _formatDecimal(format, value); + case plFormat_Private::kDigitChar: + case plFormat_Private::kDigitDefault: + return _formatChar(format, value); + } + + hsAssert(0, "Unexpected digit class"); + return plStringBuffer(); +} + +PL_FORMAT_IMPL(wchar_t) +{ + switch (format.fDigitClass) { + case plFormat_Private::kDigitBin: + return _formatNumeric(format, value, 2); + case plFormat_Private::kDigitOct: + return _formatNumeric(format, value, 8); + case plFormat_Private::kDigitHex: + return _formatNumeric(format, value, 16, false); + case plFormat_Private::kDigitHexUpper: + return _formatNumeric(format, value, 16, true); + case plFormat_Private::kDigitDec: + case plFormat_Private::kDigitDecAlwaysSigned: + return _formatDecimal(format, value); + case plFormat_Private::kDigitChar: + case plFormat_Private::kDigitDefault: + return _formatChar(format, value); + } + + hsAssert(0, "Unexpected digit class"); + return plStringBuffer(); +} + +static plStringBuffer _formatString(const plFormat_Private::FormatSpec &format, + const plStringBuffer &value) +{ + char pad = format.fPadChar ? format.fPadChar : ' '; + + if (format.fPrecisionLeft > value.GetSize()) { + plStringBuffer buf; + char *output = buf.CreateWritableBuffer(format.fPrecisionLeft); + memset(output, pad, format.fPrecisionLeft); + if (format.fAlignment == plFormat_Private::kAlignRight) { + memcpy(output + (format.fPrecisionLeft - value.GetSize()), + value.GetData(), value.GetSize()); + } else { + memcpy(output, value.GetData(), value.GetSize()); + } + + output[format.fPrecisionLeft] = 0; + return buf; + } + + return value; +} + +PL_FORMAT_IMPL(const char *) +{ + return _formatString(format, plString(value).ToUtf8()); +} + +PL_FORMAT_IMPL(const wchar_t *) +{ + return _formatString(format, plString::FromWchar(value).ToUtf8()); +} + +PL_FORMAT_IMPL(const plString &) +{ + return _formatString(format, value.ToUtf8()); +} + +PL_FORMAT_IMPL(const std::string &) +{ + return _formatString(format, plStringBuffer(value.c_str(), value.size())); +} + +PL_FORMAT_IMPL(const std::wstring &) +{ + return _formatString(format, plString::FromWchar(value.c_str(), value.size()).ToUtf8()); +} + +PL_FORMAT_IMPL(bool) +{ + return PL_FORMAT_FORWARD(format, value ? "true" : "false"); +} diff --git a/Sources/Plasma/CoreLib/plFormat.h b/Sources/Plasma/CoreLib/plFormat.h new file mode 100644 index 00000000..432ff7b7 --- /dev/null +++ b/Sources/Plasma/CoreLib/plFormat.h @@ -0,0 +1,227 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program 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, either version 3 of the License, or +(at your option) any later version. + +This program 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 this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com +or by snail mail at: +Cyan Worlds, Inc. +14617 N Newport Hwy +Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plFormat_Defined +#define plFormat_Defined + +#include "plString.h" +#include +#include + +#ifdef BUILDING_DOXYGEN // Doxygen doesn't appear to support variadic templates yet +/** Format a string using type-safe arguments + * \param format The format string -- see below for details + * + * Character Sequence | Description + * ------------------ | ----------- + * `{}` | Format a value (using defaults) + * `{{` | Escape for a single '{' char + * `{options}` | Format a value, with the specified options (see below) + * + * Formatting Options + * ------------------ + * + * Format Option | Description + * ------------- | ----------- + * `<` | Align left + * `>` | Align right + * `NNN` | Pad to NNN characters (minimum - can be more) + * `+` | Show a '+' char for positive signed values (decimal only) + * `_C` | Use C as the pad character (only '\001'..'\177' supported for now) + * `x` | Hex (lower-case) + * `X` | Hex (upper-case) + * `o` | Octal + * `b` | Binary + * `d` | Decimal (default) -- when used with char types, outputs a number instead of the UTF representation of the char + * `c` | UTF character (default for character types) + * `FFF.EEE` | Use FFF.EEE floating point precision + * `f` | Fixed floating point format (ddd.ddd) + * `e` | Exponent notation for floating point (d.ddde[+/-]dd) + * `E` | Same as 'e' format, but with upper case E (d.dddE[+/-]dd) + */ +plString plFormat(const char *format, ...); +#endif + + +namespace plFormat_Private +{ + enum Alignment : unsigned char + { + kAlignDefault, /**< Left for strings, right for numbers */ + kAlignLeft, /**< Left alignment */ + kAlignRight /**< Right alignment */ + }; + + enum DigitClass : unsigned char + { + kDigitDefault, /**< Default digit formatting */ + kDigitDec, /**< Format as decimal integer */ + kDigitDecAlwaysSigned, /**< Same as `kDigitDec`, but include a '+' for positive numbers too */ + kDigitHex, /**< Hex integer (assume unsigned) */ + kDigitHexUpper, /**< Hex integer with upper-case digits */ + kDigitOct, /**< Octal integer (assume unsigned) */ + kDigitBin, /**< Binary integer (assume unsigned) */ + kDigitChar /**< Single unicode character (as UTF-8) */ + }; + + enum FloatClass : unsigned char + { + kFloatDefault, /**< Use Fixed or Exp format depending on value */ + kFloatFixed, /**< Use Fixed notation (ddd.ddd) */ + kFloatExp, /**< Use Exp notation (d.ddde[+/-]dd) */ + kFloatExpUpper /**< Same as `kFloatExp`, but with an upper-case E */ + }; + + /** Represents a parsed format tag, for use in formatter implementations. */ + struct FormatSpec + { + int fPrecisionLeft = 0; /**< Requested padding and/or precision */ + int fPrecisionRight = 0; /**< Requested precision after the . for floating-point */ + + char fPadChar = 0; /**< Explicit padding char (default is space) */ + Alignment fAlignment = kAlignDefault; /**< Requested pad alignment */ + DigitClass fDigitClass = kDigitDefault; /**< Requested int formatting */ + FloatClass fFloatClass = kFloatDefault; /**< Requested float formatting */ + }; + + // These need to be publically visible for the macros below, but shouldn't + // be used directly outside of plFormat and its macros + struct _IFormatDataObject + { + const char *fFormatStr; + std::list> fOutput; + }; + + extern FormatSpec _FetchNextFormat(_IFormatDataObject &data); +} + +/** Declare a formattable type for `plFormat`. + * \sa PL_FORMAT_IMPL() + */ +#define PL_FORMAT_TYPE(_type) \ + extern plStringBuffer _impl_plFormat_DataHandler( \ + const plFormat_Private::FormatSpec &format, _type value); \ + namespace plFormat_Private \ + { \ + template \ + plString _IFormat(_IFormatDataObject &data, _type value, _Args... args) \ + { \ + plFormat_Private::FormatSpec format = plFormat_Private::_FetchNextFormat(data); \ + data.fOutput.push_back(_impl_plFormat_DataHandler(format, value)); \ + return _IFormat(data, args...); \ + } \ + } \ + template \ + plString plFormat(const char *fmt_str, _type value, _Args... args) \ + { \ + plFormat_Private::_IFormatDataObject data; \ + data.fFormatStr = fmt_str; \ + plFormat_Private::FormatSpec format = plFormat_Private::_FetchNextFormat(data); \ + data.fOutput.push_back(_impl_plFormat_DataHandler(format, value)); \ + return plFormat_Private::_IFormat(data, args...); \ + } + +/** Provide the implementation for a formattable type for `plFormat`. + * \sa PL_FORMAT_TYPE(), PL_FORMAT_FORWARD() + * + * Example: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * PL_FORMAT_IMPL(const MyType &) + * { + * return plFormat("MyType[data={},count={}]", value.data, value.count); + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ +#define PL_FORMAT_IMPL(_type) \ + plStringBuffer _impl_plFormat_DataHandler( \ + const plFormat_Private::FormatSpec &format, _type value) + +/** Shortcut to call another `PL_FORMAT_IMPL` formatter. + * \sa PL_FORMAT_IMPL() + * + * Example: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * PL_FORMAT_IMPL(const MyType &) + * { + * return PL_FORMAT_FORWARD(format, value.ToString()); + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define PL_FORMAT_FORWARD(format, fwd_value) \ + _impl_plFormat_DataHandler((format), (fwd_value)) + +PL_FORMAT_TYPE(char) +PL_FORMAT_TYPE(wchar_t) +PL_FORMAT_TYPE(signed char) +PL_FORMAT_TYPE(unsigned char) +PL_FORMAT_TYPE(short) +PL_FORMAT_TYPE(unsigned short) +PL_FORMAT_TYPE(int) +PL_FORMAT_TYPE(unsigned) +PL_FORMAT_TYPE(long) +PL_FORMAT_TYPE(unsigned long) +PL_FORMAT_TYPE(int64_t) +PL_FORMAT_TYPE(uint64_t) +PL_FORMAT_TYPE(const char *) +PL_FORMAT_TYPE(const wchar_t *) +PL_FORMAT_TYPE(const plString &) + +// TODO: Remove these when they're no longer needed +PL_FORMAT_TYPE(const std::string &) +PL_FORMAT_TYPE(const std::wstring &) + +// TODO: Implement floating point types (float, double). They're harder +// than the others, so I'll get around to them later >.> + +// Formats as "true" or "false", following normal string formatting rules. +// To use other formats, don't pass us a bool directly... +PL_FORMAT_TYPE(bool) + +namespace plFormat_Private +{ + // End of the chain -- emits the last piece (if any) and builds the final string + plString _IFormat(_IFormatDataObject &data); +} + +#endif // plFormat_Defined diff --git a/Sources/Plasma/CoreLib/plProduct.cpp b/Sources/Plasma/CoreLib/plProduct.cpp index b5593cd1..4a230488 100644 --- a/Sources/Plasma/CoreLib/plProduct.cpp +++ b/Sources/Plasma/CoreLib/plProduct.cpp @@ -44,7 +44,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #pragma hdrstop #include "plProduct.h" -#include "plString.h" +#include "plFormat.h" static_assert(PRODUCT_BUILD_ID > 0, "Build ID cannot be zero"); static_assert(PRODUCT_BUILD_TYPE > 0, "Build Type cannot be zero"); @@ -89,8 +89,8 @@ const char *plProduct::UUID() { return PRODUCT_UUID; } plString plProduct::ProductString() { - static plString _cache = plString::Format( - "%s.%u.%u - " RELEASE_ACCESS "." RELEASE_TYPE, - CoreName().c_str(), BranchId(), BuildId()); + static plString _cache = plFormat( + "{}.{}.{} - " RELEASE_ACCESS "." RELEASE_TYPE, + CoreName(), BranchId(), BuildId()); return _cache; } diff --git a/Sources/Plasma/CoreLib/plString.cpp b/Sources/Plasma/CoreLib/plString.cpp index 92b7b6b8..517014aa 100644 --- a/Sources/Plasma/CoreLib/plString.cpp +++ b/Sources/Plasma/CoreLib/plString.cpp @@ -47,6 +47,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #pragma hdrstop #include "plString.h" +#include "plFormat.h" #include const plString plString::Null; @@ -609,7 +610,7 @@ bool plString::REMatch(const char *pattern, CaseSensitivity sense) const if (std::regex_match(c_str(), re)) return true; } catch (const std::regex_error& e) { - hsAssert(0, plString::Format("Regex match error: %s", e.what()).c_str()); + hsAssert(0, plFormat("Regex match error: {}", e.what()).c_str()); } return false; @@ -633,7 +634,7 @@ std::vector plString::RESearch(const char *pattern, for (size_t i = 0; i < matches.size(); ++i) substrings[i] = matches[i].str().c_str(); } catch (const std::regex_error& e) { - hsAssert(0, plString::Format("Regex search error: %s", e.what()).c_str()); + hsAssert(0, plFormat("Regex search error: {}", e.what()).c_str()); } return substrings; diff --git a/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.cpp b/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.cpp index 872f70a8..2660c1ab 100644 --- a/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.cpp +++ b/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.cpp @@ -114,7 +114,12 @@ bool plLocation::IsVirtual() const // THIS SHOULD BE FOR DEBUGGING ONLY plString plLocation::StringIze() const // Format to displayable string { - return plString::Format("S0x%xF0x%x", fSequenceNumber, int(fFlags)); + return plFormat("S0x{x}F0x{x}", fSequenceNumber, fFlags); +} + +PL_FORMAT_IMPL(const plLocation &) +{ + return PL_FORMAT_FORWARD(format, value.StringIze()); } plLocation plLocation::MakeReserved(uint32_t number) @@ -261,10 +266,15 @@ plUoid& plUoid::operator=(const plUoid& rhs) // THIS SHOULD BE FOR DEBUGGING ONLY plString plUoid::StringIze() const // Format to displayable string { - return plString::Format("(0x%x:0x%x:%s:C:[%u,%u])", - fLocation.GetSequenceNumber(), - int(fLocation.GetFlags()), - fObjectName.c_str(), - GetClonePlayerID(), + return plFormat("(0x{x}:0x{x}:{}:C:[{},{}])", + fLocation.GetSequenceNumber(), + fLocation.GetFlags(), + fObjectName, + GetClonePlayerID(), GetCloneID()); } + +PL_FORMAT_IMPL(const plUoid &) +{ + return PL_FORMAT_FORWARD(format, value.StringIze()); +} diff --git a/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.h b/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.h index 2c0b8051..0c1d7255 100644 --- a/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.h +++ b/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.h @@ -57,7 +57,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "HeadSpin.h" #include "plFixedKey.h" #include "plLoadMask.h" -#include "plString.h" +#include "plFormat.h" class hsStream; @@ -144,6 +144,8 @@ public: static const plLocation kInvalidLoc; }; +PL_FORMAT_TYPE(const plLocation &) + //// plUoid ////////////////////////////////////////////////////////////////// class plUoid @@ -198,4 +200,6 @@ protected: plLoadMask fLoadMask; }; +PL_FORMAT_TYPE(const plUoid &) + #endif // plUoid_h_inc diff --git a/Sources/Plasma/NucleusLib/pnUUID/pnUUID.cpp b/Sources/Plasma/NucleusLib/pnUUID/pnUUID.cpp index 0ace55a7..d9120dd0 100644 --- a/Sources/Plasma/NucleusLib/pnUUID/pnUUID.cpp +++ b/Sources/Plasma/NucleusLib/pnUUID/pnUUID.cpp @@ -83,3 +83,8 @@ plString plUUID::AsString() const ToString(str); return str; } + +PL_FORMAT_IMPL(const plUUID &) +{ + return PL_FORMAT_FORWARD(format, value.AsString()); +} diff --git a/Sources/Plasma/NucleusLib/pnUUID/pnUUID.h b/Sources/Plasma/NucleusLib/pnUUID/pnUUID.h index 2062b917..d7dce497 100644 --- a/Sources/Plasma/NucleusLib/pnUUID/pnUUID.h +++ b/Sources/Plasma/NucleusLib/pnUUID/pnUUID.h @@ -43,7 +43,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #define pnUUID_h_inc #include "HeadSpin.h" -#include "plString.h" +#include "plFormat.h" class hsStream; @@ -96,4 +96,6 @@ public: static plUUID Generate(); }; +PL_FORMAT_TYPE(const plUUID &) + #endif // pnUUID_h_inc diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp index 6e93f451..e24982a7 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp @@ -251,7 +251,7 @@ void plNetClientMgr::SetNullSend(bool on) const char* plNetClientMgr::GetServerLogTimeAsString(plString& timestamp) const { const plUnifiedTime st=GetServerTime(); - timestamp = plString::Format("{%02d/%02d %02d:%02d:%02d}", + timestamp = plFormat("{{{_02}/{_02} {_02}:{_02}:{_02}}", st.GetMonth(), st.GetDay(), st.GetHour(), st.GetMinute(), st.GetSecond()); return timestamp.c_str(); } @@ -264,7 +264,7 @@ const char* ProcessTab(const char* fmt) static plString s; if (fmt && *fmt=='\t') { - s = plString::Format(" %s", fmt); + s = plFormat(" {}", fmt); return s.c_str(); } return fmt; @@ -1230,7 +1230,7 @@ void plNetClientMgr::IDisableNet () { if (!GetFlagsBit(plNetClientApp::kPlayingGame)) { // KI may not be loaded - plString title = plString::Format("%s Error", plProduct::CoreName().c_str()); + plString title = plFormat("{} Error", plProduct::CoreName()); hsMessageBox(fDisableMsg->str, title.c_str(), hsMessageBoxNormal, hsMessageBoxIconError ); plClientMsg *quitMsg = new plClientMsg(plClientMsg::kQuit); quitMsg->Send(hsgResMgr::ResMgr()->FindKey(kClient_KEY)); @@ -1356,8 +1356,8 @@ bool plNetClientMgr::IFindModifier(plSynchedObject* obj, int16_t classIdx) cnt++; } - hsAssert(cnt<2, plString::Format("Object %s has multiple SDL modifiers of the same kind (%s)?", - obj->GetKeyName().c_str(), plFactory::GetNameOfClass(classIdx)).c_str()); + hsAssert(cnt<2, plFormat("Object {} has multiple SDL modifiers of the same kind ({})?", + obj->GetKeyName(), plFactory::GetNameOfClass(classIdx)).c_str()); return cnt==0 ? false : true; } diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp index ca70d4b2..fee66911 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp @@ -957,9 +957,9 @@ uint8_t plNetLinkingMgr::IPreProcessLink(void) plString title; unsigned nameLen = nc->GetPlayerName().GetSize(); if (nc->GetPlayerName().ToLower().CharAt(nameLen - 1) == 's') - title = plString::Format("%s'", nc->GetPlayerName().c_str()); + title = plFormat("{}'", nc->GetPlayerName()); else - title = plString::Format("%s's", nc->GetPlayerName().c_str()); + title = plFormat("{}'s", nc->GetPlayerName()); info->SetAgeUserDefinedName(title.c_str()); } if (!info->HasAgeDescription()) @@ -968,9 +968,9 @@ uint8_t plNetLinkingMgr::IPreProcessLink(void) plString desc; unsigned nameLen = nc->GetPlayerName().GetSize(); if (nc->GetPlayerName().ToLower().CharAt(nameLen - 1) == 's') - desc = plString::Format("%s' %s", nc->GetPlayerName().c_str(), info->GetAgeInstanceName().c_str()); + desc = plFormat("{}' {}", nc->GetPlayerName(), info->GetAgeInstanceName()); else - desc = plString::Format("%s's %s", nc->GetPlayerName().c_str(), info->GetAgeInstanceName().c_str()); + desc = plFormat("{}'s {}", nc->GetPlayerName(), info->GetAgeInstanceName()); info->SetAgeDescription(desc.c_str()); } if (!info->HasAgeInstanceGuid()) { @@ -996,9 +996,9 @@ uint8_t plNetLinkingMgr::IPreProcessLink(void) plString title; unsigned nameLen = nc->GetPlayerName().GetSize(); if (nc->GetPlayerName().ToLower().CharAt(nameLen - 1) == 's') - title = plString::Format("%s'", nc->GetPlayerName().c_str()); + title = plFormat("{}'", nc->GetPlayerName()); else - title = plString::Format("%s's", nc->GetPlayerName().c_str()); + title = plFormat("{}'s", nc->GetPlayerName()); info->SetAgeUserDefinedName(title.c_str()); } @@ -1008,9 +1008,9 @@ uint8_t plNetLinkingMgr::IPreProcessLink(void) plString desc; unsigned nameLen = nc->GetPlayerName().GetSize(); if (nc->GetPlayerName().ToLower().CharAt(nameLen - 1) == 's') - desc = plString::Format("%s' %s", nc->GetPlayerName().c_str(), info->GetAgeInstanceName().c_str()); + desc = plFormat("{}' {}", nc->GetPlayerName(), info->GetAgeInstanceName()); else - desc = plString::Format("%s's %s", nc->GetPlayerName().c_str(), info->GetAgeInstanceName().c_str()); + desc = plFormat("{}'s {}", nc->GetPlayerName(), info->GetAgeInstanceName()); info->SetAgeDescription( desc.c_str() ); } diff --git a/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.h b/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.h index 987eb9a5..a5994c20 100644 --- a/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.h +++ b/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.h @@ -330,7 +330,7 @@ public: // debug plString AsString() const { - return plString::Format("object=%s, %s",fObjectHelper.GetUoid().StringIze().c_str(), plNetMessage::AsString().c_str()); + return plFormat("object={}, {}",fObjectHelper.GetUoid(), plNetMessage::AsString()); } }; @@ -506,7 +506,7 @@ public: plString AsString() const { const char* noc=plFactory::GetTheFactory()->GetNameOfClass(StreamInfo()->GetStreamType()); - return plString::Format("%s %s",plNetMsgStream::AsString().c_str(), noc ? noc : "?"); + return plFormat("{} {}", plNetMsgStream::AsString(), noc ? noc : "?"); } }; @@ -556,8 +556,8 @@ public: // debug plString AsString() const { - return plString::Format("object=%s initial=%d, %s",fObjectHelper.GetUoid().StringIze().c_str(), fIsInitialState, - plNetMsgGameMessage::AsString().c_str()); + return plFormat("object={} initial={}, {}",fObjectHelper.GetUoid(), fIsInitialState, + plNetMsgGameMessage::AsString()); } }; @@ -647,7 +647,7 @@ public: // debug plString AsString() const { - return plString::Format("pageFlags:%02X, paging %s, requestingState:%s, resetting=%d", + return plFormat("pageFlags:{_02X}, paging {}, requestingState:{}, resetting={}", fPageFlags, (fPageFlags&kPagingOut)?"out":"in", (fPageFlags&kRequestState)?"yes":"no", (fPageFlags & kResetList)!=0); } @@ -754,7 +754,7 @@ public: // debug plString AsString() const { - return plString::Format("len=%d",fVoiceData.size()); + return plFormat("len={}",fVoiceData.size()); } }; @@ -792,7 +792,7 @@ public: // debug plString AsString() const { - return plString::Format("lockReq=%d, %s",fLockRequest, plNetMsgStreamedObject::AsString().c_str()); + return plFormat("lockReq={}, {}",fLockRequest, plNetMsgStreamedObject::AsString()); } }; @@ -979,11 +979,11 @@ public: plString b1, b2; int i; for(i=0;i