597 lines
12 KiB
C++
597 lines
12 KiB
C++
/*
|
|
** 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/****************************************************************************\
|
|
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
|
|
******************************************************************************
|
|
Project Name: Carpenter (The RedAlert ladder creator)
|
|
File Name : string.cpp
|
|
Author : Neal Kettler
|
|
Start Date : June 1, 1997
|
|
Last Update : June 17, 1997
|
|
|
|
A fairly typical string class. This string class always copies any input
|
|
string to it's own memory (for assignment or construction).
|
|
\***************************************************************************/
|
|
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "wstring.h"
|
|
|
|
#define PADSIZE 32 // include a little padding on alloc for future growth
|
|
|
|
Wstring::Wstring() : str(NULL), strsize(0)
|
|
{ }
|
|
|
|
Wstring::Wstring(IN char *string):str(NULL), strsize(0)
|
|
{ set(string); }
|
|
|
|
Wstring::Wstring(IN Wstring &other):str(NULL), strsize(0)
|
|
{
|
|
if (other.str!=NULL)
|
|
{
|
|
str=new char[strlen(other.str)+PADSIZE+1];
|
|
strsize=strlen(other.str)+PADSIZE+1;
|
|
strcpy(str,other.str);
|
|
}
|
|
}
|
|
|
|
Wstring::~Wstring()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
bool Wstring::operator<(IN Wstring &other) RO
|
|
{
|
|
if (str == NULL && other.str == NULL)
|
|
return false;
|
|
|
|
if (str == NULL)
|
|
return true;
|
|
|
|
return ( strcmp(str, other.str) < 0 );
|
|
}
|
|
|
|
bit8 Wstring::operator==(IN char *other) RO
|
|
{
|
|
if ((str==NULL)&&(other==NULL))
|
|
return(TRUE);
|
|
if(strcmp(str, other) != 0)
|
|
return(FALSE);
|
|
else
|
|
return(TRUE);
|
|
}
|
|
|
|
bit8 Wstring::operator==(IN Wstring &other) RO
|
|
{
|
|
if((str == NULL) && (other.str == NULL))
|
|
return(TRUE);
|
|
|
|
if((str == NULL) || (other.str == NULL))
|
|
return(FALSE);
|
|
|
|
if(strcmp(str, other.str) != 0)
|
|
return(FALSE);
|
|
else
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
bit8 Wstring::operator!=(IN char *other) RO
|
|
{
|
|
if(strcmp(str, other) != 0)
|
|
return(TRUE);
|
|
else
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
bit8 Wstring::operator!=(IN Wstring &other) RO
|
|
{
|
|
if((str == NULL) && (other.str == NULL))
|
|
return(FALSE);
|
|
|
|
if((str == NULL) || (other.str == NULL))
|
|
return(TRUE);
|
|
|
|
if(strcmp(str, other.str) != 0)
|
|
return(TRUE);
|
|
else
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
Wstring &Wstring::operator=(IN char *other)
|
|
{
|
|
set(other);
|
|
return(*this);
|
|
}
|
|
|
|
|
|
Wstring &Wstring::operator=(IN Wstring &other)
|
|
{
|
|
if(*this == other)
|
|
return(*this);
|
|
|
|
set(other.get());
|
|
return(*this);
|
|
}
|
|
|
|
|
|
bit8 Wstring::cat(IN char *s)
|
|
{
|
|
uint32 len;
|
|
|
|
if (s==NULL) // it's OK to cat nothing
|
|
return(TRUE);
|
|
|
|
// Determine the length of the resultant string.
|
|
len = strlen(s) + 1;
|
|
if(str)
|
|
len += strlen(str);
|
|
|
|
// Space check
|
|
strgrow(len);
|
|
|
|
strcat(str, s);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
bit8 Wstring::cat(uint32 size, IN char *s)
|
|
{
|
|
uint32 len;
|
|
|
|
// Determine the length of the resultant string.
|
|
len = size + 1;
|
|
if(str)
|
|
len += strlen(str);
|
|
|
|
// Allocate memory for the new string.
|
|
strgrow(len);
|
|
|
|
strncat(str, s, size);
|
|
str[len-1]=0; // make sure null term'd
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
bit8 Wstring::cat(IN Wstring &other)
|
|
{
|
|
return cat(other.get());
|
|
}
|
|
|
|
Wstring &Wstring::operator+=(IN char *string)
|
|
{
|
|
cat(string);
|
|
return(*this);
|
|
}
|
|
|
|
Wstring &Wstring::operator+=(IN Wstring &other)
|
|
{
|
|
cat(other.get());
|
|
return(*this);
|
|
}
|
|
|
|
Wstring Wstring::operator+(IN char *string)
|
|
{
|
|
Wstring temp = *this;
|
|
temp.cat(string);
|
|
return(temp);
|
|
}
|
|
|
|
Wstring Wstring::operator+(IN Wstring &s)
|
|
{
|
|
Wstring temp = *this;
|
|
temp.cat(s);
|
|
return(temp);
|
|
}
|
|
|
|
//
|
|
// This function deletes 'count' characters indexed by `pos' from the Wstring.
|
|
// If `pos'+'count' is > the length of the array, the last 'count' characters
|
|
// of the string are removed. If an error occurs, FALSE is returned.
|
|
// Otherwise, TRUE is returned. Note: count has a default value of 1.
|
|
//
|
|
//
|
|
char Wstring::remove(sint32 pos,sint32 count)
|
|
{
|
|
//char *s;
|
|
sint32 len;
|
|
|
|
len = (sint32)strlen(str);
|
|
|
|
if(pos+count > len)
|
|
pos = len - count;
|
|
if (pos < 0)
|
|
{
|
|
count+=pos; // If they remove before 0, ignore up till beginning
|
|
pos=0;
|
|
}
|
|
if (count<=0)
|
|
return(FALSE);
|
|
|
|
memmove(str+pos,str+pos+count,len-pos-count+1);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
// Remove all instances of a char from the string
|
|
bit8 Wstring::removeChar(char c)
|
|
{
|
|
int len=0;
|
|
char *cptr=NULL;
|
|
bit8 removed=FALSE;
|
|
|
|
if (str==NULL)
|
|
return(FALSE);
|
|
|
|
len=strlen(str);
|
|
while ((cptr=strchr(str,c)) !=NULL)
|
|
{
|
|
memmove(cptr,cptr+1,len-1-((int)(cptr-str)));
|
|
len--;
|
|
str[len]=0;
|
|
removed=TRUE;
|
|
}
|
|
return(removed);
|
|
}
|
|
|
|
void Wstring::removeSpaces(void)
|
|
{
|
|
removeChar(' ');
|
|
removeChar('\t');
|
|
}
|
|
|
|
void Wstring::clear(void)
|
|
{
|
|
if(str)
|
|
delete[](str);
|
|
strsize=0;
|
|
str=NULL;
|
|
}
|
|
|
|
// This is usually used for raw storage instead of string ops...
|
|
void Wstring::setSize(sint32 size)
|
|
{
|
|
clear();
|
|
if (size<0)
|
|
return;
|
|
|
|
str=new char[size];
|
|
strsize=size;
|
|
memset(str,0,size);
|
|
}
|
|
|
|
void Wstring::cellCopy(char *dest, uint32 len)
|
|
{
|
|
uint32 i;
|
|
|
|
strncpy(dest, str, len);
|
|
for(i = (uint32)strlen(str); i < len; i++)
|
|
dest[i] = ' ';
|
|
dest[len] = 0;
|
|
}
|
|
|
|
char *Wstring::get(void) RO
|
|
{
|
|
if(!str)
|
|
return "";
|
|
return str;
|
|
}
|
|
|
|
char Wstring::get(uint32 index) RO
|
|
{
|
|
if(index < strlen(str))
|
|
return str[index];
|
|
return(0);
|
|
}
|
|
|
|
uint32 Wstring::length(void) RO
|
|
{
|
|
if(str == NULL)
|
|
return(0);
|
|
return((uint32)strlen(str));
|
|
}
|
|
|
|
|
|
// Insert at given position and shift old stuff to right
|
|
bit8 Wstring::insert(char *instring, uint32 pos)
|
|
{
|
|
if (str==NULL)
|
|
return(set(instring));
|
|
if (pos>strlen(str))
|
|
pos=strlen(str);
|
|
|
|
strgrow(strlen(str)+strlen(instring)+1);
|
|
memmove(str+pos+strlen(instring),str+pos,strlen(str)-pos+1);
|
|
memmove(str+pos,instring,strlen(instring));
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
// This function inserts the character specified by `k' into the string at the
|
|
// position indexed by `pos'. If `pos' is >= the length of the string, it is
|
|
// appended to the string. If an error occurs, FALSE is returned. Otherwise,
|
|
// TRUE is returned.
|
|
bit8 Wstring::insert(char k, uint32 pos)
|
|
{
|
|
char temp[2];
|
|
temp[0]=k;
|
|
temp[1]=0;
|
|
return(insert(temp,pos));
|
|
}
|
|
|
|
|
|
// Joe Howes (05/19/2000): This function inserts commas to nicely format a
|
|
// large number (i.e. 1234567890 -> 1,234,567,890). It doesn't really care
|
|
// if the string is really a number or not.
|
|
bit8 Wstring::beautifyNumber()
|
|
{
|
|
int len = length();
|
|
int accum = 3 - (len % 3);
|
|
int numcommas = 0;
|
|
|
|
|
|
if( accum == 3 ) accum = -1;
|
|
for(int i = 0; i < len; i++)
|
|
{
|
|
if( accum == 3 )
|
|
{
|
|
insert(',', i + numcommas);
|
|
numcommas++;
|
|
}
|
|
accum = ( accum == 3 || accum == -1 ) ? 1 : accum + 1;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
// This function replaces any occurences of the string pointed to by
|
|
// `replaceThis' with the string pointed to by `withThis'. If an error
|
|
// occurs, FALSE is returned. Otherwise, TRUE is returned.
|
|
bit8 Wstring::replace(IN char *replaceThis,IN char *withThis)
|
|
{
|
|
Wstring dest;
|
|
char *foundStr, *src;
|
|
uint32 len;
|
|
|
|
src=get();
|
|
while(src && src[0])
|
|
{
|
|
foundStr = strstr(src, replaceThis);
|
|
if(foundStr)
|
|
{
|
|
len = (uint32)foundStr - (uint32)src;
|
|
if(len)
|
|
{
|
|
if(!dest.cat(len, src))
|
|
return(FALSE);
|
|
}
|
|
if(!dest.cat(withThis))
|
|
return(FALSE);
|
|
src = foundStr + strlen(replaceThis);
|
|
}
|
|
else
|
|
{
|
|
if(!dest.cat(src))
|
|
return(FALSE);
|
|
|
|
src=NULL;
|
|
}
|
|
}
|
|
return(set(dest.get()));
|
|
}
|
|
|
|
|
|
bit8 Wstring::set(IN char *s)
|
|
{
|
|
//uint32 len;
|
|
|
|
strgrow(strlen(s)+1);
|
|
strcpy(str,s);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
bit8 Wstring::set(char c, uint32 index)
|
|
{
|
|
if(index >= (uint32)strlen(str))
|
|
return FALSE;
|
|
|
|
str[index] = c;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
char Wstring::set(uint32 size, IN char *string)
|
|
{
|
|
//uint32 len;
|
|
|
|
strgrow(size+1);
|
|
strncpy(str,string,size);
|
|
str[size]=0;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
// Added by Joe Howes. Takes a printf formatted string and a set of args.
|
|
// The expanded string must not exceed 1k or twice the length of the format
|
|
// string, whichever is larger. It would probably be better to traverse
|
|
// the format string and properly calculate, the length so this will
|
|
// work in all cases, but this should be good enough for 99% of Wstring usage.
|
|
char Wstring::setFormatted(IN char *msg, ...)
|
|
{
|
|
if( msg == NULL || strlen(msg) <= 0 ) return FALSE;
|
|
|
|
char* string;
|
|
va_list args;
|
|
int len = (strlen(msg) < 1024) ? 1024 : (strlen(msg)*2);
|
|
|
|
string = new char[len];
|
|
va_start(args, msg);
|
|
vsprintf(string, msg, args);
|
|
va_end(args);
|
|
set(string);
|
|
delete[] string;
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
// This function converts all alphabetical characters in the string to lower
|
|
// case.
|
|
void Wstring::toLower(void)
|
|
{
|
|
uint32 i;
|
|
int strlength=length();
|
|
|
|
for(i = 0; i < (uint32)strlength; i++)
|
|
{
|
|
if((str[i] >= 'A') && (str[i] <= 'Z'))
|
|
str[i] = (sint8)tolower(str[i]);
|
|
}
|
|
}
|
|
|
|
|
|
// This function converts all alphabetical characters in the string to upper
|
|
// case.
|
|
void Wstring::toUpper(void)
|
|
{
|
|
uint32 i;
|
|
int strlength=length();
|
|
|
|
for(i = 0; i < (uint32)strlength; i++)
|
|
{
|
|
if((str[i] >= 'a') && (str[i] <= 'z'))
|
|
str[i] = (sint8)toupper(str[i]);
|
|
}
|
|
}
|
|
|
|
|
|
// This function truncates the string so its length will match the specified
|
|
// `len'. If an error occurs, FALSE is returned. Otherwise, TRUE is returned.
|
|
bit8 Wstring::truncate(uint32 len)
|
|
{
|
|
strgrow(len+1);
|
|
str[len]=0;
|
|
return(TRUE);
|
|
}
|
|
|
|
// Truncate the string after the character 'c' (gets rid of 'c' as well)
|
|
// Do nothing if 'c' isn't in the string
|
|
bit8 Wstring::truncate(char c)
|
|
{
|
|
sint32 len;
|
|
|
|
if (str==NULL)
|
|
return(FALSE);
|
|
|
|
char *cptr=strchr(str,c);
|
|
if (cptr==NULL)
|
|
return(FALSE);
|
|
len=(sint32)(cptr-str);
|
|
truncate((uint32)len);
|
|
return(TRUE);
|
|
}
|
|
|
|
// Get a token from this string that's seperated by one or more
|
|
// chars from the 'delim' string , start at offset & return offset
|
|
sint32 Wstring::getToken(int offset,char *delim,Wstring &out) RO
|
|
{
|
|
int i;
|
|
sint32 start;
|
|
sint32 stop;
|
|
|
|
if (offset<0) // check for bad input
|
|
return(-1);
|
|
|
|
for (i=offset; i<(int)length(); i++) {
|
|
if(strchr(delim,str[i])==NULL)
|
|
break;
|
|
}
|
|
if (i>=(int)length())
|
|
return(-1);
|
|
start=i;
|
|
|
|
for (; i<(int)length(); i++) {
|
|
if(strchr(delim,str[i])!=NULL)
|
|
break;
|
|
}
|
|
stop=i-1;
|
|
out.set(str+start);
|
|
out.truncate((uint32)stop-start+1);
|
|
return(stop+1);
|
|
}
|
|
|
|
// Get the first line of text after offset. Lines are terminated by '\r\n' or '\n'
|
|
sint32 Wstring::getLine(int offset, Wstring &out)
|
|
{
|
|
int i;
|
|
sint32 start;
|
|
sint32 stop;
|
|
|
|
start=i=offset;
|
|
if (start >= (sint32)length())
|
|
return(-1);
|
|
|
|
for (; i<(int)length(); i++) {
|
|
if(strchr("\r\n",str[i])!=NULL)
|
|
break;
|
|
}
|
|
stop=i;
|
|
if ((str[stop]=='\r')&&(str[stop+1]=='\n'))
|
|
stop++;
|
|
|
|
out.set(str+start);
|
|
out.truncate((uint32)stop-start+1);
|
|
return(stop+1);
|
|
}
|
|
|
|
//
|
|
// Make sure there's AT LEAST length bytes in this string object
|
|
//
|
|
void Wstring::strgrow(int length)
|
|
{
|
|
if (str==NULL)
|
|
{
|
|
str=new char[length+PADSIZE];
|
|
str[0]=0;
|
|
strsize=length+PADSIZE;
|
|
return;
|
|
}
|
|
else if (strsize >= length) // no need to alloc more data
|
|
return;
|
|
else // bah, gotta grow...
|
|
{
|
|
char *newstr=new char[length+PADSIZE];
|
|
strsize=length+PADSIZE;
|
|
strcpy(newstr,str);
|
|
delete[](str);
|
|
str=newstr;
|
|
return;
|
|
}
|
|
}
|