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/plFormat.cpp b/Sources/Plasma/CoreLib/plFormat.cpp new file mode 100644 index 00000000..547fb657 --- /dev/null +++ b/Sources/Plasma/CoreLib/plFormat.cpp @@ -0,0 +1,187 @@ +/*==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" + +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 'd': + 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 = kFloatF; + break; + case 'g': + spec.fFloatClass = kFloatG; + break; + case 'e': + spec.fFloatClass = kFloatE; + 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; + } +} + +PL_FORMAT_IMPL(int) +{ + char buffer[32]; + int size = snprintf(buffer, 32, "%d", value); + return plStringBuffer(buffer, size); +} diff --git a/Sources/Plasma/CoreLib/plFormat.h b/Sources/Plasma/CoreLib/plFormat.h new file mode 100644 index 00000000..be6768ad --- /dev/null +++ b/Sources/Plasma/CoreLib/plFormat.h @@ -0,0 +1,176 @@ +/*==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 + +/* (TODO: Make this table doxygen-friendly) + * + * FORMAT SPECIFICATION + * + * {} - format a value (defaults) + * {{ - Escape for a single '{' char + * {options} - Format a value, with the specified options (see below) + * + * Options: + * < - Align left + * > - Align right + * NNN - Pad to NNN characters (minimum - can be more) + * _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 - Use 'f' format for floating point numbers + * g - Use 'g' format for floating point numbers + * e - Use 'e' format for floating point numbers + */ + +// For internal use by plFormat and its helper function +namespace plFormat_Private +{ + enum Alignment : unsigned char + { + kAlignDefault, kAlignLeft, kAlignRight + }; + + enum DigitClass : unsigned char + { + kDigitDefault, kDigitDec, kDigitHex, kDigitHexUpper, + kDigitOct, kDigitBin, kDigitChar + }; + + enum FloatClass : unsigned char + { + kFloatDefault, kFloatE, kFloatF, kFloatG + }; + + struct FormatSpec + { + const char *fEnd; + + int fPrecisionLeft = 0; + int fPrecisionRight = 0; + + char fPadChar = 0; + Alignment fAlignment = kAlignDefault; + DigitClass fDigitClass = kDigitDefault; + FloatClass fFloatClass = kFloatDefault; + }; + + struct IFormatDataObject + { + const char *fFormatStr; + std::list> fOutput; + }; + + extern FormatSpec FetchNextFormat(IFormatDataObject &data); +} + +// Fun fact: You can add your own formatters by declaring +// PL_FORMAT_TYPE(mytype) in a header, and +// PL_FORMAT_IMPL(mytype) { ... } in a source file + +#define PL_FORMAT_TYPE(_type) \ + extern plStringBuffer _impl_plFormat_DataHandler( \ + 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...); \ + } + +#define PL_FORMAT_IMPL(_type) \ + plStringBuffer _impl_plFormat_DataHandler( \ + plFormat_Private::FormatSpec &format, _type 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(float) +PL_FORMAT_TYPE(double) +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 &) + +// End of the chain -- emits the last piece (if any) and builds the final string +namespace plFormat_Private +{ + plString _IFormat(IFormatDataObject &data); +} + +#endif // plFormat_Defined