///////////////////////////////////////////////////////////////////////////////////////////////////// // // Crytek Character Animation source code // // History: // 10 Feb. 2003 :- Created by Sergiy Migdalskiy // // Contains: // Generic inline implementations of miscellaneous string manipulation utilities, // including path manipulation // ///////////////////////////////////////////////////////////////////////////////////////////////////// #ifndef _CRY_ENGINE_STRING_UTILS_HDR_ #define _CRY_ENGINE_STRING_UTILS_HDR_ #if defined(LINUX) #include #endif namespace CryStringUtils { // removes the extension from the file path inline void StripFileExtension (string&strFileName) { for (const char* p = strFileName.c_str() + strFileName.length() - 1; p >= strFileName.c_str(); --p) { switch (*p) { case ':': case '/': case '\\': // we've reached a path separator - it means there's no extension in this name return; case '.': // there's an extension in this file name strFileName.resize (p - strFileName.c_str()); return; } }; // it seems the file name is a pure name, without path or extension } // replaces the given file extension with the new one // NOTE: the new extension must NOTE be with the dot inline void ReplaceExtension (string& strFileName, const char* szNewExtension) { StripFileExtension(strFileName); strFileName += '.'; strFileName += szNewExtension; } // removes the extension from the file path inline char* StripFileExtension (char * szFilePath) { for (char* p = szFilePath + (int)strlen(szFilePath)-1; p >= szFilePath; --p) { switch(*p) { case ':': case '/': case '\\': // we've reached a path separator - it means there's no extension in this name return NULL; case '.': // there's an extension in this file name *p = '\0'; return p+1; } } // it seems the file name is a pure name, without path or extension return NULL; } // returns the parent directory of the given file or directory. // the returned path is WITHOUT the trailing slash // if the input path has a trailing slash, it's ignored // nGeneration - is the number of parents to scan up template String GetParentDirectory (const String& strFilePath, int nGeneration = 1) { for (const char* p = strFilePath.c_str() + strFilePath.length() - 2; // -2 is for the possible trailing slash: there always must be some trailing symbol which is the file/directory name for which we should get the parent p >= strFilePath.c_str(); --p) { switch (*p) { case ':': return String (strFilePath.c_str(), p); break; case '/': case '\\': // we've reached a path separator - return everything before it. if (!--nGeneration) return String(strFilePath.c_str(), p); break; } }; // it seems the file name is a pure name, without path or extension return String(); } // converts all chars to lower case inline string toLower (const string& str) { string strResult = str; for (string::iterator it = strResult.begin(); it != strResult.end(); ++it) *it = tolower (*it); return strResult; } // searches and returns the pointer to the extension of the given file inline const char* FindExtension (const char* szFileName) { const char* szEnd = szFileName + (int)strlen(szFileName); for (const char* p = szEnd-1; p >= szFileName; --p) if (*p == '.') return p+1; return szEnd; } // searches and returns the pointer to the file name in the given file path inline const char* FindFileNameInPath (const char* szFilePath) { for (const char* p = szFilePath + (int)strlen(szFilePath)-1; p >= szFilePath; --p) if (*p == '\\' || *p == '/') return p+1; return szFilePath; } // works like strstr, but is case-insensitive inline const char* stristr(const char* szString, const char* szSubstring) { int nSuperstringLength = (int)strlen(szString); int nSubstringLength = (int)strlen(szSubstring); for (int nSubstringPos = 0; nSubstringPos <= nSuperstringLength - nSubstringLength; ++nSubstringPos) { if (strnicmp(szString+nSubstringPos, szSubstring, nSubstringLength) == 0) return szString+nSubstringPos; } return NULL; } // replaces slashes inline void UnifyFilePath (string& strPath) { // replace all forward slashes with backslashes for (string::iterator itPath = strPath.begin(); itPath != strPath.end(); ++itPath) if (*itPath == '/') *itPath = '\\'; else *itPath = tolower (*itPath); } // converts the number to a string inline string toString(unsigned nNumber) { char szNumber[16]; sprintf (szNumber, "%u", nNumber); return szNumber; } inline string toString(signed int nNumber) { char szNumber[16]; sprintf (szNumber, "%d", nNumber); return szNumber; } #ifdef MATRIX_H inline string toString(const Matrix44& m) { char szBuf[0x200]; sprintf (szBuf, "{%g,%g,%g,%g}{%g,%g,%g,%g}{%g,%g,%g,%g}{%g,%g,%g,%g}", m(0,0),m(0,1),m(0,2),m(0,3), m(1,0),m(1,1),m(1,2),m(1,3), m(2,0),m(2,1),m(2,2),m(2,3), m(3,0),m(3,1),m(3,2),m(3,3)); return szBuf; } #endif #ifdef _CRYQUAT_H inline string toString (const CryQuat& q) { char szBuf[0x100]; sprintf (szBuf, "{%g,{%g,%g,%g}}", q.w, q.v.x, q.v.y, q.v.z); return szBuf; } #endif #ifdef VECTOR_H inline string toString (const Vec3& v) { char szBuf[0x80]; sprintf (szBuf, "{%g,%g,%g}", v.x, v.y, v.z); return szBuf; } #endif // does the same as strstr, but the szString is allowed to be no more than the specified size inline const char* strnstr (const char* szString, const char* szSubstring, int nSuperstringLength) { int nSubstringLength = (int)strlen(szSubstring); if (!nSubstringLength) return szString; for (int nSubstringPos = 0; szString[nSubstringPos] && nSubstringPos < nSuperstringLength - nSubstringLength; ++nSubstringPos) { if (strncmp(szString+nSubstringPos, szSubstring, nSubstringLength) == 0) return szString+nSubstringPos; } return NULL; } // calculates the number of characters in the given string, limited by the end pointer inline int strnlen (const char* szString, const char* szStringEnd) { const char* p; for (p = szString; p < szStringEnd && *p; ++p) continue; return p - szString; } // finds the string in the array of strings // returns its 0-based index or -1 if not found // comparison is case-sensitive // The string array end is demarked by the NULL value inline int findString(const char* szString, const char* arrStringList[]) { for (const char** p = arrStringList; *p; ++p) { if (0 == strcmp(*p, szString)) return p - arrStringList; } return -1; // string was not found } // This function is used for printing out sets of objects of string type. // just forms the comma-delimited string where each string in the set is presented as a formatted substring inline string toString (const std::set& setStrings) { string strResult; if (!setStrings.empty ()) { strResult += "{"; for (std::set::const_iterator it = setStrings.begin(); it != setStrings.end(); ++it) { if (it != setStrings.begin()) strResult += ", "; strResult += "\""; strResult += *it; strResult += "\""; } strResult += "}"; } return strResult ; } // cuts the string and adds leading ... if it's longer than specified maximum length inline string cutString (const string& strPath, unsigned nMaxLength) { if (strPath.length() > nMaxLength && nMaxLength > 3) return string("...") + string (strPath.c_str() + strPath.length() - (nMaxLength - 3)); else return strPath; } /* ////////////////////////////////////////////////////////////////////////// // this class is used for Boyer/Moore/Gosper-assisted 'egrep' search // First, upon construction, the delta table is compiled (you don't need to know what it is, // just believe me - it's needed for fast substring search). Then you can pass the actual text // buffer(s) to search the substring in. class CBMGSubstring { public: enum {g_nMaxPatternLength = 64}; // initializes the pattern substring; this must not be bigger than g_nMaxPatternLength CBMGSubstring(const char* szPattern); // searchees for the substring in the given text buffer const char* findSubstringIn (const char* szBuffer, int nBufLength); protected: string m_strPattern; unsigned char m_arrDelta[0x100]; }; CBMGSubstring::CBMGSubstring (const char* szPattern): m_strPattern (szPattern) { int nPatternLength = m_strPattern.length(); // pattern length if (nPatternLength > g_nMaxPatternLength) m_strPattern.resize (nPatternLength = g_nMaxPatternLength); memset(m_arrDelta, nPatternLength, sizeof(m_arrDelta)); for (int i = 0; i < nPatternLength; ++i) m_arrDelta[((const unsigned char *)szPattern)[i]] = nPatternLength - i - 1; } // searchees for the substring in the given text buffer const char* CBMGSubstring::findSubstringIn (const char* buffer, int buflen) { char *s; // temp ptr for comparisons int inc, // position increment k, // current buffer index nhits, // match ctr patlen; // pattern length nhits = 0; k = (patlen = m_strPattern.length()) - 1; for (;;) { // the following (unsigned char *) type casts save a // few clocks by freeing us from some XCHGs while ((inc = m_arrDelta[((unsigned char *)buffer)[k]]) && ((k += inc) < buflen)) ; if (k >= buflen) return NULL; // we didn't find s = buffer + (k++ - (patlen - 1)); if (!strncmp(s, m_strPattern.c_str(), patlen)) return s; } return NULL; } */ ////////////////////////////////////////////////////////////////////////// // Converts the given set of NUMBERS into the string template string toString (const std::set& setMtls, const char* szFormat, const char* szPostfix = "") { string strResult; char szBuffer[64]; if (!setMtls.empty ()) { strResult += strResult.empty()?"(":" ("; for (typename std::set::const_iterator it = setMtls.begin(); it != setMtls.end(); ) { if (it != setMtls.begin()) strResult += ", "; sprintf (szBuffer, szFormat, *it); strResult += szBuffer; T nStart = *it; ++it; if (it != setMtls.end() && *it == nStart + 1) { T nPrev = *it; // we've got a region while (++it !=setMtls.end() && *it == nPrev+1) nPrev = *it; if (nPrev == nStart + 1) // special case - range of length 1 strResult += ","; else strResult += ".."; sprintf (szBuffer, szFormat, nPrev); strResult += szBuffer; } } strResult += ")"; } return szPostfix[0]?strResult+szPostfix:strResult; } // returns true if the string matches the wildcard inline bool MatchWildcard (const char* szString, const char* szWildcard) { const char* pString = szString, *pWildcard = szWildcard; // skip the obviously the same starting substring while (*pWildcard && *pWildcard != '*' && *pWildcard != '?') if (*pString != *pWildcard) return false; // must be exact match unless there's a wildcard character in the wildcard string else ++pString, ++pWildcard; if (!*pString) { // this will only match if there are no non-wild characters in the wildcard for(; *pWildcard; ++pWildcard) if (*pWildcard != '*' && *pWildcard != '?') return false; return true; } switch(*pWildcard) { case '\0': return false; // the only way to match them after the leading non-wildcard characters is !*pString, which was already checked // we have a wildcard with wild character at the start. case '*': { // merge consecutive ? and *, since they are equivalent to a single * while (*pWildcard == '*' || *pWildcard == '?') ++pWildcard; if (!*pWildcard) return true; // the rest of the string doesn't matter: the wildcard ends with * for (; *pString; ++pString) if (MatchWildcard(pString, pWildcard)) return true; return false; } case '?': return MatchWildcard(pString+1, pWildcard + 1) || MatchWildcard(pString, pWildcard+1); default: assert (0); return false; } } // the type used to parse a yes/no string in the CAL file enum YesNoType { nYNT_Yes, nYNT_No, nYNT_Invalid }; // parse the yes/no string in the cal file inline YesNoType toYesNoType(const char* szString) { if (!stricmp(szString, "yes") || !stricmp(szString, "enable") || !stricmp(szString, "1")) return nYNT_Yes; if (!stricmp(szString, "no") || !stricmp(szString, "disable") || !stricmp(szString, "0")) return nYNT_No; return nYNT_Invalid; } } #endif