diff --git a/Sources/Plasma/CoreLib/CMakeLists.txt b/Sources/Plasma/CoreLib/CMakeLists.txt
index 2196d9a6..c6ad4945 100644
--- a/Sources/Plasma/CoreLib/CMakeLists.txt
+++ b/Sources/Plasma/CoreLib/CMakeLists.txt
@@ -1,5 +1,16 @@
add_definitions(-D_LIB)
+if(NOT WCHAR_BYTES)
+ include(CheckTypeSize)
+
+ check_type_size("wchar_t" WCHAR_BYTES)
+ if(NOT WCHAR_BYTES)
+ message(FATAL_ERROR "Could not determine sizeof(wchar_t)")
+ set(WCHAR_BYTES 0)
+ endif(NOT WCHAR_BYTES)
+endif(NOT WCHAR_BYTES)
+add_definitions(-DWCHAR_BYTES=${WCHAR_BYTES})
+
set(CoreLib_SOURCES
HeadSpin.cpp
hsBitVector.cpp
@@ -26,6 +37,7 @@ set(CoreLib_SOURCES
pcSmallRect.cpp
plGeneric.cpp
plLoadMask.cpp
+ plString.cpp
plViewTransform.cpp
)
@@ -82,6 +94,7 @@ set(CoreLib_HEADERS
plLoadMask.h
plQuality.h
plRefCnt.h
+ plString.h
plTweak.h
plViewTransform.h
)
diff --git a/Sources/Plasma/CoreLib/plString.cpp b/Sources/Plasma/CoreLib/plString.cpp
new file mode 100644
index 00000000..9db20ea8
--- /dev/null
+++ b/Sources/Plasma/CoreLib/plString.cpp
@@ -0,0 +1,662 @@
+/*==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 .
+
+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 "plString.h"
+
+#include
+#include
+
+const plString plString::Null;
+
+#if !defined(WCHAR_BYTES) || (WCHAR_BYTES != 2) && (WCHAR_BYTES != 4)
+#error "WCHAR_BYTES must be either 2 (16-bit) or 4 (32-bit)!"
+#endif
+
+#if WCHAR_BYTES == 2
+#define u16slen(str, max) wcsnlen((const wchar_t *)(str), (max))
+#else
+static inline size_t u16slen(const UInt16 *ustr, size_t max)
+{
+ size_t length = 0;
+ for ( ; *ustr++ && max--; ++length)
+ ;
+ return length;
+}
+#endif
+
+#define BADCHAR_REPLACEMENT (0xFFFDul)
+
+void plString::IConvertFromUtf8(const char *utf8, size_t size, bool steal)
+{
+ if ((long)size < 0)
+ size = strnlen(utf8, -(long)size);
+
+#ifdef _DEBUG
+ // Check to make sure the string is actually valid UTF-8
+ const char *sp = utf8;
+ while (sp < utf8 + size) {
+ unsigned char unichar = *sp++;
+ if ((unichar & 0xF8) == 0xF0) {
+ // Four bytes
+ hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (1)");
+ hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (2)");
+ hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (3)");
+ } else if ((unichar & 0xF0) == 0xE0) {
+ // Three bytes
+ hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (1)");
+ hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (2)");
+ } else if ((unichar & 0xE0) == 0xC0) {
+ // Two bytes
+ hsAssert((*sp++) & 0x80, "Invalid UTF-8 sequence byte (1)");
+ } else if ((unichar & 0xC0) == 0x80) {
+ hsAssert(0, "Invalid UTF-8 marker byte");
+ } else if ((unichar & 0x80) != 0) {
+ hsAssert(0, "UTF-8 character out of range");
+ }
+ }
+#endif
+
+ fUtf8Buffer = steal ? plStringBuffer::Steal(utf8, size)
+ : plStringBuffer(utf8, size);
+}
+
+void plString::IConvertFromUtf16(const UInt16 *utf16, size_t size)
+{
+ if ((long)size < 0)
+ size = u16slen(utf16, -(long)size);
+
+ // Calculate the UTF-8 size
+ size_t convlen = 0;
+ const UInt16 *sp = utf16;
+ while (sp < utf16 + size) {
+ if (*sp >= 0xD800 && *sp <= 0xDFFF) {
+ // Surrogate pair
+ convlen += 4;
+ ++sp;
+ }
+ else if (*sp > 0x7FF)
+ convlen += 3;
+ else if (*sp > 0x7F)
+ convlen += 2;
+ else
+ convlen += 1;
+ ++sp;
+ }
+
+ // And perform the actual conversion
+ char *utf8 = TRACKED_NEW char[convlen + 1];
+ char *dp = utf8;
+ sp = utf16;
+ while (sp < utf16 + size) {
+ if (*sp >= 0xD800 && *sp <= 0xDFFF) {
+ // Surrogate pair
+ unsigned int unichar = 0x10000;
+
+ if (sp + 1 >= utf16 + size) {
+ hsAssert(0, "Incomplete surrogate pair in UTF-16 data");
+ unichar = BADCHAR_REPLACEMENT;
+ } else if (*sp < 0xDC00) {
+ unichar += (*sp++ & 0x3FF) << 10;
+ hsAssert(*sp >= 0xDC00 && *sp <= 0xDFFF,
+ "Invalid surrogate pair in UTF-16 data");
+ unichar += (*sp & 0x3FF);
+ } else {
+ unichar += (*sp++ & 0x3FF);
+ hsAssert(*sp >= 0xD800 && *sp < 0xDC00,
+ "Invalid surrogate pair in UTF-16 data");
+ unichar += (*sp & 0x3FF) << 10;
+ }
+ *dp++ = 0xF0 | ((unichar >> 18) & 0x07);
+ *dp++ = 0x80 | ((unichar >> 12) & 0x3F);
+ *dp++ = 0x80 | ((unichar >> 6) & 0x3F);
+ *dp++ = 0x80 | ((unichar ) & 0x3F);
+ } else if (*sp > 0x7FF) {
+ *dp++ = 0xF0 | ((*sp >> 12) & 0x0F);
+ *dp++ = 0x80 | ((*sp >> 6) & 0x3F);
+ *dp++ = 0x80 | ((*sp ) & 0x3F);
+ } else if (*sp > 0x7F) {
+ *dp++ = 0xF0 | ((*sp >> 6) & 0x1F);
+ *dp++ = 0x80 | ((*sp ) & 0x3F);
+ } else {
+ *dp++ = (char)(*sp);
+ }
+ ++sp;
+ }
+ utf8[convlen] = 0;
+
+ fUtf8Buffer = plStringBuffer::Steal(utf8, convlen);
+}
+
+void plString::IConvertFromWchar(const wchar_t *wstr, size_t size)
+{
+#if WCHAR_BYTES == 2
+ // We assume that if sizeof(wchar_t) == 2, the data is UTF-16 already
+ IConvertFromUtf16((const UInt16 *)wstr, size);
+#else
+ if ((long)size < 0)
+ size = wcsnlen(wstr, -(long)size);
+
+ // Calculate the UTF-8 size
+ size_t convlen = 0;
+ const wchar_t *sp = wstr;
+ while (sp < wstr + size) {
+ if (*sp > 0x10FFFF) {
+ hsAssert(0, "UCS-4 character out of range");
+ convlen += 3; // Use U+FFFD for release builds
+ }
+ else if (*sp > 0xFFFF)
+ convlen += 4;
+ else if (*sp > 0x7FF)
+ convlen += 3;
+ else if (*sp > 0x7F)
+ convlen += 2;
+ else
+ convlen += 1;
+ ++sp;
+ }
+
+ // And perform the actual conversion
+ char *utf8 = TRACKED_NEW char[convlen + 1];
+ char *dp = utf8;
+ sp = wstr;
+ while (sp < wstr + size) {
+ if (*sp > 0x10FFFF) {
+ // Character out of range; Use U+FFFD instead
+ *dp++ = 0xE0 | ((BADCHAR_REPLACEMENT >> 12) & 0x0F);
+ *dp++ = 0x80 | ((BADCHAR_REPLACEMENT >> 6) & 0x3F);
+ *dp++ = 0x80 | ((BADCHAR_REPLACEMENT ) & 0x3F);
+ } else if (*sp > 0xFFFF) {
+ *dp++ = 0xF0 | ((*sp >> 18) & 0x07);
+ *dp++ = 0x80 | ((*sp >> 12) & 0x3F);
+ *dp++ = 0x80 | ((*sp >> 6) & 0x3F);
+ *dp++ = 0x80 | ((*sp ) & 0x3F);
+ } else if (*sp > 0x7FF) {
+ *dp++ = 0xF0 | ((*sp >> 12) & 0x0F);
+ *dp++ = 0x80 | ((*sp >> 6) & 0x3F);
+ *dp++ = 0x80 | ((*sp ) & 0x3F);
+ } else if (*sp > 0x7F) {
+ *dp++ = 0xF0 | ((*sp >> 6) & 0x1F);
+ *dp++ = 0x80 | ((*sp ) & 0x3F);
+ } else {
+ *dp++ = (char)(*sp);
+ }
+ ++sp;
+ }
+ utf8[convlen] = 0;
+
+ fUtf8Buffer = plStringBuffer::Steal(utf8, convlen);
+#endif
+}
+
+void plString::IConvertFromIso8859_1(const char *astr, size_t size)
+{
+ if ((long)size < 0)
+ size = strnlen(astr, -(long)size);
+
+ // Calculate the UTF-8 size
+ size_t convlen = 0;
+ const char *sp = astr;
+ while (sp < astr + size) {
+ if ((*sp++) & 0x80)
+ convlen += 2;
+ else
+ convlen += 1;
+ }
+
+ // And perform the actual conversion
+ char *utf8 = TRACKED_NEW char[convlen + 1];
+ char *dp = utf8;
+ sp = astr;
+ while (sp < astr + size) {
+ if (*astr & 0x80) {
+ *dp++ = 0xC0 | ((*sp >> 6) & 0x1F);
+ *dp++ = 0x80 | ((*sp ) & 0x3F);
+ } else {
+ *dp++ = *sp;
+ }
+ ++sp;
+ }
+ utf8[convlen] = 0;
+
+ fUtf8Buffer = plStringBuffer::Steal(utf8, convlen);
+}
+
+plStringBuffer plString::ToUtf16() const
+{
+ if (IsNull())
+ return plStringBuffer();
+
+ // Calculate the UTF-16 size
+ size_t convlen = 0;
+ const char *utf8 = fUtf8Buffer.GetData();
+ const char *sp = utf8;
+ size_t srcSize = fUtf8Buffer.GetSize();
+ while (sp < utf8 + srcSize) {
+ if ((*sp & 0xF8) == 0xF0) {
+ // Will require a surrogate pair
+ ++convlen;
+ sp += 4;
+ }
+ else if ((*sp & 0xF0) == 0xE0)
+ sp += 3;
+ else if ((*sp & 0xE0) == 0xC0)
+ sp += 2;
+ else
+ sp += 1;
+ ++convlen;
+ }
+
+ // And perform the actual conversion
+ UInt16 *ustr = TRACKED_NEW UInt16[convlen + 1];
+ UInt16 *dp = ustr;
+ sp = utf8;
+ while (sp < utf8 + srcSize) {
+ unsigned int unichar;
+ if ((*sp & 0xF8) == 0xF0) {
+ unichar = (*sp++ & 0x07) << 18;
+ unichar |= (*sp++ & 0x3F) << 12;
+ unichar |= (*sp++ & 0x3F) << 6;
+ unichar |= (*sp++ & 0x3F);
+
+ *dp++ = 0xD800 | ((unichar >> 10) & 0x3FF);
+ *dp++ = 0xDC00 | ((unichar ) & 0x3FF);
+ } else if ((*sp & 0xF0) == 0xE0) {
+ unichar = (*sp++ & 0x0F) << 12;
+ unichar |= (*sp++ & 0x3F) << 6;
+ unichar |= (*sp++ & 0x3F);
+ *dp++ = unichar;
+ } else if ((*sp & 0xE0) == 0xC0) {
+ unichar = (*sp++ & 0x1F) << 6;
+ unichar |= (*sp++ & 0x3F);
+ *dp++ = unichar;
+ } else {
+ *dp++ = *sp++;
+ }
+ }
+ ustr[convlen] = 0;
+
+ return plStringBuffer::Steal(ustr, convlen);
+}
+
+plStringBuffer plString::ToWchar() const
+{
+#if WCHAR_BYTES == 2
+ // We assume that if sizeof(wchar_t) == 2, the data is UTF-16 already
+ plStringBuffer utf16 = ToUtf16();
+ return *reinterpret_cast*>(&utf16);
+#else
+ if (IsNull())
+ return plStringBuffer();
+
+ // Calculate the UCS-4 size
+ size_t convlen = 0;
+ const char *utf8 = fUtf8Buffer.GetData();
+ const char *sp = utf8;
+ size_t srcSize = fUtf8Buffer.GetSize();
+ while (sp < utf8 + srcSize) {
+ if ((*sp & 0xF8) == 0xF0)
+ sp += 4;
+ else if ((*sp & 0xF0) == 0xE0)
+ sp += 3;
+ else if ((*sp & 0xE0) == 0xC0)
+ sp += 2;
+ else
+ sp += 1;
+ ++convlen;
+ }
+
+ // And perform the actual conversion
+ wchar_t *wstr = TRACKED_NEW wchar_t[convlen + 1];
+ wchar_t *dp = wstr;
+ sp = utf8;
+ while (sp < utf8 + srcSize) {
+ unsigned int unichar;
+ if ((*sp & 0xF8) == 0xF0) {
+ unichar = (*sp++ & 0x07) << 18;
+ unichar |= (*sp++ & 0x3F) << 12;
+ unichar |= (*sp++ & 0x3F) << 6;
+ unichar |= (*sp++ & 0x3F);
+ } else if ((*sp & 0xF0) == 0xE0) {
+ unichar = (*sp++ & 0x0F) << 12;
+ unichar |= (*sp++ & 0x3F) << 6;
+ unichar |= (*sp++ & 0x3F);
+ } else if ((*sp & 0xE0) == 0xC0) {
+ unichar = (*sp++ & 0x1F) << 6;
+ unichar |= (*sp++ & 0x3F);
+ } else {
+ unichar = *sp++;
+ }
+ *dp++ = unichar;
+ }
+ wstr[convlen] = 0;
+
+ return plStringBuffer::Steal(wstr, convlen);
+#endif
+}
+
+plStringBuffer plString::ToIso8859_1() const
+{
+ if (IsNull())
+ return plStringBuffer();
+
+ // Calculate the ASCII size
+ size_t convlen = 0;
+ const char *utf8 = fUtf8Buffer.GetData();
+ const char *sp = utf8;
+ size_t srcSize = fUtf8Buffer.GetSize();
+ while (sp < utf8 + srcSize) {
+ if ((*sp & 0xF8) == 0xF0)
+ sp += 4;
+ else if ((*sp & 0xF0) == 0xE0)
+ sp += 3;
+ else if ((*sp & 0xE0) == 0xC0)
+ sp += 2;
+ else
+ sp += 1;
+ ++convlen;
+ }
+
+ // And perform the actual conversion
+ char *astr = TRACKED_NEW char[convlen + 1];
+ char *dp = astr;
+ sp = utf8;
+ while (sp < utf8 + srcSize) {
+ unsigned int unichar;
+ if ((*sp & 0xF8) == 0xF0) {
+ unichar = (*sp++ & 0x07) << 18;
+ unichar |= (*sp++ & 0x3F) << 12;
+ unichar |= (*sp++ & 0x3F) << 6;
+ unichar |= (*sp++ & 0x3F);
+ } else if ((*sp & 0xF0) == 0xE0) {
+ unichar = (*sp++ & 0x0F) << 12;
+ unichar |= (*sp++ & 0x3F) << 6;
+ unichar |= (*sp++ & 0x3F);
+ } else if ((*sp & 0xE0) == 0xC0) {
+ unichar = (*sp++ & 0x1F) << 6;
+ unichar |= (*sp++ & 0x3F);
+ } else {
+ unichar = *sp++;
+ }
+ *dp++ = (unichar < 0xFF) ? unichar : '?';
+ }
+ astr[convlen] = 0;
+
+ return plStringBuffer::Steal(astr, convlen);
+}
+
+plStringBuffer plString::GetUnicodeArray() const
+{
+ static UniChar empty[1] = {0};
+
+ if (IsNull())
+ return plStringBuffer(empty, 0);
+
+ size_t convlen = GetUniCharCount();
+ UniChar *ustr = new UniChar[convlen + 1];
+ iterator iter = GetIterator();
+ size_t dp = 0;
+ while (!iter.AtEnd())
+ ustr[dp++] = *iter++;
+ ustr[convlen] = 0;
+ return plStringBuffer::Steal(ustr, convlen);
+}
+
+int plString::ToInt(int base) const
+{
+ return static_cast(strtol(s_str(), nil, base));
+}
+
+unsigned int plString::ToUInt(int base) const
+{
+ return static_cast(strtoul(s_str(), nil, base));
+}
+
+float plString::ToFloat() const
+{
+ // strtof is C99, which MS doesn't support...
+ return (float)strtod(s_str(), nil);
+}
+
+double plString::ToDouble() const
+{
+ return strtod(s_str(), nil);
+}
+
+// Microsoft doesn't provide this for us
+#ifdef _MSC_VER
+#define va_copy(dest, src) (dest) = (src)
+#endif
+
+plString plString::IFormat(const char *fmt, va_list vptr)
+{
+ char buffer[256];
+ va_list vptr_save;
+ va_copy(vptr_save, vptr);
+
+ int chars = vsnprintf(buffer, 256, fmt, vptr);
+ if (chars < 0) {
+ // We will need to try this multiple times until we get a
+ // large enough buffer :(
+ int size = 4096;
+ for ( ;; ) {
+ va_copy(vptr, vptr_save);
+ char *bigbuffer = TRACKED_NEW char[size];
+ chars = vsnprintf(bigbuffer, size, fmt, vptr);
+ if (chars >= 0)
+ return plString::Steal(bigbuffer);
+
+ delete [] bigbuffer;
+ size *= 2;
+ }
+ } else if (chars >= 256) {
+ va_copy(vptr, vptr_save);
+ char *bigbuffer = TRACKED_NEW char[chars+1];
+ vsnprintf(bigbuffer, chars+1, fmt, vptr);
+ return plString::Steal(bigbuffer);
+ }
+
+ return plString::FromUtf8(buffer);
+}
+
+plString plString::Format(const char *fmt, ...)
+{
+ va_list vptr;
+ va_start(vptr, fmt);
+ plString str = IFormat(fmt, vptr);
+ va_end(vptr);
+ return str;
+}
+
+int plString::Find(char ch, CaseSense sense) const
+{
+ if (sense == kCaseSensitive) {
+ const char *cp = strchr(s_str(), ch);
+ return cp ? (cp - c_str()) : -1;
+ } else {
+ // No need to check for null, since s_str() will return { 0 } if it is null
+ const char *cp = s_str();
+ while (*cp) {
+ if (tolower(*cp) == tolower(ch))
+ return cp - c_str();
+ }
+ return -1;
+ }
+}
+
+int plString::FindReverse(char ch, CaseSense sense) const
+{
+ if (IsEmpty())
+ return -1;
+
+ if (sense == kCaseSensitive) {
+ const char *cp = strrchr(s_str(), ch);
+ return cp ? (cp - c_str()) : -1;
+ } else {
+ const char *cp = c_str();
+ cp += strlen(cp);
+
+ while (--cp >= c_str()) {
+ if (tolower(*cp) == tolower(ch))
+ return cp - c_str();
+ }
+ return -1;
+ }
+}
+
+static bool in_set(char key, const char *charset)
+{
+ for (const char *cs = charset; *cs; ++cs) {
+ if (*cs == key)
+ return true;
+ }
+ return false;
+}
+
+plString plString::TrimLeft(const char *charset) const
+{
+ if (IsEmpty())
+ return Null;
+
+ const char *cp = c_str();
+ while (*cp && in_set(*cp, charset))
+ ++cp;
+
+ return Substr(cp - c_str());
+}
+
+plString plString::TrimRight(const char *charset) const
+{
+ if (IsEmpty())
+ return Null;
+
+ const char *cp = c_str();
+ cp += strlen(cp);
+
+ while (--cp >= c_str() && in_set(*cp, charset))
+ ;
+
+ return Substr(0, cp - c_str() + 1);
+}
+
+plString plString::Trim(const char *charset) const
+{
+ if (IsEmpty())
+ return Null;
+
+ const char *lp = c_str();
+ const char *rp = lp + strlen(lp);
+
+ while (*lp && in_set(*lp, charset))
+ ++lp;
+ while (--rp >= lp && in_set(*rp, charset))
+ ;
+
+ return Substr(lp - c_str(), rp - lp + 1);
+}
+
+plString plString::Substr(int start, size_t size) const
+{
+ size_t maxSize = GetSize();
+
+ if (start > maxSize)
+ return Null;
+ if (start < 0)
+ start = 0;
+ if (start + size > maxSize)
+ size = maxSize - start;
+
+ if (start == 0 && size == maxSize)
+ return *this;
+
+ char *substr = TRACKED_NEW char[size + 1];
+ memcpy(substr, c_str() + start, size);
+ substr[size] = 0;
+
+ // Don't re-check UTF-8 on this
+ plString str;
+ str.fUtf8Buffer.Steal(substr, size);
+ return str;
+}
+
+plString &plString::operator+=(const plString &str)
+{
+ size_t catsize = GetSize() + str.GetSize();
+ char *catstr = TRACKED_NEW char[catsize + 1];
+ memcpy(catstr, s_str(), GetSize());
+ memcpy(catstr + GetSize(), str.s_str(), str.GetSize());
+ catstr[catsize] = 0;
+ fUtf8Buffer.Steal(catstr, catsize);
+ return *this;
+}
+
+plString operator+(const plString &left, const plString &right)
+{
+ size_t catsize = left.GetSize() + right.GetSize();
+ char *catstr = TRACKED_NEW char[catsize + 1];
+ memcpy(catstr, left.s_str(), left.GetSize());
+ memcpy(catstr + left.GetSize(), right.s_str(), right.GetSize());
+ catstr[catsize] = 0;
+
+ // Don't re-check UTF-8 on this
+ plString str;
+ str.fUtf8Buffer.Steal(catstr, catsize);
+ return str;
+}
+
+plStringStream &plStringStream::operator<<(const char *text)
+{
+ size_t length = strlen(text);
+ if (fLength + length > fBufSize) {
+ char *bigger = new char[fBufSize * 2];
+ memcpy(bigger, fBuffer, fBufSize);
+ delete [] fBuffer;
+ fBuffer = bigger;
+ fBufSize *= 2;
+ }
+ memcpy(fBuffer + fLength, text, length);
+ fLength += length;
+ return *this;
+}
+
+plStringStream &plStringStream::operator<<(int num)
+{
+ char buffer[12];
+ snprintf(buffer, 12, "%d", num);
+ return operator<<(buffer);
+}
+
+plStringStream &plStringStream::operator<<(unsigned int num)
+{
+ char buffer[12];
+ snprintf(buffer, 12, "%u", num);
+ return operator<<(buffer);
+}
+
+plStringStream &plStringStream::operator<<(char ch)
+{
+ char buffer[2] = { ch, 0 };
+ return operator<<(buffer);
+}
diff --git a/Sources/Plasma/CoreLib/plString.h b/Sources/Plasma/CoreLib/plString.h
new file mode 100644
index 00000000..368b4a96
--- /dev/null
+++ b/Sources/Plasma/CoreLib/plString.h
@@ -0,0 +1,387 @@
+/*==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 .
+
+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 plString_Defined
+#define plString_Defined
+
+#include "hsTypes.h"
+#include "hsUtils.h"
+#include
+#include
+#include
+
+#define _TEMP_CONVERT_FROM_LITERAL(x) plString::FromUtf8((x))
+#define _TEMP_CONVERT_FROM_WCHAR_T(x) plString::FromWchar((x))
+
+typedef unsigned int UniChar;
+
+template
+class plStringBuffer
+{
+private:
+ struct StringRef
+ {
+ unsigned int fRefs;
+ const _Ch *fStringData;
+ const size_t fSize;
+
+ StringRef(const _Ch *data, const size_t size)
+ : fRefs(1), fStringData(data), fSize(size) { }
+
+ inline void AddRef() { ++fRefs; }
+ inline void DecRef()
+ {
+ if (--fRefs == 0) {
+ delete [] fStringData;
+ delete this;
+ }
+ }
+ };
+
+ StringRef *fData;
+
+public:
+ plStringBuffer() : fData(0) { }
+
+ plStringBuffer(const plStringBuffer<_Ch> ©)
+ {
+ fData = copy.fData;
+ if (fData)
+ fData->AddRef();
+ }
+
+ plStringBuffer(const _Ch *data, size_t size)
+ {
+ _Ch *copyData = new _Ch[size + 1];
+ memcpy(copyData, data, size);
+ copyData[size] = 0;
+
+ fData = new StringRef(copyData, size);
+ }
+
+ ~plStringBuffer<_Ch>()
+ {
+ if (fData)
+ fData->DecRef();
+ }
+
+ static plStringBuffer<_Ch> Steal(const _Ch *data, size_t size)
+ {
+ plStringBuffer<_Ch> string;
+ string.fData = new StringRef(data, size);
+ return string;
+ }
+
+ plStringBuffer<_Ch> &operator=(const plStringBuffer<_Ch> ©)
+ {
+ if (copy.fData)
+ copy.fData->AddRef();
+ if (fData)
+ fData->DecRef();
+ fData = copy.fData;
+ return *this;
+ }
+
+ const _Ch *GetData() const { return fData ? fData->fStringData : 0; }
+ const size_t GetSize() const { return fData ? fData->fSize : 0; }
+};
+
+
+class plString
+{
+#pragma warning(push)
+#pragma warning(disable : 4146)
+ enum {
+ kSizeAuto = (size_t)(-2147483648L)
+ };
+#pragma warning(pop)
+
+ enum CaseSense {
+ kCaseSensitive, kCaseInsensitive
+ };
+
+public:
+ static const plString Null;
+
+private:
+ plStringBuffer fUtf8Buffer;
+
+ void IConvertFromUtf8(const char *utf8, size_t size, bool steal);
+ void IConvertFromUtf16(const UInt16 *utf16, size_t size);
+ void IConvertFromWchar(const wchar_t *wstr, size_t size);
+ void IConvertFromIso8859_1(const char *astr, size_t size);
+
+public:
+ plString() { }
+
+ //plString(const char *utf8) { IConvertFromUtf8(utf8, kSizeAuto, false); }
+ //plString(const wchar_t *wstr) { IConvertFromWchar(wstr, kSizeAuto); }
+ plString(const plString ©) : fUtf8Buffer(copy.fUtf8Buffer) { }
+
+ //plString &operator=(const char *utf8) { IConvertFromUtf8(utf8, kSizeAuto, false); return *this; }
+ //plString &operator=(const wchar_t *wstr) { IConvertFromWchar(wstr, kSizeAuto); return *this; }
+ plString &operator=(const plString ©) { fUtf8Buffer = copy.fUtf8Buffer; return *this; }
+
+ plString &operator+=(const plString &str);
+
+ static inline plString FromUtf8(const char *utf8, size_t size = kSizeAuto)
+ {
+ plString str;
+ str.IConvertFromUtf8(utf8, size, false);
+ return str;
+ }
+
+ static inline plString FromUtf16(const UInt16 *utf16, size_t size = kSizeAuto)
+ {
+ plString str;
+ str.IConvertFromUtf16(utf16, size);
+ return str;
+ }
+
+ static inline plString FromWchar(const wchar_t *wstr, size_t size = kSizeAuto)
+ {
+ plString str;
+ str.IConvertFromWchar(wstr, size);
+ return str;
+ }
+
+ static inline plString FromIso8859_1(const char *astr, size_t size = kSizeAuto)
+ {
+ plString str;
+ str.IConvertFromIso8859_1(astr, size);
+ return str;
+ }
+
+ const char *c_str() const { return fUtf8Buffer.GetData(); }
+ const char *s_str() const { return c_str() ? c_str() : ""; }
+ plStringBuffer ToUtf8() const { return fUtf8Buffer; }
+ plStringBuffer ToUtf16() const;
+ plStringBuffer ToWchar() const;
+ plStringBuffer ToIso8859_1() const;
+
+ // For use in displaying characters in a GUI
+ plStringBuffer GetUnicodeArray() const;
+
+ size_t GetSize() const { return fUtf8Buffer.GetSize(); }
+ bool IsEmpty() const { return fUtf8Buffer.GetSize() == 0; }
+ bool IsNull() const { return fUtf8Buffer.GetData() == 0; }
+
+ int ToInt(int base = 0) const;
+ unsigned int ToUInt(int base = 0) const;
+ float ToFloat() const;
+ double ToDouble() const;
+
+ static plString Format(const char *fmt, ...);
+ static plString IFormat(const char *fmt, va_list vptr);
+ static plString Steal(const char *utf8, size_t size = kSizeAuto)
+ {
+ plString str;
+ str.IConvertFromUtf8(utf8, size, true);
+ return str;
+ }
+
+ int Compare(const plString &str, CaseSense sense = kCaseSensitive) const
+ {
+ return (sense == kCaseSensitive) ? strcmp(s_str(), str.s_str())
+ : stricmp(s_str(), str.s_str());
+ }
+
+ int Compare(const char *str, CaseSense sense = kCaseSensitive) const
+ {
+ return (sense == kCaseSensitive) ? strcmp(s_str(), str)
+ : stricmp(s_str(), str);
+ }
+
+ bool operator==(const plString &other) const { return Compare(other) == 0; }
+ bool operator!=(const plString &other) const { return Compare(other) != 0; }
+
+ int Find(char ch, CaseSense sense = kCaseSensitive) const;
+ int FindReverse(char ch, CaseSense sense = kCaseSensitive) const;
+
+ plString TrimLeft(const char *charset = " \t\n\r") const;
+ plString TrimRight(const char *charset = " \t\n\r") const;
+ plString Trim(const char *charset = " \t\n\r") const;
+
+ plString Substr(int start, size_t size = kSizeAuto) const;
+ plString Left(size_t size) const { return Substr(0, size); }
+ plString Right(size_t size) const { return Substr(GetSize() - size, size); }
+
+public:
+ struct less : public std::binary_function
+ {
+ bool operator()(const plString &_L, const plString &_R) const
+ { return _L.Compare(_R, kCaseSensitive) < 0; }
+ };
+
+ struct less_i : public std::binary_function
+ {
+ bool operator()(const plString &_L, const plString &_R) const
+ { return _L.Compare(_R, kCaseInsensitive) < 0; }
+ };
+
+ struct equal : public std::binary_function
+ {
+ bool operator()(const plString &_L, const plString &_R) const
+ { return _L.Compare(_R, kCaseSensitive) == 0; }
+ };
+
+ struct equal_i : public std::binary_function
+ {
+ bool operator()(const plString &_L, const plString &_R) const
+ { return _L.Compare(_R, kCaseInsensitive) == 0; }
+ };
+
+public:
+ struct iterator
+ {
+ iterator() : m_ptr(nil), m_end(nil) { }
+ iterator(const iterator ©) : m_ptr(copy.m_ptr), m_end(copy.m_end) { }
+
+ iterator &operator=(const iterator ©)
+ { m_ptr = copy.m_ptr; m_end = copy.m_end; return *this; }
+
+ iterator &operator++()
+ {
+ if ((*m_ptr & 0xF8) == 0xF0)
+ m_ptr += 4;
+ else if ((*m_ptr & 0xF0) == 0xE0)
+ m_ptr += 3;
+ else if ((*m_ptr & 0xE0) == 0xC0)
+ m_ptr += 2;
+ else
+ m_ptr += 1;
+ return *this;
+ }
+
+ iterator &operator+=(size_t delta)
+ {
+ while (delta) {
+ operator++();
+ --delta;
+ }
+ return *this;
+ }
+
+ iterator operator+(size_t delta) const
+ {
+ iterator copy(*this);
+ copy += delta;
+ return copy;
+ }
+
+ int operator-(const iterator &other) const
+ {
+ return (int)(m_ptr - other.m_ptr);
+ }
+
+ bool operator==(const iterator &other) const { return m_ptr == other.m_ptr; }
+ bool operator!=(const iterator &other) const { return m_ptr != other.m_ptr; }
+ bool operator<(const iterator &other) const { return m_ptr < other.m_ptr; }
+ bool operator>(const iterator &other) const { return m_ptr > other.m_ptr; }
+ bool operator<=(const iterator &other) const { return m_ptr <= other.m_ptr; }
+ bool operator>=(const iterator &other) const { return m_ptr >= other.m_ptr; }
+
+ UniChar operator*() const
+ {
+ UniChar ch;
+ if ((*m_ptr & 0xF8) == 0xF0) {
+ ch = (m_ptr[0] & 0x07) << 18;
+ ch |= (m_ptr[1] & 0x3F) << 12;
+ ch |= (m_ptr[2] & 0x3F) << 6;
+ ch |= (m_ptr[3] & 0x3F);
+ } else if ((*m_ptr & 0xF0) == 0xE0) {
+ ch = (m_ptr[0] & 0x0F) << 12;
+ ch |= (m_ptr[1] & 0x3F) << 6;
+ ch |= (m_ptr[2] & 0x3F);
+ } else if ((*m_ptr & 0xE0) == 0xC0) {
+ ch = (m_ptr[0] & 0x1F) << 6;
+ ch |= (m_ptr[1] & 0x3F);
+ } else {
+ ch = m_ptr[0];
+ }
+ return ch;
+ }
+
+ bool AtEnd() const { return m_ptr >= m_end; }
+ bool IsValid() const { return m_ptr != 0; }
+
+ private:
+ friend class plString;
+ iterator(const char *ptr, size_t size) : m_ptr(ptr), m_end(ptr + size) { }
+
+ const char *m_ptr;
+ const char *m_end;
+ };
+
+ iterator GetIterator() const { return iterator(s_str(), GetSize()); }
+
+ size_t GetUniCharCount() const
+ {
+ iterator iter = GetIterator();
+ size_t count = 0;
+ while (!iter.AtEnd()) {
+ ++iter;
+ ++count;
+ }
+ return count;
+ }
+
+private:
+ friend plString operator+(const plString &left, const plString &right);
+};
+
+plString operator+(const plString &left, const plString &right);
+
+
+class plStringStream
+{
+public:
+ plStringStream() : fBufSize(256), fLength(0)
+ {
+ fBuffer = new char[fBufSize];
+ }
+ ~plStringStream() { delete [] fBuffer; }
+
+ plStringStream &operator<<(const char *text);
+ plStringStream &operator<<(int num);
+ plStringStream &operator<<(unsigned int num);
+ plStringStream &operator<<(char ch);
+
+ plStringStream &operator<<(const plString &text)
+ {
+ return operator<<(text.s_str());
+ }
+
+ size_t GetLength() const { return fLength; }
+ plString GetString() { return plString::FromUtf8(fBuffer, fLength); }
+
+private:
+ char *fBuffer;
+ size_t fBufSize;
+ size_t fLength;
+};
+
+#endif //plString_Defined