///////////////////////////////////////////////////////////////////////////////
/// @file deString.cpp
///
/// @brief object-oriented string manipulation class
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Mar 2002
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

//#include <memory.h> // handled by string.h
#include <cstring>
//#include <windows.h>
#include <cstdio>
#include <cstdarg>
#include <cstdlib>

#include "deGlobalTypes.hpp"
#include "deString.hpp"

#ifdef __GNUC__
#include <ctype.h>
#define ultoa _ultoa
#endif

#ifndef NULL
#define NULL (0)
#endif

const char LOCALE_DECIMAL_POINT = '.';

#ifndef __GNUC__
#ifdef _DEBUG
#undef new
#include <crtdbg.h>
#endif
#endif

#ifndef _CRTDBG_MAP_ALLOC
void* deString::operator new (size_t Size)
{
	return malloc(Size);
}

void* deString::operator new (size_t Size, void* buffer)
{
#ifdef _DEBUG
	Size = 0;
#endif
	return buffer;
}

void* deString::operator new[] (size_t Size)
{
	return malloc(Size);
}

void* deString::operator new[] (size_t Size, int type, char * file, int line)
{
#ifndef __GNUC__
#ifdef _DEBUG
	return _malloc_dbg(Size, type, file, line);
#else
	return malloc(Size);
#endif
#else
    return malloc(Size);
#endif
}

void deString::operator delete (void *p)
{
	free(p);
	return;
}

void deString::operator delete (void *p, void* buffer)
{
#ifdef _DEBUG
	p = 0;
	buffer = 0;
#endif
	return;
}

void deString::operator delete[] (void *p)
{
	free(p);
	return;
}

void deString::operator delete[] (void *p, int t, char * , int )
{
#ifndef __GNUC__
#ifdef _DEBUG
	_free_dbg(p, t);
#else
	free(p);
#endif
#else
	free(p);
#endif

}
#endif // _CRTDBG_MAP_ALLOC

deString::deString()
: m_Length(0), m_Alloc(1), m_CaseSensitive(deFALSE)
{
	m_String = new char[1];
	m_String[0] = '\0';
}

deString::deString(const char * rhs)
: m_CaseSensitive(deFALSE)
{
	//get the length of the string
	m_Length = strlen(rhs);
	m_Alloc = m_Length+1;

	//allocate a new string plus a null
	m_String = new char[m_Alloc];

	//copy the string and its null
	memcpy(m_String, rhs, m_Length+1);
}

deString::deString(const deString &rhs )
:	m_CaseSensitive(rhs.m_CaseSensitive), 
	m_Length(rhs.m_Length)
{
	m_String = rhs.Copy();
}

deString::~deString()
{
	delete[] m_String;
#ifdef _DEBUG
	if (m_String)
		m_String = NULL;
	m_Length = -1;
	m_Alloc = 0;
#endif
}

char * deString::Copy() const
{
	//copy the string to a new memory area and return it
	char * Str = new char[m_Length+1];
	if(!Str)
		return NULL;

	//copy the string and its null
	memcpy(Str, m_String, m_Length+1);

	//return
	return Str;
}

static unsigned long SmallestLargerPow2(unsigned long x)
{
	int retval = 1;
	if (x & 0xffff0000)
	{ retval += 16; x >>= 16; }
	if (x & 0x0000ff00)
	{ retval += 8; x >>= 8; }
	if (x & 0x000000f0)
	{ retval += 4; x >>= 4; }
	if (x & 0x0000000c)
	{ retval += 2; x >>= 2; }
	if (x & 0x00000002)
	{ retval += 1; x >>= 1; }
	return (1 << retval);
}

void deString::Resize(long newsize)
{
	if (newsize >= m_Alloc || newsize < m_Alloc/3)
	{
		m_Alloc = SmallestLargerPow2(newsize+1);	// add 1 for the NULL char
		char * newStr = new char[m_Alloc];			// alloc array
		memcpy(newStr, m_String, min(newsize,m_Length)+1);		// copy with null
		delete[] m_String;							// delete the old array
		m_String = newStr;							// assign to new
	}
	m_Length = (int)newsize;	// new length
	m_String[m_Length] = '\0';	// tack a NULL on the end just in case
}

deString deString::operator+(const deString & rhs) const
{
	int newLength = m_Length + rhs.m_Length;
	char * newStr = new char[newLength + 1];

	//combine 2 strings together

	//copy the first string
	memcpy(newStr, m_String, m_Length);

	//append the 2nd string (with the null on the 2nd string)
	memcpy(&newStr[m_Length], rhs.m_String, rhs.m_Length + 1);

	//st the new length and string
	deString ret;
	delete[] ret.m_String;
	ret.m_Length = newLength;
	ret.m_Alloc = newLength+1;
	ret.m_String = newStr;

	//return
	return ret;
}

deString deString::operator+(const char * rhs) const
{
	int strLength = strlen(rhs);
	int newLength = m_Length + strLength;
	char * newStr = new char[newLength + 1];

	//combine 2 strings together

	//copy the first string
	memcpy(newStr, m_String, m_Length);

	//copy the 2nd string with it's null
	memcpy(&newStr[m_Length], rhs, strLength+1);

	//set the length and string
	deString ret;
	delete[] ret.m_String;
	ret.m_Length = newLength;
	ret.m_Alloc = newLength+1;
	ret.m_String = newStr;

	//return
	return ret;
}

const deString& deString::operator+=(const deString & rhs)
{
	int curLength = m_Length;

	//combine 2 strings

	//copy the first
	Resize(m_Length + rhs.m_Length);

	//copy the 2nd with the null
	memcpy(&m_String[curLength], rhs.m_String, rhs.m_Length+1);

	return *this;
}

const deString& deString::operator+=(const char * rhs)
{
	int curLength = m_Length;
	int strLength = strlen(rhs);

	//combine 2 strings

	//copy the first
	Resize(m_Length + strLength);

	//copy the 2nd string with the null
	memcpy(&m_String[curLength], rhs, strLength+1);

	return *this;
}

const deString& deString::operator+=(const char rhs)
{
	// append a single character
	Resize(m_Length+1);			// add a character to the array
	// length was incremented by Resize
	m_String[m_Length] = '\0';	// tack a NULL on the end
	m_String[m_Length-1] = rhs;	// and stick the new char in there

	return *this;
}

const deString & deString::operator=(const deString & rhs)
{
	//copy the string from 1 class to another

	//delete our string
	delete[] m_String;

	//copy it
	m_String = rhs.Copy();

	//set the length and case sensitive setting
	m_Length = rhs.m_Length;
	m_Alloc = m_Length + 1;	// Copy() only gets "just enough" memory
	m_CaseSensitive = rhs.m_CaseSensitive;

	//return
	return *this;
}

const deString & deString::operator=(const char * rhs)
{
	//set the string equal to the string passed in

	//set the length of the passed string to our length
	m_Length = strlen(rhs);
	m_Alloc = m_Length + 1;

	//remove our string
	delete[] m_String;

	//allocate memory for the new string
	m_String = new char[m_Alloc];

	//copy it with the null
	memcpy(m_String, rhs, m_Length+1);

	//return
	return *this;
}

void deString::SetCaseSensitivity(deBoolean Sensitive)
{
	//set the case sensitivity
	m_CaseSensitive = Sensitive;
}

deBoolean deString::operator==(const deString & rhs) const
{
	//see if 2 strings are equal

	//if they arent the same lenght, fail
	if (m_Length != rhs.m_Length)
		return deFALSE;

	//return wether or not they match
	return operator==(rhs.m_String);
}

deBoolean deString::operator==(const char * rhs) const
{
	//return wether or not 2 strings match

	//if no string, fail
	if (rhs == NULL)
		return deFALSE;

	//return wether or not 2 strings match

	//see if a case sensitive check is needed
	if(m_CaseSensitive)
		return (strcmp(m_String, rhs) == 0);
	else
		return (stricmp(m_String, rhs) == 0);
}

deBoolean deString::operator!=(const deString & rhs) const
{
	//see if 2 strings are not equal to each other
	return !(operator==(rhs));
}

deBoolean deString::operator!=(const char * rhs) const
{
	//see if 2 strings are not equal to each other
	return !operator==(rhs);
}

char deString::operator[](long index) const
{
	//return a certain character from the string
	if (index < 0 || index >= m_Length)
	{
		DEBUGRETURN(m_String[0], "operator[]: Invalid index");
	}
	return m_String[index];
}

char& deString::operator[](long index)
{
	//return a certain const char from the string
	if (index < 0 || index >= m_Length)
	{
		DEBUGRETURN(m_String[0], "operator[]: Invalid index");
	}
	return m_String[index];
}

void deString::operator<< (char c)
{
	(*this) += (const char)(c);
}

#define LONG_SIZE (12)
#define INT64_SIZE (22)
#define FLOAT_SIZE (6)
#define DOUBLE_SIZE (20)
void deString::operator<< (long l)
{
	char buffer[LONG_SIZE];	// max size of signed long (with null)
	ltoa(l, buffer, 10);	// long ==> ascii
	(*this) += (const char*)(buffer);	// append
}

void deString::operator<< (unsigned long ul)
{
	char buffer[LONG_SIZE];	// max size of signed long (with null)
	ultoa(ul, buffer, 10);	// unsigned long ==> ascii
	(*this) += (const char*)(buffer);	// append
}

void deString::operator<< (__int64 i)
{
	char buffer[INT64_SIZE];	// max size of signed __int64 (with null)
	_i64toa(i, buffer, 10);		// unsigned long ==> ascii
	(*this) += (const char*)(buffer);	// append
}

void deString::operator<< (unsigned __int64 ui)
{
	char buffer[INT64_SIZE];	// max size of signed __int64 (with null)
	_ui64toa(ui, buffer, 10);	// unsigned long ==> ascii
	(*this) += (const char*)(buffer);	// append
}

void deString::operator<< (float f)
{
	char buffer[FLOAT_SIZE+3];		// max size we want for double (with null)
	gcvt(f, FLOAT_SIZE, buffer);	// convert to ascii
	(*this) += (const char*)(buffer);	// append
}

void deString::operator<< (double d)
{
	char buffer[DOUBLE_SIZE+3];		// max size we want for double (with null)
	gcvt(d, DOUBLE_SIZE, buffer);	// convert to ascii
	(*this) += (const char*)(buffer);	// append
}

void deString::operator<< (const char * str)
{
	(*this) += str;	// append
}

void deString::operator<< (const deString& str)
{
	(*this) += str;	// append
}

static inline bool isnum(char c)
{
	return (c >= '0' && c <= '9');
}

static inline char toupper(char c)
{
	return (c >= 'a' && c <= 'z') ? (char)(c-('a'-'A')) : c;
}

int deString::Format(const char * format, ...)
{
#pragma todo (test and debug this some more with formatting)
	// variable args
	va_list arg;
	// counters
	int cnum, i, j, k, printed;
	int len;
	bool valid;
	// field entries
	char flag;
	int width;
	int precision;
	char size;
	char type;
	bool haswidth, hasprecision;
	enum
	{
		TypeChar,
		TypeInt,
		TypeFloatE,
		TypeFloatF,		
		TypeString,		
	} DataType = TypeChar;
	// temp buffers
	char * pch;
	char tempbuf2[32];
	char tempbuffer[512] = {0};

	va_start(arg, format);

	//reset string
	len = strlen(format);
	delete[] m_String;
	m_Length = 0;
	m_Alloc = len;
	m_String = new char[m_Alloc];
	m_String[0] = '\0';

	printed = 0;

	for (cnum = 0; cnum < len; ++cnum)
	{
		i = 0;
		while (format[cnum] != '%' && cnum < len && i < 255)
		{
			tempbuffer[i] = format[cnum];
			++i;
			++cnum;
		}
		if (cnum >= len)
		{
			tempbuffer[i] = '\0';
			(*this) << tempbuffer;
			break;
		}
		if (i >= 255)
		{
			i = 255;
			tempbuffer[i] = '\0';
			printed += i;
			((*this) += tempbuffer); // kinda hackish, but saves a strlen()
			continue;
		}
		if (i > 0)
		{
			tempbuffer[i] = '\0';
			printed += i;
			((*this) += tempbuffer); // kinda hackish, but saves a strlen()
			// drop through and process the % symbol
		}

		// we hit a % symbol, process it
		i = 0;
		++cnum; // ditch the %
		if (cnum < len && format[cnum] == '%')
		{
			cnum++;
			(*this) += '%';
			printed++;
			continue;
		}
		while (cnum < len && i < 256
				&& format[cnum] != 'd' && format[cnum] != 'i'
				&& format[cnum] != 's'
				&& format[cnum] != 'c' && format[cnum] != 'x'
				&& format[cnum] != 'X' && format[cnum] != 'g'
				&& format[cnum] != 'e' && format[cnum] != 'f'
				&& format[cnum] != 'E' && format[cnum] != 'G'
				&& format[cnum] != 'p' && format[cnum] != 'o'
				&& format[cnum] != 'u' && format[cnum] != 'n'
				&& format[cnum] != 'S' && format[cnum] != 'C')
		{
			// gobble up characters until we get a type specifier or run out
			tempbuffer[i] = format[cnum];
			++cnum;
			++i;
		}
		// if we ran out, break out of the loop, we're done
		if (cnum >= len)
			break;
		// if our temp buffer would be overflowed... bail out
		if (i >= 256)
			break;
		type = format[cnum];
		for (j = 0; j < i; ++j)
		{
			if (tempbuffer[j] == '.')
				break;
		}
		if (tempbuffer[0] == '-' || tempbuffer[0] == '+' || tempbuffer[0] == '0'
			|| tempbuffer[0] == ' ' || tempbuffer[0] == '#')
		{
			// we have a valid flag
			flag = tempbuffer[0];
		}
		else
			flag = 0;
		
		if (i >= 1 && (tempbuffer[i-1] == 'h' || tempbuffer[i-1] == 'l' || tempbuffer[i-1] == 'L'))
		{
			size = tempbuffer[i-1];
			--i;
		}
		else if(i >= 3 && (tempbuffer[i-1] == '4' && tempbuffer[i-2] == '6' && tempbuffer[i-3] == 'I'))
		{
			size = 'I';
			i -= 3;
		}
		else
		{
			size = 0;
		}

		haswidth = hasprecision = false;
		width = precision = 0;
		if (j < i || (flag && i >= 2) || (!flag && i >= 1))
		{
			// we have a dot (grab width)
			// or we don't have a dot, but have digits to read
			if ((flag && j-1 >0) || (!flag && j > 0)) // account for the flag
			{
				if (flag)
				{
					k = min(j-1,31);
					memcpy(tempbuf2, tempbuffer+1, k);
				}
				else
				{
					k = min(j,31);
					memcpy(tempbuf2, tempbuffer, k);
				}
				tempbuf2[k] = '\0';
				
				valid = true;
				for (k = 0; k < 32 && tempbuf2[k] != '\0'; ++k)
				{
					if (!isnum(tempbuf2[k]))
					{
						// no minus signs, no letters... just numbers
						valid = false;
						break;
					}
				}
				if (valid)
				{
					width = atoi(tempbuf2);
					haswidth = true;
				}
			}
		}
		if (j < i)
		{
			// we have a dot, grab precision
			++j; // account for the dot
			if (j < i)
			{
				k = min(i-j,31);
				memcpy(tempbuf2, tempbuffer+j, k);
				tempbuf2[k] = '\0';
				valid = true;
				for (k = 0; k < 32 && tempbuf2[k] != '\0'; ++k)
				{
					if (!isnum(tempbuf2[k]))
					{
						// no minus signs, no letters... just numbers
						valid = false;
						break;
					}
				}
				if (valid)
				{
					precision = atoi(tempbuf2);
					hasprecision = true;
				}
			}
			else
			{
				precision = 0;
				hasprecision = true;
			}
		}

		// tempbuffer contents can be overwritten in switch statement now
		valid = true;
		switch (type)
		{
		default:
			valid = false;
			break;
		// c = single char
		case 'c':
			tempbuffer[0] = (char)va_arg(arg, int);
			tempbuffer[1] = '\0';
			DataType = TypeChar;
			break;
		// C = wide char
		case 'C':
			tempbuffer[0] = (char)va_arg(arg, int); //wint_t
			tempbuffer[1] = '\0';
			DataType = TypeChar;
			break;
		// d,i = signed int (decimal)
		case 'd':
		case 'i':
			DataType = TypeInt;
			{
				__int64 i64;
				if (!size)
				{
					i64 = va_arg(arg, int);
				}
				else if (size == 'h')
				{
					i64 = va_arg(arg, int); // short int
				}
				else if (size == 'l')
				{
					i64 = va_arg(arg, int); // long int
				}
				else if (size == 'I')
				{
					i64 = va_arg(arg, __int64);
				}
				else
				{
					i64 = va_arg(arg, int);
				}
				if (flag == '+')
				{
					tempbuffer[0] = (i64 >= 0) ? '+' : '-';
					pch = tempbuffer+1;
				}
				else
					pch = tempbuffer;

				_i64toa(i64, pch, 10); // decimal radix = 10
			}
			break;
		// o = unsigned int (octal)
		case 'o':
		// u = unsigned int (decimal)
		case 'u':
		// x = unsigned int (hex, [0x]abcdef)
		case 'x':
		// X = unsigned int (hex, [0X]ABCDEF)
		case 'X':
			DataType = TypeInt;
			{
				__int64 i64;
				if (!size)
				{
					i64 = va_arg(arg, unsigned int);
				}
				else if (size == 'h')
				{
					i64 = va_arg(arg, unsigned int); // unsigned short int
				}
				else if (size == 'l')
				{
					i64 = va_arg(arg, unsigned int); // unsigned long int
				}
				else if (size == 'I')
				{
					i64 = va_arg(arg, unsigned __int64);
				}
				else
				{
					i64 = va_arg(arg, unsigned int);
				}
				
				pch = tempbuffer;
				if (flag == '#')
				{
					if (type == 'o')
					{
						tempbuffer[0] = '0';
						pch++;
					}
					else if (type == 'x' || type == 'X')
					{
						tempbuffer[0] = '0';
						tempbuffer[1] = 'x';
						pch+=2;
					}
				}

				if (type == 'u')
					_ui64toa(i64, pch, 10); // decimal radix = 10
				else if (type == 'x' || type == 'X')
					_ui64toa(i64, pch, 16); // hexadecimal radix = 16
				else if (type == 'x' || type == 'o')
					_ui64toa(i64, pch, 8); // octal radix = 8
				if (type == 'X')
				{
					k = strlen(tempbuffer);
					if (type == 'X')
					{
						for (i = 0; i < k; ++i)
							tempbuffer[i] = toupper(tempbuffer[i]);
					}
				}
			}
			break;
		// e = double of format [-]d.dddd e{+-}ddd
		case 'e':
		// E = double of format [-]d.dddd E{+-}ddd
		case 'E':
			DataType = TypeFloatE;
			{
				int exponent;
				int negative;
				double value;
				
				value = va_arg(arg, double);
				if (!hasprecision)
					precision = 6; // default precision value
				if (precision > 30) // not really worth much past there
					break;
#if 1 // switch to 0 to use ecvt()
				double value2;
				negative = (value < 0);
				if (negative)
					value *= -1;
				exponent = 0;

				if (value > 10)
				{
					while (value > 10)
					{
						value /= 10;
						exponent++;
					}
				}
				else if (value < 1 && value != 0)
				{
					while (value < 1)
					{
						value *= 10;
						exponent--;
					}
				}

				pch = tempbuf2;
				for (i = 0; i < precision+1; ++i)
				{
					if (i == precision)
						value += 0.5;
					value2 = int(value+(1.0/256));
					value2 = (value2/10);
					value2 -= int((value+(1.0/256))/10);
					value2 *= 10;
					pch[i] = (char)(value2 + '0');
					value *= 10;
				}
				
#else
#pragma todo (possible mutex needed on the ecvt return value?)
				pch = ecvt(value, precision+1, &exponent, &negative);
				exponent--;
#endif
				// print the digits
				if (negative || flag == '+')
				{
					tempbuffer[0] = ((negative) ? '-' : '+');
					i = 1;
					if (precision > 0)
					{
						// stick in the decimal point
						tempbuffer[1] = pch[0];
						tempbuffer[2] = LOCALE_DECIMAL_POINT;
						pch++;
						i = 3;
					}
				}
				else
				{
					i = 0;
					if (precision > 0)
					{
						// stick in the decimal point
						tempbuffer[0] = pch[0];
						tempbuffer[1] = LOCALE_DECIMAL_POINT;
						pch++;
						i = 2;
					}
					else
					{
						precision++;
					}
				}
				strncpy(tempbuffer+i, pch, precision);
				pch = tempbuffer+i;

				// go to the end to print the exponent
				pch += precision;
				*pch = type; // 'e' or 'E'
				pch++;
				if (exponent < 0)
				{
					*pch = '-';
					exponent = -exponent;
				}
				else
				{
					*pch = '+';
				}
				pch++;
				if (exponent < 10)
				{
					*pch = '0';
					*(pch+1) = '0';
					pch += 2;
				}
				else if (exponent < 100)
				{
					*pch = '0';
					pch++;
				}
				itoa(exponent, pch, 10);
			}
			break;
		// f = double of format [-]dddd.dddd
		case 'f':
			DataType = TypeFloatF;
			{
				int len;
				int exponent;
				int negative;
				double value;
				double value2;

				value = va_arg(arg, double);
				if (!hasprecision)
					precision = 6; // default precision value
				if (precision > 30) // not really worth much past there
					break;
				negative = (value < 0);
				if (negative)
					value *= -1;
				exponent = 0;
				
				if (value > 10)
				{
					while (value > 10)
					{
						value /= 10;
						exponent++;
					}
				}
				else if (value < 1 && value != 0)
				{
					while (value < 1)
					{
						value *= 10;
						exponent--;
					}
				}
				
				pch = tempbuffer;
				if (negative || flag == '+')
				{
					*pch = (negative) ? '-' : '+';
					pch++;
				}

				// print the digits
				if (exponent <= 0)
				{
					len = precision+exponent;
					for (i = 0; i < len; ++i)
					{
						pch[i] = '0';
					}
					pch += len;
				}
				len = max(1,exponent+precision+1);
				for (i = 0; i < len; ++i)
				{
					if (i == len-1)
						value += 0.5;
					value2 = int(value+(1.0/256));
					value2 = (value2/10);
					value2 -= int((value+(1.0/256))/10);
					value2 *= 10;
					pch[i] = (char)(value2 + '0');
					value *= 10;
				}
				pch[i] = '\0';
				if (exponent <= 0)
					pch -= precision+exponent;
				
				// move the decimal digits over
				if (precision > 0)
				{
//#pragma todo (check the behavior of an overlapping memcpy) // memmove handles overlap
					len = max(exponent+1, 1);
					memmove(pch+len+1, pch+len, precision);
					// and stick in the decimal point
					pch[len] = LOCALE_DECIMAL_POINT;
					pch[len+precision+1] = '\0';
				}
			}
			break;
		// g = e or f, depending which is more compact
		case 'g':
		// G = E or f, depending which is more compact
		case 'G':
			{
				int len;
				int exponent;
				int negative;
				double value;
				double value2;
				
				value = va_arg(arg, double);
				if (!hasprecision)
					precision = 6; // default precision value
				if (precision > 30) // not really worth much past there
					break;
				negative = (value < 0);
				if (negative)
					value *= -1;
				exponent = 0;
				
				if (value > 10)
				{
					while (value > 10)
					{
						value /= 10;
						exponent++;
					}
				}
				else if (value < 1 && value != 0)
				{
					while (value < 1)
					{
						value *= 10;
						exponent--;
					}
				}
				
				pch = tempbuffer;
				if (negative || flag == '+')
				{
					*pch = (negative) ? '-' : '+';
					pch++;
				}
				
				if (exponent >= -4 && exponent < precision)
				{
					// f format
					DataType = TypeFloatF;

					// print the digits
					if (exponent <= 0)
					{
						len = precision+exponent;
						for (i = 0; i < len; ++i)
						{
							pch[i] = '0';
						}
						pch += len;
					}
					if (exponent < 0)
						len = max(1,precision+exponent); // already printed '-exponent' digits
					else
						len = max(1,precision);
					for (i = 0; i < len; ++i)
					{
						if (i == len-1)
							value += 0.5;
						value2 = int(value+(1.0/256));
						value2 = (value2/10);
						value2 -= int((value+(1.0/256))/10);
						value2 *= 10;
						pch[i] = (char)(value2 + '0');
						value *= 10;
					}
					pch[i] = '\0';
					if (exponent <= 0)
						pch -= precision+exponent;
					
					// move the decimal digits over
					if (precision > 0 && exponent+1 < precision)
					{
//#pragma todo (check the behavior of an overlapping memcpy) // memmove handles overlap
						len = max(exponent+1, 1);
						memmove(pch+len+1, pch+len, precision);
						// and stick in the decimal point
						pch[len] = LOCALE_DECIMAL_POINT;
						pch[len+precision+1] = '\0';
					}
				}
				else
				{
					// e format
					DataType = TypeFloatE;

					int firstzero = 0; // trailing zeroes are truncated

					for (i = 0; i < precision; ++i)
					{
						if (i == 1)
						{
							pch[1] = LOCALE_DECIMAL_POINT;
							i++;
							precision++;
						}
						if (i == precision-1)
							value += 0.5;
						value2 = int(value);
						value2 = (value2/10);
						value2 -= int((value)/10);
						value2 *= 10;
						value2 += 1.0/256;
						pch[i] = (char)(value2 + '0');
						if (pch[i] == '0')
						{
							if (firstzero == 0)
								firstzero = i;
							// else it's already set
						}
						else
							firstzero = 0;
						value *= 10;
					}
					// go to the end to print the exponent
					if (firstzero > 1)
						pch += firstzero+1;
					else
						pch += precision;
					*pch = (type == 'g') ? 'e' : 'E';
					pch++;
					if (exponent < 0)
					{
						*pch = '-';
						exponent = -exponent;
					}
					else
					{
						*pch = '+';
					}
					pch++;
					if (exponent < 10)
					{
						*pch = '0';
						*(pch+1) = '0';
						pch += 2;
					}
					else if (exponent < 100)
					{
						*pch = '0';
						pch++;
					}
					itoa(exponent, pch, 10);
					// for data handling, this has "one less precision" than real E format
					precision--;
				}
			}
			break;
		// s = string (single char)
		case 's':
			DataType = TypeString;
			pch = va_arg(arg, char*);
			if (pch == NULL)
			{
				static char* NullString = "<null>";
				pch = NullString;
			}
			i = strlen(pch);
			if (!hasprecision || i <= precision)
			{
				// got some spare room left over
				printed += i;
				if (haswidth && flag != '-')
				{
					for (; i < width; ++i)
						(*this) += ' ';
				}
				(*this) += pch;
				if (haswidth && flag == '-')
				{
					for (; i < width; ++i)
						(*this) += ' ';
				}
			}
			else
			{
				k = m_Length;
				if (haswidth)
					width = max(width, precision);
				else
					width = precision;
				Resize(m_Length + width);
				if (haswidth && flag != '-')
				{
					memset(&m_String[k], ' ', width-precision);
					k += width - precision;
				}
				memcpy(&m_String[k], pch, precision);
				if (haswidth && flag == '-')
				{
					memset(&m_String[k+precision], ' ', width-precision);
				}
				m_String[k+width] = '\0';
			}
			break;
		// S = string (wide char)
		case 'S':
			DataType = TypeString;
			pch = va_arg(arg, char*);
			break;
		// n = int pointer for storing # of chars printed in
		case 'n':
			{
				va_arg(arg, int*);
				// not implementing %n
				valid = false;
			}
			break;
		// p = void pointer in format XXXX:YYYY
		case 'p':
			{
				DWORD Value;
				WORD Low, High;
				Value = va_arg(arg, DWORD);
				Low = WORD(Value);
				High= WORD((DWORD)Value >> 16);
				memcpy(tempbuffer, "0000:0000", 9);
				if (High < 0x10)
					k = 3;
				else if (High < 0x100)
					k = 2;
				else if (High < 0x1000)
					k = 1;
				else
					k = 0;
				ultoa(High, tempbuffer+k, 16);
				tempbuffer[4] = ':';
				if (Low < 0x10)
					k = 3;
				else if (Low < 0x100)
					k = 2;
				else if (Low < 0x1000)
					k = 1;
				else
					k = 0;
				ultoa(Low, tempbuffer+k+5, 16);
				printed += 9;
				((*this) += tempbuffer); // kinda hackish, but saves a strlen()
			}
			break;
		}

		// don't print anything if the last thing wasn't valid
		if (!valid)
			continue;

//#pragma todo(implement flag conformancy) // done
		switch (DataType)
		{
		default:
			break;
		case TypeChar:
			break;
		case TypeInt:
			k = strlen(tempbuffer);
			if (haswidth || hasprecision)
			{
				if (hasprecision)
					precision = max(k, precision);
				else
					precision = k;
				if (haswidth)
					width = max(width, precision);
				else
					width = precision;
				for (i = width; i > precision; --i)
				{
					if (flag != '-')
						(*this) << ' ';
					++printed;
				}
				for (; i > k; --i)
				{
					(*this) << '0';
					++printed;
				}
			}
			(*this) << tempbuffer;
			printed += k;
			if (haswidth && flag == '-')
			{
				for (i = width; i > precision; --i)
				{
					(*this) << ' ';
				}
			}
			break;
		case TypeFloatE:
			k = strlen(tempbuffer);
			precision += 6;
			if (haswidth)
			{
				width = max(width, precision);
				for (i = width; i > precision; --i)
				{
					if (flag != '-')
						(*this) << ' ';
					++printed;
				}
				for (; i > k; --i)
				{
					(*this) << '0';
					++printed;
				}
			}
			(*this) << tempbuffer;
			printed += k;
			if (flag == '-')
			{
				for (i = width; i > precision; --i)
				{
					(*this) << ' ';
				}
			}
			break;
		case TypeFloatF:
			k = strlen(tempbuffer);
			precision += 6;
			if (haswidth)
			{
				width = max(width, precision);
				for (i = width; i > precision; --i)
				{
					if (flag != '-')
						(*this) << ' ';
					++printed;
				}
				for (; i > k; --i)
				{
					(*this) << '0';
					++printed;
				}
			}
			(*this) << tempbuffer;
			printed += k;
			if (flag == '-')
			{
				for (i = width; i > precision; --i)
				{
					(*this) << ' ';
				}
			}
			break;
		case TypeString:
			break;
		}
	}

	va_end(arg);
	return printed;
}

void deString::Split(char Splitter, deTArray <deString> &ResultStrings)
{
	int count;
	char *	CharPos;
	char *	OldCharPos;

	//loop thru, counting up each find for the number of entries
	CharPos = m_String;
	count = 0;
	do
	{
		//get the next occurence of the splitter
		CharPos = strchr(CharPos, (int)Splitter);
		CharPos++;
		
		//count an entry
		count++;
	} while(CharPos-1 != NULL); // strchr() returns NULL if not found

	//resize the array for the number of strings we found
	//note, the loop above is forced to go thru once so we
	//have at least 1 string
	ResultStrings.Resize(count);

	//now, split the string up
	CharPos = m_String;
	count = 0;
	do
	{
		//set the old position to the current position
		OldCharPos = CharPos;

		//find the next occurrence
		CharPos = strchr(OldCharPos, (int)Splitter);

		//copy the string to the array

		//first, set the char to null so the string class knows where to stop
		if(CharPos)
			CharPos[0] = '\0';

		//now, copy the string
		ResultStrings[count] = OldCharPos;

		//restore the splitting char
		if(CharPos)
			CharPos[0] = Splitter;

		//increment the counter
		count++;
		CharPos++;
	} while(CharPos-1 != NULL);
}

int deString::Find(char ToFind, deBoolean FindLast)
{
	char* ch;
	if (FindLast)
		ch = strrchr(m_String, ToFind);
	else
	{
		char temp[2] = {ToFind, '\0'};
		ch = strstr(m_String, temp);
	}
	if (ch)
		return ch - m_String;
	return -1;
}

void deString::Replace(char Find, char Replace)
{
	int k;
	for (k = 0; k < m_Length; ++k)
	{
		if (m_String[k] == Find)
			m_String[k] = Replace;
	}
}

static inline void Swap(char & c1, char & c2) { char temp = c1; c1 = c2; c2 = temp;}

deBoolean deString::Strip(const deString &Stripped, deString &Result)
{
	int i, j, k, length;
	length = Stripped.Length();
	if (length <= 0)
		return deTRUE;

	// make a new string to hold
	char * newStr = new char[m_Length+1];
	char * StripString = Stripped.m_String;
	if (newStr == NULL || StripString == NULL)
		DEBUGRETURN(deFALSE, "Strip: Failed to create string memory");
	k = 0;
	bool Cut;
	for (i = 0; i < m_Length; ++i)
	{
		Cut = false;
		for (j = 0; j < length; ++j)
		{
			if (m_String[i] == StripString[j])
			{
				Cut = true;
				break;
			}
		}
		if (!Cut)
		{
			newStr[k] = m_String[i];
			++k;
		}
	}
	newStr[k] = '\0';
	delete[] Result.m_String;
	Result.m_Length = k;
	Result.m_Alloc = k+1;
	Result.m_String = new char[k+1];
	memcpy(Result.m_String, newStr, k+1);
	delete[] newStr;

	return deTRUE;
}

deString deString::SubString(int start, int stop)
{
	deString newstr;
	DE_ASSERT(start >= 0 || stop < m_Length);
	int size = stop - start;
	if (size <= 0)
		return newstr;

	delete[] newstr.m_String;
	newstr.m_Length = size;
	newstr.m_String = new char[size+1];
	memcpy(newstr.m_String, &(m_String[start]), size);
	newstr.m_String[size] = '\0';

	return newstr;
}

char * deString::Reserve(int Length)
{
	//allocate or extend the memory for the string

	//if they want a size smaller than the current, leave it (waste of memory?)
	if (Length <= m_Length)
		return m_String;

	Resize(Length);

	/*	
	char * tempStr;

	//allocate new memory for the string
	tempStr = new char[Length+1];
	if (!tempStr)
		return deFALSE;

	//copy the string and it's null
	memcpy(tempStr, m_String, m_Length+1);

	//remove the original string
	delete[] m_String;

	//set the new params
	m_String = tempStr;
	m_Length = Length;
	*/ 

	//return the string
	return m_String;
}

deBoolean deString::Trim(int Length)
{
	//trim the string down to a certain length

	//if they want something larger than the current, fail
	if (Length > m_Length)
		return deFALSE;

	//if the length is -1, then set the length to the length of the string
	if (Length == -1)
		Length = strlen(m_String);

	Resize(Length);

	/*
	//if the current length is 2x or more larger than the new length, resize it
	if ((m_Length /2) > Length)
	{
		char * tempStr;

		//allocate memory for the string
		tempStr = new char[Length+1];
		if (!tempStr)
			return deFALSE;

		//copy the string and it's null
		memcpy(tempStr, m_String, Length + 1);

		//remove the old string
		delete[] m_String;

		//set the new string
		m_String = tempStr;
	}
	else
	{
		//move the null for the string
		m_String[Length] = '\0';
	}

	//set the length and return
	m_Length = Length;
	*/
	
	return deTRUE;
}













































#include <cstring>

#ifdef _UNICODE
#include <cwchar>
#define strlen wcslen
#define strcmp wcscmp
#define strcmpi wcsicmp
#define toupper towupper
#endif


//=======================================================
// deString2
//=======================================================

inline deString2::StringData::StringData(const deString_char* str)
:	mRefs(1)
{
	if (str)
		mLength = strlen(str);
	else
		mLength = 0;
	mAlloc = SmallestLargerPow2(mLength+1);
	mString = new deString_char[mAlloc];
	memcpy(mString, str, mLength*sizeof(deString_char));
	mString[mLength] = 0;
}

inline deString2::StringData::StringData(const StringData & ref)
:	mRefs(1), 
	mLength(ref.mLength), 
	mAlloc(ref.mAlloc)
{
	mString = new deString_char[mAlloc];
	memcpy(mString, ref.mString, mLength*sizeof(deString_char));
	mString[mLength] = 0;
}

inline deString2::StringData::StringData(long max_len)
:	mRefs(1), 
	mLength(0)
{
	mAlloc = SmallestLargerPow2(max_len+1);
	mString = new deString_char[mAlloc];
}

inline deString2::StringData* deString2::StringData::getModString()
{
	if (mRefs == 1)
		return this;
	return new StringData(*this);
}

deString2::deString2()
:	mCaseSensitive(deTRUE)
{
	mData = new StringData;
}

deString2::deString2(const deString_char * str)
:	mCaseSensitive(deTRUE)
{
	mData = new StringData(str);
}

deString2::deString2(const deString2 &ref)
:	mCaseSensitive(ref.mCaseSensitive)
{
	ref.mData->Claim();
	mData = ref.mData;
}

deString2::deString2(StringData* ref)
:	mCaseSensitive(deTRUE),
	mData(ref)
{
}

deString2::~deString2()
{
	mData->Release();
}

void deString2::OwnBuffer()
{
	if (mData->mRefs > 1)
	{
		mData->Release();
		mData = new StringData(*mData);
	}
}

void deString2::resizeData(long newlen) // non-const
{
	// grab or create a stringdata that we're allowed to modify
	// (can only modify when mRefs = 1)
	if (mData->mRefs > 1 || mData->mAlloc <= newlen)
	{
		// we need a new stringdata - our existing one had other claims or wasn't big enough
		StringData* toResize = new StringData(newlen);
		// copy old data
		long movesize = min(mData->mLength, newlen);
		memcpy(toResize->mString, mData->mString, movesize*sizeof(deString_char));
		toResize->mString[movesize] = 0;
		mData->Release(); // release old data
		mData = toResize; // refs = 1
	}
	mData->mLength = newlen;
	mData->mString[newlen] = 0;
}

void deString2::reserve(long max_size) // non-const
{
	long myLength = mData->mLength;
	resizeData(max_size);
	mData->mLength = myLength;
	mData->mString[myLength] = 0;
}

void deString2::resize(long size)
{
	resizeData(size);
}

deString2::deString_char* deString2::c_str() // non-const
{
	// can't rely on return value staying constant, so we need to make a copy
	OwnBuffer();
	return mData->mString;
}

deString2::deString_char deString2::get_at(long index) const
{
	DE_ASSERT(index >= 0 && index < mData->mLength);
	return mData->mString[index];
}

void deString2::set_at(long index, deString_char ch) // non-const
{
	DE_ASSERT(index >= 0 && index < mData->mLength);
	OwnBuffer();
	mData->mString[index] = ch;
}

deString2 deString2::operator+(const deString2 & rhs) const
{
	StringData * newdata = new StringData(mData->mLength + rhs.mData->mLength);
	memcpy(newdata->mString,
		mData->mString,
		mData->mLength*sizeof(deString_char));
	memcpy(newdata->mString + mData->mLength,
		rhs.mData->mString,
		rhs.mData->mLength*sizeof(deString_char));
	newdata->mLength = mData->mLength + rhs.mData->mLength;
	newdata->mString[newdata->mLength] = 0;
	return deString2(newdata);
}

void deString2::operator+=(const deString2 & rhs) // non-const
{
	long myLength = mData->mLength;
	resizeData(myLength + rhs.mData->mLength);
	memcpy(mData->mString + myLength, rhs.mData->mString, rhs.mData->mLength*sizeof(deString_char));
	mData->mString[mData->mLength] = 0;
}

void deString2::operator+=(const deString_char * rhs)
{
	long myLength = mData->mLength;
	long rhslen = strlen(rhs);
	resizeData(myLength + rhslen);
	memcpy(mData->mString + myLength, rhs, rhslen*sizeof(deString_char));
	mData->mString[mData->mLength] = 0;
}

void deString2::operator=(const deString2 & rhs) // non-const
{
	rhs.mData->Claim();
	mData->Release();
	mData = rhs.mData;
}

bool deString2::operator==(const deString2 & rhs) const
{
	if (mData->mLength != rhs.mData->mLength)
		return deFALSE;
	// now lengths equal
	if (mData->mLength == 0)
		return deTRUE;
	// now lengths > 0
	if (mCaseSensitive)
		return (strcmp(mData->mString, rhs.mData->mString) == 0)? true : false;
	else
		return (strcmpi(mData->mString, rhs.mData->mString) == 0)? true : false;
}

bool deString2::operator!=(const deString2 & rhs) const
{
	if (mData->mLength != rhs.mData->mLength)
		return deTRUE;
	// now lengths equal
	if (mData->mLength == 0)
		return deFALSE;
	// now lengths > 0
	if (mCaseSensitive)
		return (strcmp(mData->mString, rhs.mData->mString) == 0)? false : true;
	else
		return (strcmpi(mData->mString, rhs.mData->mString) == 0)? false : true;
}

bool deString2::operator==(const deString_char * rhs) const
{
	long len = strlen(rhs);
	if (mData->mLength != len)
		return deFALSE;
	// now lengths equal
	if (mData->mLength == 0)
		return deTRUE;
	// now lengths > 0
	if (mCaseSensitive)
		return (strcmp(mData->mString, rhs) == 0)? true : false;
	else
		return (strcmpi(mData->mString, rhs) == 0)? true : false;
}

bool deString2::operator!=(const deString_char * rhs) const
{
	long len = strlen(rhs);
	if (mData->mLength != len)
		return deTRUE;
	// now lengths equal
	if (mData->mLength == 0)
		return deFALSE;
	// now lengths > 0
	if (mCaseSensitive)
		return (strcmp(mData->mString, rhs) == 0)? false : true;
	else
		return (strcmpi(mData->mString, rhs) == 0)? false : true;
}

bool deString2::operator<(const deString2 & rhs) const
{
	if (mCaseSensitive)
		return (strcmp(mData->mString, rhs.mData->mString) < 0)? true : false;
	else
		return (strcmpi(mData->mString, rhs.mData->mString) < 0)? true : false;
}

bool deString2::operator<(const deString_char * rhs) const
{
	if (mCaseSensitive)
		return (strcmp(mData->mString, rhs) < 0)? true : false;
	else
		return (strcmpi(mData->mString, rhs) < 0)? true : false;
}

void deString2::Replace(deString_char find, deString_char replace) // non-const
{
	OwnBuffer();
	long index;
	if (mCaseSensitive)
	{
		for (index = 0; index < mData->mLength; ++index)
		{
			if (find == mData->mString[index])
				mData->mString[index] = replace;
		}
	}
	else
	{
		find = toupper(find);
		for (index = 0; index < mData->mLength; ++index)
		{
			if (find == toupper(mData->mString[index]))
				mData->mString[index] = replace;
		}
	}
}

long deString2::Find(deString_char find, long start_at, string_flag search_order) const
{
	long index;
	if (search_order == search_forwards)
	{
		index = start_at;
		if (mCaseSensitive)
		{
			for (; index < mData->mLength; ++index)
			{
				if (find == mData->mString[index])
					return index;
			}
		}
		else
		{
			find = toupper(find);
			for (; index < mData->mLength; ++index)
			{
				if (find == toupper(mData->mString[index]))
					return index;
			}
		}
	}
	else if (search_order == search_backwards)
	{
		index = mData->mLength - start_at - 1;
		if (mCaseSensitive)
		{
			for (; index >= 0; --index)
			{
				if (find == mData->mString[index])
					return index;
			}
		}
		else
		{
			find = toupper(find);
			for (; index >= 0; --index)
			{
				if (find == toupper(mData->mString[index]))
					return index;
			}
		}
	}
	return -1;
}

void deString2::Trim(long newsize) // non-const
{
	if (newsize >= mData->mLength)
		return;
	resizeData(newsize);
}

deString2 deString2::DropChars(const deString2 &Chars) const
{
	int numdrop;
	numdrop = Chars.size();
	if (numdrop <= 0)
		return *this;
	StringData * newStr = new StringData(mData->mLength);
	const deString_char * StripString = Chars.const_str();
	const deString_char * myString = mData->mString;
	long i, j, k, myLength;
	k = 0;
	myLength = mData->mLength;
	for (i = 0; i < myLength; ++i)
	{
		bool Cut = false;
		for (j = 0; j < numdrop; ++j)
		{
			if (myString[i] == StripString[j])
			{
				Cut = true;
				break;
			}
		}
		if (!Cut)
		{
			newStr->mString[k] = myString[i];
			++k;
		}
	}
	newStr->mString[k] = 0;
	newStr->mLength = k;
	return deString2(newStr);
}

// half-closed range: [start,stop)
deString2 deString2::SubString(int start, int stop) const
{
	long size = stop - start;
	if (size < 0 || start < 0 || stop > mData->mLength)
		DEBUGRETURN(deString2(""), "deString2::SubString - invalid substring index values");
	if (size == 0)
		return deString2("");
	StringData * newdata = new StringData(size);

	newdata->mLength = size;
	memcpy(newdata->mString, mData->mString + start, size*sizeof(deString_char));
	newdata->mString[size] = 0;
	return deString2(newdata);
}

void deString2::ToLower() // non-const
{
	OwnBuffer();
	int i;
	for (i = 0; i < mData->mLength; ++i)
	{
		mData->mString[i] = (deString_char)tolower(mData->mString[i]);
	}
}

void deString2::ToUpper() // non-const
{
	OwnBuffer();
	int i;
	for (i = 0; i < mData->mLength; ++i)
	{
		mData->mString[i] = (deString_char)toupper(mData->mString[i]);
	}
}

static unsigned long
hash(char* str_in)
{
	unsigned char* str = (unsigned char*)str_in;
	unsigned long hash = 5381;
	int c;
	
	for (c = *str; c; c = *str++)
	{
		hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
	}
	
	return hash;
}

u32 deString2::Hash() const
{
	return hash(mData->mString);
}

