Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
1295 lines
26 KiB
C++
1295 lines
26 KiB
C++
#define __INTERNAL__
|
|
#include "stdafx.h"
|
|
#include "malloc.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static CHAR THIS_FILE[] = __FILE__;
|
|
#define new DEBUG_NEW
|
|
#define delete DEBUG_DELETE
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#pragma pack(push, 1)
|
|
#endif // WIN32
|
|
struct
|
|
#ifdef WIN32
|
|
__declspec(novtable)
|
|
#endif // WIN32
|
|
SStrDummy
|
|
{
|
|
#ifdef USE_STRLOCK
|
|
WORD m_wRefCount;
|
|
#endif // USE_STRLOCK
|
|
CHAR m_sz[1];
|
|
};
|
|
#ifdef WIN32
|
|
#pragma pack(pop)
|
|
#endif // WIN32
|
|
|
|
#define NIL_LOCKCOUNT 0x8000
|
|
|
|
#ifdef WIN32
|
|
#pragma data_seg(".SHARED_DATA")
|
|
#endif // WIN32
|
|
// all data in this section must be INITIALIZED!!!!!!!!!!!!!
|
|
static SStrDummy l_sz =
|
|
{
|
|
#ifdef USE_STRLOCK
|
|
NIL_LOCKCOUNT, // WORD m_wRefCount;
|
|
#endif // USE_STRLOCK
|
|
{ 0, }, // CHAR m_sz;
|
|
};
|
|
#ifdef WIN32
|
|
#pragma data_seg()
|
|
#pragma comment(linker, "/section:.SHARED_DATA,RWS")
|
|
#endif // WIN32
|
|
|
|
#ifdef _DEBUG
|
|
void CStrTestMain();
|
|
|
|
class s_CAutoRun
|
|
{
|
|
public:
|
|
s_CAutoRun()
|
|
{
|
|
CStrTestMain();
|
|
}
|
|
} s_a;
|
|
#endif // _DEBUG
|
|
|
|
#define l_SZ (&l_sz.m_sz[0])
|
|
|
|
#ifdef USE_STRLOCK
|
|
#define STR_LOCKCOUNT STR_LOCKCOUNT2(m_psz)
|
|
#define STR_LOCKCOUNT2(psz) ((WORD*)psz)[-1]
|
|
static inline CHAR* str_alloc(int nMax)
|
|
{
|
|
CHAR* psz = (CHAR*)malloc(sizeof(WORD) + sizeof(CHAR) * nMax);
|
|
*(WORD*)psz = 0;
|
|
|
|
CHAR* p = (CHAR*)&((BYTE*)psz)[sizeof(WORD)];
|
|
p[0] = '\0';
|
|
|
|
return p;
|
|
}
|
|
|
|
static inline CHAR* str_realloc(CHAR* psz, int nMax)
|
|
{
|
|
return (CHAR*)&((BYTE*)realloc(&STR_LOCKCOUNT2(psz), sizeof(WORD) + sizeof(CHAR) * nMax))[sizeof(WORD)];
|
|
}
|
|
|
|
static inline void str_free(CHAR* psz)
|
|
{
|
|
free(&STR_LOCKCOUNT2(psz));
|
|
}
|
|
#else // !USE_STRLOCK
|
|
#define STR_LOCKCOUNT ASSERT(FALSE)
|
|
#define STR_LOCKCOUNT2(psz) ASSERT(FALSE)
|
|
//#define str_alloc(nMax) ((CHAR*)malloc(sizeof(CHAR)*nMax))
|
|
static inline CHAR* str_alloc(int nMax)
|
|
{
|
|
CHAR* p = (CHAR*)malloc(sizeof(CHAR) * nMax);
|
|
p[0] = '\0';
|
|
|
|
return p;
|
|
}
|
|
#define str_realloc(psz, nMax) ((CHAR*)realloc(psz,sizeof(CHAR)*nMax))
|
|
#define str_free(psz) (free(psz))
|
|
#endif // USE_STRLOCK
|
|
|
|
#ifdef USE_STRLOCK
|
|
#define IS_NIL(psz) (((const WORD*)psz)[-1] == NIL_LOCKCOUNT) // check m_wRefCount
|
|
#else // !USE_STRLOCK
|
|
#define IS_NIL(psz) ((psz) == l_SZ) // check pointer...
|
|
#endif // USE_STRLOCK
|
|
|
|
static BOOL LSTRS_IsValid(int c, const char* pcszVals);
|
|
|
|
static BOOL LSTRS_IsValid(int c, const char* pcszVals)
|
|
{
|
|
if (pcszVals) {
|
|
if (strchr(pcszVals, c)) {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
if ((' ' < c) && (c != 0x7f) && (c != 0xff)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int __cdecl str_memcmp(const void* elem1, const void* elem2)
|
|
{
|
|
const char* s1 = ((const CStr*)elem1)->String();
|
|
const char* s2 = ((const CStr*)elem2)->String();
|
|
|
|
int n1 = strlen(s1);
|
|
int n2 = strlen(s2);
|
|
if (n1 > n2) {
|
|
n2 = n1;
|
|
}
|
|
return memcmp(s1, s2, n2);
|
|
}
|
|
#if !defined(WIN32)
|
|
char* _strupr(char *string)
|
|
{
|
|
char c, *p = string;
|
|
|
|
while((c = *p) != '\0') {
|
|
if (('a' <= c) && (c <= 'z')) {
|
|
c -= ('a' - 'A');
|
|
*p = c;
|
|
}
|
|
p++;
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
char* _strlwr(char *string)
|
|
{
|
|
char c, *p = string;
|
|
|
|
while((c = *p) != '\0') {
|
|
if (('A' <= c) && (c <= 'Z')) {
|
|
c += ('a' - 'A');
|
|
*p = c;
|
|
}
|
|
p++;
|
|
}
|
|
|
|
return string;
|
|
}
|
|
#endif // !defined(WIN32)
|
|
int __cdecl str_Compare(const void* elem1, const void* elem2)
|
|
{
|
|
return lstrcmp(((const CStr*)elem1)->String(), ((const CStr*)elem2)->String());
|
|
}
|
|
|
|
int __cdecl str_CompareReverse(const void* elem1, const void* elem2)
|
|
{
|
|
return -lstrcmp(((const CStr*)elem1)->String(), ((const CStr*)elem2)->String());
|
|
}
|
|
|
|
int __cdecl str_CompareIC(const void* elem1, const void* elem2)
|
|
{
|
|
return lstrcmpi(((const CStr*)elem1)->String(), ((const CStr*)elem2)->String());
|
|
}
|
|
|
|
int __cdecl str_CompareICReverse(const void* elem1, const void* elem2)
|
|
{
|
|
return -lstrcmpi(((const CStr*)elem1)->String(), ((const CStr*)elem2)->String());
|
|
}
|
|
|
|
CStr::CStr()
|
|
{
|
|
m_psz = l_SZ;
|
|
}
|
|
|
|
CStr::CStr(const CHAR* pcsz)
|
|
{
|
|
#ifdef _DEBUG
|
|
m_psz = (CHAR*)l_SZ;
|
|
#endif // _DEBUG
|
|
AssignNoCheck(pcsz);
|
|
}
|
|
|
|
CStr::CStr(const CHAR* pcsz, int nLen)
|
|
{
|
|
#ifdef _DEBUG
|
|
m_psz = (CHAR*)l_SZ;
|
|
#endif // _DEBUG
|
|
AssignNoCheck(pcsz, nLen);
|
|
}
|
|
|
|
CStr::CStr(CHAR c, int nLen)
|
|
{
|
|
ASSERT((c != '\0') && (nLen >= 0));
|
|
|
|
if ((c != '\0') && (nLen > 0)) {
|
|
CHAR* psz = str_alloc(nLen + 1);
|
|
memset(psz, c, nLen);
|
|
psz[nLen] = '\0';
|
|
|
|
m_psz = psz;
|
|
} else {
|
|
m_psz = l_SZ;
|
|
}
|
|
}
|
|
|
|
CStr::CStr(const CStr& that)
|
|
{
|
|
#ifdef _DEBUG
|
|
m_psz = l_SZ;
|
|
#endif // _DEBUG
|
|
AssignNoCheck(that.m_psz);
|
|
}
|
|
|
|
CStr::CStr(int nMax)
|
|
{
|
|
ASSERT(nMax > 0);
|
|
m_psz = str_alloc(nMax);
|
|
}
|
|
|
|
CStr::~CStr()
|
|
{
|
|
#ifdef USE_STRLOCK
|
|
#ifdef _DEBUG
|
|
if (!IS_NIL(m_psz) && (STR_LOCKCOUNT != 0)) {
|
|
TRACE("CStr::~CStr() called for CStr Locked: %d times\n", (int)STR_LOCKCOUNT);
|
|
}
|
|
#endif // _DEBUG
|
|
#endif // USE_STRLOCK
|
|
if (!IS_NIL(m_psz)) {
|
|
str_free(m_psz);
|
|
}
|
|
#ifdef _DEBUG
|
|
m_psz = NULL;
|
|
#endif
|
|
}
|
|
|
|
const CHAR* CStr::Empty()
|
|
{
|
|
if (!IS_NIL(m_psz)) {
|
|
#ifdef USE_STRLOCK
|
|
if (STR_LOCKCOUNT) {
|
|
m_psz[0] = '\0';
|
|
return m_psz;
|
|
}
|
|
#endif // USE_STRLOCK
|
|
str_free(m_psz);
|
|
m_psz = l_SZ;
|
|
}
|
|
|
|
return l_SZ;
|
|
}
|
|
|
|
void CStr::Swap(CStr& that)
|
|
{
|
|
#ifdef _DEBUG
|
|
#ifdef USE_STRLOCK
|
|
if (IsLocked() || that.IsLocked())
|
|
TRACE("CStr::Swap() called for Locked CStr(s)\n");
|
|
#endif // USE_STRLOCK
|
|
#endif // _DEBUG
|
|
CHAR* psz = m_psz;
|
|
m_psz = that.m_psz;
|
|
that.m_psz = psz;
|
|
}
|
|
#ifdef USE_STRLOCK
|
|
WORD CStr::IsLocked() const
|
|
{
|
|
ASSERT_VALIDSTRING(m_psz);
|
|
if (!IS_NIL(m_psz))
|
|
return STR_LOCKCOUNT;
|
|
return 0;
|
|
}
|
|
|
|
CHAR* CStr::LockBuffer()
|
|
{
|
|
ASSERT_VALIDSTRING(m_psz);
|
|
if (!IS_NIL(m_psz)) {
|
|
++STR_LOCKCOUNT;
|
|
} else {
|
|
TRACE("Trying To Lock Empty String...\n");
|
|
}
|
|
return m_psz;
|
|
}
|
|
|
|
WORD CStr::UnlockBuffer()
|
|
{
|
|
ASSERT_VALIDSTRING(m_psz);
|
|
if (!IS_NIL(m_psz)) {
|
|
ASSERT(STR_LOCKCOUNT != 0);
|
|
WORD wRefCount = --STR_LOCKCOUNT;
|
|
if (wRefCount == 0) {
|
|
if (m_psz[0] == '\0') {
|
|
Reset();
|
|
}
|
|
}
|
|
return wRefCount;
|
|
} else {
|
|
TRACE("Trying To Unlock Empty String...\n");
|
|
return 0;
|
|
}
|
|
}
|
|
#endif // USE_STRLOCK
|
|
const CHAR* CStr::Assign(const CHAR* pcsz, int nLen/* = -1*/)
|
|
{
|
|
ASSERT(this);
|
|
ASSERT_VALIDSTRING(m_psz);
|
|
|
|
#ifdef USE_STRLOCK
|
|
if (IsLocked()) {
|
|
if (nLen < 0) {
|
|
if (!pcsz || ((nLen = strlen(pcsz)) <= 0)) {
|
|
return ResetLocked();
|
|
}
|
|
nLen++;
|
|
memcpy(m_psz, pcsz, nLen);
|
|
} else {
|
|
if (nLen <= 0) {
|
|
return ResetLocked();
|
|
}
|
|
ASSERT(pcsz);
|
|
ASSERT((UINT)nLen <= strlen(pcsz));
|
|
memcpy(m_psz, pcsz, nLen);
|
|
m_psz[nLen] = '\0';
|
|
}
|
|
} else {
|
|
#endif // USE_STRLOCK
|
|
if (IS_NIL(m_psz)) {
|
|
return AssignNoCheck(pcsz, nLen);
|
|
}
|
|
|
|
if (nLen < 0) {
|
|
if (!pcsz || ((nLen = strlen(pcsz)) <= 0)) {
|
|
return Reset();
|
|
}
|
|
nLen++;
|
|
memcpy(m_psz = str_realloc(m_psz, nLen), pcsz, nLen);
|
|
} else {
|
|
if (nLen <= 0) {
|
|
return Reset();
|
|
}
|
|
ASSERT(pcsz);
|
|
ASSERT((UINT)nLen <= strlen(pcsz));
|
|
int nAlloc = nLen + 1;
|
|
memcpy(m_psz = str_realloc(m_psz, nAlloc), pcsz, nLen);
|
|
m_psz[nLen] = '\0';
|
|
}
|
|
#ifdef USE_STRLOCK
|
|
}
|
|
#endif // USE_STRLOCK
|
|
|
|
return m_psz;
|
|
}
|
|
|
|
CStr CStr::operator + (const CHAR* pcsz) const
|
|
{
|
|
int nLen;
|
|
|
|
if (pcsz && ((nLen = strlen(pcsz)) > 0)) {
|
|
CHAR* psz = m_psz;
|
|
|
|
if (psz[0] == '\0') {
|
|
return CStr(pcsz, nLen);
|
|
} else {
|
|
int n = strlen(psz);
|
|
|
|
CStr str(n + ++nLen);
|
|
|
|
CHAR* p = str.m_psz;
|
|
|
|
memcpy(p, psz, n);
|
|
memcpy(&p[n], pcsz, nLen);
|
|
|
|
return str;
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
CStr& CStr::operator += (const CHAR* pcsz)
|
|
{
|
|
int nLen;
|
|
|
|
#ifdef USE_STRLOCK
|
|
if (IsLocked()) {
|
|
if (pcsz && ((nLen = strlen(pcsz)) > 0)) {
|
|
CHAR* psz = m_psz;
|
|
memcpy(&psz[strlen(psz)], pcsz, nLen + 1);
|
|
}
|
|
} else {
|
|
#endif // USE_STRLOCK
|
|
if (pcsz && ((nLen = strlen(pcsz)) > 0)) {
|
|
CHAR* psz = m_psz;
|
|
|
|
if (IS_NIL(psz)) {
|
|
AssignNoCheck(pcsz, nLen);
|
|
} else {
|
|
nLen++;
|
|
int n = strlen(psz);
|
|
psz = str_realloc(psz, n + nLen);
|
|
memcpy(&psz[n], pcsz, nLen);
|
|
m_psz = psz;
|
|
}
|
|
}
|
|
#ifdef USE_STRLOCK
|
|
}
|
|
#endif // USE_STRLOCK
|
|
|
|
return *this;
|
|
}
|
|
|
|
CStr CStr::operator + (CHAR c) const
|
|
{
|
|
CHAR ca[2];
|
|
ca[0] = c;
|
|
ca[1] = '\0';
|
|
|
|
return *this + ca;
|
|
}
|
|
|
|
CStr& CStr::operator += (CHAR c)
|
|
{
|
|
CHAR ca[2];
|
|
ca[0] = c;
|
|
ca[1] = '\0';
|
|
|
|
return *this += ca;
|
|
}
|
|
|
|
void CStr::MakeReverse()
|
|
{
|
|
int nLen = strlen(m_psz);
|
|
if (nLen >= 2) {
|
|
CHAR* pszStart = m_psz;
|
|
CHAR* pszEnd = &pszStart[nLen - 1];
|
|
nLen /= 2;
|
|
while(nLen-- > 0) {
|
|
CHAR c;
|
|
|
|
c = *pszStart;
|
|
*pszStart = *pszEnd;
|
|
*pszEnd = c;
|
|
|
|
pszStart++;
|
|
pszEnd--;
|
|
}
|
|
}
|
|
}
|
|
|
|
const CHAR* CStr::Prune(const CHAR* pcszDELs/* = NULL*/)
|
|
{
|
|
if (!pcszDELs) {
|
|
pcszDELs = "\t ";
|
|
}
|
|
ASSERT_VALIDSTRING(pcszDELs);
|
|
ASSERT(pcszDELs[0]);
|
|
CHAR* pszHead = m_psz;
|
|
CHAR c;
|
|
|
|
while((c = *pszHead) != '\0') {
|
|
if (strchr(pcszDELs, c)) {
|
|
CHAR* pszStart = pszHead;
|
|
while((c = *++pszHead) != '\0') {
|
|
if (!strchr(pcszDELs, c)) {
|
|
strcpy(pszStart, pszHead);
|
|
goto no_DELs;
|
|
}
|
|
}
|
|
ASSERT(c == '\0');
|
|
pszStart[0] = '\0';
|
|
break;
|
|
} else {
|
|
no_DELs:
|
|
pszHead++;
|
|
}
|
|
}
|
|
ReleaseBuffer();
|
|
|
|
return m_psz;
|
|
}
|
|
|
|
const CHAR* CStr::TrimLeft()
|
|
{
|
|
CHAR* pszHead = m_psz;
|
|
CHAR c;
|
|
|
|
while((c = *pszHead) != '\0') {
|
|
if ((c != ' ') && (c != '\t')) {
|
|
if (m_psz != pszHead)
|
|
strcpy(m_psz, pszHead);
|
|
return m_psz;
|
|
}
|
|
pszHead++;
|
|
}
|
|
return Empty();
|
|
}
|
|
|
|
const CHAR* CStr::TrimRight()
|
|
{
|
|
CHAR* pszHead = m_psz;
|
|
CHAR c;
|
|
CHAR* pszTail = &pszHead[strlen(pszHead)];
|
|
|
|
while(pszTail != pszHead) {
|
|
c = *--pszTail;
|
|
if ((c != ' ') && (c != '\t')) {
|
|
pszTail[1] = '\0';
|
|
return m_psz;
|
|
}
|
|
}
|
|
return Empty();
|
|
}
|
|
|
|
const CHAR* CStr::TrimAll()
|
|
{
|
|
TrimLeft();
|
|
|
|
return TrimRight();
|
|
}
|
|
|
|
void __cdecl CStr::Format(LPCTSTR lpszFormat, ...)
|
|
{
|
|
va_list marker;
|
|
|
|
va_start(marker, lpszFormat);
|
|
vFormat(lpszFormat, marker);
|
|
va_end(marker);
|
|
}
|
|
|
|
void CStr::vFormat(LPCTSTR lpszFormat, va_list marker)
|
|
{
|
|
CHAR szBuf[512];
|
|
|
|
#ifdef _DEBUG
|
|
int n = vsprintf(szBuf, lpszFormat, marker);
|
|
ASSERT((0 <= n) && (n + 1 <= NOFA(szBuf)));
|
|
#else // !_DEBUG
|
|
vsprintf(szBuf, lpszFormat, marker);
|
|
#endif // _DEBUG
|
|
|
|
Assign(szBuf);
|
|
}
|
|
|
|
CHAR* CStr::GetBuffer(int nLen)
|
|
{
|
|
ASSERT(nLen >= 0);
|
|
ASSERT_VALIDSTRING(m_psz);
|
|
|
|
CHAR* psz = m_psz;
|
|
|
|
#ifdef USE_STRLOCK
|
|
if (!IsLocked()) {
|
|
#endif // USE_STRLOCK
|
|
int n = strlen(psz);
|
|
|
|
if ((n -= nLen) < 0) {
|
|
nLen++;
|
|
psz = (IS_NIL(psz)) ? str_alloc(nLen): str_realloc(psz, nLen);
|
|
m_psz = psz;
|
|
}
|
|
#ifdef USE_STRLOCK
|
|
}
|
|
#endif // USE_STRLOCK
|
|
|
|
return psz;
|
|
}
|
|
|
|
const CHAR* CStr::ReleaseBuffer(int nLen/* = -1*/)
|
|
{
|
|
ASSERT_VALIDSTRING(m_psz);
|
|
|
|
CHAR* psz = m_psz;
|
|
|
|
#ifdef USE_STRLOCK
|
|
if (IsLocked()) {
|
|
if (nLen >= 0) {
|
|
psz[nLen] = '\0';
|
|
}
|
|
return psz;
|
|
} else {
|
|
#endif // USE_STRLOCK
|
|
if (nLen >= 0) {
|
|
#ifdef _DEBUG
|
|
if (psz[nLen] != '\0') // when _DEBUG, (pcsz == const l_SZ) is possible
|
|
#endif // _DEBUG
|
|
psz[nLen] = '\0';
|
|
} else {
|
|
nLen = strlen(psz);
|
|
}
|
|
if (nLen == 0) {
|
|
return Reset();
|
|
} else {
|
|
return m_psz = str_realloc(psz, nLen + 1);
|
|
}
|
|
#ifdef USE_STRLOCK
|
|
}
|
|
#endif // USE_STRLOCK
|
|
}
|
|
|
|
CHAR* CStr::GetBufferSetLength(int nLen)
|
|
{
|
|
ASSERT(nLen >= 0);
|
|
|
|
#ifdef _DEBUG
|
|
CHAR* p1 = GetBuffer(nLen);
|
|
if (!IS_NIL(p1)) // when _DEBUG, (pcsz == const l_SZ) is possible
|
|
m_psz[nLen] = '\0';
|
|
#else // !_DEBUG
|
|
GetBuffer(nLen);
|
|
#endif // _DEBUG
|
|
|
|
return m_psz;
|
|
}
|
|
|
|
void CStr::ChangeChars(CHAR cBefore, CHAR cAfter)
|
|
{
|
|
ASSERT(cBefore && cAfter);
|
|
CHAR* psz = m_psz;
|
|
CHAR c;
|
|
|
|
while((c = *psz) != '\0') {
|
|
if (c == cBefore) {
|
|
*psz = cAfter;
|
|
}
|
|
psz++;
|
|
}
|
|
}
|
|
|
|
BOOL CStr::AsDirectory(BOOL bRemoveLastDEL/* = FALSE*/)
|
|
{
|
|
int nPos = rFindSet("/\\");
|
|
|
|
if (nPos != -1) {
|
|
if (!bRemoveLastDEL)
|
|
nPos++;
|
|
ReleaseBuffer(nPos);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CStr::AddDirDEL()
|
|
{
|
|
int nLen = GetLength();
|
|
|
|
if (nLen > 0) {
|
|
CHAR c = m_psz[nLen - 1];
|
|
if ((c != '\\') && (c != '/')) {
|
|
#ifdef _WINDOWS
|
|
*this += '\\';
|
|
#else // !_WINDOWS
|
|
*this += '/';
|
|
#endif // _WINDOWS
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int CStr::FindExtPos() const
|
|
{
|
|
int nPos = rFind('.');
|
|
|
|
if (nPos != -1) {
|
|
const CHAR* pcsz = &m_psz[nPos + 1];
|
|
if (strchr(pcsz, '\\') || strchr(pcsz, '/')) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return nPos;
|
|
}
|
|
|
|
const char* CStr::FindExt() const
|
|
{
|
|
int nPos = FindExtPos();
|
|
|
|
if (nPos != -1) {
|
|
return &m_psz[nPos];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOL CStr::ChangeExt(const char* pcszExt, BOOL bForce/* = TRUE*/)
|
|
{
|
|
ASSERT_VALIDSTRING(pcszExt);
|
|
int nPos = FindExtPos();
|
|
|
|
if (nPos != -1) {
|
|
if (bForce && strcmp(&m_psz[nPos], pcszExt)) {
|
|
int nLen = nPos + strlen(pcszExt);
|
|
CHAR* psz = GetBuffer(nLen);
|
|
strcpy(&psz[nPos], pcszExt);
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
if (*pcszExt) {
|
|
*this += pcszExt;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
const CHAR* CStr::Left(CStr& strLeft, int nCount) const
|
|
{
|
|
ASSERT(&strLeft);
|
|
ASSERT(nCount >= 0);
|
|
ASSERT_VALIDSTRING(strLeft);
|
|
ASSERT_VALIDSTRING(m_psz);
|
|
|
|
CHAR* psz = m_psz;
|
|
int n = strlen(psz);
|
|
|
|
if ((n -= nCount) < 0) {
|
|
nCount += n;
|
|
return strLeft.Assign(psz, nCount);
|
|
} else {
|
|
CHAR* p = strLeft.GetBuffer(nCount);
|
|
memcpy(p, psz, nCount);
|
|
return strLeft.ReleaseBuffer(nCount);
|
|
}
|
|
}
|
|
|
|
const CHAR* CStr::Right(CStr& strRight, int nCount) const
|
|
{
|
|
ASSERT(&strRight);
|
|
ASSERT(nCount >= 0);
|
|
ASSERT_VALIDSTRING(strRight);
|
|
ASSERT_VALIDSTRING(m_psz);
|
|
|
|
CHAR* psz = m_psz;
|
|
int n = strlen(psz);
|
|
|
|
if ((n -= nCount) < 0) {
|
|
nCount += n;
|
|
} else {
|
|
psz += n;
|
|
}
|
|
|
|
return strRight.Assign(psz, nCount);
|
|
}
|
|
|
|
const CHAR* CStr::Mid(CStr& strMid, int nFirst) const
|
|
{
|
|
ASSERT(&strMid);
|
|
ASSERT(nFirst >= 0);
|
|
ASSERT_VALIDSTRING(strMid);
|
|
ASSERT_VALIDSTRING(m_psz);
|
|
|
|
CHAR* psz = m_psz;
|
|
int n = strlen(psz);
|
|
|
|
if (nFirst > 0) {
|
|
if ((n -= nFirst) <= 0) {
|
|
strMid.Empty();
|
|
return l_SZ;
|
|
}
|
|
psz += nFirst;
|
|
}
|
|
|
|
return strMid.Assign(psz, n);
|
|
}
|
|
|
|
const CHAR* CStr::Mid(CStr& strMid, int nFirst, int nCount) const
|
|
{
|
|
ASSERT(&strMid);
|
|
ASSERT(nFirst >= 0);
|
|
ASSERT(nCount >= 0);
|
|
ASSERT_VALIDSTRING(strMid);
|
|
ASSERT_VALIDSTRING(m_psz);
|
|
|
|
if (nCount <= 0) {
|
|
return strMid.Empty();
|
|
}
|
|
|
|
CHAR* psz = m_psz;
|
|
int n = strlen(psz);
|
|
|
|
if (nFirst > 0) {
|
|
if ((n -= nFirst) <= 0) {
|
|
return strMid.Empty();
|
|
}
|
|
psz += nFirst;
|
|
}
|
|
|
|
if ((n -= nCount) <= 0) {
|
|
nCount += n;
|
|
return strMid.Assign(psz, nCount);
|
|
} else {
|
|
CHAR* p = strMid.GetBuffer(nCount);
|
|
memcpy(p, psz, nCount);
|
|
return strMid.ReleaseBuffer(nCount);
|
|
}
|
|
}
|
|
|
|
CStr CStr::Left(int nCount) const
|
|
{
|
|
CStr str;
|
|
|
|
Left(str, nCount);
|
|
|
|
return str;
|
|
}
|
|
|
|
CStr CStr::Right(int nCount) const
|
|
{
|
|
CStr str;
|
|
|
|
Right(str, nCount);
|
|
|
|
return str;
|
|
}
|
|
|
|
CStr CStr::Mid(int nFirst) const
|
|
{
|
|
CStr str;
|
|
|
|
Mid(str, nFirst);
|
|
|
|
return str;
|
|
}
|
|
|
|
CStr CStr::Mid(int nFirst, int nCount) const
|
|
{
|
|
CStr str;
|
|
|
|
Mid(str, nFirst, nCount);
|
|
|
|
return str;
|
|
}
|
|
|
|
int CStr::FindNext(CHAR c, int nStart) const
|
|
{
|
|
ASSERT(c != '\0');
|
|
const CHAR* pcsz = m_psz;
|
|
if (nStart < 0)
|
|
nStart = 0;
|
|
else {
|
|
ASSERT(strlen(pcsz) >= nStart);
|
|
pcsz = &pcsz[nStart];
|
|
if (*pcsz == '\0')
|
|
return -1;
|
|
pcsz++;
|
|
nStart++;
|
|
}
|
|
const char* pcsz2 = strchr(pcsz, c);
|
|
|
|
if (pcsz2) {
|
|
return nStart + (pcsz2 - pcsz);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int CStr::Find(CHAR c) const
|
|
{
|
|
ASSERT(c != '\0');
|
|
const CHAR* pcsz = strchr(m_psz, c);
|
|
|
|
if (pcsz) {
|
|
return pcsz - m_psz;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int CStr::rFindNext(CHAR c, int nStart) const
|
|
{
|
|
ASSERT(c != '\0');
|
|
const CHAR* pcsz = m_psz;
|
|
const CHAR* pcsz2 = pcsz;
|
|
if (nStart < 0) {
|
|
nStart = strlen(pcsz2);
|
|
} else {
|
|
ASSERT(strlen(pcsz2) >= nStart);
|
|
}
|
|
pcsz2 = &pcsz2[nStart];
|
|
while(--pcsz2 >= pcsz) {
|
|
nStart--;
|
|
if (*pcsz2 == c) {
|
|
return nStart;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int CStr::rFind(CHAR c) const
|
|
{
|
|
ASSERT(c != '\0');
|
|
const CHAR* pcsz = strrchr(m_psz, c);
|
|
|
|
if (pcsz) {
|
|
return pcsz - m_psz;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int CStr::Find(const CHAR* pcsz, int nStart) const
|
|
{
|
|
ASSERT_VALIDSTRING(pcsz);
|
|
|
|
if (nStart < 0)
|
|
nStart = 0;
|
|
const CHAR* pcsz2 = m_psz;
|
|
while(nStart > 0) {
|
|
if (*pcsz2++ == '\0')
|
|
return -1;
|
|
nStart--;
|
|
}
|
|
pcsz = strstr(pcsz2, pcsz);
|
|
|
|
if (pcsz) {
|
|
return pcsz - m_psz;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int CStr::Find(const CHAR* pcsz) const
|
|
{
|
|
ASSERT_VALIDSTRING(pcsz);
|
|
|
|
pcsz = strstr(m_psz, pcsz);
|
|
|
|
if (pcsz) {
|
|
return pcsz - m_psz;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int CStr::FindSet(const CHAR* pcszSet) const
|
|
{
|
|
ASSERT_VALIDSTRING(pcszSet);
|
|
|
|
CHAR* psz = m_psz;
|
|
CHAR c;
|
|
|
|
while((c = *psz) != '\0') {
|
|
if (strchr(pcszSet, c)) {
|
|
return psz - m_psz;
|
|
}
|
|
psz++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int CStr::rFindSet(const CHAR* pcszSet) const
|
|
{
|
|
ASSERT_VALIDSTRING(pcszSet);
|
|
|
|
CHAR* pszStart = m_psz;
|
|
CHAR* psz = &pszStart[strlen(pszStart)];
|
|
|
|
while(psz-- > pszStart) {
|
|
if (strchr(pcszSet, *psz)) {
|
|
return psz - m_psz;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
#if defined(WIN32)
|
|
BOOL CStr::GetWindowText(HWND hWnd)
|
|
{
|
|
Empty();
|
|
if (IsWindow(hWnd)) {
|
|
int nSize = SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0);
|
|
if (nSize > 0) {
|
|
nSize += 10; // for safety...
|
|
char* p = GetBuffer(nSize + 1/*'\0'*/);
|
|
SendMessage(hWnd, WM_GETTEXT, (WPARAM)nSize, (LPARAM)p);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
#endif // defined(WIN32)
|
|
const CHAR* CStr::AssignNoCheck(const CHAR* pcsz, int nLen/* = -1*/)
|
|
{
|
|
ASSERT(IS_NIL(m_psz)); // when !_DEBUG then m_psz is !initialized...
|
|
|
|
CHAR* psz;
|
|
if (nLen < 0) {
|
|
if (!pcsz || ((nLen = strlen(pcsz)) <= 0)) {
|
|
return m_psz = l_SZ;
|
|
}
|
|
nLen++;
|
|
psz = str_alloc(nLen);
|
|
memcpy(psz, pcsz, nLen);
|
|
} else {
|
|
if (nLen <= 0) {
|
|
return m_psz = l_SZ;
|
|
}
|
|
ASSERT(pcsz);
|
|
ASSERT((UINT)nLen <= strlen(pcsz));
|
|
int nAlloc = nLen + 1;
|
|
psz = str_alloc(nAlloc);
|
|
memcpy(psz, pcsz, nLen);
|
|
psz[nLen] = '\0';
|
|
}
|
|
|
|
return m_psz = psz;
|
|
}
|
|
|
|
CHAR* CStr::Reset()
|
|
{
|
|
#ifdef USE_STRLOCK
|
|
ASSERT(!IsLocked());
|
|
#endif // USE_STRLOCK
|
|
if (!IS_NIL(m_psz)) {
|
|
str_free(m_psz);
|
|
m_psz = l_SZ;
|
|
}
|
|
return l_SZ;
|
|
}
|
|
#ifdef USE_STRLOCK
|
|
CHAR* CStr::ResetLocked()
|
|
{
|
|
ASSERT(IsLocked());
|
|
ASSERT(!IS_NIL(m_psz));
|
|
|
|
m_psz[0] = '\0';
|
|
return m_psz;
|
|
}
|
|
#endif // USE_STRLOCK
|
|
#ifdef USE_CSTR_ISTREAM
|
|
istream& operator >> (istream& s, CStr& str)
|
|
{
|
|
str.Empty();
|
|
|
|
int c;
|
|
|
|
while((c = s.get()) != EOF) {
|
|
if (!isspace(c)) {
|
|
do {
|
|
str += (char)c;
|
|
if ((c = s.get()) == EOF)
|
|
break;
|
|
} while(!isspace(c));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return s;
|
|
}
|
|
#endif // USE_CSTR_ISTREAM
|
|
int CStrArray::GetTotalLength() const
|
|
{
|
|
int nTotalLength;
|
|
int i, nSize = GetSize();
|
|
|
|
nTotalLength = nSize; // nSize���� '\0'
|
|
for(i = 0; i < nSize; i++) {
|
|
nTotalLength += strlen(GetAt(i));
|
|
}
|
|
|
|
return nTotalLength;
|
|
}
|
|
|
|
int CStrArray::Find(const char* pcsz) const
|
|
{
|
|
ASSERT(pcsz);
|
|
int i, nSize = GetSize();
|
|
|
|
for(i = 0; i < nSize; i++) {
|
|
if (strcmp(GetAt(i), pcsz) == 0)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int CStrArray::AddIfNotFound(const char* pcsz)
|
|
{
|
|
int nFound = Find(pcsz);
|
|
if (nFound == -1) {
|
|
Add() = pcsz;
|
|
}
|
|
|
|
return nFound;
|
|
}
|
|
|
|
int CStrArray::AddIfNotFound(const CStr astrs[], int nCount)
|
|
{
|
|
int nAdded = 0;
|
|
|
|
for(int i = 0; i < nCount; i++) {
|
|
if (AddIfNotFound(astrs[i]) == -1)
|
|
nAdded++;
|
|
}
|
|
|
|
return nAdded;
|
|
}
|
|
|
|
void CStrArray::Sort(QSortCompareProc pQSortCompareProc/* = (QSortCompareProc)NULL*/)
|
|
{
|
|
int nSize = GetSize();
|
|
|
|
if (nSize > 0) {
|
|
if (!pQSortCompareProc)
|
|
pQSortCompareProc = str_Compare;
|
|
qsort(&GetAt(0), nSize, sizeof(CStr), pQSortCompareProc);
|
|
}
|
|
}
|
|
|
|
BOOL CStrArray::LoadStrs(const char* pcszFile, const char* pcszVals/* = NULL*/, UINT uFlags/* = 0*/)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
FILE* pFile = fopen(pcszFile, "rt");
|
|
if (pFile) {
|
|
bRet = LoadStrs(pFile, pcszVals, uFlags);
|
|
fclose(pFile);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
BOOL CStrArray::LoadStrs(FILE* pFile, const char* pcszVals/* = NULL*/, UINT uFlags/* = 0*/)
|
|
{
|
|
if (pFile) {
|
|
int c;
|
|
|
|
CStr str;
|
|
while((c = getc(pFile)) != EOF) {
|
|
if (LSTRS_IsValid(c, pcszVals)) {
|
|
str += (char)c;
|
|
} else {
|
|
LSTRS_CheckAdd(str, uFlags);
|
|
}
|
|
}
|
|
LSTRS_CheckAdd(str, uFlags);
|
|
if (uFlags & LSTRS_DOSORT)
|
|
Sort();
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void CStrArray::LSTRS_CheckAdd(CStr& str, UINT uFlags)
|
|
{
|
|
if (!str.IsEmpty()) {
|
|
UINT uFlagsCase = uFlags & (LSTRS_TOLOWER | LSTRS_TOUPPER);
|
|
if (uFlagsCase) {
|
|
if (uFlagsCase & LSTRS_TOLOWER) {
|
|
str.MakeLower();
|
|
if (uFlagsCase & LSTRS_TOUPPER) {
|
|
str.SetAt(0, (CHAR)toupper(str.GetAt(0)));
|
|
}
|
|
} else { // LSTRS_TOUPPER
|
|
str.MakeUpper();
|
|
}
|
|
}
|
|
if (uFlags & LSTRS_ALLOWDUP)
|
|
Add() = str;
|
|
else
|
|
AddIfNotFound(str);
|
|
str.Empty();
|
|
}
|
|
}
|
|
#ifdef _DEBUG
|
|
BOOL IsValidString(const CHAR* pcszStr, int nLen/* = -1*/)
|
|
{
|
|
// nLen: maximum size of string
|
|
return pcszStr && !::IsBadStringPtr(pcszStr, (UINT)nLen);
|
|
}
|
|
|
|
BOOL IsValidStringSafe(const CHAR* pcszStr, int nLen/* = -1*/)
|
|
{
|
|
// nLen: maximum size of string
|
|
return !pcszStr || !::IsBadStringPtr(pcszStr, (UINT)nLen);
|
|
}
|
|
|
|
BOOL IsValidMemory(const void* pMem, UINT uBytes, BOOL bWriteCheck/* = TRUE*/)
|
|
{
|
|
return pMem && !(::IsBadReadPtr(pMem, uBytes) || (bWriteCheck && ::IsBadWritePtr((void*)pMem, uBytes)));
|
|
}
|
|
|
|
BOOL IsValidMemorySafe(const void* pMem, UINT uBytes, BOOL bWriteCheck/* = TRUE*/)
|
|
{
|
|
return !pMem || !(::IsBadReadPtr(pMem, uBytes) || (bWriteCheck && ::IsBadWritePtr((void*)pMem, uBytes)));
|
|
}
|
|
#endif // _DEBUG
|
|
#ifdef _DEBUG
|
|
void CStrTestMain()
|
|
{
|
|
const CHAR* g_pcsza[] = {
|
|
(const CHAR*)NULL,
|
|
"",
|
|
"a",
|
|
"abc ",
|
|
" 123456789",
|
|
" abCABD ",
|
|
};
|
|
int i, nStrs = NOFA(g_pcsza);
|
|
CStr k;
|
|
|
|
TARRAYREF(CStr) aStrs;
|
|
|
|
aStrs.Add(nStrs);
|
|
|
|
for(i = 0; i < nStrs; i++) {
|
|
BOOL bLocked = FALSE;
|
|
if (rand() % 2) {
|
|
aStrs.GetAt(i).GetBuffer(256);
|
|
aStrs.GetAt(i).LockBuffer();
|
|
bLocked = TRUE;
|
|
}
|
|
CStr a(g_pcsza[i]);
|
|
|
|
CStr b(a, a.GetLength());
|
|
|
|
CStr c((CHAR)(rand() % 254 + 1), rand() % 3);
|
|
|
|
a += b;
|
|
a += c;
|
|
b = a;
|
|
c = a;
|
|
|
|
a += (const CHAR*)NULL;
|
|
b += "";
|
|
c += "sl123KDFklsd ";
|
|
|
|
a = c;
|
|
b = c;
|
|
|
|
a.MakeLower();
|
|
b.MakeUpper();
|
|
c.MakeReverse();
|
|
|
|
a += b;
|
|
c = a + b;
|
|
|
|
a.TrimLeft();
|
|
a.TrimRight();
|
|
b.TrimLeft();
|
|
b.TrimRight();
|
|
c.TrimLeft();
|
|
c.TrimRight();
|
|
|
|
aStrs[i] = c;
|
|
|
|
a.Left(k, 1000);
|
|
a.Right(k, 1000);
|
|
a.Mid(k, 1);
|
|
a.Mid(k, 100);
|
|
a.Mid(k, 1, a.GetLength() / 2);
|
|
|
|
b.Left(k, 1000);
|
|
b.Right(k, 1000);
|
|
b.Mid(k, 1);
|
|
b.Mid(k, 100);
|
|
b.Mid(k, 1, a.GetLength() / 2);
|
|
|
|
c.Left(k, 1000);
|
|
c.Right(k, 1000);
|
|
c.Mid(k, 1);
|
|
c.Mid(k, 100);
|
|
c.Mid(k, 1, a.GetLength() / 2);
|
|
|
|
if (bLocked)
|
|
aStrs.GetAt(i).UnlockBuffer();
|
|
}
|
|
}
|
|
#endif // _DEBUG
|