aboutsummaryrefslogtreecommitdiff
path: root/Src/Wasabi/bfc/string
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Wasabi/bfc/string')
-rw-r--r--Src/Wasabi/bfc/string/PathString.cpp7
-rw-r--r--Src/Wasabi/bfc/string/PathString.h21
-rw-r--r--Src/Wasabi/bfc/string/StringW.cpp783
-rw-r--r--Src/Wasabi/bfc/string/StringW.h467
-rw-r--r--Src/Wasabi/bfc/string/bfcstring.h473
-rw-r--r--Src/Wasabi/bfc/string/bigstring.cpp95
-rw-r--r--Src/Wasabi/bfc/string/bigstring.h56
-rw-r--r--Src/Wasabi/bfc/string/encodedstr.cpp156
-rw-r--r--Src/Wasabi/bfc/string/encodedstr.h42
-rw-r--r--Src/Wasabi/bfc/string/playstring.cpp57
-rw-r--r--Src/Wasabi/bfc/string/playstring.h49
-rw-r--r--Src/Wasabi/bfc/string/string.cpp884
-rw-r--r--Src/Wasabi/bfc/string/stringdict.h96
-rw-r--r--Src/Wasabi/bfc/string/url.cpp205
-rw-r--r--Src/Wasabi/bfc/string/url.h33
-rw-r--r--Src/Wasabi/bfc/string/utf8.cpp25
-rw-r--r--Src/Wasabi/bfc/string/utf8.h8
17 files changed, 3457 insertions, 0 deletions
diff --git a/Src/Wasabi/bfc/string/PathString.cpp b/Src/Wasabi/bfc/string/PathString.cpp
new file mode 100644
index 00000000..5bbd8756
--- /dev/null
+++ b/Src/Wasabi/bfc/string/PathString.cpp
@@ -0,0 +1,7 @@
+#include "PathString.h"
+#include <shlwapi.h>
+
+PathString::PathString(const wchar_t *directory, const wchar_t *filename)
+{
+ PathCombineW(path, directory, filename);
+} \ No newline at end of file
diff --git a/Src/Wasabi/bfc/string/PathString.h b/Src/Wasabi/bfc/string/PathString.h
new file mode 100644
index 00000000..324fc16d
--- /dev/null
+++ b/Src/Wasabi/bfc/string/PathString.h
@@ -0,0 +1,21 @@
+#ifndef NULLSOFT_BFC_PATHSTRING_H
+#define NULLSOFT_BFC_PATHSTRING_H
+
+
+#ifdef _WIN32
+#include <windows.h>
+class PathString
+{
+public:
+ PathString(const wchar_t *directory, const wchar_t *filename);
+private:
+ wchar_t path[MAX_PATH];
+};
+
+#else
+
+#error port me!
+
+#endif
+
+#endif \ No newline at end of file
diff --git a/Src/Wasabi/bfc/string/StringW.cpp b/Src/Wasabi/bfc/string/StringW.cpp
new file mode 100644
index 00000000..54408749
--- /dev/null
+++ b/Src/Wasabi/bfc/string/StringW.cpp
@@ -0,0 +1,783 @@
+#include <bfc/wasabi_std.h>
+#include <bfc/std_mem.h>
+#include <bfc/nsguid.h>
+#include "StringW.h"
+#ifdef _WIN32
+#include <shlwapi.h>
+#endif
+
+StringW::StringW(const wchar_t *initial_val)
+ : val(NULL)
+{
+ setValue(initial_val);
+}
+
+StringW::StringW(const StringW &s)
+ : val(NULL)
+{
+ if (s == NULL) setValue(NULL);
+ else setValue(s.getValue());
+}
+
+StringW::StringW(const StringW *s)
+ : val(NULL)
+{
+ if (s == NULL) setValue(NULL);
+ else setValue(s->getValue());
+}
+
+StringW::~StringW()
+{
+ FREE(val);
+ val = NULL;
+}
+
+int StringW::replaceNumericField(int value, wchar_t fieldchar)
+{
+ if (val == NULL || *val == '\0') return 0;
+ int nrep = 0;
+ for (const wchar_t *p = val; *p; p++)
+ {
+ if (*p == fieldchar) nrep++;
+ else if (nrep) break;
+ }
+ if (nrep == 0) return 0; // no field found
+ StringW rc;
+ wchar_t fc[2] = { 0, 0 };
+ fc[0] = fieldchar;
+ for (int i = 0; i < nrep; i++) rc.cat(fc);
+ StringPrintfW fmt(L"%%0%0dd", nrep);
+ StringPrintfW replacement(fmt.getValue(), value);
+ return replace(rc, replacement);
+}
+
+// Returns index of first found, -1 if not found.
+size_t StringW::lFindChar(wchar_t findval)
+{
+ size_t length = len();
+ for (size_t i = 0; i != length; i++)
+ {
+ if (val[i] == findval)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+const wchar_t *StringW::setValue(const wchar_t *newval)
+{
+ if (newval != val)
+ {
+ if ((unsigned long long)newval == NULL || ((unsigned long long)newval <= 65535))
+ {
+ FREE(val);
+ val = NULL;
+ }
+ else
+ {
+ if (val)
+ {
+ size_t len = wcslen(newval);
+ if (val != NULL)
+#ifdef STRING_REALLOC_OPTIMS
+ {
+ size_t oldlen = wcslen(val);
+ // if smaller but greater than half previous size, don't realloc
+ if (len > oldlen || len < oldlen / 2)
+ val = (wchar_t *)REALLOC(val, sizeof(wchar_t) * (len + 1));
+ }
+#else
+ val = (wchar_t *)REALLOC(val, sizeof(wchar_t) * (len + 1));
+#endif
+ else
+ val = WMALLOC(len + 1);
+ ASSERT(newval != NULL);
+ MEMCPY_(val, newval, sizeof(wchar_t)*(len + 1));
+ }
+ else
+ val = WCSDUP(newval);
+ }
+ }
+ return getValue();
+}
+
+int StringW::isequal(const wchar_t *otherval) const
+{
+ // TODO: benski> move to WCSCMPSAFE
+ if (!otherval)
+ otherval = L"";
+ if (!getValue())
+ return !wcscmp(L"", otherval);
+ else
+ return !wcscmp(getValue(), otherval);
+}
+
+int StringW::iscaseequal(const wchar_t *otherval) const
+{
+ return !WCSICMPSAFE(getValue(), otherval);
+}
+
+int StringW::isempty() const
+{
+ return (!val || !*val);
+}
+
+void StringW::toupper()
+{
+ if (!isempty())
+ {
+#ifdef _WIN32
+ CharUpperW(val);
+#else
+ wchar_t *itr = val;
+ while (itr && *itr)
+ {
+ *itr = ::towupper(*itr);
+ itr++;
+ }
+#endif
+ }
+}
+
+void StringW::tolower()
+{
+ if (!isempty())
+ {
+#ifdef _WIN32
+ CharLowerW(val);
+#else
+ wchar_t *itr = val;
+ while (itr && *itr)
+ {
+ *itr = ::towlower(*itr);
+ itr++;
+ }
+#endif
+ }
+}
+
+const wchar_t *StringW::getValueSafe(const wchar_t *def_val) const
+{
+ if (val == NULL)
+ return def_val;
+ else
+ return val;
+}
+
+const wchar_t *StringW::cat(const wchar_t *value)
+{
+ if (value == NULL || *value == 0)
+ return getValue();
+ if (val == NULL)
+ return setValue(value);
+ return catn(value, wcslen(value));
+}
+
+const wchar_t *StringW::catn(const wchar_t *value, size_t len)
+{
+ if (len == 0) return val;
+ if (value == NULL || *value == 0) return getValue();
+ if (val == NULL) return ncpy(value, len);
+ size_t ol = wcslen(val);
+ val = (wchar_t *)REALLOC(val, sizeof(wchar_t) * (ol + len + 1));
+ val[ol + len] = 0;
+ wcsncpy(val + ol, value, len);
+ return val;
+}
+
+// replaces string with n chars of val or length of val, whichever is less.
+const wchar_t *StringW::ncpy(const wchar_t *newstr, size_t numchars)
+{
+ val = (wchar_t *)REALLOC(val, sizeof(wchar_t) * (numchars + 1));
+ val[numchars] = 0;
+ wcsncpy(val, newstr, numchars);
+ return getValue();
+}
+
+// swaps buffers with another string
+void StringW::swap(StringW *swapper)
+{
+ wchar_t *tempChar = swapper->val;
+ swapper->val = val;
+ val = tempChar;
+}
+
+void StringW::swap(StringW &swapper) // swaps buffers with another string
+{
+ wchar_t *tempChar = swapper.val;
+ swapper.val = val;
+ val = tempChar;
+}
+
+// take ownership of a buffer
+void StringW::own(wchar_t *swapper)
+{
+ if (val)
+ FREE(val);
+ val = swapper;
+}
+
+const wchar_t *StringW::catPostSeparator(const wchar_t *value, const wchar_t separator)
+{
+ if (value == NULL || *value == 0 || separator == 0) return getValue();
+ size_t oldLen = val ? wcslen(val) : 0;
+ size_t newLen = wcslen(value);
+ val = (wchar_t *)REALLOC(val, sizeof(wchar_t) * (oldLen + newLen + 1 + 1)); // +1 for separator, +1 for null character
+ wcsncpy(val + oldLen, value, newLen + 1);
+ val[oldLen + newLen] = separator; // add the separator
+ val[oldLen + newLen + 1] = 0; // null terminate
+ return val;
+}
+
+size_t StringW::va_sprintf(const wchar_t *format, va_list args)
+{
+ if (!format) return 0;
+
+ va_list saveargs = args;
+ // roughly evaluate size of dest string
+ const wchar_t *p = format;
+ size_t length = 0;
+ while (p && *p)
+ {
+ if (*(p++) != '%') length++;
+ else
+ {
+ void *arg = va_arg(args, void *);
+ for (;;)
+ {
+ const wchar_t f = *p++;
+ if (f == 'c') length++;
+ else if (f == 'i') length += 16;
+ else if (f == 'u') length += 16;
+ else if (f == 'f') length += 64;
+ else if (f == 'd' || f == 'f') length += 64;
+ else if (f == 'x') length += 32; // Hex with LC Alphas: 0x0009a64c
+ else if (f == 'X') length += 32; // Hex with UC Alphas: 0x0009A64C
+ else if (f == 's')
+ { // ::vsrintf can properly handle null.
+ if (arg == NULL)
+ {
+ length += wcslen(L"(null)"); // Just to be explicit.
+ }
+ else
+ {
+ length += wcslen((const wchar_t *)arg);
+ }
+ }
+ else if (f == 'S') // uppercase S mean narrow string
+ { // ::vsrintf can properly handle null.
+ if (arg == NULL)
+ {
+ length += STRLEN("(null)"); // Just to be explicit.
+ }
+ else
+ {
+ length += STRLEN((const char *)arg);
+ }
+ }
+ else if (ISDIGIT(f)) continue;
+ else if (f == '.') continue;
+ else if (f == '%') length++;
+ else ASSERTPR(0, "undefined format passed to stringprintf!");
+ break;
+ }
+ }
+ }
+ if (val)
+ {
+ if (len() < length)
+ val = (wchar_t *)REALLOC(val, sizeof(wchar_t) * (length + 1));
+ }
+ else
+ val = WMALLOC(length + 1);
+
+ // now write the string in val
+#ifdef _WIN32
+ size_t remain;
+ StringCchVPrintfExW(val, length+1, 0, &remain, 0, format, saveargs);
+ return length-remain;
+#elif defined(__APPLE__)
+ int real_len = ::vswprintf(val, length+1, format, saveargs);
+ ASSERTPR(real_len <= (int)length, "String.printf overflow");
+ return real_len;
+#endif
+
+}
+
+size_t StringW::len() const
+{
+ return (val == NULL) ? 0 : wcslen(val);
+}
+
+void StringW::trunc(int newlen)
+{
+ if (val)
+ {
+ int oldlen = (int)wcslen(val);
+ if (newlen < 0) newlen = MAX(oldlen + newlen, 0);
+ int trimLen = MIN(oldlen, newlen);
+ val[trimLen] = 0;
+ }
+}
+
+int StringW::lastChar()
+{
+ if (isempty()) return -1;
+ return val[len() - 1];
+}
+
+const wchar_t *StringW::prepend(const wchar_t *value)
+{
+ if (value == NULL || *value == 0) return getValue();
+ if (val == NULL) return setValue(value);
+ StringPrintfW temp(L"%s%s", value, getValue());
+ swap(&temp);
+ return getValue();
+}
+
+int StringW::replace(const wchar_t *find, const wchar_t *replace)
+{
+ if (len() == 0 || find == NULL || replace == NULL) return 0;
+
+ int find_count = 0;
+
+ wchar_t *p, *p2;
+ size_t rep_len = wcslen(replace);
+ size_t find_len = wcslen(find);
+ ptrdiff_t size_diff = rep_len - find_len;
+
+ if ( size_diff == 0 )
+ {
+ p = val;
+ while ( p = wcsstr( p, find ) )
+ {
+ wcsncpy( p, replace, rep_len );
+ p += find_len;
+ find_count++;
+ }
+ }
+ else
+ {
+ wchar_t *new_buf, *in;
+
+ p = val;
+ while ( p = wcsstr( p, find ) )
+ {
+ find_count++;
+ p += find_len;
+ }
+
+ int length = (int)( len() + find_count * size_diff + 1 );
+ new_buf = (wchar_t *)MALLOC(sizeof(wchar_t) * length);
+
+ p = val;
+ in = new_buf;
+ while ( p2 = wcsstr( p, find ) )
+ {
+ wcsncpy( in, p, p2 - p );
+ in += p2 - p;
+ wcsncpy( in, replace, rep_len );
+ in += rep_len;
+ p = p2 + find_len;
+ }
+ wcscpy( in, p );
+ new_buf[ len() + find_count * size_diff ] = 0;
+
+ // just swap buffers
+
+ FREE(val);
+ val = new_buf;
+ }
+ return find_count;
+}
+
+const wchar_t *StringW::printf(const wchar_t *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_sprintf(format, args);
+ va_end(args);
+ return getValue();
+}
+
+void StringW::AppendPath(const wchar_t *path)
+{
+ FixSlashes();
+ if (val)
+ {
+#ifdef _WIN32
+ wchar_t temp[MAX_PATH] = {0};
+ PathCombineW(temp, val, path);
+ setValue(temp);
+#else
+#warning find a better way
+ wchar_t temp[PATH_MAX] = {0};
+ swprintf(temp, PATH_MAX, L"%s%s", val, path);
+ setValue(temp);
+#endif
+ }
+ else
+ setValue(path);
+}
+
+void StringW::AppendFolder(const wchar_t *path)
+{
+#ifdef _WIN32
+ FixSlashes();
+ wchar_t temp[MAX_PATH] = {0};
+ if (val)
+ PathCombineW(temp, val, path);
+ else
+ WCSCPYN(temp, path, MAX_PATH);
+
+ PathAddBackslashW(temp);
+ setValue(temp);
+#else
+#warning find a better way
+ wchar_t temp[PATH_MAX] = {0};
+ swprintf(temp, PATH_MAX, L"%s%s/", val, path);
+ setValue(temp);
+#endif
+}
+
+void StringW::AddBackslash()
+{
+ FixSlashes();
+ if (val)
+ {
+#ifdef _WIN32
+ wchar_t temp[MAX_PATH] = {0};
+ WCSCPYN(temp, val, MAX_PATH);
+ PathAddBackslashW(temp);
+#else
+ wchar_t temp[PATH_MAX] = {0};
+ WCSCPYN(temp, val, PATH_MAX);
+ wcscat(temp, L"/");
+#warning port me
+#endif
+ setValue(temp);
+ }
+}
+
+void StringW::changeChar(wchar_t from, wchar_t to)
+{
+ if (val == NULL) return ;
+ size_t length = len();
+ for (size_t i = 0; i != length; i++)
+ if (val[i] == from) val[i] = to;
+}
+
+void StringW::RemovePath()
+{
+#ifdef _WIN32
+ // benski> the OS-level function fucks up if there are forward slashes, so we'll fix it
+ // TODO: remove eventually
+ FixSlashes();
+
+ if (val)
+ PathRemoveFileSpecW(val);
+#else
+#warning port me
+#endif
+}
+
+void StringW::purge()
+{
+ FREE(val);
+ val = NULL;
+}
+
+StringW StringW::rSplitChar(const wchar_t *findval)
+{
+ if (val == NULL) return StringW();
+ // The index of the found character
+ size_t idxval = rFindChar(findval);
+ return rSplit(idxval);
+}
+
+// Same as above, save the "findval" is a string where it searches
+// for any of the characters in the string.
+size_t StringW::rFindChar(const wchar_t *findval)
+{
+ size_t length = len();
+ size_t numchars = wcslen(findval);
+ for (size_t i = length - 1; i > 0; i--)
+ {
+ for (size_t j = 0; j != numchars; j++)
+ {
+ if (val[i] == findval[j])
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+StringW StringW::lSplitChar(const wchar_t *findval)
+{
+ if (val == NULL) return StringW();
+ // The index of the found character
+ size_t idxval = lFindChar(findval);
+ return lSplit(idxval);
+}
+
+// Same as above, save the "findval" is a string where it searches
+// for any of the characters in the string.
+size_t StringW::lFindChar(const wchar_t *findval)
+{
+ size_t length = len();
+ size_t numchars = wcslen(findval);
+ for (size_t i = 0; i != length; i++)
+ {
+ for (size_t j = 0; j != numchars; j++)
+ {
+ if (val[i] == findval[j])
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+StringW StringW::rSplit(size_t idxval)
+{
+ if (val == NULL) return StringW();
+ if (idxval == -1)
+ { // Not Found
+ // Copy our contents to return on the stack
+ StringW retval(val);
+ // And zero the string.
+ val[0] = 0;
+ return retval;
+ }
+ else
+ {
+ // Copy from the found index downwards to the retval
+ StringW retval(val + idxval);
+ // Terminate the found char index
+ val[idxval] = 0;
+ // That was easier, wasn't it?
+ return retval;
+ }
+}
+
+// Splits string at findval. Characters passed by search, including the
+// found character, are MOVED to the returned string. If there is no char
+// to be found, the entire string is returnef and the called instance is
+// left empty. (Makes looped splits very easy).
+StringW StringW::lSplit(size_t idxval)
+{
+ if (val == NULL) return StringW();
+ if (idxval == -1)
+ { // Not Found
+ // Copy our contents to return on the stack
+ StringW retval(val);
+ // And zero the string.
+ if (val)
+ {
+ val[0] = 0;
+ }
+ return retval;
+ }
+ else
+ {
+ StringW retval;
+ // Copy into retval the number of characters to the found char index.
+ retval.ncpy(val, idxval + 1);
+ {
+ StringW testscope;
+ // Copy into retval the number of characters to the found char index.
+ testscope.ncpy(val, idxval + 1);
+ }
+#if USE == FAST_METHODS
+ size_t len = wcslen(val + idxval + 1);
+ MEMCPY(val, val + idxval + 1, sizeof(wchar_t)*(len + 1));
+#elif USE == SAFE_METHODS
+ // Copy from the found index downwards to save for this object
+ StringW temp(val + idxval + 1);
+ // And then copy into ourselves the tempspace.
+ *this = temp;
+#endif
+ return retval;
+ }
+}
+
+// Same as split, except the find char is cut completely.
+StringW StringW::lSpliceChar(const wchar_t *findval)
+{
+ if (val == NULL) return StringW();
+ //CUT // Auto-scope reference allows us to avoid a copy.
+ //CUT String & retval = lSplitChar(findval);
+ //BU gcc doesn't agree with you and neither do I :/
+ StringW retval = lSplitChar(findval);
+ // We need to strip the findval char, which is the end char.
+ size_t end = retval.len();
+ size_t num = wcslen(findval);
+ if (end)
+ {
+ for (size_t i = 0; i != num; i++)
+ {
+ if (retval.val[end - 1] == findval[i])
+ {
+ retval.val[end - 1] = 0;
+ }
+ }
+ }
+ return retval;
+}
+
+StringW StringW::rSpliceChar(const wchar_t *findval)
+{
+ if (val == NULL) return StringW();
+ //CUT // Auto-scope reference allows us to avoid a copy.
+ //CUT String & retval = rSplitChar(findval);
+ //BU gcc doesn't agree with you and neither do I :/
+ StringW retval = rSplitChar(findval);
+ // We need to strip the findval char, which is the first char.
+ // (But we still check for empty string:)
+ size_t end = retval.len();
+ size_t num = wcslen(findval);
+ if (end)
+ {
+ for (size_t i = 0; i != num; i++)
+ {
+ if (retval.val[0] == findval[i])
+ {
+#if USE == FAST_METHODS
+ size_t len = wcslen(retval.val + 1);
+ MEMCPY(retval.val, retval.val + 1, sizeof(wchar_t)*(len + 1));
+#elif USE == SAFE_METHODS
+ StringW temp(retval.val + 1);
+ retval = temp;
+#endif
+ return retval;
+ }
+ }
+ }
+ return retval;
+}
+
+void StringW::FixSlashes()
+{
+ if (val)
+ {
+ wchar_t *itr = val;
+ while (itr && *itr)
+ {
+ if (*itr == '\\' || *itr == '/')
+ *itr = Wasabi::Std::dirChar();
+ #ifdef _WIN32
+ itr = CharNextW(itr);
+#else
+ itr++;
+#endif
+ }
+ }
+}
+
+/* ------------ */
+StringPrintfW::StringPrintfW(const wchar_t *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_sprintf(format, args);
+ va_end(args);
+}
+
+StringPrintfW::StringPrintfW(int value)
+{
+ *this += value;
+}
+
+StringPrintfW::StringPrintfW(double value)
+{
+ // TODO: review to use locale variant...
+ wchar_t* locale = _wcsdup(_wsetlocale(LC_NUMERIC, NULL));
+ _wsetlocale(LC_NUMERIC, L"C");
+ *this += StringPrintfW(L"%f", value);
+ if (locale)
+ {
+ _wsetlocale(LC_NUMERIC, locale);
+ free(locale);
+ }
+}
+
+StringPrintfW::StringPrintfW(GUID g)
+{
+ wchar_t splab[nsGUID::GUID_STRLEN + 1] = {0};
+ nsGUID::toCharW(g, splab);
+ cat(splab);
+}
+
+/* ------------ */
+int StringWComparator::compareItem(StringW *p1, StringW* p2)
+{
+ return wcscmp(p1->getValue(), p2->getValue());
+}
+
+int StringWComparator::compareAttrib(const wchar_t *attrib, StringW *item)
+{
+ return wcscmp(attrib, item->getValue());
+}
+
+/* ------------ */
+_DebugStringW::_DebugStringW(const wchar_t *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_sprintf(format, args);
+ va_end(args);
+ debugPrint();
+}
+
+void _DebugStringW::debugPrint()
+{
+#ifdef _WIN32
+ OutputDebugStringW(getValue());
+ if (lastChar() != L'\n') OutputDebugStringW(L"\n");
+#else
+#warning port me
+#endif
+}
+
+StringPathCombine::StringPathCombine(const wchar_t *path, const wchar_t *filename)
+{
+#ifdef _WIN32
+ wchar_t temp[MAX_PATH] = {0};
+ PathCombineW(temp, path, filename);
+ setValue(temp);
+#else
+ setValue(path);
+ AppendPath(filename);
+#endif
+}
+
+// StringW operators using StringPrintf
+const wchar_t *StringW::operator +=(wchar_t value)
+{
+ wchar_t add[2]={value, 0};
+ return cat(add);
+ // the "Fast" methods and the Printf be
+ // built off of that?
+}
+
+const wchar_t *StringW::operator +=(int value)
+{
+ wchar_t num[64] = {0};
+#ifdef _WIN32
+ _itow(value, num, 10);
+ #else
+ WCSNPRINTF(num, 64, L"%d", value);
+ #endif
+ return cat(num);
+}
+
+const wchar_t *StringW::operator +=(GUID guid)
+{
+ wchar_t guidstr[64] = {0};
+ nsGUID::toCharW(guid, guidstr);
+ return cat(guidstr);
+} \ No newline at end of file
diff --git a/Src/Wasabi/bfc/string/StringW.h b/Src/Wasabi/bfc/string/StringW.h
new file mode 100644
index 00000000..d78a59f1
--- /dev/null
+++ b/Src/Wasabi/bfc/string/StringW.h
@@ -0,0 +1,467 @@
+#ifndef _STRINGW_H_WASABI
+#define _STRINGW_H_WASABI
+
+#ifdef __cplusplus
+
+/**
+ This is a very basic string class. It is not trying to be a be-all end-all
+ string class. It's meant to be just enough functionality to avoid STRDUP().
+ It is available to use in client apps, but no API in Wasabi depends on its
+ use by client code, so you can ignore it if you like. ;)
+ Also note that one of the design goals is to use same storage space as
+ a char *, which means no virtual methods.
+ @short Basic string class, to replace STRDUP/FREE
+ @see StringPrintf
+ @see DebugString
+*/
+
+#include <bfc/platform/types.h>
+
+class StringW
+{
+public:
+ StringW(const wchar_t *initial_val = NULL);
+ StringW(const StringW &s);
+ StringW(const StringW *s);
+ ~StringW();
+
+ /**
+ Returns the value of the string. It is a pointer to the internal storage
+ used by the class, so you must not assume the pointer will not change,
+ because it will.
+ @see getValueSafe();
+ @ret The value of the string.
+ */
+ const wchar_t *getValue() const { return val; }
+ const wchar_t *v() const { return getValue(); } // for ease of typing
+ operator const wchar_t *() const { return getValue(); }
+ /**
+ Gets the value of the string, or a safe value if the value is NULL.
+ @param def_val The value to return if the string's value is NULL. Defaults to "".
+ @see getValue()
+ @ret The value of the string, or a safe value if the value is NULL.
+ */
+ const wchar_t *getValueSafe(const wchar_t *def_val = L"") const; // returns def_val if NULL
+
+ /**
+ Returns the value of the character at a position in the string. If the
+ position is invalid, returns -1. Note that unless bounds_check is set to
+ TRUE, the pos will only be checked for negative values.
+ @param pos The position of the character to return.
+ @param bounds_check If TRUE, pos is checked against the length of the string.
+ @see setChar()
+ @ret The value of the character at the position in the string, or -1 if the position is invalid.
+ */
+ //int getChar(int pos, int bounds_check = FALSE);
+ /**
+ Sets the value of the character at a position in the string. Not multibyte UTF-8 safe yet. No lengthwise bounds checking yet.
+ @param pos The position to set.
+ @param value The value to set.
+ @ret The value of the character set, or -1 if pos < 0.
+ */
+ //int setChar(int pos, int value);
+
+ /**
+ Sets the value of the string. The given value will be copied.
+ Can accept NULL. Note that this may cause the
+ pointer returned by getValue() et al to change.
+ @param newval The new value of the string, nul-terminated.
+ @ret The new value of the string.
+ @see getValue()
+ */
+ const wchar_t *setValue(const wchar_t *newval);
+ /**
+ Gets the value of the string in the form of a non-const wchar_t *. WARNING: you
+ don't usually need to call this. If you want to modify the string,
+ you can generally just use setChar(), or setValue().
+ @ret The value of the string, casted to a non-const wchar_t *.
+ @see getValue()
+ @see setValue();
+ @see setChar();
+ */
+ wchar_t *getNonConstVal() { return const_cast<wchar_t *>(getValue()); }
+ const wchar_t *operator =(const wchar_t *newval) { return setValue(newval); }
+ const wchar_t *operator +=(const wchar_t *addval)
+ {
+ return cat(addval);
+ }
+ const wchar_t *operator +=(wchar_t value);
+ const wchar_t *operator +=(int value);
+ const wchar_t *operator +=(GUID guid);
+
+ // copy assignment operator
+ StringW &operator =(const StringW &s)
+ {
+ if (this != &s)
+ setValue(s);
+ return *this;
+ }
+
+ // comparator operators
+ //inline int operator ==(const wchar_t *val) const
+ //{
+ // if (!val) return isempty();
+ // return isequal(val);
+ //}
+ //inline int operator <(const wchar_t *val) const
+ //{
+ // return islessthan(val);
+ //}
+ //inline int operator !=(const wchar_t *val) const
+ //{
+ // if (!val) return !isempty();
+ // return !isequal(val);
+ //}
+ //inline int operator >(const wchar_t *val) const
+ //{
+ // return (!islessthan(val)) && (!isequal(val));
+ //}
+ inline StringW operator +(const wchar_t *val)
+ {
+ StringW retval = *this;
+ return retval += val;
+ }
+ //inline int operator ==(const StringW &val) const
+ //{
+ // return isequal(val);
+ //}
+ //inline int operator <(const StringW &val) const
+ //{
+ // return islessthan(val);
+ //}
+ //inline int operator !=(const StringW &val) const
+ //{
+ // return !isequal(val);
+ //}
+ //inline int operator >(const StringW &val) const
+ //{
+ // return (!islessthan(val)) && (!isequal(val));
+ //}
+ //inline StringW operator +(const StringW &val)
+ //{
+ // StringW retval = *this;
+ // return retval += val;
+ //}
+ //inline StringW operator +(const wchar_t val)
+ //{
+ // StringW retval = *this;
+ // return retval += val;
+ //}
+
+ /**
+ Gets the length of the string's value. Note that a 0 length can result from
+ both a value of NULL and a value of "".
+ @ret The length of the string's value;
+ */
+ size_t len() const;
+ /**
+ Returns TRUE if the string's value is either NULL or "".
+ @ret TRUE if the string's value is either NULL or "", FALSE otherwise.
+ */
+ int isempty() const;
+ /**
+ Converts entire string to uppercase. Not multibyte UTF-8 safe yet.
+ @see tolower()
+ */
+ void toupper();
+ /**
+ Converts entire string to lowercase. Not multibyte UTF-8 safe yet.
+ @see tolower()
+ */
+ void tolower();
+ /**
+ Checks string value equality against a nul-terminated wchar_t *.
+ @param otherval The value to check against. If NULL, will be treated as "".
+ @ret TRUE if the string matches exactly, FALSE otherwise.
+ @see iscaseequal()
+ @see islessthan()
+ */
+ int isequal(const wchar_t *otherval) const; // basically !strcmp
+ /**
+ Checks string value equality against a nul-terminated wchar_t *, case insensitively. I.e. "Blah" is case equal to "bLaH".
+ @param otherval The value to check against. If NULL, will be treated as "".
+ @ret TRUE if the string matches case-insensitively, FALSE otherwise.
+ @see isequal()
+ @see islessthan()
+ */
+ int iscaseequal(const wchar_t *otherval) const; // basically !strcasecmp
+ //int islessthan(const wchar_t *otherval) const; // basically strcmp < 0
+
+ /**
+ Changes all instances of a character to another character throughout the
+ string. Not multibyte UTF-8 aware yet. Note you can use a 'to' of NULL,
+ but this practice is not encouraged: try to use trunc() or truncateOnChar() instead.
+ @param from The character value to modify.
+ @param to The character value to replace with.
+ @see trunc()
+ @see truncateOnChar()
+ */
+ void changeChar(wchar_t from, wchar_t to);
+ /**
+ Truncates the string value at the first given character value found. If fromright==TRUE, searches from the right, otherwise goes left-to-right. Not UTF-8 multibyte aware yet.
+ Ex:
+ StringW x("abcd");
+ x.truncateOnChar('c');
+ x now contains "ab"
+ @see changeChar()
+ */
+ //void truncateOnChar(int which, int fromright = FALSE);
+
+ /**
+ Gets the last character value (rightmost).
+ @see getChar()
+ @ret The rightmost character value, or -1 if string is empty.
+ */
+ int lastChar(); // -1 if empty
+
+ /**
+ Executes a standard printf type call and sets the string's value to it.
+ @ret The new value of the string.
+ @param format The formatting string to use.
+ */
+ const wchar_t *printf(const wchar_t *format, ...);
+ /**
+ Concatenates the given value onto the end of the string. NULL will be
+ treated as "".
+ @param value The value to concatenate.
+ @ret The new value of the string.
+ @see catn()
+ @see prepend()
+ */
+ const wchar_t *cat(const wchar_t *value);
+ /**
+ Concatenates a certain number of characters from the given value onto the end of the string. NULL will be treated as "".
+ @param value The value to concatenate.
+ @param len How many characters of value to use.
+ @ret The new value of the string.
+ @see cat()
+ @see prepend()
+ */
+ const wchar_t *catn(const wchar_t *value, size_t len);
+
+ /**
+ Useful for making directory paths and stuff
+ adds a string plus a separator character.
+ i.e.
+ StringW x = "/usr/";
+ x.catPostSeparator("bin", '/');
+ creates "/usr/bin/"
+ */
+ const wchar_t *catPostSeparator(const wchar_t *value, const wchar_t separator);
+ /**
+ similiar to above, but puts the separator first
+ i.e.
+ StringW x = "/usr";
+ x.catPostSeparator('/' "bin");
+ creates "/usr/bin"
+ */
+ //const wchar_t *catPreSeparator(const wchar_t separator, const wchar_t *value);
+ /**
+ Inserts the given string value at the beginning of the string. NULL will be
+ treated as "".
+ @param value The value to insert.
+ @ret The new value of the string.
+ @see cat()
+ @see catn()
+ */
+ const wchar_t *prepend(const wchar_t *value);
+ // replaces string with n chars of val or length of val, whichever is less.
+ const wchar_t *ncpy(const wchar_t *newstr, size_t numchars);
+
+ /**
+ Copies up to maxlen chars from the string into the destination. Differs from
+ STRNCPY in that it makes sure the destination is always nul-terminated, so
+ note that maxlen includes the terminating nul.
+ @param dest The destination to copy to.
+ @param maxlen How many bytes, at most, to copy.
+ */
+ //void strncpyTo(wchar_t *dest, int maxlen);
+
+ // -----------------------------------------
+ // Character based find-n-splice methods --
+ // "l" and "r" prefixes specify to begin at
+ // front or back of string:
+
+ // Returns index of first found, -1 if not found.
+ size_t lFindChar(wchar_t findval);
+ size_t lFindChar(const wchar_t *findval); // a list of chars to search for
+ //int rFindChar(wchar_t findval);
+ size_t rFindChar(const wchar_t *findval); // a list of chars to search for
+
+ // Splits string at findval. Characters passed by search, including the
+ // found character, are MOVED to the returned string. If there is no wchar_t
+ // to be found, the entire string is returned and the called instance is
+ // left empty. (Makes looped splits very easy).
+ StringW lSplit(size_t idxval);
+ //StringW lSplitChar(wchar_t findval);
+ StringW lSplitChar(const wchar_t *findval);
+ StringW rSplit(size_t idxval);
+ //StringW rSplitChar(wchar_t findval);
+ StringW rSplitChar(const wchar_t *findval);
+
+ // Same as split, except the find wchar_t is cut completely.
+ //StringW lSpliceChar(wchar_t findval);
+ StringW lSpliceChar(const wchar_t *findval);
+ //StringW rSpliceChar(wchar_t findval);
+ StringW rSpliceChar(const wchar_t *findval);
+
+ /**
+ Replaces all occurences of the value specified by 'find' with the value
+ specified by 'replace'.
+ @param find The value to find.
+ @param replace The value to replace with.
+ @ret The number of replacements that were executed.
+ */
+ int replace(const wchar_t *find, const wchar_t *replace);
+
+ /**
+ Replaces fields of same character with 0-padded text representation of an int.
+ Example: blah$$$$.png becomes blah0000.png
+ */
+ int replaceNumericField(int value, wchar_t fieldchar = '\x24');
+
+ // UTF8-Aware "Character Based" Methods
+ /**
+ Returns how many characters are in the string value. Same as len(), but multibyte UTF-8 aware.
+ @see len()
+ @ret Number of logical UTF-8 character in the string.
+ */
+ //int numCharacters();
+
+ /**
+ Truncates the length of the string to newlen. If newlen is negative, trims
+ -newlen characters from the end. Multibyte UTF-8 aware.
+ @param newlen The new length of the string. If the string is shorter than this, nothing happens. If this value is negative, then the absolute value of newlen is how many characters to trim from the right. I.e. -1 means trim off one character from the end.
+ @see truncateOnChar()
+ */
+ void trunc(int newlen);
+
+// void trim(const wchar_t *whitespace = " \t\r\n", int left = TRUE, int right = TRUE);
+
+ /**
+ Does a vsprintf. Used the same way as printf(), but with a va_list instead of "...".
+ @param format The format string to use.
+ @param args The argument list in va_list format.
+ @ret The number of characters in the final string.
+ */
+ size_t va_sprintf(const wchar_t *format, va_list args);
+
+ /**
+ Ensures that the string drops any memory it might have allocated.
+ */
+ void purge();
+ void swap(StringW *); // swaps buffers with another string
+ void swap(StringW &); // swaps buffers with another string
+ void own(wchar_t *); // take ownership of a buffer
+
+ void AppendPath(const wchar_t *path);
+ void AppendFolder(const wchar_t *path);
+ void AddBackslash();
+ void RemovePath();
+ void FixSlashes();
+protected:
+ wchar_t * val;
+ enum { wastage_allowed = 128 };
+};
+
+
+class StringPathCombine : public StringW
+{
+public:
+ StringPathCombine(const wchar_t *path, const wchar_t *filename);
+};
+
+
+/**
+ StringW class with a printf-style constructor. Otherwise identical to StringW.
+ Also takes some standard types, like int, double, and GUID.
+ @see GUID
+ @see StringW
+*/
+class StringPrintfW : public StringW
+{
+public:
+ StringPrintfW(const wchar_t *format = NULL, ...);
+ StringPrintfW(int value);
+ StringPrintfW(double value);
+ StringPrintfW(GUID g);
+};
+
+
+
+#if defined(NDEBUG) && defined(WASABI_NO_RELEASEMODE_DEBUGSTRINGS)
+#define DebugStringW __noop
+#else
+#define DebugStringW _DebugStringW
+#endif
+
+class _DebugStringW : public StringW
+{
+public:
+ _DebugStringW(const wchar_t *format = NULL, ...);
+ _DebugStringW(const StringW &s);
+ _DebugStringW(const StringW *s);
+
+ void debugPrint();
+};
+
+
+
+#if 0
+
+class StringToLower : public StringW
+{
+public:
+ StringToLower(const char *val = NULL) : StringW(val)
+ {
+ tolower();
+ }
+};
+class StringToUpper : public StringW
+{
+public:
+ StringToUpper(const char *val = NULL) : StringW(val)
+ {
+ toupper();
+ }
+};
+
+
+//
+// Global operator overrides to allow string to take over for
+// the use of standard operators with const char pointers as
+// left hand operands.
+inline int operator ==(const char *v1, const StringW &v2)
+{
+ return v2.isequal(v1);
+}
+inline int operator !=(const char *v1, const StringW &v2)
+{
+ return !v2.isequal(v1);
+}
+inline int operator <(const char *v1, const StringW &v2)
+{
+ return !v2.islessthan(v1);
+}
+inline int operator >(const char *v1, const StringW &v2)
+{
+ return v2.islessthan(v1);
+}
+
+#endif
+/**
+ Compares two strings. Generally used with PtrListSorted<>
+ @see PtrListSorted
+*/
+class StringWComparator
+{
+public:
+ // comparator for sorting
+ static int compareItem(StringW *p1, StringW* p2);
+ // comparator for searching
+ static int compareAttrib(const wchar_t *attrib, StringW *item);
+};
+#endif
+#endif // __cplusplus
+
+
diff --git a/Src/Wasabi/bfc/string/bfcstring.h b/Src/Wasabi/bfc/string/bfcstring.h
new file mode 100644
index 00000000..08bf0202
--- /dev/null
+++ b/Src/Wasabi/bfc/string/bfcstring.h
@@ -0,0 +1,473 @@
+#ifndef _STRING_H_WASABI
+#define _STRING_H_WASABI
+
+#ifdef __cplusplus
+
+/**
+ This is a very basic string class. It is not trying to be a be-all end-all
+ string class. It's meant to be just enough functionality to avoid STRDUP().
+ It is available to use in client apps, but no API in Wasabi depends on its
+ use by client code, so you can ignore it if you like. ;)
+ Also note that one of the design goals is to use same storage space as
+ a char *, which means no virtual methods.
+ @short Basic string class, to replace STRDUP/FREE
+ @see StringPrintf
+ @see DebugString
+*/
+
+#include <bfc/platform/types.h>
+//#include <bfc/std.h>
+#include <stdarg.h>
+
+class String
+{
+public:
+ String(const char *initial_val = NULL);
+ String(const String &s);
+ String(const String *s);
+ ~String();
+
+ /**
+ Returns the value of the string. It is a pointer to the internal storage
+ used by the class, so you must not assume the pointer will not change,
+ because it will.
+ @see getValueSafe();
+ @ret The value of the string.
+ */
+ const char *getValue() const { return val; }
+ const char *v() const { return getValue(); } // for ease of typing
+ operator const char *() const { return getValue(); }
+ /**
+ Gets the value of the string, or a safe value if the value is NULL.
+ @param def_val The value to return if the string's value is NULL. Defaults to "".
+ @see getValue()
+ @ret The value of the string, or a safe value if the value is NULL.
+ */
+ const char *getValueSafe(const char *def_val = "") const; // returns def_val if NULL
+
+ /**
+ Returns the value of the character at a position in the string. If the
+ position is invalid, returns -1. Note that unless bounds_check is set to
+ TRUE, the pos will only be checked for negative values.
+ @param pos The position of the character to return.
+ @param bounds_check If TRUE, pos is checked against the length of the string.
+ @see setChar()
+ @ret The value of the character at the position in the string, or -1 if the position is invalid.
+ */
+ int getChar(int pos, int bounds_check = false);
+ /**
+ Sets the value of the character at a position in the string. Not multibyte UTF-8 safe yet. No lengthwise bounds checking yet.
+ @param pos The position to set.
+ @param value The value to set.
+ @ret The value of the character set, or -1 if pos < 0.
+ */
+ int setChar(int pos, int value);
+
+ /**
+ Sets the value of the string. The given value will be copied.
+ Can accept NULL. Note that this may cause the
+ pointer returned by getValue() et al to change.
+ @param newval The new value of the string, nul-terminated.
+ @ret The new value of the string.
+ @see getValue()
+ */
+ const char *setValue(const char *newval);
+ /**
+ Gets the value of the string in the form of a non-const char *. WARNING: you
+ don't usually need to call this. If you want to modify the string,
+ you can generally just use setChar(), or setValue().
+ @ret The value of the string, casted to a non-const char *.
+ @see getValue()
+ @see setValue();
+ @see setChar();
+ */
+ char *getNonConstVal() { return const_cast<char *>(getValue()); }
+ const char *operator =(const char *newval) { return setValue(newval); }
+ const char *operator +=(const char *addval)
+ {
+ return cat(addval);
+ }
+ const char *operator +=(char value);
+ const char *operator +=(int value);
+ const char *operator +=(GUID guid);
+
+ // copy assignment operator
+ String &operator =(const String &s)
+ {
+ if (this != &s)
+ setValue(s);
+ return *this;
+ }
+
+ // comparator operators
+ inline int operator ==(const char *val) const
+ {
+ if (!val) return isempty();
+ return isequal(val);
+ }
+ inline int operator <(const char *val) const
+ {
+ return islessthan(val);
+ }
+ inline int operator !=(const char *val) const
+ {
+ if (!val) return !isempty();
+ return !isequal(val);
+ }
+ inline int operator >(const char *val) const
+ {
+ return (!islessthan(val)) && (!isequal(val));
+ }
+ inline String operator +(const char *val)
+ {
+ String retval = *this;
+ return retval += val;
+ }
+ inline int operator ==(const String &val) const
+ {
+ return isequal(val);
+ }
+ inline int operator <(const String &val) const
+ {
+ return islessthan(val);
+ }
+ inline int operator !=(const String &val) const
+ {
+ return !isequal(val);
+ }
+ inline int operator >(const String &val) const
+ {
+ return (!islessthan(val)) && (!isequal(val));
+ }
+ inline String operator +(const String &val)
+ {
+ String retval = *this;
+ return retval += val;
+ }
+ inline String operator +(const char val)
+ {
+ String retval = *this;
+ return retval += val;
+ }
+
+ /**
+ Gets the length of the string's value. Note that a 0 length can result from
+ both a value of NULL and a value of "".
+ @ret The length of the string's value;
+ */
+ int len() const;
+ /**
+ Returns TRUE if the string's value is either NULL or "".
+ @ret TRUE if the string's value is either NULL or "", FALSE otherwise.
+ */
+ int isempty() const;
+ /**
+ Converts entire string to uppercase. Not multibyte UTF-8 safe yet.
+ @see tolower()
+ */
+ void toupper();
+ /**
+ Converts entire string to lowercase. Not multibyte UTF-8 safe yet.
+ @see tolower()
+ */
+ void tolower();
+ /**
+ Checks string value equality against a nul-terminated char *.
+ @param otherval The value to check against. If NULL, will be treated as "".
+ @ret TRUE if the string matches exactly, FALSE otherwise.
+ @see iscaseequal()
+ @see islessthan()
+ */
+ int isequal(const char *otherval) const; // basically !strcmp
+ /**
+ Checks string value equality against a nul-terminated char *, case insensitively. I.e. "Blah" is case equal to "bLaH".
+ @param otherval The value to check against. If NULL, will be treated as "".
+ @ret TRUE if the string matches case-insensitively, FALSE otherwise.
+ @see isequal()
+ @see islessthan()
+ */
+ int iscaseequal(const char *otherval) const; // basically !strcasecmp
+ int islessthan(const char *otherval) const; // basically strcmp < 0
+
+ /**
+ Changes all instances of a character to another character throughout the
+ string. Not multibyte UTF-8 aware yet. Note you can use a 'to' of NULL,
+ but this practice is not encouraged: try to use trunc() or truncateOnChar() instead.
+ @param from The character value to modify.
+ @param to The character value to replace with.
+ @see trunc()
+ @see truncateOnChar()
+ */
+ void changeChar(int from, int to);
+ /**
+ Truncates the string value at the first given character value found. If fromright==TRUE, searches from the right, otherwise goes left-to-right. Not UTF-8 multibyte aware yet.
+ Ex:
+ String x("abcd");
+ x.truncateOnChar('c');
+ x now contains "ab"
+ @see changeChar()
+ */
+ void truncateOnChar(int which, int fromright = false);
+
+ /**
+ Gets the last character value (rightmost).
+ @see getChar()
+ @ret The rightmost character value, or -1 if string is empty.
+ */
+ int lastChar(); // -1 if empty
+
+ /**
+ Executes a standard printf type call and sets the string's value to it.
+ @ret The new value of the string.
+ @param format The formatting string to use.
+ */
+ const char *printf(const char *format, ...);
+ /**
+ Concatenates the given value onto the end of the string. NULL will be
+ treated as "".
+ @param value The value to concatenate.
+ @ret The new value of the string.
+ @see catn()
+ @see prepend()
+ */
+ const char *cat(const char *value);
+ /**
+ Concatenates a certain number of characters from the given value onto the end of the string. NULL will be treated as "".
+ @param value The value to concatenate.
+ @param len How many characters of value to use.
+ @ret The new value of the string.
+ @see cat()
+ @see prepend()
+ */
+ const char *catn(const char *value, int len);
+
+ /**
+ Useful for making directory paths and stuff
+ adds a string plus a separator character.
+ i.e.
+ String x = "/usr/";
+ x.catPostSeparator("bin", '/');
+ creates "/usr/bin/"
+ */
+ const char *catPostSeparator(const char *value, const char separator);
+ /**
+ similiar to above, but puts the separator first
+ i.e.
+ String x = "/usr";
+ x.catPostSeparator('/' "bin");
+ creates "/usr/bin"
+ */
+ const char *catPreSeparator(const char separator, const char *value);
+ /**
+ Inserts the given string value at the beginning of the string. NULL will be
+ treated as "".
+ @param value The value to insert.
+ @ret The new value of the string.
+ @see cat()
+ @see catn()
+ */
+ const char *prepend(const char *value);
+ // replaces string with n chars of val or length of val, whichever is less.
+ const char *ncpy(const char *newstr, int numchars);
+
+ /**
+ Copies up to maxlen chars from the string into the destination. Differs from
+ STRNCPY in that it makes sure the destination is always nul-terminated, so
+ note that maxlen includes the terminating nul.
+ @param dest The destination to copy to.
+ @param maxlen How many bytes, at most, to copy.
+ */
+ void strncpyTo(char *dest, int maxlen);
+
+ // -----------------------------------------
+ // Character based find-n-splice methods --
+ // "l" and "r" prefixes specify to begin at
+ // front or back of string:
+
+ // Returns index of first found, -1 if not found.
+ int lFindChar(char findval);
+ int lFindChar(const char *findval); // a list of chars to search for
+ int rFindChar(char findval);
+ int rFindChar(const char *findval); // a list of chars to search for
+
+ // Splits string at findval. Characters passed by search, including the
+ // found character, are MOVED to the returned string. If there is no char
+ // to be found, the entire string is returned and the called instance is
+ // left empty. (Makes looped splits very easy).
+ String lSplit(int idxval);
+ String lSplitChar(char findval);
+ String lSplitChar(const char *findval);
+ String rSplit(int idxval);
+ String rSplitChar(char findval);
+ String rSplitChar(const char *findval);
+
+ // Same as split, except the find char is cut completely.
+ String lSpliceChar(char findval);
+ String lSpliceChar(const char *findval);
+ String rSpliceChar(char findval);
+ String rSpliceChar(const char *findval);
+
+ /**
+ Replaces all occurences of the value specified by 'find' with the value
+ specified by 'replace'.
+ @param find The value to find.
+ @param replace The value to replace with.
+ @ret The number of replacements that were executed.
+ */
+ int replace(const char *find, const char *replace);
+
+ /**
+ Replaces fields of same character with 0-padded text representation of an int.
+ Example: blah$$$$.png becomes blah0000.png
+ */
+ int replaceNumericField(int value, int fieldchar = '\x24');
+
+ // UTF8-Aware "Character Based" Methods
+ /**
+ Returns how many characters are in the string value. Same as len(), but multibyte UTF-8 aware.
+ @see len()
+ @ret Number of logical UTF-8 character in the string.
+ */
+ int numCharacters();
+
+ /**
+ Truncates the length of the string to newlen. If newlen is negative, trims
+ -newlen characters from the end. Multibyte UTF-8 aware.
+ @param newlen The new length of the string. If the string is shorter than this, nothing happens. If this value is negative, then the absolute value of newlen is how many characters to trim from the right. I.e. -1 means trim off one character from the end.
+ @see truncateOnChar()
+ */
+ void trunc(int newlen);
+
+ void trim(const char *whitespace = " \t\r\n", int left = true, int right = true);
+
+ /**
+ Does a vsprintf. Used the same way as printf(), but with a va_list instead of "...".
+ @param format The format string to use.
+ @param args The argument list in va_list format.
+ @ret The number of characters in the final string.
+ */
+ int va_sprintf(const char *format, va_list args);
+
+ /**
+ Ensures that the string drops any memory it might have allocated.
+ */
+ void purge();
+ void swap(String *); // swaps buffers with another string
+ void swap(String &); // swaps buffers with another string
+ void own(char *); // take ownership of a buffer
+
+ void AppendPath(const char *path);
+
+protected:
+ char * val;
+ enum { wastage_allowed = 128 };
+};
+
+/**
+ String class with a printf-style constructor. Otherwise identical to String.
+ Also takes some standard types, like int, double, and GUID.
+ @see GUID
+ @see String
+*/
+class StringPrintf : public String
+{
+public:
+ StringPrintf(const char *format = NULL, ...);
+ StringPrintf(int value);
+ StringPrintf(double value);
+ StringPrintf(GUID g);
+};
+
+class StringToLower : public String
+{
+public:
+ StringToLower(const char *val = NULL) : String(val)
+ {
+ tolower();
+ }
+};
+class StringToUpper : public String
+{
+public:
+ StringToUpper(const char *val = NULL) : String(val)
+ {
+ toupper();
+ }
+};
+
+#if defined(NDEBUG) && defined(WASABI_NO_RELEASEMODE_DEBUGSTRINGS)
+#define DebugString __noop
+#else
+#define DebugString _DebugString
+#endif
+
+class _DebugString : public String
+{
+public:
+ _DebugString(const char *format = NULL, ...);
+ _DebugString(const String &s);
+ _DebugString(const String *s);
+
+ void debugPrint();
+};
+
+
+#define RecycleString String
+
+// String operators using StringPrintf
+
+inline const char *String::operator +=(char value)
+{
+ return cat(StringPrintf("%c", value)); // Uhm. Shouldn't the string be given
+ // the "Fast" methods and the Printf be
+ // built off of that?
+}
+
+inline const char *String::operator +=(int value)
+{
+ return cat(StringPrintf("%i", value));
+}
+
+inline const char *String::operator +=(GUID guid)
+{
+ return cat(StringPrintf(guid));
+}
+
+//
+// Global operator overrides to allow string to take over for
+// the use of standard operators with const char pointers as
+// left hand operands.
+inline int operator ==(const char *v1, const String &v2)
+{
+ return v2.isequal(v1);
+}
+inline int operator !=(const char *v1, const String &v2)
+{
+ return !v2.isequal(v1);
+}
+inline int operator <(const char *v1, const String &v2)
+{
+ return !v2.islessthan(v1);
+}
+inline int operator >(const char *v1, const String &v2)
+{
+ return v2.islessthan(v1);
+}
+
+
+/**
+ Compares two strings. Generally used with PtrListSorted<>
+ @see PtrListSorted
+*/
+class StringComparator
+{
+public:
+ // comparator for sorting
+ static int compareItem(String *p1, String* p2);
+ // comparator for searching
+ static int compareAttrib(const wchar_t *attrib, String *item);
+};
+
+#endif // __cplusplus
+
+#endif
diff --git a/Src/Wasabi/bfc/string/bigstring.cpp b/Src/Wasabi/bfc/string/bigstring.cpp
new file mode 100644
index 00000000..20c7c09f
--- /dev/null
+++ b/Src/Wasabi/bfc/string/bigstring.cpp
@@ -0,0 +1,95 @@
+#include "precomp_wasabi_bfc.h"
+#include "bigstring.h"
+
+BigString::BigString() {
+ mem = NULL;
+ m_linecount = 0;
+}
+
+BigString::~BigString() {
+ if (mem != NULL) {
+ FREE(mem);
+ }
+ strings.deleteAll();
+}
+
+const char *BigString::getValue() /*const*/ {
+ if (mem != NULL) return mem;
+ size_t l = 0;
+ foreach(strings)
+ l += strings.getfor()->len();
+ endfor;
+ mem = (char *)MALLOC(l+1);
+ char *p = mem;
+ String *s = NULL;
+ size_t sl = 0;
+ foreach(strings)
+ s = strings.getfor();
+ sl = s->len();
+ if (sl > 0) MEMCPY((void *)p, (void *)s->getValue(), sl);
+ p += sl;
+ endfor;
+ *p = 0;
+ return mem;
+}
+
+void BigString::setValue(const char *val) {
+ if (mem != NULL) {
+ FREE(mem);
+ mem = NULL;
+ }
+ strings.deleteAll();
+ cat(val);
+}
+
+int BigString::isempty() {
+ if (strings.getNumItems() == 0) return 1;
+ foreach(strings)
+ if (!strings.getfor()->isempty()) return 0;
+ endfor;
+ return 1;
+}
+
+void BigString::reset() {
+ if (mem != NULL) {
+ FREE(mem);
+ mem = NULL;
+ }
+ strings.deleteAll();
+ m_linecount = 0;
+}
+
+void BigString::catn(const char *s, int n) {
+ String *str = new String();
+ str->catn(s, n);
+ cat(str->getValue());
+}
+
+void BigString::cat(const char *s) {
+ if (mem != NULL) {
+ FREE(mem);
+ mem = NULL;
+ }
+ char *p = (char *)s;
+ while (p && *p) {
+ if (*p == '\r' || *p == '\n') {
+ if (*(p+1) == '\n' && *p == '\r') p++;
+ m_linecount++;
+ }
+ p++;
+ }
+ strings.addItem(new String(s));
+}
+
+char BigString::lastChar() {
+ return strings.getLast()->lastChar();
+}
+
+char BigString::firstChar() {
+ const char *s = strings.getFirst()->getValue();
+ return s ? *s : 0;
+}
+
+int BigString::getLineCount() {
+ return m_linecount;
+}
diff --git a/Src/Wasabi/bfc/string/bigstring.h b/Src/Wasabi/bfc/string/bigstring.h
new file mode 100644
index 00000000..ef2ff207
--- /dev/null
+++ b/Src/Wasabi/bfc/string/bigstring.h
@@ -0,0 +1,56 @@
+#ifndef __BIGSTRING_H
+#define __BIGSTRING_H
+
+/*
+
+A class tor concatenate chunks of texts into one big pool. This is much faster than using String if you are adding
+a lot of tiny pieces into one giant block (a typical cases of this is when writing xml). Upon request for the value,
+the class allocates one big block of memory and copies all the strings into it serially (as opposed to String
+reallocating the entire block at each concatenation). Note that because of the type of implementation BigString has
+to use, you cannot get the full block as a return value to your concatenations and assignments (+= and = return void).
+To do this, request the value explicitely (this should be kept to a strict minimum or the advantage of BigString over
+String will disapear)
+
+*/
+
+#include <bfc/string/bfcstring.h>
+#include <bfc/ptrlist.h>
+
+class BigString {
+ public:
+ BigString();
+ virtual ~BigString();
+
+ operator const char *() /*const*/ { return getValue(); }
+ const char *getValue() /*const*/;
+ char *getNonConstVal() { return (char *)getValue(); }
+ void setValue(const char *val);
+ // copy assignment operator
+ BigString& operator =(/*const*/ BigString &s) {
+ if (this != &s)
+ setValue(s);
+ return *this;
+ }
+
+ void operator =(const char *newval) { setValue(newval); }
+ void operator +=(const char *addval) {
+ cat(addval);
+ }
+
+ int isempty();
+
+ void reset();
+
+ void catn(const char *s, int n);
+ void cat(const char *s);
+ char lastChar();
+ char firstChar();
+ int getLineCount();
+
+ private:
+ PtrList<String> strings;
+ char *mem;
+ int m_linecount;
+};
+
+#endif
diff --git a/Src/Wasabi/bfc/string/encodedstr.cpp b/Src/Wasabi/bfc/string/encodedstr.cpp
new file mode 100644
index 00000000..eddba7da
--- /dev/null
+++ b/Src/Wasabi/bfc/string/encodedstr.cpp
@@ -0,0 +1,156 @@
+#include "precomp_wasabi_bfc.h"
+// someday, there will be a file here.
+#include <bfc/wasabi_std.h>
+#include "encodedstr.h"
+#include <bfc/string/bfcstring.h>
+#include <api/service/svcs/svc_stringconverter.h>
+#include <api/memmgr/api_memmgr.h>
+EncodedStr::EncodedStr(FOURCC encodingType, void *encodedBuffer, int bufferSize, int deleteIt) {
+ encoding_type = encodingType;
+ encoded_buffer = encodedBuffer;
+ buffer_size = bufferSize;
+ delete_it = deleteIt;
+}
+
+EncodedStr::~EncodedStr() {
+ if (delete_it && (encoded_buffer != NULL)) {
+#ifdef WASABI_COMPILE_MEMMGR
+ WASABI_API_MEMMGR->sysFree(encoded_buffer);
+#else
+ free(encoded_buffer);
+#endif
+ }
+}
+
+void EncodedStr::resetBuffer(FOURCC encodingType, void *encodedBuffer, int bufferSize, int deleteIt) {
+ // if there's someone already there, toss them.
+ if (delete_it && (encoded_buffer != NULL)) {
+#ifdef WASABI_COMPILE_MEMMGR
+ WASABI_API_MEMMGR->sysFree(encoded_buffer);
+#else
+ free(encoded_buffer);
+#endif
+ }
+ encoding_type = encodingType;
+ encoded_buffer = encodedBuffer;
+ buffer_size = bufferSize;
+ delete_it = deleteIt;
+}
+
+int EncodedStr::convertToUTF8(String &output_str) {
+ int retval = 0;
+ StringConverterEnum myServiceEnum(encoding_type);
+ svc_stringConverter *myConv = myServiceEnum.getFirst();
+ if (myConv != NULL) {
+
+ void *in_buffer = encoded_buffer;
+ int size_in_bytes = buffer_size;
+
+ if (encoded_buffer != NULL) {
+ // Preflight
+ int u8size = myConv->preflightToUTF8(encoding_type, in_buffer, size_in_bytes);
+ // Alloc
+#ifdef WASABI_COMPILE_MEMMGR
+ char *u8str = reinterpret_cast<char *>(WASABI_API_MEMMGR->sysMalloc(u8size));
+#else
+ char *u8str = reinterpret_cast<char *>(MALLOC(u8size));
+#endif
+ if (u8str != NULL) {
+ // Convert
+ retval = myConv->convertToUTF8(encoding_type, in_buffer, size_in_bytes, u8str, u8size);
+ if (retval < 0) {
+ // Clear on error.
+#ifdef WASABI_COMPILE_MEMMGR
+ WASABI_API_MEMMGR->sysFree(u8str);
+#else
+ free(encoded_buffer);
+#endif
+ u8str = NULL;
+ }
+ } else {
+ ASSERTPR(u8str != NULL, "Malloc failed in string converter\n");
+ }
+ // And call the method to inject the pointer into our string (cleared on error).
+ output_str.setValue(u8str);
+ }
+
+ // Once we use our service, release our locked instance of it.
+ myServiceEnum.release(myConv);
+ } else {
+ // Clear the string on error.
+ retval = SvcStrCnv::ERROR_UNAVAILABLE;
+ output_str.setValue(NULL);
+ }
+ return retval;
+}
+
+int EncodedStr::convertFromUTF8(FOURCC encodingType, const String &inputStr) {
+ int retval = 0;
+ int written = 0;
+ void *buffer = NULL;
+ StringConverterEnum myServiceEnum(encodingType);
+ svc_stringConverter *myConv = myServiceEnum.getFirst();
+
+ if (myConv != NULL) {
+ if (inputStr != NULL) {
+ const char *val = inputStr.getValue();
+ int valsize = STRLEN(val) + 1; // count the null char in your size-in-bytes!
+ // Preflight
+ int size = myConv->preflightFromUTF8(encodingType, val, valsize);
+ if (size > 0) {
+ // Alloc
+#ifdef WASABI_COMPILE_MEMMGR
+ buffer = WASABI_API_MEMMGR->sysMalloc(size);
+#else
+ buffer = MALLOC(size);
+#endif
+ if (buffer != NULL) {
+ // Convert
+ written = myConv->convertFromUTF8(encodingType, val, valsize, buffer, size);
+ if (written > 0) {
+ retval = written;
+ } else {
+ // Clear on error.
+#ifdef WASABI_COMPILE_MEMMGR
+ WASABI_API_MEMMGR->sysFree(buffer);
+#else
+ free(buffer);
+#endif
+ buffer = NULL;
+ retval = written;
+ written = 0;
+ }
+ } else {
+ ASSERTPR(buffer != NULL, "Malloc failed in string converter\n");
+ }
+ } else {
+ // Clear on error.
+ buffer = NULL;
+ retval = size;
+ written = 0;
+ }
+ }
+ // Once we use our service, release our locked instance of it.
+ myServiceEnum.release(myConv);
+ } else {
+ // On error locking down a service, all the default values are errors and called through resetBuffer.
+ retval = SvcStrCnv::ERROR_UNAVAILABLE;
+ }
+ resetBuffer(encodingType, buffer, written);
+ return retval;
+}
+
+// This is for debugging.
+int EncodedStr::operator ==(const EncodedStr &in_string) {
+ if (encoding_type == in_string.encoding_type) {
+ switch (encoding_type) {
+ case SvcStrCnv::OSNATIVE:
+ return (STRCMP(reinterpret_cast<char *>(encoded_buffer), reinterpret_cast<char *>(in_string.encoded_buffer)) == 0);
+ break;
+ default:
+ return 0;
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/Src/Wasabi/bfc/string/encodedstr.h b/Src/Wasabi/bfc/string/encodedstr.h
new file mode 100644
index 00000000..ee5e49c0
--- /dev/null
+++ b/Src/Wasabi/bfc/string/encodedstr.h
@@ -0,0 +1,42 @@
+#ifndef _ENCODEDSTR_H
+#define _ENCODEDSTR_H
+
+#include <api/service/svcs/svc_stringtypes.h>
+
+class String;
+
+class EncodedStr {
+public:
+ // The EncodedStr object will automatically delete its ram, unless you
+ // specify 0 in that "delete it" parameter there, partner.
+ EncodedStr(FOURCC encodingType = 0, void *encodedBuffer = NULL, int bufferSize = 0, int deleteIt = 1);
+ ~EncodedStr();
+
+ // A "reset" will ensure any previously set buffer will be deleted
+ void resetBuffer(FOURCC encodingType, void *encodedBuffer, int bufferSize, int deleteIt = 1);
+
+ // All the calls to the service level functions are through here.
+ int convertToUTF8(String &output_str);
+ // This method will reset this object (ie: delete RAM if necessary)
+ int convertFromUTF8(FOURCC encodingType, const String &inputStr);
+
+ // Accessor inlines
+ inline FOURCC getEncodingType() { return encoding_type; }
+ inline void *getEncodedBuffer() { return encoded_buffer; }
+ inline int getBufferSize() { return buffer_size; }
+ inline int getDeleteIt() { return delete_it; }
+
+// This is for debugging.
+ int operator ==(const EncodedStr &in_string);
+
+private:
+ FOURCC encoding_type;
+ void * encoded_buffer;
+ int buffer_size;
+ int delete_it;
+};
+
+
+#endif//_ENCODEDSTR_H
+
+
diff --git a/Src/Wasabi/bfc/string/playstring.cpp b/Src/Wasabi/bfc/string/playstring.cpp
new file mode 100644
index 00000000..88583265
--- /dev/null
+++ b/Src/Wasabi/bfc/string/playstring.cpp
@@ -0,0 +1,57 @@
+#include "precomp_wasabi_bfc.h"
+
+#include "playstring.h"
+
+#define USE_TABLE
+
+Playstring::Playstring(const wchar_t *_val) {
+ val = NULL;
+ setValue(_val);
+}
+
+Playstring::Playstring(const Playstring &ps) {
+ val = NULL;
+ setValue(ps.getValue());
+}
+
+Playstring::~Playstring() {
+ setValue(NULL);
+}
+
+void Playstring::setValue(const wchar_t *newval) {
+ _setValue(newval, 0);
+}
+
+void Playstring::_setValue(const wchar_t *newval, int tablenum)
+{
+#ifdef USE_TABLE
+#ifdef WASABI_COMPILE_METADB
+ if (val != NULL) WASABI_API_METADB->metadb_releasePlaystring(val, tablenum);
+#else
+ FREE((void*)val);
+#endif
+#else
+ FREE((void*)val);
+#endif
+
+ val = NULL;
+
+ if (newval != NULL /*&& *newval != 0*/) {
+#ifdef USE_TABLE
+#ifdef WASABI_COMPILE_METADB
+ val = WASABI_API_METADB->metadb_dupPlaystring(newval, tablenum);
+#else
+ val = WCSDUP(newval);
+#endif
+#else
+ val = STRDUP(newval);
+#endif
+ }
+}
+
+Playstring& Playstring::operator =(const Playstring &ps) {
+ if (this != &ps) {
+ setValue(ps.getValue());
+ }
+ return *this;
+}
diff --git a/Src/Wasabi/bfc/string/playstring.h b/Src/Wasabi/bfc/string/playstring.h
new file mode 100644
index 00000000..2e9b183f
--- /dev/null
+++ b/Src/Wasabi/bfc/string/playstring.h
@@ -0,0 +1,49 @@
+#ifndef _PLAYSTRING_H
+#define _PLAYSTRING_H
+
+#include <bfc/common.h>
+#include <bfc/string/StringW.h>
+
+class Playstring
+{
+public:
+ Playstring(const wchar_t *val=NULL);
+ Playstring(const Playstring &ps);
+ ~Playstring();
+
+ const wchar_t *getValue() const { return val; }
+ operator const wchar_t *() const { return getValue(); }
+
+ void setValue(const wchar_t *newval);
+
+ // copy
+ Playstring& operator =(const Playstring &ps);
+
+protected:
+ void _setValue(const wchar_t *newval, int tablenum);
+
+private:
+ const wchar_t *val;
+};
+
+class PlaystringComparator {
+public:
+ // comparator for sorting
+ static int compareItem(Playstring *p1, Playstring* p2) {
+ return wcscmp(p1->getValue(), p2->getValue());
+ }
+};
+
+template <int tablenum=0>
+class PlaystringT : public Playstring {
+public:
+ PlaystringT(const wchar_t *newval) {
+ val = NULL; setValue(newval);
+ }
+ PlaystringT(const Playstring &ps) {
+ val = NULL; setValue(ps.getValue());
+ }
+ void setValue(const wchar_t *newval) { _setValue(newval, tablenum); }
+};
+
+#endif
diff --git a/Src/Wasabi/bfc/string/string.cpp b/Src/Wasabi/bfc/string/string.cpp
new file mode 100644
index 00000000..49b7aade
--- /dev/null
+++ b/Src/Wasabi/bfc/string/string.cpp
@@ -0,0 +1,884 @@
+#include <bfc/wasabi_std.h>
+#include "string.h"
+#include <bfc/nsguid.h>
+
+String::String(const char *initial_val)
+ : val(NULL)
+{
+ setValue(initial_val);
+}
+
+String::String(const String &s)
+ : val(NULL)
+{
+ if (s == NULL) setValue(NULL);
+ else setValue(s.getValue());
+}
+
+String::String(const String *s)
+ : val(NULL)
+{
+ if (s == NULL) setValue(NULL);
+ else setValue(s->getValue());
+}
+
+String::~String()
+{
+ FREE(val);
+}
+
+const char *String::getValueSafe(const char *def_val) const
+{
+ if (val == NULL)
+ return def_val;
+ else
+ return val;
+}
+
+int String::getChar(int pos, int bounds_check)
+{
+ if (pos < 0 || val == NULL) return -1;
+ if (bounds_check && pos >= len()) return -1;
+ return val[pos];
+}
+
+int String::setChar(int pos, int value)
+{
+ if (pos < 0 || val == NULL) return -1;
+ return val[pos] = value;
+}
+
+const char *String::setValue(const char *newval)
+{
+ if (newval != val)
+ {
+ if (newval == NULL)
+ {
+ FREE(val);
+ val = NULL;
+ }
+ else
+ {
+ int len = STRLEN(newval);
+ if (val != NULL)
+#ifdef STRING_REALLOC_OPTIMS
+ {
+ int oldlen = STRLEN(val);
+ // if smaller but greater than half previous size, don't realloc
+ if (len > oldlen || len < oldlen / 2)
+ val = (char *)REALLOC(val, len + 1);
+ }
+#else
+ val = (char *)REALLOC(val, len + 1);
+#endif
+ else
+ val = (char *)MALLOC(len + 1);
+ ASSERT(newval != NULL);
+ MEMCPY_(val, newval, len + 1);
+ }
+ }
+ return getValue();
+}
+
+int String::len() const
+{
+ return (val == NULL) ? 0 : STRLEN(val);
+}
+
+int String::isempty() const
+{
+ return (!val || !*val);
+}
+
+void String::toupper()
+{
+ if (!isempty())
+ STRTOUPPER(val);
+}
+
+void String::tolower()
+{
+ if (!isempty())
+ STRTOLOWER(val);
+}
+
+int String::isequal(const char *otherval) const
+{
+ return !STRCMPSAFE(getValue(), otherval);
+}
+
+int String::iscaseequal(const char *otherval) const
+{
+ return !STRICMPSAFE(getValue(), otherval);
+}
+
+int String::islessthan(const char *otherval) const
+{
+ return STRCMPSAFE(getValue(), otherval) < 0;
+}
+
+void String::changeChar(int from, int to)
+{
+ if (val == NULL) return ;
+ int length = len();
+ for (int i = 0; i < length; i++)
+ if (val[i] == from) val[i] = to;
+}
+
+void String::truncateOnChar(int which, int fromright)
+{
+ if (fromright)
+ {
+ if (val == NULL) return ;
+ int length = len();
+ for (int i = length - 1; i >= 0; i--)
+ {
+ if (val[i] == which)
+ {
+ val[i] = '\0';
+ return ;
+ }
+ }
+ }
+ else
+ {
+ changeChar(which, '\0');
+ }
+}
+
+int String::lastChar()
+{
+ if (isempty()) return -1;
+ return val[len() - 1];
+}
+
+const char *String::printf(const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_sprintf(format, args);
+ va_end(args);
+ return getValue();
+}
+
+const char *String::cat(const char *value)
+{
+ if (value == NULL || *value == 0) return getValue();
+ if (val == NULL) return setValue(value);
+ return catn(value, STRLEN(value));
+}
+
+const char *String::catn(const char *value, int len)
+{
+ if (len == 0) return val;
+ if (value == NULL || *value == 0) return getValue();
+ if (val == NULL) return ncpy(value, len);
+ int ol = STRLEN(val);
+ val = (char *)REALLOC(val, ol + len + 1);
+ val[ol + len] = 0;
+ STRNCPY(val + ol, value, len);
+ return val;
+}
+
+const char *String::prepend(const char *value)
+{
+ if (value == NULL || *value == 0) return getValue();
+ if (val == NULL) return setValue(value);
+ StringPrintf temp("%s%s", value, getValue());
+ swap(&temp);
+ return getValue();
+}
+
+// replaces string with n chars of val or length of val, whichever is less.
+const char *String::ncpy(const char *newstr, int numchars)
+{
+ val = (char *)REALLOC(val, numchars + 1);
+ val[numchars] = 0;
+ STRNCPY(val, newstr, numchars);
+ return getValue();
+}
+
+void String::strncpyTo(char *dest, int maxlen)
+{
+ ASSERT(dest != NULL);
+ ASSERT(maxlen >= 0);
+ if (maxlen == 0) return ;
+ else if (maxlen == 1)
+ {
+ *dest = '\0';
+ return ;
+ }
+ const char *src = val;
+ if (src == NULL) src = "";
+ int copylen = MIN(STRLEN(src), maxlen);
+ if (maxlen == copylen) copylen--; // make room for 0-termination
+ MEMCPY(dest, src, copylen);
+ dest[copylen] = 0;
+}
+
+// -----------------------------------------
+// Character based find-n-splice methods --
+// "l" and "r" prefixes specify to begin at
+// front or back of string:
+
+#ifdef FAST_METHODS
+#undef FAST_METHODS
+#endif//FAST_METHODS
+#ifdef SAFE_METHODS
+#undef SAFE_METHODS
+#endif//SAFE_METHODS
+#ifdef USE
+#undef USE
+#endif//USE
+
+#define FAST_METHODS 1
+#define SAFE_METHODS 0
+
+#define USE FAST_METHODS
+
+// Returns index of first found, -1 if not found.
+int String::lFindChar(char findval)
+{
+ int length = len();
+ for (int i = 0; i < length; i++)
+ {
+ if (val[i] == findval)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// Same as above, save the "findval" is a string where it searches
+// for any of the characters in the string.
+int String::lFindChar(const char *findval)
+{
+ int length = len();
+ int numchars = STRLEN(findval);
+ for (int i = 0; i < length; i++)
+ {
+ for (int j = 0; j < numchars; j++)
+ {
+ if (val[i] == findval[j])
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+int String::rFindChar(char findval)
+{
+ int length = len();
+ for (int i = length - 1; i > 0; i--)
+ {
+ if (val[i] == findval)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// Same as above, save the "findval" is a string where it searches
+// for any of the characters in the string.
+int String::rFindChar(const char *findval)
+{
+ int length = len();
+ int numchars = STRLEN(findval);
+ for (int i = length - 1; i > 0; i--)
+ {
+ for (int j = 0; j < numchars; j++)
+ {
+ if (val[i] == findval[j])
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+// Splits string at findval. Characters passed by search, including the
+// found character, are MOVED to the returned string. If there is no char
+// to be found, the entire string is returnef and the called instance is
+// left empty. (Makes looped splits very easy).
+String String::lSplit(int idxval)
+{
+ if (val == NULL) return String();
+ if (idxval == -1)
+ { // Not Found
+ // Copy our contents to return on the stack
+ String retval(val);
+ // And zero the string.
+ if (val)
+ {
+ val[0] = 0;
+ }
+ return retval;
+ }
+ else
+ {
+ String retval;
+ // Copy into retval the number of characters to the found char index.
+ retval.ncpy(val, idxval + 1);
+ {
+ String testscope;
+ // Copy into retval the number of characters to the found char index.
+ testscope.ncpy(val, idxval + 1);
+ }
+#if USE == FAST_METHODS
+ size_t len = strlen(val + idxval + 1);
+ MEMCPY(val, val + idxval + 1, len + 1);
+#elif USE == SAFE_METHODS
+ // Copy from the found index downwards to save for this object
+ String temp(val + idxval + 1);
+ // And then copy into ourselves the tempspace.
+ *this = temp;
+#endif
+ return retval;
+ }
+ // this will never be hit. many compilers are too stupid to realize this.
+ return String();
+}
+
+String String::lSplitChar(char findval)
+{
+ if (val == NULL) return String();
+ // The index of the found character
+ int idxval = lFindChar(findval);
+ return lSplit(idxval);
+}
+
+String String::lSplitChar(const char *findval)
+{
+ if (val == NULL) return String();
+ // The index of the found character
+ int idxval = lFindChar(findval);
+ return lSplit(idxval);
+}
+
+String String::rSplit(int idxval)
+{
+ if (val == NULL) return String();
+ if (idxval == -1)
+ { // Not Found
+ // Copy our contents to return on the stack
+ String retval(val);
+ // And zero the string.
+ val[0] = 0;
+ return retval;
+ }
+ else
+ {
+ // Copy from the found index downwards to the retval
+ String retval(val + idxval);
+ // Terminate the found char index
+ val[idxval] = 0;
+ // That was easier, wasn't it?
+ return retval;
+ }
+ // this will never be hit. many compilers are too stupid to realize this.
+ return String();
+}
+
+String String::rSplitChar(char findval)
+{
+ if (val == NULL) return String();
+ // The index of the found character
+ int idxval = rFindChar(findval);
+ return rSplit(idxval);
+}
+
+String String::rSplitChar(const char *findval)
+{
+ if (val == NULL) return String();
+ // The index of the found character
+ int idxval = rFindChar(findval);
+ return rSplit(idxval);
+}
+
+// Same as split, except the find char is cut completely.
+String String::lSpliceChar(char findval)
+{
+ if (val == NULL) return String();
+ //CUT // Auto-scope reference allows us to avoid a copy.
+ //CUT String & retval = lSplitChar(findval);
+ //BU gcc doesn't agree with you and neither do I :/
+ String retval = lSplitChar(findval);
+
+ // We need to strip the findval char, which is the end char.
+ int end = retval.len();
+ if (end)
+ {
+ if (retval.val[end - 1] == findval)
+ {
+ retval.val[end - 1] = 0;
+ }
+ }
+ return retval;
+}
+
+// Same as split, except the find char is cut completely.
+String String::lSpliceChar(const char *findval)
+{
+ if (val == NULL) return String();
+ //CUT // Auto-scope reference allows us to avoid a copy.
+ //CUT String & retval = lSplitChar(findval);
+ //BU gcc doesn't agree with you and neither do I :/
+ String retval = lSplitChar(findval);
+ // We need to strip the findval char, which is the end char.
+ int end = retval.len();
+ int num = STRLEN(findval);
+ if (end)
+ {
+ for (int i = 0; i < num; i++)
+ {
+ if (retval.val[end - 1] == findval[i])
+ {
+ retval.val[end - 1] = 0;
+ }
+ }
+ }
+ return retval;
+}
+
+String String::rSpliceChar(char findval)
+{
+ if (val == NULL) return String();
+ //CUT // Auto-scope reference allows us to avoid a copy.
+ //CUT String & retval = rSplitChar(findval);
+ //BU gcc doesn't agree with you and neither do I :/
+ String retval = rSplitChar(findval);
+ // We need to strip the findval char, which is the first char.
+ // (But we still check for empty string:)
+ size_t end = retval.len();
+ if (end)
+ {
+ if (retval.val[0] == findval)
+ {
+#if USE == FAST_METHODS
+ size_t len = strlen(retval.val + 1);
+ MEMCPY(retval.val, retval.val + 1, len + 1);
+#elif USE == SAFE_METHODS
+ String temp(retval.val + 1);
+ retval = temp;
+#endif
+ return retval;
+ }
+ }
+ return retval;
+}
+
+String String::rSpliceChar(const char *findval)
+{
+ if (val == NULL) return String();
+ //CUT // Auto-scope reference allows us to avoid a copy.
+ //CUT String & retval = rSplitChar(findval);
+ //BU gcc doesn't agree with you and neither do I :/
+ String retval = rSplitChar(findval);
+ // We need to strip the findval char, which is the first char.
+ // (But we still check for empty string:)
+ size_t end = retval.len();
+ size_t num = STRLEN(findval);
+ if (end)
+ {
+ for (size_t i = 0; i < num; i++)
+ {
+ if (retval.val[0] == findval[i])
+ {
+#if USE == FAST_METHODS
+ size_t len = strlen(retval.val + 1);
+ MEMCPY(retval.val, retval.val + 1, len + 1);
+#elif USE == SAFE_METHODS
+ String temp(retval.val + 1);
+ retval = temp;
+#endif
+ return retval;
+ }
+ }
+ }
+ return retval;
+}
+
+int String::replace(const char *find, const char *replace)
+{
+ if (len() == 0 || find == NULL || replace == NULL) return 0;
+
+ int find_count = 0;
+
+ char *p, *p2;
+ int rep_len = STRLEN( replace );
+ int find_len = STRLEN( find );
+ int size_diff = rep_len - find_len;
+
+ if ( size_diff == 0 )
+ {
+ p = val;
+ while ( p = STRSTR( p, find ) )
+ {
+ STRNCPY( p, replace, rep_len );
+ p += find_len;
+ find_count++;
+ }
+ }
+ else
+ {
+ char *new_buf, *in;
+
+ p = val;
+ while ( p = STRSTR( p, find ) )
+ {
+ find_count++;
+ p += find_len;
+ }
+
+ new_buf = (char *)MALLOC( len() + find_count * size_diff + 1 );
+
+ p = val;
+ in = new_buf;
+ while ( p2 = STRSTR( p, find ) )
+ {
+ STRNCPY( in, p, (int)(p2 - p) );
+ in += p2 - p;
+ STRNCPY( in, replace, rep_len );
+ in += rep_len;
+ p = p2 + find_len;
+ }
+ STRCPY( in, p );
+ new_buf[ len() + find_count * size_diff ] = 0;
+
+ // just swap buffers
+
+ FREE(val);
+ val = new_buf;
+ }
+ return find_count;
+}
+
+int String::replaceNumericField(int value, int fieldchar)
+{
+ if (val == NULL || *val == '\0') return 0;
+ int nrep = 0;
+ for (const char *p = val; *p; p++)
+ {
+ if (*p == fieldchar) nrep++;
+ else if (nrep) break;
+ }
+ if (nrep == 0) return 0; // no field found
+ String rc;
+ char fc[2] = { 0, 0 };
+ fc[0] = fieldchar;
+ for (int i = 0; i < nrep; i++) rc.cat(fc);
+ StringPrintf fmt("%%0%0dd", nrep);
+ StringPrintf replacement(fmt.getValue(), value);
+ return replace(rc, replacement);
+}
+
+#undef USE
+#undef SAFE_METHODS
+#undef FAST_METHODS
+
+int String::numCharacters()
+{
+ // count newsize characters over how many bytes?
+ int count, bytes;
+ for (bytes = 0, count = 0; val[bytes]; count++, bytes++)
+ {
+ // If we encounter a lead byte, skip over the trail bytes.
+ switch (val[bytes] & 0xC0)
+ {
+ case 0x80: // trail bytes
+ // THIS SHOULD NEVER EVER EVER EVER HAPPEN!
+ // but we'll fall through anyhow, just in case someone
+ // sends us non-UTF8.
+ case 0xC0: // lead bytes
+ do
+ {
+ bytes++;
+ if (val[bytes] == 0)
+ {
+ // if people are giving us lame encodings, break here.
+ break;
+ }
+ }
+ while ((val[bytes + 1] & 0xC0) == 0x80);
+ break;
+ }
+ }
+ return count;
+}
+
+void String::trunc(int newlen)
+{
+ if (val == NULL) return ;
+ int oldlen = numCharacters();
+ if (newlen < 0) newlen = MAX(oldlen + newlen, 0);
+ if (newlen >= oldlen) return ;
+
+ // count newsize characters over how many bytes?
+ int count, bytes;
+ for (bytes = 0, count = 0; count < newlen; count++, bytes++)
+ {
+ // If we encounter a lead byte, skip over the trail bytes.
+ switch (val[bytes] & 0xC0)
+ {
+ case 0x80: // trail bytes
+ // THIS SHOULD NEVER EVER EVER EVER HAPPEN!
+ // but we'll fall through anyhow, just in case someone
+ // sends us non-UTF8.
+ case 0xC0: // lead bytes
+ do
+ {
+ bytes++;
+ }
+ while ((val[bytes + 1] & 0xC0) == 0x80);
+ break;
+ }
+ }
+
+ val[bytes] = 0;
+}
+
+void String::trim(const char *whitespace, int left, int right)
+{
+ if (val == NULL) return ;
+ if (left)
+ { // trim left
+ for (;;)
+ {
+ int restart = 0;
+ const char *p;
+ for (p = whitespace; *p; p++)
+ {
+ if (*p == val[0])
+ {
+ STRCPY(val, val + 1);
+ restart = 1;
+ break;
+ }
+ }
+ if (!restart) break;
+ }
+ } //left
+ if (right)
+ {
+ char *ptr = val;
+ while (ptr && *ptr) ptr++;
+ ptr--;
+ if (ptr <= val) return ;
+ for (; ptr > val; ptr--)
+ {
+ const char *p;
+ for (p = whitespace; *p; p++)
+ {
+ if (*p == *ptr)
+ {
+ *ptr = '\0';
+ break;
+ }
+ }
+ if (!*p) break;
+ }
+ } //right
+}
+
+int String::va_sprintf(const char *format, va_list args)
+{
+ if (!format) return 0;
+
+ va_list saveargs = args;
+ // roughly evaluate size of dest string
+ const char *p = format;
+ int length = 0;
+ while (p && *p)
+ {
+ if (*(p++) != '%') length++;
+ else
+ {
+ void *arg = va_arg(args, void *);
+ for (;;)
+ {
+ const char f = *p++;
+ if (f == 'c') length++;
+ else if (f == 'i') length += 16;
+ else if (f == 'u') length += 16;
+ else if (f == 'f') length += 64;
+ else if (f == 'd' || f == 'f') length += 64;
+ else if (f == 'x') length += 32; // Hex with LC Alphas: 0x0009a64c
+ else if (f == 'X') length += 32; // Hex with UC Alphas: 0x0009A64C
+ else if (f == 's')
+ { // ::vsrintf can properly handle null.
+ if (arg == NULL)
+ {
+ length += STRLEN("(null)"); // Just to be explicit.
+ }
+ else
+ {
+ length += STRLEN((const char *)arg);
+ }
+ }
+ else if (f == 'S')
+ { // ::vsrintf can properly handle null.
+ if (arg == NULL)
+ {
+ length += (int)wcslen(L"(null)"); // Just to be explicit.
+ }
+ else
+ {
+ length += (int)wcslen((const wchar_t *)arg);
+ }
+ }
+ else if (ISDIGIT(f)) continue;
+ else if (f == '.') continue;
+ else if (f == '%') length++;
+ else ASSERTPR(0, "undefined format passed to stringprintf!");
+ break;
+ }
+ }
+ }
+ if (val)
+ {
+ if (len() < length)
+ val = (char *)REALLOC(val, length + 1);
+ }
+ else val = (char *)MALLOC(length + 1);
+
+ // now write the string in val
+ int real_len = ::vsprintf_s(val, length + 1, format, saveargs);
+ ASSERTPR(real_len <= length, "String.printf overflow");
+ return real_len;
+}
+
+void String::purge()
+{
+ FREE(val);
+ val = NULL;
+}
+
+// swaps buffers with another string
+void String::swap(String *swapper)
+{
+ char *tempChar = swapper->val;
+ swapper->val = val;
+ val = tempChar;
+}
+
+void String::swap(String &swapper) // swaps buffers with another string
+{
+ char *tempChar = swapper.val;
+ swapper.val = val;
+ val = tempChar;
+}
+
+// take ownership of a buffer
+void String::own(char *swapper)
+{
+ if (val)
+ FREE(val);
+ val = swapper;
+}
+
+const char *String::catPostSeparator(const char *value, const char separator)
+{
+ if (value == NULL || *value == 0 || separator == 0) return getValue();
+ int oldLen = val ? STRLEN(val) : 0;
+ int newLen = STRLEN(value);
+ val = (char *)REALLOC(val, oldLen + newLen + 1 + 1); // +1 for separator, +1 for null character
+ STRCPY(val + oldLen, value);
+ val[oldLen + newLen] = separator; // add the separator
+ val[oldLen + newLen + 1] = 0; // null terminate
+ return val;
+}
+
+const char *String::catPreSeparator(const char separator, const char *value)
+{
+ if (value == NULL || *value == 0 || separator == 0) return getValue();
+ int oldLen = val ? STRLEN(val) : 0;
+ int newLen = STRLEN(value);
+ val = (char *)REALLOC(val, oldLen + newLen + 1 + 1); // +1 for separator, +1 for null character
+ val[oldLen] = separator; // add the separator
+ STRCPY(val + oldLen + 1, value); // +1 for separator (goes first)
+ val[oldLen + newLen + 1] = 0; // null terminate
+ return val;
+}
+
+void String::AppendPath(const char *path)
+{
+ catPreSeparator('/', path);
+}
+
+StringPrintf::StringPrintf(const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_sprintf(format, args);
+ va_end(args);
+}
+
+StringPrintf::StringPrintf(int value)
+{
+ *this += value;
+}
+
+StringPrintf::StringPrintf(double value)
+{
+ // TODO: review to use locale variant...
+ char* locale = _strdup(setlocale(LC_NUMERIC, NULL));
+ setlocale(LC_NUMERIC, "C");
+ *this += StringPrintf("%f", value);
+ if (locale)
+ {
+ setlocale(LC_NUMERIC, locale);
+ free(locale);
+ }
+}
+
+StringPrintf::StringPrintf(GUID g)
+{
+ char splab[nsGUID::GUID_STRLEN + 1] = {0};
+ nsGUID::toChar(g, splab);
+ cat(splab);
+}
+
+_DebugString::_DebugString(const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_sprintf(format, args);
+ va_end(args);
+
+ debugPrint();
+}
+
+_DebugString::_DebugString(const String &s) : String(s)
+{
+ debugPrint();
+}
+
+_DebugString::_DebugString(const String *s) : String(s)
+{
+ debugPrint();
+}
+
+void _DebugString::debugPrint()
+{
+#ifdef _WIN32
+ OutputDebugStringA(getValue());
+ if (lastChar() != '\n') OutputDebugStringA("\n");
+#else
+#warning port me
+#endif
+}
+
+int StringComparator::compareItem(String *p1, String* p2)
+{
+ return STRCMP(p1->getValue(), p2->getValue());
+}
+
+int StringComparator::compareAttrib(const wchar_t *attrib, String *item)
+{
+ return STRCMP((const char *)attrib, item->getValue());
+} \ No newline at end of file
diff --git a/Src/Wasabi/bfc/string/stringdict.h b/Src/Wasabi/bfc/string/stringdict.h
new file mode 100644
index 00000000..d88c21e7
--- /dev/null
+++ b/Src/Wasabi/bfc/string/stringdict.h
@@ -0,0 +1,96 @@
+#ifndef __STRINGDICT_H
+#define __STRINGDICT_H
+
+#include <bfc/string/StringW.h>
+#include <bfc/ptrlist.h>
+
+// a convenient class to statically declare strings and their IDs with binary search lookups
+//
+// example of use :
+//
+// BEGIN_STRINGDICTIONARY(_identifier)
+// SDI("somestring", 0);
+// SDI("someotherstring", 1);
+// SDI("andyetanotherstring", 2);
+// END_STRINGDICTIONARY(_identifier, identifier)
+
+// foo (const char *str) {
+// int a = identifier.getId(str);
+// if (a < 0) {
+// // not found!
+// }
+// return a;
+// }
+
+class StringDictionaryItem
+{
+public:
+ StringDictionaryItem(const wchar_t *string, int stringid) : str(string), id(stringid) {}
+ virtual ~StringDictionaryItem() {}
+ const wchar_t *getString()
+ {
+ return str;
+ }
+ int getId()
+ {
+ return id;
+ }
+private:
+ StringW str;
+ int id;
+};
+
+class StringDictionaryItemCompare
+{
+public:
+ static int compareItem(void *p1, void *p2)
+ {
+ return WCSICMP(((StringDictionaryItem *)p1)->getString(), ((StringDictionaryItem *)p2)->getString());
+ }
+ static int compareAttrib(const wchar_t *attrib, void *item)
+ {
+ return WCSICMP(attrib, ((StringDictionaryItem *)item)->getString());
+ }
+};
+
+class StringDictionary
+{
+public:
+ StringDictionary() {}
+ virtual ~StringDictionary()
+ {
+ items.deleteAll();
+ }
+ virtual void addItem(const wchar_t *str, int id)
+ {
+ ASSERT(id != -1);
+ items.addItem(new StringDictionaryItem(str, id));
+ }
+ virtual int getId(const wchar_t *str)
+ {
+ StringDictionaryItem *i = items.findItem(str);
+ if (i == NULL)
+ return -1;
+ return i->getId();
+ }
+private:
+ PtrListQuickSorted<StringDictionaryItem, StringDictionaryItemCompare> items;
+};
+
+#define BEGIN_STRINGDICTIONARY(class_ident) \
+ class class_ident : public StringDictionary { \
+ public: \
+ class_ident() {
+
+#define SDI(str, id) \
+ addItem(str, id);
+
+#define END_STRINGDICTIONARY(class_ident, instance_ident) \
+ }; \
+ virtual ~class_ident() { } \
+ }; \
+ \
+ class_ident instance_ident;
+
+
+#endif
diff --git a/Src/Wasabi/bfc/string/url.cpp b/Src/Wasabi/bfc/string/url.cpp
new file mode 100644
index 00000000..ea52966d
--- /dev/null
+++ b/Src/Wasabi/bfc/string/url.cpp
@@ -0,0 +1,205 @@
+#include "precomp_wasabi_bfc.h"
+#include "url.h"
+
+#include <bfc/wasabi_std.h>
+
+void Url::encode(StringW &dest, int use_plus_for_space, int encoding, int style)
+{
+ if (dest.isempty()) return;
+ StringW srcstr = dest;
+ const wchar_t *src = srcstr;
+ dest = NULL;
+/*
+ if (encoding & URLENCODE_EXCLUDEHTTPPREFIX)
+ if (!_wcsnicmp(src, L"http://", 7))
+ {
+ src += 7;
+ dest += L"http://";
+ }
+*/
+ while (src && *src)
+ {
+ int encode = 1;
+
+// if (encoding & URLENCODE_NOTHING) encode = 0;
+
+ if ((encoding & URLENCODE_EXCLUDEALPHANUM)
+ && (ISALPHA(*src) || ISDIGIT(*src)
+ || *src == '_' || *src == '-' || *src == '.' || *src == '~')
+ && *src < 128)
+ encode = 0;
+
+// if ((encoding & URLENCODE_EXCLUDE_8BIT) && (*src > 127)) encode = 0;
+ if ((encoding & URLENCODE_EXCLUDE_ABOVEEQ32) && (*src >= 32)) encode = 0;
+// if ((encoding & URLENCODE_ENCODESPACE) && *src == ' ') encode = 1;
+// if ((encoding & URLENCODE_ENCODEXML) && (*src == '<' || *src == '>' || *src == '&')) encode = 1;
+ if ((*src == '&' && (style == URLENCODE_STYLE_ANDPOUND || style == URLENCODE_STYLE_ANDPOUNDX)) ||
+ (*src == '%' && style == URLENCODE_STYLE_PERCENT)) encode = 1;
+ if ((encoding & URLENCODE_EXCLUDESLASH) && (*src == '/' || *src == ':')) encode = 0;
+ if (!encode)
+ {
+ dest += *src;
+ }
+ else if (use_plus_for_space && *src == ' ')
+ {
+ dest += '+';
+ }
+ else
+ {
+ switch (style)
+ {
+ case URLENCODE_STYLE_PERCENT:
+ dest += StringPrintfW(L"%%%02X", (int) * src);
+ break;
+ case URLENCODE_STYLE_ANDPOUND:
+ dest += StringPrintfW(L"&#%02d;", (int) * src);
+ break;
+ case URLENCODE_STYLE_ANDPOUNDX:
+ dest += StringPrintfW(L"&#x%02X;", (int) * src);
+ break;
+ }
+ }
+ src++;
+ }
+}
+
+void Url::encode(String &dest, int use_plus_for_space, int encoding, int style)
+{
+ if (dest.isempty()) return;
+ String srcstr = dest;
+ const char *src = srcstr;
+ dest = NULL;
+/*
+ if (encoding & URLENCODE_EXCLUDEHTTPPREFIX)
+ if (!_wcsnicmp(src, L"http://", 7))
+ {
+ src += 7;
+ dest += L"http://";
+ }
+*/
+ while (src && *src)
+ {
+ int encode = 1;
+
+// if (encoding & URLENCODE_NOTHING) encode = 0;
+
+ if ((encoding & URLENCODE_EXCLUDEALPHANUM)
+ && (ISALPHA(*src) || ISDIGIT(*src)
+ || *src == '_' || *src == '-' || *src == '.' || *src == '~')
+ && *(unsigned char *)src < 128)
+ encode = 0;
+
+// if ((encoding & URLENCODE_EXCLUDE_8BIT) && (*src > 127)) encode = 0;
+ if ((encoding & URLENCODE_EXCLUDE_ABOVEEQ32) && (*src >= 32)) encode = 0;
+// if ((encoding & URLENCODE_ENCODESPACE) && *src == ' ') encode = 1;
+// if ((encoding & URLENCODE_ENCODEXML) && (*src == '<' || *src == '>' || *src == '&')) encode = 1;
+ if ((*src == '&' && (style == URLENCODE_STYLE_ANDPOUND || style == URLENCODE_STYLE_ANDPOUNDX)) ||
+ (*src == '%' && style == URLENCODE_STYLE_PERCENT)) encode = 1;
+ if ((encoding & URLENCODE_EXCLUDESLASH) && (*src == '/' || *src == ':')) encode = 0;
+ if (!encode)
+ {
+ dest += *src;
+ }
+ else if (use_plus_for_space && *src == ' ')
+ {
+ dest += '+';
+ }
+ else
+ {
+ switch (style)
+ {
+ case URLENCODE_STYLE_PERCENT:
+ dest += StringPrintf("%%%02X", (unsigned char)*src);
+ break;
+ case URLENCODE_STYLE_ANDPOUND:
+ dest += StringPrintf("&#%02d;", (unsigned char)*src);
+ break;
+ case URLENCODE_STYLE_ANDPOUNDX:
+ dest += StringPrintf("&#x%02X;", (unsigned char)*src);
+ break;
+ }
+ }
+ src++;
+ }
+}
+
+void Url::decode(StringW &str, int use_plus_for_space)
+{
+ if (str.isempty()) return;
+ Url::decode(str.getNonConstVal());
+}
+
+static uint8_t quickhex(wchar_t c)
+{
+ int hexvalue = c;
+ if (hexvalue & 0x10)
+ hexvalue &= ~0x30;
+ else
+ {
+ hexvalue &= 0xF;
+ hexvalue += 9;
+ }
+ return hexvalue;
+}
+
+static uint8_t DecodeEscape(const wchar_t *&str)
+{
+ uint8_t a = quickhex(*++str);
+ uint8_t b = quickhex(*++str);
+ str++;
+ return a * 16 + b;
+}
+
+static void DecodeEscapedUTF8(wchar_t *&output, const wchar_t *&input)
+{
+ uint8_t utf8_data[1024] = {0}; // hopefully big enough!!
+ int num_utf8_words=0;
+ bool error=false;
+
+ while (input && *input && *input == '%' && num_utf8_words < sizeof(utf8_data))
+ {
+ if (iswxdigit(input[1]) && iswxdigit(input[2]))
+ {
+ utf8_data[num_utf8_words++]=DecodeEscape(input);
+ }
+ else if (input[1] == '%')
+ {
+ input+=2;
+ utf8_data[num_utf8_words++]='%';
+ }
+ else
+ {
+ error = true;
+ break;
+ }
+ }
+
+ int len = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)utf8_data, num_utf8_words, 0, 0);
+ MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)utf8_data, num_utf8_words, output, len);
+ output += len;
+
+ if (error)
+ {
+ *output++ = *input++;
+ }
+}
+
+// benski> We have the luxury of knowing that decoding will ALWAYS produce smaller strings
+// so we can do it in-place
+void Url::decode(wchar_t *str)
+{
+ const wchar_t *itr = str;
+ while (itr && *itr)
+ {
+ switch (*itr)
+ {
+ case '%':
+ DecodeEscapedUTF8(str, itr);
+ break;
+ default:
+ *str++ = *itr++;
+ break;
+ }
+ }
+ *str = 0;
+} \ No newline at end of file
diff --git a/Src/Wasabi/bfc/string/url.h b/Src/Wasabi/bfc/string/url.h
new file mode 100644
index 00000000..49c72e80
--- /dev/null
+++ b/Src/Wasabi/bfc/string/url.h
@@ -0,0 +1,33 @@
+#ifndef _URL_H
+#define _URL_H
+
+#include <bfc/common.h>
+#include <bfc/string/StringW.h>
+
+//#define URLENCODE_NOTHING 1 // encode nothing (add some flags!), default is to start with everything
+#define URLENCODE_EXCLUDEALPHANUM 2 // do not encode characters that are alphanumeric (a-z,0-1) or _ - and .
+#define URLENCODE_EXCLUDESLASH 4 // don't encode '/' or ':'
+//#define URLENCODE_EXCLUDEHTTPPREFIX 8 // don't encode "http://"
+//#define URLENCODE_EXCLUDE_8BIT 16 // do not encode > 127
+#define URLENCODE_EXCLUDE_ABOVEEQ32 32 // do not encode >= 32
+//#define URLENCODE_ENCODESPACE 64 // force encoding space
+//#define URLENCODE_ENCODEXML 128 // force encoding '>' and '<'
+
+#define URLENCODE_DEFAULT URLENCODE_EXCLUDEALPHANUM
+//#define URLENCODE_FULLURL (URLENCODE_DEFAULT|URLENCODE_EXCLUDEHTTPPREFIX|URLENCODE_EXCLUDESLASH|URLENCODE_EXCLUDE_7BIT_ABOVEEQ32|URLENCODE_ENCODESPACE)
+
+class Url {
+public:
+ static void encode(StringW &dest, int use_plus_for_space = FALSE, int encoding = URLENCODE_DEFAULT, int style=URLENCODE_STYLE_PERCENT);
+ static void encode(String &dest, int use_plus_for_space = FALSE, int encoding = URLENCODE_DEFAULT, int style=URLENCODE_STYLE_PERCENT);
+ static void decode(StringW &str, int use_plus_for_space = FALSE);
+ static void decode(wchar_t *str); // in place url decode
+
+ enum {
+ URLENCODE_STYLE_PERCENT = 0, // %AB
+ URLENCODE_STYLE_ANDPOUND = 1, // &#171;
+ URLENCODE_STYLE_ANDPOUNDX = 2, // &#xAB;
+ };
+};
+
+#endif
diff --git a/Src/Wasabi/bfc/string/utf8.cpp b/Src/Wasabi/bfc/string/utf8.cpp
new file mode 100644
index 00000000..389c231f
--- /dev/null
+++ b/Src/Wasabi/bfc/string/utf8.cpp
@@ -0,0 +1,25 @@
+#include "precomp_wasabi_bfc.h"
+
+#include "utf8.h"
+
+// this STILL doesn't work perfectly but at least it decodes what we write out
+// mostly just waiting on our wide character strategy
+
+void COMEXP UTF8_to_ASCII(const char *in, char *out) {
+ unsigned const char *src = (unsigned const char *)in;
+ unsigned char *dst = (unsigned char *)out;
+ *dst = 0;
+ for (; *src; src++) {
+ int c = *src;
+ if ((c & 0x80) == 0) {
+ *dst++ = c;
+ continue;
+ }
+ if ((c & 224) != 192) continue; // fuck you, we only check for single bytes
+ int v = (c & 0x3) << 6;
+ ++src;
+ v |= *src & 0x3f;
+ *dst++ = v;
+ }
+ *dst = 0;
+}
diff --git a/Src/Wasabi/bfc/string/utf8.h b/Src/Wasabi/bfc/string/utf8.h
new file mode 100644
index 00000000..d5847058
--- /dev/null
+++ b/Src/Wasabi/bfc/string/utf8.h
@@ -0,0 +1,8 @@
+#ifndef _UTF8_H
+#define _UTF8_H
+
+#include <bfc/common.h>
+
+void COMEXP UTF8_to_ASCII(const char *in, char *out);
+
+#endif