Initial commit
This commit is contained in:
599
engines/bagel/boflib/string.cpp
Normal file
599
engines/bagel/boflib/string.cpp
Normal file
@@ -0,0 +1,599 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/str.h"
|
||||
#include "bagel/boflib/string.h"
|
||||
#include "bagel/boflib/misc.h"
|
||||
#include "bagel/boflib/string_functions.h"
|
||||
|
||||
namespace Bagel {
|
||||
|
||||
#define MAX_STRING 256
|
||||
|
||||
#define BUF_EXTRA 20
|
||||
|
||||
CBofString::CBofString() {
|
||||
init();
|
||||
}
|
||||
|
||||
CBofString::CBofString(int nLength) {
|
||||
init();
|
||||
allocBuffer(nLength);
|
||||
}
|
||||
|
||||
CBofString::CBofString(const CBofString &cString) {
|
||||
init();
|
||||
copy(cString._pszData);
|
||||
}
|
||||
|
||||
CBofString::CBofString(const char *pszBuf) {
|
||||
init();
|
||||
copy(pszBuf);
|
||||
}
|
||||
|
||||
CBofString::CBofString(char *pszBuff, int pszBuffLen) {
|
||||
init();
|
||||
|
||||
// Force a max length (To stop uninitialized buffer from killing us)
|
||||
*(pszBuff + pszBuffLen - 1) = '\0';
|
||||
|
||||
// Passing in un-initialized data could cause problems depending on what
|
||||
// string functions are used after this constructor.
|
||||
_nLength = (uint16)strlen(pszBuff);
|
||||
|
||||
// Use the high byte of the buffer size to determine if we're using stack memory.
|
||||
// Make sure that we don't have an obscenely large string
|
||||
|
||||
assert((pszBuffLen & mUseStackMem) == false);
|
||||
SETBUFFERSIZE(pszBuffLen, true);
|
||||
_pszData = pszBuff;
|
||||
}
|
||||
|
||||
CBofString::CBofString(char ch, int nRepeat) {
|
||||
assert(nRepeat > 0);
|
||||
|
||||
init();
|
||||
allocBuffer(nRepeat);
|
||||
memset(_pszData, ch, nRepeat);
|
||||
}
|
||||
|
||||
CBofString::~CBofString() {
|
||||
assert(isValidObject(this));
|
||||
|
||||
free();
|
||||
}
|
||||
|
||||
void CBofString::init() {
|
||||
assert(isValidObject(this));
|
||||
|
||||
SETBUFFERSIZE(0, false);
|
||||
_nLength = 0;
|
||||
_pszData = nullptr;
|
||||
}
|
||||
|
||||
void CBofString::allocBuffer(int nLen) {
|
||||
assert(isValidObject(this));
|
||||
assert(nLen >= 0);
|
||||
|
||||
// These 3 lines "should" do the same thing
|
||||
assert(nLen < SHRT_MAX); // max size (enough room for 1 extra)
|
||||
assert((uint16)nLen < mUseStackMem);
|
||||
assert((nLen & mUseStackMem) == 0);
|
||||
|
||||
// Delete any previous buffer
|
||||
free();
|
||||
|
||||
// Don't do anything about zero length allocations
|
||||
if (nLen > 0) {
|
||||
// Allocate a buffer filled with 0s
|
||||
_pszData = (char *)bofCleanAlloc(nLen + 1);
|
||||
}
|
||||
|
||||
_nLength = 0;
|
||||
|
||||
// Use the high byte of the bufflen to indicate if we're using
|
||||
// stack mem or not. Make sure our buff size is not too big
|
||||
assert((nLen & mUseStackMem) == 0);
|
||||
SETBUFFERSIZE(nLen, false);
|
||||
}
|
||||
|
||||
void CBofString::free() {
|
||||
assert(isValidObject(this));
|
||||
bool bStackMem = USESSTACKMEM();
|
||||
|
||||
// Free any attached data
|
||||
// Only free if it's not stack memory
|
||||
if (_pszData != nullptr && bStackMem == false) {
|
||||
bofFree(_pszData);
|
||||
_pszData = nullptr;
|
||||
}
|
||||
_nLength = 0;
|
||||
SETBUFFERSIZE(0, bStackMem);
|
||||
}
|
||||
|
||||
void CBofString::copy(const char *pszBuf) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
if (NORMALIZEBUFFERSIZE() != 0)
|
||||
*_pszData = '\0';
|
||||
|
||||
_nLength = 0;
|
||||
if (pszBuf != nullptr) {
|
||||
int n = strlen(pszBuf);
|
||||
|
||||
if (NORMALIZEBUFFERSIZE() <= n) {
|
||||
allocBuffer(n + 1);
|
||||
}
|
||||
|
||||
Common::strcpy_s(_pszData, n + 1, pszBuf);
|
||||
_nLength = (uint16)strlen(_pszData);
|
||||
}
|
||||
}
|
||||
|
||||
void CBofString::allocCopy(CBofString &dest, int nCopyLen, int nCopyIndex, int nExtraLen) const {
|
||||
assert(isValidObject(this));
|
||||
|
||||
// Will clone the data attached to this string allocating 'nExtraLen' characters
|
||||
// Places results in uninitialized string 'dest'
|
||||
// Will copy the part or all of original data to start of new string
|
||||
|
||||
int nNewLen = nCopyLen + nExtraLen;
|
||||
|
||||
if (nNewLen == 0) {
|
||||
dest.free();
|
||||
|
||||
} else {
|
||||
dest.allocBuffer(nNewLen);
|
||||
|
||||
assert(_pszData != nullptr);
|
||||
memcpy(dest._pszData, &_pszData[nCopyIndex], nCopyLen * sizeof(char));
|
||||
}
|
||||
}
|
||||
|
||||
const CBofString &CBofString::operator=(const CBofString &cString) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
copy(cString._pszData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const CBofString &CBofString::operator=(const char *lpsz) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
copy(lpsz);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void CBofString::concatCopy(int nSrc1Len, const char *lpszSrc1Data, int nSrc2Len, const char *lpszSrc2Data, int nAllocLen) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
// -- Master concatenation routine
|
||||
// concatenate two sources
|
||||
// -- Assume that 'this' is a new CBofString object
|
||||
|
||||
int nNewLen = nSrc1Len + nSrc2Len;
|
||||
allocBuffer((nAllocLen == 0 ? nNewLen : nAllocLen));
|
||||
memcpy(_pszData, lpszSrc1Data, nSrc1Len * sizeof(char));
|
||||
memcpy(&_pszData[nSrc1Len], lpszSrc2Data, nSrc2Len * sizeof(char));
|
||||
// RMS
|
||||
_nLength = (uint16)nNewLen;
|
||||
}
|
||||
|
||||
CBofString operator+(const CBofString &string1, const CBofString &string2) {
|
||||
CBofString s;
|
||||
s.concatCopy(string1._nLength, string1._pszData, string2._nLength, string2._pszData);
|
||||
return s;
|
||||
}
|
||||
|
||||
CBofString operator+(const CBofString &string, const char *lpsz) {
|
||||
CBofString s;
|
||||
s.concatCopy(string._nLength, string._pszData, CBofString::safeStrlen(lpsz), lpsz);
|
||||
return s;
|
||||
}
|
||||
|
||||
CBofString operator+(const char *lpsz, const CBofString &string) {
|
||||
CBofString s;
|
||||
s.concatCopy(CBofString::safeStrlen(lpsz), lpsz, string._nLength, string._pszData);
|
||||
return s;
|
||||
}
|
||||
|
||||
void CBofString::concatInPlace(int nSrcLen, const char *lpszSrcData) {
|
||||
char szLocalBuff[512];
|
||||
assert(isValidObject(this));
|
||||
|
||||
// -- The main routine for += operators
|
||||
|
||||
// If the buffer is too small, or we have a width mis-match, just
|
||||
// allocate a new buffer (slow but sure)
|
||||
//
|
||||
// Make sure we have an underlying buffer.
|
||||
if (_nLength + nSrcLen >= NORMALIZEBUFFERSIZE()) {
|
||||
// Don't increment by buf extra, but set the size if we're
|
||||
// less than that default threshold.
|
||||
int nAllocAmount = nSrcLen;
|
||||
|
||||
if (nAllocAmount < BUF_EXTRA) {
|
||||
nAllocAmount = BUF_EXTRA;
|
||||
}
|
||||
|
||||
if (NORMALIZEBUFFERSIZE() == 0) {
|
||||
allocBuffer(_nLength + nAllocAmount);
|
||||
memcpy(_pszData, lpszSrcData, nSrcLen);
|
||||
*(_pszData + nSrcLen) = '\0';
|
||||
_nLength = (uint16)(_nLength + (uint16)nSrcLen);
|
||||
|
||||
} else {
|
||||
assert(_pszData != nullptr);
|
||||
|
||||
// We have to grow the buffer, use the concat in place routine
|
||||
char *lpszOldData;
|
||||
|
||||
if ((_nLength + nSrcLen + 1) < 512)
|
||||
lpszOldData = szLocalBuff;
|
||||
else
|
||||
lpszOldData = new char[_nLength + nSrcLen + 1];
|
||||
|
||||
memcpy(lpszOldData, _pszData, (_nLength + 1) * sizeof(char));
|
||||
concatCopy(_nLength, lpszOldData, nSrcLen, lpszSrcData, _nLength + nAllocAmount);
|
||||
|
||||
if (lpszOldData != szLocalBuff)
|
||||
delete[] lpszOldData;
|
||||
}
|
||||
} else {
|
||||
|
||||
// Fast concatenation when buffer big enough
|
||||
// Optimize for a single byte
|
||||
|
||||
if (nSrcLen == 1) {
|
||||
_pszData[_nLength] = *lpszSrcData;
|
||||
} else {
|
||||
memcpy(&_pszData[_nLength], lpszSrcData, nSrcLen * sizeof(char));
|
||||
}
|
||||
_nLength = (uint16)(_nLength + (uint16)nSrcLen);
|
||||
}
|
||||
|
||||
assert(_nLength <= NORMALIZEBUFFERSIZE());
|
||||
_pszData[_nLength] = '\0';
|
||||
}
|
||||
|
||||
const CBofString &CBofString::operator+=(const char *lpsz) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
concatInPlace(safeStrlen(lpsz), lpsz);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const CBofString &CBofString::operator+=(char ch) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
concatInPlace(1, &ch);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const CBofString &CBofString::operator+=(const CBofString &string) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
concatInPlace(string._nLength, string._pszData);
|
||||
return *this;
|
||||
}
|
||||
|
||||
char *CBofString::getBuffer() {
|
||||
assert(isValidObject(this));
|
||||
|
||||
if (_pszData == nullptr) {
|
||||
allocBuffer(1);
|
||||
}
|
||||
|
||||
return _pszData;
|
||||
}
|
||||
|
||||
int CBofString::findNumOccurrences(const char *pszSub) {
|
||||
assert(isValidObject(this));
|
||||
assert(pszSub != nullptr);
|
||||
assert(*pszSub != '\0');
|
||||
|
||||
int nHits = 0;
|
||||
if (_pszData != nullptr) {
|
||||
char *pszCur = _pszData;
|
||||
while (pszCur != nullptr) {
|
||||
pszCur = strstr(pszCur, pszSub);
|
||||
if (pszCur != nullptr) {
|
||||
nHits++;
|
||||
pszCur++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nHits;
|
||||
}
|
||||
|
||||
const CBofString &CBofString::operator=(char ch) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
char szBuf[4];
|
||||
|
||||
Common::sprintf_s(szBuf, "%c", ch);
|
||||
copy(szBuf);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CBofString operator+(const CBofString &string1, char ch) {
|
||||
CBofString s;
|
||||
s.concatCopy(string1._nLength, string1._pszData, 1, &ch);
|
||||
return s;
|
||||
}
|
||||
|
||||
CBofString operator+(char ch, const CBofString &string) {
|
||||
CBofString s;
|
||||
s.concatCopy(1, &ch, string._nLength, string._pszData);
|
||||
return s;
|
||||
}
|
||||
|
||||
CBofString CBofString::mid(int nFirst) const {
|
||||
assert(isValidObject(this));
|
||||
|
||||
return mid(nFirst, _nLength - nFirst);
|
||||
}
|
||||
|
||||
CBofString CBofString::mid(int nFirst, int nCount) const {
|
||||
assert(isValidObject(this));
|
||||
|
||||
assert(nFirst >= 0);
|
||||
assert(nCount >= 0);
|
||||
|
||||
// Out-of-bounds requests return sensible things
|
||||
if (nFirst + nCount > _nLength)
|
||||
nCount = _nLength - nFirst;
|
||||
if (nFirst > _nLength)
|
||||
nCount = 0;
|
||||
|
||||
CBofString dest;
|
||||
allocCopy(dest, nCount, nFirst, 0);
|
||||
return dest;
|
||||
}
|
||||
|
||||
CBofString CBofString::right(int nCount) const {
|
||||
assert(isValidObject(this));
|
||||
|
||||
assert(nCount >= 0);
|
||||
|
||||
if (nCount > _nLength)
|
||||
nCount = _nLength;
|
||||
|
||||
CBofString dest;
|
||||
allocCopy(dest, nCount, _nLength - nCount, 0);
|
||||
return dest;
|
||||
}
|
||||
|
||||
CBofString CBofString::left(int nCount) const {
|
||||
assert(isValidObject(this));
|
||||
|
||||
assert(nCount >= 0);
|
||||
|
||||
if (nCount > _nLength)
|
||||
nCount = _nLength;
|
||||
|
||||
CBofString dest;
|
||||
allocCopy(dest, nCount, 0, 0);
|
||||
return dest;
|
||||
}
|
||||
|
||||
void CBofString::deleteLastChar() {
|
||||
if (!isEmpty()) {
|
||||
*(_pszData + _nLength - 1) = '\0';
|
||||
--_nLength;
|
||||
}
|
||||
}
|
||||
|
||||
// Find a sub-string (like strstr)
|
||||
int CBofString::find(const char *lpszSub) const {
|
||||
assert(isValidObject(this));
|
||||
|
||||
// Find first matching substring
|
||||
char *lpsz = nullptr;
|
||||
|
||||
if (_pszData != nullptr)
|
||||
lpsz = strstr(_pszData, lpszSub);
|
||||
|
||||
// Return -1 for not found, distance from beginning otherwise
|
||||
return (lpsz == nullptr) ? -1 : (int)(lpsz - _pszData);
|
||||
}
|
||||
|
||||
// CBofString formatting
|
||||
|
||||
#define FORCE_ANSI 0x10000
|
||||
#define FORCE_UNICODE 0x20000
|
||||
|
||||
int CBofString::safeStrlen(const char *psz) {
|
||||
return (psz == nullptr) ? 0 : strlen(psz);
|
||||
}
|
||||
|
||||
// CBofString support (windows specific)
|
||||
//
|
||||
int CBofString::compare(const char *psz) const {
|
||||
assert(isValidObject(this));
|
||||
assert(psz != nullptr);
|
||||
|
||||
int n = -1;
|
||||
|
||||
if (_pszData != nullptr)
|
||||
n = strcmp(_pszData, psz);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int CBofString::compareNoCase(const char *psz) const {
|
||||
assert(isValidObject(this));
|
||||
|
||||
int n = -1;
|
||||
|
||||
if (_pszData != nullptr)
|
||||
n = scumm_stricmp(_pszData, psz);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
char CBofString::getAt(int nIndex) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
assert(nIndex >= 0);
|
||||
assert(nIndex < _nLength);
|
||||
|
||||
return _pszData[nIndex];
|
||||
}
|
||||
|
||||
char CBofString::operator[](int nIndex) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
return getAt(nIndex);
|
||||
}
|
||||
|
||||
void CBofString::replaceCharAt(int nIndex, char chNew) {
|
||||
if (_pszData != nullptr && nIndex < _nLength) {
|
||||
_pszData[nIndex] = chNew;
|
||||
}
|
||||
}
|
||||
|
||||
void CBofString::replaceChar(char chOld, char chNew) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
// Would never find the terminator
|
||||
assert(chOld != '\0');
|
||||
|
||||
if (_pszData != nullptr) {
|
||||
// Walk through the string and replace the specified character
|
||||
char *p = _pszData;
|
||||
for (int i = 0; i < _nLength; i++) {
|
||||
if (*p == chOld) {
|
||||
*p = chNew;
|
||||
|
||||
// If we just inserted the terminator, then the length of
|
||||
// this string has been changed, and we don't have to search
|
||||
// any more.
|
||||
//
|
||||
if (chNew == '\0') {
|
||||
_nLength = (uint16)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBofString::replaceStr(const char *pszOld, const char *pszNew) {
|
||||
assert(isValidObject(this));
|
||||
assert(pszOld != nullptr);
|
||||
assert(pszNew != nullptr);
|
||||
|
||||
if (_pszData != nullptr) {
|
||||
char *p, *pszSearch;
|
||||
|
||||
int nOldLen = strlen(pszOld);
|
||||
int nNewLen = strlen(pszNew);
|
||||
|
||||
// 1st pass to determine how much more storage space we might need
|
||||
if (nNewLen > nOldLen) {
|
||||
int nDiff = nNewLen - nOldLen;
|
||||
int nNeedLength = _nLength + 1;
|
||||
p = _pszData;
|
||||
pszSearch = strstr(p, pszOld);
|
||||
while (pszSearch != nullptr) {
|
||||
p = pszSearch + nOldLen;
|
||||
|
||||
nNeedLength += nDiff;
|
||||
pszSearch = strstr(p, pszOld);
|
||||
}
|
||||
|
||||
// If we need more storage space for the buffer, then get some
|
||||
if (nNeedLength > NORMALIZEBUFFERSIZE()) {
|
||||
growTo(nNeedLength);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop on the search and replace
|
||||
// Make sure we loop on this, not just once as we can have several occurrences
|
||||
// of the token that we are searching for.
|
||||
|
||||
p = _pszData;
|
||||
pszSearch = strstr(p, pszOld);
|
||||
while (pszSearch != nullptr) {
|
||||
strreplaceStr(p, pszOld, pszNew);
|
||||
p = pszSearch + nNewLen;
|
||||
pszSearch = strstr(p, pszOld);
|
||||
}
|
||||
|
||||
// Get new length
|
||||
_nLength = (uint16)strlen(_pszData);
|
||||
}
|
||||
}
|
||||
|
||||
void CBofString::growTo(int nNewSize) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
// If there is nothing in the buffer to save, then just allocate what
|
||||
// is needed
|
||||
if (_nLength == 0) {
|
||||
allocBuffer(nNewSize);
|
||||
|
||||
} else {
|
||||
// Otherwise, we must keep track of whats in the buffer
|
||||
// Create a temp buffer to save string
|
||||
char *p = (char *)bofAlloc(_nLength + 2);
|
||||
|
||||
// Save copy of string
|
||||
Common::strcpy_s(p, MAX_STRING, _pszData);
|
||||
|
||||
// Make the new buffer
|
||||
allocBuffer(nNewSize);
|
||||
|
||||
// Copy saved string back
|
||||
strncpy(_pszData, p, nNewSize - 1);
|
||||
|
||||
// Get it's new length
|
||||
_nLength = (uint16)strlen(_pszData);
|
||||
|
||||
// Don't need temp buffer anymore
|
||||
bofFree(p);
|
||||
}
|
||||
}
|
||||
|
||||
int CBofString::hash() const {
|
||||
int returnValue = 0;
|
||||
|
||||
// Needs to be case in-sensitive
|
||||
for (int i = 0; i < _nLength; i++) {
|
||||
returnValue = returnValue + (char)toupper(_pszData[i]);
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void CBofString::makeUpper() {
|
||||
Common::String s(_pszData);
|
||||
s.toUppercase();
|
||||
|
||||
strncpy(_pszData, s.c_str(), _nLength);
|
||||
}
|
||||
|
||||
} // namespace Bagel
|
||||
Reference in New Issue
Block a user