/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /****************************************************************************** * * FILE * $Archive: /Commando/Code/Launcher/Toolkit/Support/UString.cpp $ * * DESCRIPTION * String management class (Unicode) * * PROGRAMMER * Denzil E. Long, Jr. * $Author: Denzil_l $ * * VERSION INFO * $Modtime: 8/18/00 10:39a $ * $Revision: 1 $ * ******************************************************************************/ #include "VisualC.h" #include "UString.h" #include "StringConvert.h" #include #include #include // Convert character to lowercase template T CharToLower(const T ch) { if ((ch >= (T)'A') && (ch <= (T)'Z')) { return (ch + 32); } return ch; } // Convert character to uppercase template T CharToUpper(const T ch) { if ((ch >= (T)'a') && (ch <= (T)'z')) { return (ch - 32); } return ch; } // Check if character is one of the specified characters templatebool IsCharacter(WChar ch, const T* oneOf) { assert(oneOf != NULL); int length = 0; while (oneOf[length] != 0) { length++; } for (int index = 0; index < length; index++) { if (ch == (WChar)oneOf[index]) { return true; } } return false; } // Strip all left side characters that are trim chars template bool StripLeft(WChar* string, const T* trimChars) { // Strip leading trim characters from the string. WChar* start = string; while ((*start != 0) && IsCharacter(*start, trimChars)) { start++; } if (start != string) { wcscpy(string, start); } return true; } // Strip all right side characters that are trim chars template bool StripRight(WChar* string, const T* trimChars) { int length = wcslen(string) - 1; int index = length; while (index >= 0) { if (!IsCharacter(string[index], trimChars)) { break; } string[index] = 0; index--; } return (index != length); } /****************************************************************************** * * NAME * UString::UString - Default constructor * * DESCRIPTION * Create a new empty UString. * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ UString::UString() : mData(NULL), mCapacity(0) { } /****************************************************************************** * * NAME * UString::UString - Capacity contructor * * DESCRIPTION * Create a new empty UString with the specified capacity. * * INPUTS * Capacity - Number of characters to allocated to string. * * RESULT * NONE * ******************************************************************************/ UString::UString(UInt capacity) : mData(NULL), mCapacity(0) { AllocString(capacity); } /****************************************************************************** * * NAME * UString::UString - ANSI string literal constructor * * DESCRIPTION * Create a new UString from an ANSI string literal * * INPUTS * String - Pointer to a NULL terminated ANSI string * * RESULT * NONE * ******************************************************************************/ UString::UString(const Char* s) : mData(NULL), mCapacity(0) { Copy(s); } /****************************************************************************** * * NAME * UString::UString - UNICODE string literal constructor * * DESCRIPTION * Create a new UString from a UNICODE string literal * * INPUTS * String - Pointer to a NULL terminated UNICODE string * * RESULT * NONE * ******************************************************************************/ UString::UString(const WChar* ws) : mData(NULL), mCapacity(0) { Copy(ws); } /****************************************************************************** * * NAME * UString::UString - Copy constructor * * DESCRIPTION * Create a new UString from another UString * * INPUTS * String - Reference to UString to copy * * RESULT * NONE * ******************************************************************************/ UString::UString(const UString& s) : mData(NULL), mCapacity(0) { Copy(s); } /****************************************************************************** * * NAME * UString::~UString - Destructor * * DESCRIPTION * Destroy the object * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ UString::~UString() { if (mData != NULL) { delete mData; } } /****************************************************************************** * * NAME * UString::Length * * DESCRIPTION * Retrieve the length of the string in characters. * * INPUTS * NONE * * RESULT * Length - Length of string * ******************************************************************************/ UInt UString::Length(void) const { if (mData == NULL) { return 0; } return wcslen(mData); } /****************************************************************************** * * NAME * UString::Copy - ANSI string literal * * DESCRIPTION * Copy ANSI string literal. * * INPUTS * String - Pointer to ANSI string literal * * RESULT * NONE * ******************************************************************************/ void UString::Copy(const Char* s) { assert(s != NULL); UInt length = strlen(s); if (length == 0) { return; } if (Capacity() < length) { AllocString(length); } // Copy and convert ansi string to unicode assert(Capacity() >= length); WChar* wsPtr = mData; const Char* sPtr = s; while (length-- > 0) { *wsPtr++ = (WChar)*sPtr++; } *wsPtr = 0; } /****************************************************************************** * * NAME * UString::Copy - Unicode string literal * * DESCRIPTION * Copy a Unicode string literal * * INPUTS * String - Pointer to UNICODE string literal * * RESULT * NONE * ******************************************************************************/ void UString::Copy(const WChar* ws) { assert(ws != NULL); UInt length = wcslen(ws); if (length == 0) { return; } if (Capacity() < length) { AllocString(length); } assert(Capacity() >= length); wcscpy(mData, ws); } /****************************************************************************** * * NAME * UString::Copy * * DESCRIPTION * Copy string * * INPUTS * String - Reference to UString * * RESULT * NONE * ******************************************************************************/ void UString::Copy(const UString& s) { Copy(s.Get()); } /****************************************************************************** * * NAME * UString::Concat - ANSI string literal * * DESCRIPTION * Concatenate an ANSI string literal to this string * * INPUTS * String - Pointer to ANSI string literal * * RESULT * NONE * ******************************************************************************/ void UString::Concat(const Char* s) { // Parameter check assert(s != NULL); UInt length = Length(); UInt additional = strlen(s); UInt totalLength = (length + additional); // Resize the string if the combined size is to small if (Capacity() < totalLength) { Resize(totalLength); } // Concatenate and convert ansi string to unicode WChar* wsPtr = &mData[length]; const Char* sPtr = s; while (additional-- > 0) { *wsPtr++ = (WChar)*sPtr++; } *wsPtr = 0; } /****************************************************************************** * * NAME * UString::Concat - Unicode string literal * * DESCRIPTION * Concatenate a Unicode string literal to this string * * INPUTS * String - Pointer to UNICODE string literal * * RESULT * NONE * ******************************************************************************/ void UString::Concat(const WChar* ws) { assert(ws != NULL); UInt length = (Length() + wcslen(ws)); if (Capacity() < length) { Resize(length); } wcscat(mData, ws); } /****************************************************************************** * * NAME * UString::Concat * * DESCRIPTION * Concatenate string * * INPUTS * String - Reference to UString * * RESULT * NONE * ******************************************************************************/ void UString::Concat(const UString& s) { Concat(s.Get()); } /****************************************************************************** * * NAME * UString::Compare - ANSI string literal * * DESCRIPTION * Compare strings, returning a value representing their relationship. * * INPUTS * String - String to compare against * * RESULT * Relationship - < 0 String less than this. * = 0 Strings identical * > 0 String greater than this. * ******************************************************************************/ Int UString::Compare(const Char* s) const { // If comparing string is NULL and this string is NULL then strings are equal, // otherwise comparing string is less than this string. if (s == NULL) { if (Get() == NULL) { return 0; } return -1; } // If this string is NULL then comparing string is greater if (Get() == NULL) { return 1; } // Compare each character const WChar* ws = Get(); Int index = 0; for (;;) { // Difference between characters Int diff = ((WChar)s[index] - ws[index]); // If the difference is not zero then the characters differ if (diff != 0) { return diff; } // If the end of either string has been reached then terminate loop if ((ws[index] == L'\0') || (s[index] == '\0')) { break; } // Advance to next character index++; } return 0; } /****************************************************************************** * * NAME * UString::Compare - Unicode string literal * * DESCRIPTION * Compare strings, returning a value representing their relationship. * * INPUTS * String - String to compare against * * RESULT * Relationship - < 0 String less than this. * = 0 Strings identical * > 0 String greater than this. * ******************************************************************************/ Int UString::Compare(const WChar* ws) const { return wcscmp(ws, Get()); } /****************************************************************************** * * NAME * UString::Compare * * DESCRIPTION * Compare strings, returning a value representing their relationship. * * INPUTS * String - String to compare against * * RESULT * Relationship - < 0 String less than this. * = 0 Strings identical * > 0 String greater than this. * ******************************************************************************/ Int UString::Compare(const UString& s) const { return Compare(s.Get()); } /****************************************************************************** * * NAME * UString::CompareNoCase - ANSI string literal * * DESCRIPTION * Compare strings (case insensitive), returning a value representing their * relationship. * * INPUTS * String - String to compare against * * RESULT * Relationship - < 0 String less than this. * = 0 Strings identical * > 0 String greater than this. * ******************************************************************************/ Int UString::CompareNoCase(const Char* s) const { // If comparing string is NULL and this string is NULL then strings are // equal, otherwise comparing string is less than this string. if (s == NULL) { if (Get() == NULL) { return 0; } return -1; } // If this string is NULL then comparing string is greater. if (Get() == NULL) { return 1; } // Compare each character const WChar* ws = Get(); Int index = 0; for (;;) { // Convert to lowercase for compare WChar sc = (WChar)CharToLower(s[index]); WChar wc = CharToLower(ws[index]); // Difference between characters. Int diff = (sc - wc); // If the difference is not zero then the characters differ. if (diff != 0) { return diff; } // If the end of either string has been reached then terminate loop. if ((ws[index] == L'\0') || (s[index] == '\0')) { break; } // Advance to next character index++; } return 0; } /****************************************************************************** * * NAME * UString::CompareNoCase - Unicode string literal * * DESCRIPTION * Compare strings (case insensitive), returning a value representing their * relationship. * * INPUTS * String - String to compare against * * RESULT * Relationship - < 0 String less than this. * = 0 Strings identical * > 0 String greater than this. * ******************************************************************************/ Int UString::CompareNoCase(const WChar* ws) const { return wcsicmp(ws, Get()); } /****************************************************************************** * * NAME * UString::CompareNoCase * * DESCRIPTION * Compare strings (case insensitive), returning a value representing their * relationship. * * INPUTS * String - String to compare against * * RESULT * Relationship - < 0 String less than this. * = 0 Strings identical * > 0 String greater than this. * ******************************************************************************/ Int UString::CompareNoCase(const UString& s) const { return CompareNoCase(s.Get()); } /****************************************************************************** * * NAME * UString::Find - ANSI character * * DESCRIPTION * Find the first occurance of character * * INPUTS * Char - ANSI character to search for * * RESULT * Position - Position of character (-1 if not found) * ******************************************************************************/ Int UString::Find(Char c) const { return Find((WChar)c); } /****************************************************************************** * * NAME * UString::Find - Unicode character * * DESCRIPTION * Find the first occurance of character * * INPUTS * Char - Unicode character to search for. * * RESULT * Position - Position of character (-1 if not found) * ******************************************************************************/ Int UString::Find(WChar c) const { WChar* ptr = wcschr(Get(), c); // Not found? if (ptr == NULL) { return -1; } return ((ptr - mData) / sizeof(WChar)); } /****************************************************************************** * * NAME * UString::FindLast - ANSI character * * DESCRIPTION * Find the last occurance of a character * * INPUTS * Char - ANSI character * * RESULT * Position - Position of character (-1 if not found) * ******************************************************************************/ Int UString::FindLast(Char c) const { return FindLast((WChar)c); } /****************************************************************************** * * NAME * UString::FindLast - Unicode character * * DESCRIPTION * Find the last occurance of a character * * INPUTS * Char - Unicode character * * RESULT * Position - Position of character (-1 if not found) * ******************************************************************************/ Int UString::FindLast(WChar c) const { assert(mData != NULL); WChar* ptr = wcsrchr(mData, (WChar)c); // Not found? if (ptr == NULL) { return -1; } return ((ptr - mData) / sizeof(WChar)); } /****************************************************************************** * * NAME * UString::SubString - ANSI string literal * * DESCRIPTION * Find a substring * * INPUTS * SubString - Substring to search for. * * RESULT * * ******************************************************************************/ UString UString::SubString(const Char* s) { assert(false); assert(s != NULL); return UString(""); } UString UString::SubString(const WChar* ws) { assert(false); assert(ws != NULL); return UString(""); } UString UString::SubString(const UString& s) { assert(false); return SubString(s.mData); } /****************************************************************************** * * NAME * UString::Left * * DESCRIPTION * Extract left part of the string. * * INPUTS * Count - Number of characters to extract * * RESULT * Left - Extracted left part of the string * ******************************************************************************/ UString UString::Left(UInt count) { assert(false); // If the count is zero then return an empty string. if ((Length() == 0) || (count == 0)) { return UString(""); } return UString(""); } /****************************************************************************** * * NAME * UString::Middle * * DESCRIPTION * Extract middle part of the string. * * INPUTS * First - Position of first character * Count - Number of characters to extract * * RESULT * Middle - Extracted middle part of the string * ******************************************************************************/ UString UString::Middle(UInt first, UInt count) { assert(false); // If the first character position is greater than the length of the string // or the count is zero then return an empty string. if ((Length() < first) || (count == 0)) { return UString(""); } return UString(""); } /****************************************************************************** * * NAME * UString::Right * * DESCRIPTION * Extract right part of the string. * * INPUTS * Count - Number of characters to extract * * RESULT * Right - Extracted right part of the string * ******************************************************************************/ UString UString::Right(UInt count) { UInt length = Length(); // If the count is zero then return an empty string. if ((length == 0) || (count == 0)) { return UString(""); } const WChar* ptr = Get(); UInt pos = (length - count); return UString(ptr[pos]); } /****************************************************************************** * * NAME * UString::ToUpper * * DESCRIPTION * Convert string to uppercase * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void UString::ToUpper(void) { if (mData != NULL) { wcsupr(mData); } } /****************************************************************************** * * NAME * UString::ToLower * * DESCRIPTION * Convert string to lowercase * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void UString::ToLower(void) { if (mData != NULL) { wcslwr(mData); } } /****************************************************************************** * * NAME * UString::Reverse * * DESCRIPTION * Reverse characters of string * * INPUTS * NONE * * RESULT * NONE * ******************************************************************************/ void UString::Reverse(void) { if (mData != NULL) { wcsrev(mData); } } /****************************************************************************** * * NAME * UString::Trim * * DESCRIPTION * Remove leading and trailing characters from string. * * INPUTS * TrimChars - Characters to trim * * RESULT * Removed - True if any characters removed * ******************************************************************************/ bool UString::Trim(const Char* trimChars) { bool leftRemoved = TrimLeft(trimChars); bool rightRemoved = TrimRight(trimChars); return (leftRemoved || rightRemoved); } bool UString::Trim(const WChar* trimChars) { bool leftRemoved = TrimLeft(trimChars); bool rightRemoved = TrimRight(trimChars); return (leftRemoved || rightRemoved); } bool UString::Trim(const UString& trimChars) { bool leftRemoved = TrimLeft(trimChars); bool rightRemoved = TrimRight(trimChars); return (leftRemoved || rightRemoved); } /****************************************************************************** * * NAME * UString::TrimLeft * * DESCRIPTION * Remove characters from left side of string * * INPUTS * TrimChars - Characters to trim * * RESULT * Removed - True if any characters removed * ******************************************************************************/ bool UString::TrimLeft(const Char* trimChars) { if ((trimChars == NULL) || (strlen(trimChars) == 0)) { return false; } return StripLeft(mData, trimChars); } bool UString::TrimLeft(const WChar* trimChars) { if ((trimChars == NULL) || (wcslen(trimChars) == 0)) { return false; } return StripLeft(mData, trimChars); } bool UString::TrimLeft(const UString& trimChars) { return TrimLeft(trimChars.Get()); } /****************************************************************************** * * NAME * UString::TrimRight * * DESCRIPTION * Remove characters from right side of string * * INPUTS * TrimChars - Characters to trim * * RESULT * Removed - True if any characters removed * ******************************************************************************/ bool UString::TrimRight(const Char* trimChars) { if ((trimChars == NULL) || (strlen(trimChars) == 0)) { return false; } return StripRight(mData, trimChars); } bool UString::TrimRight(const WChar* trimChars) { if ((trimChars == NULL) || (wcslen(trimChars) == 0)) { return false; } return StripRight(mData, trimChars); } bool UString::TrimRight(const UString& trimChars) { return TrimRight(trimChars.mData); } /****************************************************************************** * * NAME * UString::ConvertToANSI * * DESCRIPTION * Convert the string to ANSI string * * INPUTS * Buffer - Buffer to convert into * Length - Maximum length of buffer * * RESULT * NONE * ******************************************************************************/ void UString::ConvertToANSI(Char* buffer, UInt bufferLength) const { UStringToANSI(*this, buffer, bufferLength); } /****************************************************************************** * * NAME * UString::Size * * DESCRIPTION * Retrieve the size of the string in bytes * * INPUTS * NONE * * RESULT * Size - Size of string in bytes * ******************************************************************************/ UInt UString::Size(void) const { if (mData == NULL) { return 0; } return ((Length() + 1) * sizeof(WChar)); } /****************************************************************************** * * NAME * UString::Capacity * * DESCRIPTION * Retrieve the capacity (maximum number of characters) of the string. * * INPUTS * NONE * * RESULT * Capacity - Maximum number of characters that string can hold. * ******************************************************************************/ UInt UString::Capacity(void) const { return mCapacity; } /****************************************************************************** * * NAME * UString::Resize * * DESCRIPTION * Resize the string capacity. * * INPUTS * NewSize - Size to resize string to. * * RESULT * Success - True if successful; otherwise false * ******************************************************************************/ bool UString::Resize(UInt size) { // Allocate new storage assert(size > 0); WChar* data = new WChar[size + 1]; assert(data != NULL); if (data == NULL) { return false; } // Copy existing string into new storage buffer if (mData != NULL) { UInt minSize = __min(Capacity(), size); wcsncpy(data, mData, minSize); data[minSize] = 0; delete mData; } // Set new storage mData = data; mCapacity = size; return true; } /****************************************************************************** * * NAME * UString::AllocString * * DESCRIPTION * Allocate string storage * * INPUTS * Size - Number of characters * * RESULT * Success - True if allocate successful; otherwise false * ******************************************************************************/ bool UString::AllocString(UInt size) { WChar* data = new WChar[size + 1]; assert(data != NULL); if (data == NULL) { return false; } data[0] = 0; if (mData != NULL) { delete mData; } mData = data; mCapacity = size; return true; }