2003-02-16 04:01:45 +00:00
|
|
|
#include "global.h"
|
2001-11-03 10:52:42 +00:00
|
|
|
#include "RageUtil.h"
|
2005-05-19 23:29:39 +00:00
|
|
|
#include "RageMath.h"
|
2003-07-03 02:41:27 +00:00
|
|
|
#include "RageLog.h"
|
2003-12-07 23:43:58 +00:00
|
|
|
#include "RageFile.h"
|
2002-07-23 01:41:40 +00:00
|
|
|
|
2002-10-24 07:09:38 +00:00
|
|
|
#include <numeric>
|
2004-04-05 05:22:32 +00:00
|
|
|
#include <ctime>
|
2003-01-06 02:48:14 +00:00
|
|
|
#include <map>
|
2003-07-03 09:22:03 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
2003-02-15 00:32:32 +00:00
|
|
|
|
2004-10-07 04:10:45 +00:00
|
|
|
int randseed = time(NULL);
|
|
|
|
|
|
|
|
|
|
// From "Numerical Recipes in C".
|
|
|
|
|
float RandomFloat( int &seed )
|
|
|
|
|
{
|
|
|
|
|
const int MASK = 123459876;
|
|
|
|
|
seed ^= MASK;
|
|
|
|
|
|
|
|
|
|
const int IA = 16807;
|
|
|
|
|
const int IM = 2147483647;
|
|
|
|
|
|
|
|
|
|
const int IQ = 127773;
|
|
|
|
|
const int IR = 2836;
|
|
|
|
|
|
|
|
|
|
long k = seed / IQ;
|
|
|
|
|
seed = IA*(seed-k*IQ)-IR*k;
|
|
|
|
|
if( seed < 0 )
|
|
|
|
|
seed += IM;
|
|
|
|
|
|
|
|
|
|
const float AM = .999999f / IM;
|
|
|
|
|
float ans = AM * seed;
|
|
|
|
|
|
|
|
|
|
seed ^= MASK;
|
|
|
|
|
return ans;
|
|
|
|
|
}
|
2003-01-03 05:26:15 +00:00
|
|
|
|
2003-07-30 20:31:50 +00:00
|
|
|
RandomGen::RandomGen( unsigned long seed_ )
|
|
|
|
|
{
|
|
|
|
|
seed = seed_;
|
|
|
|
|
if(seed == 0)
|
|
|
|
|
seed = time(NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int RandomGen::operator() ( int maximum )
|
|
|
|
|
{
|
2004-10-07 04:10:45 +00:00
|
|
|
return int(RandomFloat( seed ) * maximum);
|
2003-07-30 20:31:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-04-16 22:49:40 +00:00
|
|
|
void fapproach( float& val, float other_val, float to_move )
|
|
|
|
|
{
|
2004-06-16 00:38:31 +00:00
|
|
|
ASSERT_M( to_move >= 0, ssprintf("to_move: %f", to_move) );
|
2003-04-16 22:49:40 +00:00
|
|
|
if( val == other_val )
|
|
|
|
|
return;
|
|
|
|
|
float fDelta = other_val - val;
|
|
|
|
|
float fSign = fDelta / fabsf( fDelta );
|
|
|
|
|
float fToMove = fSign*to_move;
|
|
|
|
|
if( fabsf(fToMove) > fabsf(fDelta) )
|
|
|
|
|
fToMove = fDelta; // snap
|
|
|
|
|
val += fToMove;
|
|
|
|
|
}
|
|
|
|
|
|
2003-02-05 20:18:02 +00:00
|
|
|
/* Return a positive x mod y. */
|
|
|
|
|
float fmodfp(float x, float y)
|
|
|
|
|
{
|
|
|
|
|
x = fmodf(x, y); /* x is [-y,y] */
|
|
|
|
|
x += y; /* x is [0,y*2] */
|
|
|
|
|
x = fmodf(x, y); /* x is [0,y] */
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
|
2002-12-20 06:28:22 +00:00
|
|
|
int power_of_two(int input)
|
|
|
|
|
{
|
|
|
|
|
int value = 1;
|
|
|
|
|
|
|
|
|
|
while ( value < input ) value <<= 1;
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-03 05:26:15 +00:00
|
|
|
bool IsAnInt( const CString &s )
|
2001-12-28 10:15:59 +00:00
|
|
|
{
|
2003-05-22 21:13:02 +00:00
|
|
|
if( !s.size() )
|
2001-12-28 10:15:59 +00:00
|
|
|
return false;
|
|
|
|
|
|
2002-12-17 06:47:19 +00:00
|
|
|
for( int i=0; s[i]; i++ )
|
2001-12-28 10:15:59 +00:00
|
|
|
if( s[i] < '0' || s[i] > '9' )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-03 22:51:01 +00:00
|
|
|
bool IsHexVal( const CString &s )
|
|
|
|
|
{
|
2003-05-22 21:13:29 +00:00
|
|
|
if( !s.size() )
|
2003-01-03 22:51:01 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for( int i=0; s[i]; i++ )
|
|
|
|
|
if( !(s[i] >= '0' && s[i] <= '9') &&
|
|
|
|
|
!(toupper(s[i]) >= 'A' && toupper(s[i]) <= 'F'))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-22 19:51:46 +00:00
|
|
|
float HHMMSSToSeconds( const CString &sHHMMSS )
|
2002-04-28 20:42:32 +00:00
|
|
|
{
|
|
|
|
|
CStringArray arrayBits;
|
2004-02-22 19:51:46 +00:00
|
|
|
split( sHHMMSS, ":", arrayBits, false );
|
2002-04-28 20:42:32 +00:00
|
|
|
|
2002-10-31 02:11:52 +00:00
|
|
|
while( arrayBits.size() < 3 )
|
2002-10-24 19:55:09 +00:00
|
|
|
arrayBits.insert(arrayBits.begin(), "0" ); // pad missing bits
|
2002-04-28 20:42:32 +00:00
|
|
|
|
|
|
|
|
float fSeconds = 0;
|
|
|
|
|
fSeconds += atoi( arrayBits[0] ) * 60 * 60;
|
|
|
|
|
fSeconds += atoi( arrayBits[1] ) * 60;
|
2004-08-10 20:57:59 +00:00
|
|
|
fSeconds += strtof( arrayBits[2], NULL );
|
2002-04-28 20:42:32 +00:00
|
|
|
|
|
|
|
|
return fSeconds;
|
|
|
|
|
}
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2004-02-22 19:51:46 +00:00
|
|
|
CString SecondsToHHMMSS( float fSecs )
|
2002-08-01 13:42:56 +00:00
|
|
|
{
|
2003-06-24 20:21:09 +00:00
|
|
|
const int iMinsDisplay = (int)fSecs/60;
|
|
|
|
|
const int iSecsDisplay = (int)fSecs - iMinsDisplay*60;
|
2004-02-22 19:51:46 +00:00
|
|
|
CString sReturn = ssprintf( "%02d:%02d:%02d", iMinsDisplay/60, iMinsDisplay%60, iSecsDisplay );
|
|
|
|
|
return sReturn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString SecondsToMMSSMsMs( float fSecs )
|
|
|
|
|
{
|
|
|
|
|
const int iMinsDisplay = (int)fSecs/60;
|
|
|
|
|
const int iSecsDisplay = (int)fSecs - iMinsDisplay*60;
|
2004-06-25 23:39:40 +00:00
|
|
|
const int iLeftoverDisplay = (int) ( (fSecs - iMinsDisplay*60 - iSecsDisplay) * 100 );
|
|
|
|
|
CString sReturn = ssprintf( "%02d:%02d.%02d", iMinsDisplay, iSecsDisplay, min(99,iLeftoverDisplay) );
|
2004-02-22 19:51:46 +00:00
|
|
|
return sReturn;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-08 22:03:41 +00:00
|
|
|
CString SecondsToMSSMsMs( float fSecs )
|
|
|
|
|
{
|
|
|
|
|
const int iMinsDisplay = (int)fSecs/60;
|
|
|
|
|
const int iSecsDisplay = (int)fSecs - iMinsDisplay*60;
|
|
|
|
|
const int iLeftoverDisplay = (int) ( (fSecs - iMinsDisplay*60 - iSecsDisplay) * 100 );
|
|
|
|
|
CString sReturn = ssprintf( "%01d:%02d.%02d", iMinsDisplay, iSecsDisplay, min(99,iLeftoverDisplay) );
|
|
|
|
|
return sReturn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#include "LuaFunctions.h"
|
|
|
|
|
#include "LuaManager.h"
|
2005-05-29 02:21:24 +00:00
|
|
|
LuaFunction( SecondsToMSSMsMs, SecondsToMSSMsMs( FArg(1) ) )
|
2005-05-08 22:03:41 +00:00
|
|
|
|
2004-04-06 23:21:27 +00:00
|
|
|
CString SecondsToMMSSMsMsMs( float fSecs )
|
|
|
|
|
{
|
|
|
|
|
const int iMinsDisplay = (int)fSecs/60;
|
|
|
|
|
const int iSecsDisplay = (int)fSecs - iMinsDisplay*60;
|
2004-06-25 23:39:40 +00:00
|
|
|
const int iLeftoverDisplay = (int) ( (fSecs - iMinsDisplay*60 - iSecsDisplay) * 1000 );
|
|
|
|
|
CString sReturn = ssprintf( "%02d:%02d.%03d", iMinsDisplay, iSecsDisplay, min(999,iLeftoverDisplay) );
|
2004-04-06 23:21:27 +00:00
|
|
|
return sReturn;
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-22 19:51:46 +00:00
|
|
|
CString PrettyPercent( float fNumerator, float fDenominator)
|
|
|
|
|
{
|
2004-02-22 21:22:27 +00:00
|
|
|
return ssprintf("%0.2f%%",fNumerator/fDenominator*100);
|
2004-02-22 19:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString Commify( int iNum )
|
|
|
|
|
{
|
|
|
|
|
CString sNum = ssprintf("%d",iNum);
|
|
|
|
|
CString sReturn;
|
2004-02-22 21:13:27 +00:00
|
|
|
for( unsigned i=0; i<sNum.length(); i++ )
|
2003-06-24 20:21:09 +00:00
|
|
|
{
|
2004-02-22 19:51:46 +00:00
|
|
|
char cDigit = sNum[sNum.length()-1-i];
|
|
|
|
|
if( i!=0 && i%3 == 0 )
|
|
|
|
|
sReturn = ',' + sReturn;
|
|
|
|
|
sReturn = cDigit + sReturn;
|
2002-10-16 18:25:14 +00:00
|
|
|
}
|
2002-10-02 05:42:59 +00:00
|
|
|
return sReturn;
|
2002-08-01 13:42:56 +00:00
|
|
|
}
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2005-05-19 23:29:39 +00:00
|
|
|
CString FormatNumberAndSuffix( int i )
|
2005-03-31 02:26:37 +00:00
|
|
|
{
|
|
|
|
|
CString sSuffix;
|
|
|
|
|
switch( i%10 )
|
|
|
|
|
{
|
|
|
|
|
case 1: sSuffix = "st"; break;
|
|
|
|
|
case 2: sSuffix = "nd"; break;
|
|
|
|
|
case 3: sSuffix = "rd"; break;
|
|
|
|
|
default: sSuffix = "th"; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// "11th", "113th", etc.
|
|
|
|
|
if( ((i%100) / 10) == 1 )
|
|
|
|
|
sSuffix = "th";
|
|
|
|
|
|
|
|
|
|
return ssprintf("%i", i) + sSuffix;
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-14 11:19:18 +00:00
|
|
|
struct tm GetLocalTime()
|
2004-02-14 08:27:09 +00:00
|
|
|
{
|
|
|
|
|
const time_t t = time(NULL);
|
|
|
|
|
struct tm tm;
|
|
|
|
|
localtime_r( &t, &tm );
|
2004-02-14 11:19:18 +00:00
|
|
|
return tm;
|
2004-02-14 08:27:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-08-29 21:02:04 +00:00
|
|
|
CString ssprintf( const char *fmt, ...)
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
|
|
|
|
va_list va;
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
|
return vssprintf(fmt, va);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-08-29 21:02:04 +00:00
|
|
|
CString vssprintf( const char *fmt, va_list argList)
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
|
|
|
|
CString str;
|
|
|
|
|
str.FormatV(fmt, argList);
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
2002-12-09 03:09:59 +00:00
|
|
|
#ifdef WIN32
|
2005-05-11 04:43:54 +00:00
|
|
|
|
2003-07-14 22:23:04 +00:00
|
|
|
#ifdef _XBOX
|
2005-05-11 04:43:54 +00:00
|
|
|
# include <D3DX8Core.h>
|
2003-07-14 22:23:04 +00:00
|
|
|
#else
|
2005-05-11 04:43:54 +00:00
|
|
|
# include <windows.h>
|
|
|
|
|
# include <dxerr8.h>
|
2005-05-30 23:43:37 +00:00
|
|
|
# if defined(_MSC_VER) && !defined(_XBOX)
|
2005-05-11 04:43:54 +00:00
|
|
|
# pragma comment(lib, "dxerr8.lib")
|
|
|
|
|
# endif
|
2003-07-14 22:23:04 +00:00
|
|
|
#endif
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-12-09 03:09:59 +00:00
|
|
|
CString hr_ssprintf( int hr, const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list va;
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
|
CString s = vssprintf( fmt, va );
|
|
|
|
|
va_end(va);
|
|
|
|
|
|
2003-07-14 22:23:04 +00:00
|
|
|
#ifdef _XBOX
|
2003-07-11 03:15:28 +00:00
|
|
|
char szError[1024] = "";
|
|
|
|
|
D3DXGetErrorString( hr, szError, sizeof(szError) );
|
2003-07-14 22:23:04 +00:00
|
|
|
#else
|
|
|
|
|
const char *szError = DXGetErrorString8( hr );
|
|
|
|
|
#endif
|
|
|
|
|
|
2003-07-11 03:15:28 +00:00
|
|
|
return s + ssprintf( " (%s)", szError );
|
2002-12-09 03:09:59 +00:00
|
|
|
}
|
2002-12-21 08:40:40 +00:00
|
|
|
|
|
|
|
|
CString werr_ssprintf( int err, const char *fmt, ...)
|
|
|
|
|
{
|
2003-05-22 05:28:37 +00:00
|
|
|
char buf[1024] = "";
|
|
|
|
|
#ifndef _XBOX
|
2002-12-21 08:40:40 +00:00
|
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
|
|
|
|
|
0, err, 0, buf, sizeof(buf), NULL);
|
2003-05-22 05:28:37 +00:00
|
|
|
#endif
|
2002-12-21 08:40:40 +00:00
|
|
|
|
2003-12-21 07:04:33 +00:00
|
|
|
/* Why is FormatMessage returning text ending with \r\n? */
|
|
|
|
|
CString text = buf;
|
|
|
|
|
text.Replace( "\n", "" );
|
|
|
|
|
text.Replace( "\r", " " ); /* foo\r\nbar -> foo bar */
|
|
|
|
|
TrimRight( text ); /* "foo\r\n" -> "foo" */
|
|
|
|
|
|
|
|
|
|
va_list va;
|
2002-12-21 08:40:40 +00:00
|
|
|
va_start(va, fmt);
|
|
|
|
|
CString s = vssprintf( fmt, va );
|
|
|
|
|
va_end(va);
|
|
|
|
|
|
2003-12-21 07:04:33 +00:00
|
|
|
return s += ssprintf( " (%s)", text.c_str() );
|
2002-12-21 08:40:40 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-20 00:03:09 +00:00
|
|
|
CString ConvertWstringToACP( wstring s )
|
|
|
|
|
{
|
|
|
|
|
if( s.empty() )
|
|
|
|
|
return "";
|
|
|
|
|
|
|
|
|
|
int iBytes = WideCharToMultiByte( CP_ACP, 0, s.data(), s.size(),
|
|
|
|
|
NULL, 0, NULL, FALSE );
|
|
|
|
|
ASSERT_M( iBytes > 0, werr_ssprintf( GetLastError(), "WideCharToMultiByte" ).c_str() );
|
|
|
|
|
|
|
|
|
|
CString ret;
|
|
|
|
|
WideCharToMultiByte( CP_ACP, 0, s.data(), s.size(),
|
|
|
|
|
ret.GetBuf( iBytes ), iBytes, NULL, FALSE );
|
|
|
|
|
ret.RelBuf( iBytes );
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString ConvertUTF8ToACP( CString s )
|
|
|
|
|
{
|
|
|
|
|
return ConvertWstringToACP( CStringToWstring(s) );
|
|
|
|
|
}
|
|
|
|
|
|
2002-12-09 03:09:59 +00:00
|
|
|
#endif
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2002-06-24 22:04:31 +00:00
|
|
|
CString join( const CString &Deliminator, const CStringArray& Source)
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2002-10-31 02:11:52 +00:00
|
|
|
if( Source.empty() )
|
2002-06-27 17:49:10 +00:00
|
|
|
return "";
|
|
|
|
|
|
2001-11-03 10:52:42 +00:00
|
|
|
CString csTmp;
|
|
|
|
|
|
|
|
|
|
// Loop through the Array and Append the Deliminator
|
2002-10-31 02:11:52 +00:00
|
|
|
for( unsigned iNum = 0; iNum < Source.size()-1; iNum++ ) {
|
2002-10-24 19:55:09 +00:00
|
|
|
csTmp += Source[iNum];
|
2001-11-03 10:52:42 +00:00
|
|
|
csTmp += Deliminator;
|
|
|
|
|
}
|
2002-10-24 19:55:09 +00:00
|
|
|
csTmp += Source.back();
|
2002-06-24 22:04:31 +00:00
|
|
|
return csTmp;
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-07 22:54:50 +00:00
|
|
|
CString join( const CString &Delimitor, CStringArray::const_iterator begin, CStringArray::const_iterator end )
|
|
|
|
|
{
|
|
|
|
|
if( begin == end )
|
|
|
|
|
return "";
|
|
|
|
|
|
|
|
|
|
CString ret;
|
|
|
|
|
while( begin != end )
|
|
|
|
|
{
|
|
|
|
|
ret += *begin;
|
|
|
|
|
++begin;
|
|
|
|
|
if( begin != end )
|
|
|
|
|
ret += Delimitor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-05 03:59:07 +00:00
|
|
|
template <class S>
|
2004-10-03 14:41:28 +00:00
|
|
|
static int DelimitorLength( const S &Delimitor )
|
|
|
|
|
{
|
|
|
|
|
return Delimitor.size();
|
|
|
|
|
}
|
|
|
|
|
|
2004-11-08 07:12:17 +00:00
|
|
|
static int DelimitorLength( char Delimitor )
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int DelimitorLength( wchar_t Delimitor )
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-03 14:41:28 +00:00
|
|
|
template <class S, class C>
|
|
|
|
|
void do_split( const S &Source, const C Delimitor, vector<S> &AddIt, const bool bIgnoreEmpty )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2005-04-24 06:32:51 +00:00
|
|
|
/* Short-circuit if the source is empty; we want to return an empty vector if
|
|
|
|
|
* the string is empty, even if bIgnoreEmpty is true. */
|
|
|
|
|
if( Source.empty() )
|
|
|
|
|
return;
|
|
|
|
|
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t startpos = 0;
|
2001-11-03 10:52:42 +00:00
|
|
|
|
|
|
|
|
do {
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t pos;
|
2004-10-03 14:41:28 +00:00
|
|
|
pos = Source.find( Delimitor, startpos );
|
2003-10-28 22:37:20 +00:00
|
|
|
if( pos == Source.npos )
|
|
|
|
|
pos = Source.size();
|
|
|
|
|
|
|
|
|
|
if( pos-startpos > 0 || !bIgnoreEmpty )
|
|
|
|
|
{
|
2004-10-03 14:41:28 +00:00
|
|
|
/* Optimization: if we're copying the whole string, avoid substr; this
|
|
|
|
|
* allows this copy to be refcounted, which is much faster. */
|
|
|
|
|
if( startpos == 0 && pos-startpos == Source.size() )
|
|
|
|
|
AddIt.push_back(Source);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const S AddCString = Source.substr(startpos, pos-startpos);
|
|
|
|
|
AddIt.push_back(AddCString);
|
|
|
|
|
}
|
2003-10-28 22:37:20 +00:00
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2004-10-03 14:41:28 +00:00
|
|
|
startpos = pos+DelimitorLength(Delimitor);
|
2003-01-05 03:59:07 +00:00
|
|
|
} while ( startpos <= Source.size() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-11-08 07:12:17 +00:00
|
|
|
void split( const CString &Source, const CString &Delimitor, CStringArray &AddIt, const bool bIgnoreEmpty )
|
2003-01-05 03:59:07 +00:00
|
|
|
{
|
2004-11-08 07:12:17 +00:00
|
|
|
if( Delimitor.size() == 1 )
|
|
|
|
|
do_split( Source, Delimitor[0], AddIt, bIgnoreEmpty );
|
|
|
|
|
else
|
|
|
|
|
do_split( Source, Delimitor, AddIt, bIgnoreEmpty );
|
2003-01-05 03:59:07 +00:00
|
|
|
}
|
|
|
|
|
|
2004-11-08 07:12:17 +00:00
|
|
|
void split( const wstring &Source, const wstring &Delimitor, vector<wstring> &AddIt, const bool bIgnoreEmpty )
|
2003-01-05 03:59:07 +00:00
|
|
|
{
|
2004-11-08 07:12:17 +00:00
|
|
|
if( Delimitor.size() == 1 )
|
|
|
|
|
do_split( Source, Delimitor[0], AddIt, bIgnoreEmpty );
|
|
|
|
|
else
|
|
|
|
|
do_split( Source, Delimitor, AddIt, bIgnoreEmpty );
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-15 05:34:24 +00:00
|
|
|
/* Use:
|
|
|
|
|
|
|
|
|
|
CString str="a,b,c";
|
|
|
|
|
int start = 0, size = -1;
|
|
|
|
|
while( 1 )
|
|
|
|
|
{
|
|
|
|
|
do_split( str, ",", begin, size );
|
|
|
|
|
if( begin == str.end() )
|
|
|
|
|
break;
|
|
|
|
|
str[begin] = 'Q';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
template <class S>
|
|
|
|
|
void do_split( const S &Source, const S &Delimitor, int &begin, int &size, int len, const bool bIgnoreEmpty )
|
|
|
|
|
{
|
|
|
|
|
if( size != -1 )
|
|
|
|
|
{
|
|
|
|
|
/* Start points to the beginning of the last delimiter. Move it up. */
|
|
|
|
|
begin += size+Delimitor.size();
|
|
|
|
|
begin = min( begin, len );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size = 0;
|
|
|
|
|
|
|
|
|
|
if( bIgnoreEmpty )
|
|
|
|
|
{
|
|
|
|
|
/* Skip delims. */
|
|
|
|
|
while( begin + Delimitor.size() < Source.size() &&
|
|
|
|
|
!Source.compare( begin, Delimitor.size(), Delimitor ) )
|
|
|
|
|
++begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Where's the string function to find within a substring? C++ strings apparently
|
|
|
|
|
* are missing that ... */
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t pos;
|
2003-12-15 05:34:24 +00:00
|
|
|
if( Delimitor.size() == 1 )
|
|
|
|
|
pos = Source.find( Delimitor[0], begin );
|
|
|
|
|
else
|
|
|
|
|
pos = Source.find( Delimitor, begin );
|
|
|
|
|
if( pos == Source.npos || (int) pos > len )
|
|
|
|
|
pos = len;
|
|
|
|
|
size = pos - begin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void split( const CString &Source, const CString &Delimitor, int &begin, int &size, int len, const bool bIgnoreEmpty )
|
|
|
|
|
{
|
|
|
|
|
do_split( Source, Delimitor, begin, size, len, bIgnoreEmpty );
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void split( const wstring &Source, const wstring &Delimitor, int &begin, int &size, int len, const bool bIgnoreEmpty )
|
|
|
|
|
{
|
|
|
|
|
do_split( Source, Delimitor, begin, size, len, bIgnoreEmpty );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void split( const CString &Source, const CString &Delimitor, int &begin, int &size, const bool bIgnoreEmpty )
|
|
|
|
|
{
|
|
|
|
|
do_split( Source, Delimitor, begin, size, Source.size(), bIgnoreEmpty );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void split( const wstring &Source, const wstring &Delimitor, int &begin, int &size, const bool bIgnoreEmpty )
|
|
|
|
|
{
|
|
|
|
|
do_split( Source, Delimitor, begin, size, Source.size(), bIgnoreEmpty );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
/*
|
|
|
|
|
* foo\fum\ -> "foo\fum\", "", ""
|
|
|
|
|
* c:\foo\bar.txt -> "c:\foo\", "bar", ".txt"
|
|
|
|
|
* \\foo\fum -> "\\foo\", "fum", ""
|
|
|
|
|
*/
|
2003-10-29 21:22:28 +00:00
|
|
|
void splitpath( const CString &Path, CString& Dir, CString& Filename, CString& Ext )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2003-01-16 22:49:22 +00:00
|
|
|
Dir = Filename = Ext = "";
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
CStringArray mat;
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2003-01-23 04:22:07 +00:00
|
|
|
/* One level of escapes for the regex, one for C. Ew.
|
|
|
|
|
* This is really:
|
|
|
|
|
* ^(.*[\\/])?(.*)$ */
|
|
|
|
|
static Regex sep("^(.*[\\\\/])?(.*)$");
|
2003-07-07 23:14:50 +00:00
|
|
|
bool check = sep.Compare(Path, mat);
|
|
|
|
|
ASSERT(check);
|
2003-01-16 22:49:22 +00:00
|
|
|
|
|
|
|
|
Dir = mat[0];
|
2003-10-29 21:22:28 +00:00
|
|
|
const CString Base = mat[1];
|
2003-01-16 22:49:22 +00:00
|
|
|
|
2003-01-23 04:22:07 +00:00
|
|
|
/* ^(.*)(\.[^\.]+)$ */
|
|
|
|
|
static Regex SplitExt("^(.*)(\\.[^\\.]+)$");
|
2003-10-29 21:22:28 +00:00
|
|
|
if( SplitExt.Compare(Base, mat) )
|
2003-01-16 22:49:22 +00:00
|
|
|
{
|
|
|
|
|
Filename = mat[0];
|
|
|
|
|
Ext = mat[1];
|
|
|
|
|
} else
|
2003-10-29 21:22:28 +00:00
|
|
|
Filename = Base;
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2001-12-28 10:15:59 +00:00
|
|
|
|
2003-10-29 20:59:40 +00:00
|
|
|
/* "foo.bar", "baz" -> "foo.baz"
|
|
|
|
|
* "foo", "baz" -> "foo.baz"
|
|
|
|
|
* "foo.bar", "" -> "foo" */
|
2003-09-04 19:28:39 +00:00
|
|
|
CString SetExtension( const CString &path, const CString &ext )
|
|
|
|
|
{
|
|
|
|
|
CString Dir, FName, OldExt;
|
|
|
|
|
splitpath( path, Dir, FName, OldExt );
|
2003-10-29 20:59:40 +00:00
|
|
|
return Dir + FName + (ext.size()? ".":"") + ext;
|
2003-09-04 19:28:39 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-07 22:54:50 +00:00
|
|
|
CString GetExtension( const CString &sPath )
|
2003-09-03 18:22:55 +00:00
|
|
|
{
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t pos = sPath.rfind( '.' );
|
2003-10-22 05:49:26 +00:00
|
|
|
if( pos == sPath.npos )
|
|
|
|
|
return "";
|
|
|
|
|
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t slash = sPath.find( '/', pos );
|
2003-10-22 05:49:26 +00:00
|
|
|
if( slash != sPath.npos )
|
|
|
|
|
return ""; /* rare: path/dir.ext/fn */
|
|
|
|
|
|
|
|
|
|
return sPath.substr( pos+1, sPath.size()-pos+1 );
|
2003-09-03 18:22:55 +00:00
|
|
|
}
|
|
|
|
|
|
2005-05-31 01:17:37 +00:00
|
|
|
CString GetFileNameWithoutExtension( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
CString sThrowAway, sFName;
|
|
|
|
|
splitpath( sPath, sThrowAway, sFName, sThrowAway );
|
|
|
|
|
return sFName;
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-05 06:20:55 +00:00
|
|
|
CString GetCwd()
|
|
|
|
|
{
|
2003-11-13 00:39:36 +00:00
|
|
|
#ifdef _XBOX
|
2003-12-07 04:20:07 +00:00
|
|
|
return SYS_BASE_PATH;
|
2003-11-13 00:39:36 +00:00
|
|
|
#else
|
2003-09-05 06:20:55 +00:00
|
|
|
char buf[PATH_MAX];
|
|
|
|
|
bool ret = getcwd(buf, PATH_MAX) != NULL;
|
|
|
|
|
ASSERT(ret);
|
|
|
|
|
return buf;
|
2003-11-13 00:39:36 +00:00
|
|
|
#endif
|
2003-09-05 06:20:55 +00:00
|
|
|
}
|
|
|
|
|
|
2004-12-11 23:33:11 +00:00
|
|
|
/*
|
|
|
|
|
* Calculate a standard CRC32. iCRC should be initialized to 0.
|
|
|
|
|
* References:
|
|
|
|
|
* http://www.theorem.com/java/CRC32.java,
|
|
|
|
|
* http://www.faqs.org/rfcs/rfc1952.html
|
|
|
|
|
*/
|
2004-12-12 00:54:44 +00:00
|
|
|
void CRC32( unsigned int &iCRC, const void *pVoidBuffer, size_t iSize )
|
2002-05-19 01:59:48 +00:00
|
|
|
{
|
2003-05-22 20:32:08 +00:00
|
|
|
static unsigned tab[256];
|
|
|
|
|
static bool initted = false;
|
2004-12-11 23:33:11 +00:00
|
|
|
if( !initted )
|
2003-05-22 20:32:08 +00:00
|
|
|
{
|
|
|
|
|
initted = true;
|
|
|
|
|
const unsigned POLY = 0xEDB88320;
|
2002-07-23 01:41:40 +00:00
|
|
|
|
2004-12-11 23:33:11 +00:00
|
|
|
for( int i = 0; i < 256; ++i )
|
2003-05-22 20:32:08 +00:00
|
|
|
{
|
|
|
|
|
tab[i] = i;
|
2004-12-11 23:33:11 +00:00
|
|
|
for( int j = 0; j < 8; ++j )
|
2003-05-22 20:32:08 +00:00
|
|
|
{
|
2004-12-11 23:33:11 +00:00
|
|
|
if( tab[i] & 1 )
|
|
|
|
|
tab[i] = (tab[i] >> 1) ^ POLY;
|
|
|
|
|
else
|
|
|
|
|
tab[i] >>= 1;
|
2003-05-22 20:32:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-07-23 01:41:40 +00:00
|
|
|
|
2004-12-12 01:39:30 +00:00
|
|
|
iCRC ^= 0xFFFFFFFF;
|
|
|
|
|
|
2004-12-12 00:54:44 +00:00
|
|
|
const char *pBuffer = (const char *) pVoidBuffer;
|
2004-12-11 23:33:11 +00:00
|
|
|
for( unsigned i = 0; i < iSize; ++i )
|
|
|
|
|
iCRC = (iCRC >> 8) ^ tab[(iCRC ^ pBuffer[i]) & 0xFF];
|
2004-12-12 01:39:30 +00:00
|
|
|
|
|
|
|
|
iCRC ^= 0xFFFFFFFF;
|
2004-12-11 23:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int GetHashForString ( const CString &s )
|
|
|
|
|
{
|
|
|
|
|
unsigned crc = 0;
|
|
|
|
|
CRC32( crc, s.data(), s.size() );
|
2003-05-22 20:32:08 +00:00
|
|
|
return crc;
|
2002-05-19 01:59:48 +00:00
|
|
|
}
|
|
|
|
|
|
2004-12-11 23:33:11 +00:00
|
|
|
|
2003-12-07 22:54:50 +00:00
|
|
|
/* Return true if "dir" is empty or does not exist. */
|
|
|
|
|
bool DirectoryIsEmpty( const CString &dir )
|
|
|
|
|
{
|
|
|
|
|
if(dir == "")
|
|
|
|
|
return true;
|
|
|
|
|
if(!DoesFileExist(dir))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
CStringArray asFileNames;
|
|
|
|
|
GetDirListing( dir, asFileNames );
|
|
|
|
|
return asFileNames.empty();
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-24 08:17:09 +00:00
|
|
|
bool CompareCStringsAsc(const CString &str1, const CString &str2)
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2002-11-08 20:12:19 +00:00
|
|
|
return str1.CompareNoCase( str2 ) < 0;
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2002-10-24 08:17:09 +00:00
|
|
|
bool CompareCStringsDesc(const CString &str1, const CString &str2)
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2002-11-08 20:12:19 +00:00
|
|
|
return str1.CompareNoCase( str2 ) > 0;
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2002-10-24 08:17:09 +00:00
|
|
|
void SortCStringArray( CStringArray &arrayCStrings, const bool bSortAscending )
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2002-10-24 08:40:27 +00:00
|
|
|
sort( arrayCStrings.begin(), arrayCStrings.end(),
|
2002-10-24 08:17:09 +00:00
|
|
|
bSortAscending?CompareCStringsAsc:CompareCStringsDesc);
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
2001-11-26 08:28:48 +00:00
|
|
|
|
2002-10-24 07:09:38 +00:00
|
|
|
float calc_mean(const float *start, const float *end)
|
|
|
|
|
{
|
|
|
|
|
return accumulate(start, end, 0.f) / distance(start, end);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float calc_stddev(const float *start, const float *end)
|
|
|
|
|
{
|
|
|
|
|
/* Calculate the mean. */
|
|
|
|
|
float mean = calc_mean(start, end);
|
|
|
|
|
|
|
|
|
|
/* Calculate stddev. */
|
|
|
|
|
float dev = 0.0f;
|
|
|
|
|
for( const float *i=start; i != end; ++i )
|
|
|
|
|
dev += (*i - mean) * (*i - mean);
|
|
|
|
|
dev /= distance(start, end) - 1;
|
|
|
|
|
dev = sqrtf(dev);
|
|
|
|
|
|
|
|
|
|
return dev;
|
|
|
|
|
}
|
2002-05-27 08:23:27 +00:00
|
|
|
|
2002-12-11 11:17:12 +00:00
|
|
|
void TrimLeft(CString &str, const char *s)
|
2002-10-31 08:04:34 +00:00
|
|
|
{
|
|
|
|
|
int n = 0;
|
|
|
|
|
while(n < int(str.size()) && strchr(s, str[n]))
|
|
|
|
|
n++;
|
|
|
|
|
|
|
|
|
|
str.erase(str.begin(), str.begin()+n);
|
|
|
|
|
}
|
2002-05-27 08:23:27 +00:00
|
|
|
|
2002-12-11 11:17:12 +00:00
|
|
|
void TrimRight(CString &str, const char *s)
|
2002-10-31 08:04:34 +00:00
|
|
|
{
|
2002-11-01 20:48:36 +00:00
|
|
|
int n = str.size();
|
|
|
|
|
while(n > 0 && strchr(s, str[n-1]))
|
2002-10-31 08:04:34 +00:00
|
|
|
n--;
|
|
|
|
|
|
2002-11-01 20:48:36 +00:00
|
|
|
/* Delete from n to the end. If n == str.size(), nothing is deleted;
|
|
|
|
|
* if n == 0, the whole string is erased. */
|
2002-10-31 08:04:34 +00:00
|
|
|
str.erase(str.begin()+n, str.end());
|
|
|
|
|
}
|
2003-01-01 22:02:41 +00:00
|
|
|
|
2003-02-14 22:28:29 +00:00
|
|
|
void StripCrnl(CString &s)
|
|
|
|
|
{
|
2003-07-10 01:55:37 +00:00
|
|
|
while( s.size() && (s[s.size()-1] == '\r' || s[s.size()-1] == '\n') )
|
2003-02-14 22:28:29 +00:00
|
|
|
s.erase(s.size()-1);
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-23 08:05:09 +00:00
|
|
|
void StripCvs( vector<CString> &vs )
|
|
|
|
|
{
|
|
|
|
|
for( unsigned i=0; i<vs.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
if( vs[i].Right(3).CompareNoCase("CVS") == 0 )
|
|
|
|
|
vs.erase( vs.begin()+i );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-01 22:02:41 +00:00
|
|
|
/* path is a .redir pathname. Read it and return the real one. */
|
2005-03-11 06:09:56 +00:00
|
|
|
CString DerefRedir(const CString &_path)
|
2003-01-01 22:02:41 +00:00
|
|
|
{
|
2005-03-11 06:09:56 +00:00
|
|
|
CString path = _path;
|
|
|
|
|
|
|
|
|
|
for( int i=0; i<100; i++ )
|
2003-07-10 11:47:28 +00:00
|
|
|
{
|
2005-03-11 06:09:56 +00:00
|
|
|
if( GetExtension(path) != "redir" )
|
|
|
|
|
{
|
|
|
|
|
return path;
|
|
|
|
|
}
|
2003-01-01 22:02:41 +00:00
|
|
|
|
2005-05-17 02:20:43 +00:00
|
|
|
CString sNewFileName;
|
2005-05-20 00:12:43 +00:00
|
|
|
GetFileContents( path, sNewFileName, true );
|
2003-02-16 05:12:27 +00:00
|
|
|
|
2005-03-11 06:09:56 +00:00
|
|
|
/* Empty is invalid. */
|
|
|
|
|
if( sNewFileName == "" )
|
|
|
|
|
return "";
|
2003-01-01 22:02:41 +00:00
|
|
|
|
2005-03-11 06:09:56 +00:00
|
|
|
FixSlashesInPlace( sNewFileName );
|
2003-08-02 19:28:31 +00:00
|
|
|
|
2005-03-11 06:09:56 +00:00
|
|
|
CString path2 = Dirname(path) + sNewFileName;
|
2003-08-02 19:28:31 +00:00
|
|
|
|
2005-03-11 06:09:56 +00:00
|
|
|
CollapsePath( path2 );
|
2003-08-02 19:28:31 +00:00
|
|
|
|
2005-03-11 06:09:56 +00:00
|
|
|
path2 += "*";
|
|
|
|
|
|
|
|
|
|
vector<CString> matches;
|
|
|
|
|
GetDirListing( path2, matches, false, true );
|
|
|
|
|
|
|
|
|
|
if( matches.empty() )
|
|
|
|
|
RageException::Throw( "The redirect '%s' references a file '%s' which doesn't exist.", path.c_str(), path2.c_str() );
|
|
|
|
|
else if( matches.size() > 1 )
|
|
|
|
|
RageException::Throw( "The redirect '%s' references a file '%s' with multiple matches.", path.c_str(), path2.c_str() );
|
|
|
|
|
|
|
|
|
|
path = matches[0];
|
|
|
|
|
}
|
2003-09-23 05:39:47 +00:00
|
|
|
|
2005-03-11 06:09:56 +00:00
|
|
|
RageException::Throw( "Circular redirect '%s'", path.c_str() );
|
2003-01-01 22:02:41 +00:00
|
|
|
}
|
2003-01-05 02:48:15 +00:00
|
|
|
|
2005-05-20 00:12:43 +00:00
|
|
|
bool GetFileContents( const CString &sPath, CString &sOut, bool bOneLine )
|
2003-03-15 00:16:45 +00:00
|
|
|
{
|
2005-05-17 02:20:43 +00:00
|
|
|
/* Don't warn if the file doesn't exist, but do warn if it exists and fails to open. */
|
|
|
|
|
if( !IsAFile(sPath) )
|
|
|
|
|
return false;
|
|
|
|
|
|
2004-06-06 20:56:46 +00:00
|
|
|
RageFile file;
|
2005-05-17 02:20:43 +00:00
|
|
|
if( !file.Open(sPath) )
|
2004-06-06 20:56:46 +00:00
|
|
|
{
|
2005-05-17 02:20:43 +00:00
|
|
|
LOG->Warn( "GetFileContents(%s): %s", sPath.c_str(), file.GetError().c_str() );
|
|
|
|
|
return false;
|
2004-06-06 20:56:46 +00:00
|
|
|
}
|
|
|
|
|
|
2005-05-17 02:20:43 +00:00
|
|
|
CString sData;
|
2005-05-20 00:12:43 +00:00
|
|
|
int iGot;
|
|
|
|
|
if( bOneLine )
|
|
|
|
|
iGot = file.GetLine( sData );
|
|
|
|
|
else
|
|
|
|
|
iGot = file.Read( sData, file.GetFileSize() );
|
|
|
|
|
|
|
|
|
|
if( iGot == -1 )
|
2004-06-06 20:56:46 +00:00
|
|
|
{
|
2005-05-17 02:20:43 +00:00
|
|
|
LOG->Warn( "GetFileContents(%s): %s", sPath.c_str(), file.GetError().c_str() );
|
|
|
|
|
return false;
|
2004-06-06 20:56:46 +00:00
|
|
|
}
|
2005-05-20 00:12:43 +00:00
|
|
|
|
|
|
|
|
if( bOneLine )
|
|
|
|
|
StripCrnl( sData );
|
2003-11-30 06:20:25 +00:00
|
|
|
|
2005-05-17 02:20:43 +00:00
|
|
|
sOut = sData;
|
|
|
|
|
return true;
|
2003-03-15 00:16:45 +00:00
|
|
|
}
|
|
|
|
|
|
2003-06-27 20:09:29 +00:00
|
|
|
#if 1
|
|
|
|
|
/* PCRE */
|
|
|
|
|
#include "pcre/pcre.h"
|
|
|
|
|
void Regex::Compile()
|
|
|
|
|
{
|
|
|
|
|
const char *error;
|
|
|
|
|
int offset;
|
|
|
|
|
reg = pcre_compile(pattern.c_str(), PCRE_CASELESS, &error, &offset, NULL);
|
|
|
|
|
|
|
|
|
|
if(reg == NULL)
|
|
|
|
|
RageException::Throw("Invalid regex: '%s' (%s)", pattern.c_str(), error);
|
|
|
|
|
|
|
|
|
|
int ret = pcre_fullinfo( (pcre *) reg, NULL, PCRE_INFO_CAPTURECOUNT, &backrefs);
|
|
|
|
|
ASSERT(ret >= 0);
|
|
|
|
|
|
|
|
|
|
backrefs++;
|
|
|
|
|
ASSERT(backrefs < 128);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Regex::Set(const CString &str)
|
|
|
|
|
{
|
|
|
|
|
Release();
|
|
|
|
|
pattern=str;
|
|
|
|
|
Compile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Regex::Release()
|
|
|
|
|
{
|
|
|
|
|
pcre_free(reg);
|
|
|
|
|
reg = NULL;
|
|
|
|
|
pattern = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Regex::Regex(const CString &str)
|
|
|
|
|
{
|
|
|
|
|
reg = NULL;
|
|
|
|
|
Set(str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Regex::Regex(const Regex &rhs)
|
|
|
|
|
{
|
|
|
|
|
reg = NULL;
|
|
|
|
|
Set(rhs.pattern);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Regex &Regex::operator=(const Regex &rhs)
|
|
|
|
|
{
|
|
|
|
|
if(this != &rhs) Set(rhs.pattern);
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Regex::~Regex()
|
|
|
|
|
{
|
|
|
|
|
Release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Regex::Compare(const CString &str)
|
|
|
|
|
{
|
|
|
|
|
int mat[128*3];
|
|
|
|
|
int ret = pcre_exec( (pcre *) reg, NULL,
|
|
|
|
|
str.data(), str.size(), 0, 0, mat, 128*3);
|
|
|
|
|
|
|
|
|
|
if( ret < -1 )
|
|
|
|
|
RageException::Throw("Unexpected return from pcre_exec('%s'): %i",
|
|
|
|
|
pattern.c_str(), ret);
|
|
|
|
|
|
|
|
|
|
return ret >= 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Regex::Compare(const CString &str, vector<CString> &matches)
|
|
|
|
|
{
|
|
|
|
|
matches.clear();
|
|
|
|
|
|
|
|
|
|
int mat[128*3];
|
|
|
|
|
int ret = pcre_exec( (pcre *) reg, NULL,
|
|
|
|
|
str.data(), str.size(), 0, 0, mat, 128*3);
|
|
|
|
|
|
|
|
|
|
if( ret < -1 )
|
|
|
|
|
RageException::Throw("Unexpected return from pcre_exec('%s'): %i",
|
|
|
|
|
pattern.c_str(), ret);
|
|
|
|
|
|
|
|
|
|
if(ret == -1)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for(unsigned i = 1; i < backrefs; ++i)
|
|
|
|
|
{
|
|
|
|
|
const int start = mat[i*2], end = mat[i*2+1];
|
|
|
|
|
if(start == -1)
|
|
|
|
|
matches.push_back(""); /* no match */
|
|
|
|
|
else
|
|
|
|
|
matches.push_back(str.substr(start, end - start));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
/* GNU regex */
|
|
|
|
|
#include "regex.h"
|
2003-01-23 21:03:57 +00:00
|
|
|
void Regex::Compile()
|
2003-01-05 02:48:15 +00:00
|
|
|
{
|
2003-01-23 21:03:57 +00:00
|
|
|
reg = new regex_t;
|
2003-01-23 04:22:07 +00:00
|
|
|
|
|
|
|
|
int ret = regcomp((regex_t *) reg, pattern.c_str(), REG_EXTENDED|REG_ICASE);
|
|
|
|
|
if(ret != 0)
|
|
|
|
|
RageException::Throw("Invalid regex: '%s'", pattern.c_str());
|
|
|
|
|
|
|
|
|
|
/* Count the number of backreferences. */
|
|
|
|
|
backrefs = 0;
|
|
|
|
|
for(int i = 0; i < int(pattern.size()); ++i)
|
|
|
|
|
if(pattern[i] == '(') backrefs++;
|
|
|
|
|
ASSERT(backrefs+1 < 128);
|
2003-01-05 02:48:15 +00:00
|
|
|
}
|
2003-01-24 00:26:54 +00:00
|
|
|
|
2003-03-27 20:16:17 +00:00
|
|
|
void Regex::Set(const CString &str)
|
|
|
|
|
{
|
|
|
|
|
Release();
|
|
|
|
|
pattern=str;
|
|
|
|
|
Compile();
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-23 21:03:57 +00:00
|
|
|
void Regex::Release()
|
|
|
|
|
{
|
|
|
|
|
delete (regex_t *)reg;
|
|
|
|
|
reg = NULL;
|
2003-03-28 05:23:35 +00:00
|
|
|
pattern = "";
|
2003-01-23 21:03:57 +00:00
|
|
|
}
|
|
|
|
|
|
2003-03-27 20:16:17 +00:00
|
|
|
Regex::Regex(const CString &str)
|
2003-01-23 21:03:57 +00:00
|
|
|
{
|
|
|
|
|
reg = NULL;
|
2003-03-27 20:16:17 +00:00
|
|
|
Set(str);
|
2003-01-23 21:03:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Regex::Regex(const Regex &rhs)
|
|
|
|
|
{
|
|
|
|
|
reg = NULL;
|
2003-03-28 05:23:35 +00:00
|
|
|
Set(rhs.pattern);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Regex &Regex::operator=(const Regex &rhs)
|
|
|
|
|
{
|
|
|
|
|
if(this != &rhs) Set(rhs.pattern);
|
|
|
|
|
return *this;
|
2003-01-23 21:03:57 +00:00
|
|
|
}
|
2003-01-05 02:48:15 +00:00
|
|
|
|
2003-01-23 04:22:07 +00:00
|
|
|
Regex::~Regex()
|
2003-01-05 02:48:15 +00:00
|
|
|
{
|
2003-01-23 21:03:57 +00:00
|
|
|
Release();
|
2003-01-05 02:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-23 04:22:07 +00:00
|
|
|
bool Regex::Compare(const CString &str)
|
2003-01-05 02:48:15 +00:00
|
|
|
{
|
2003-01-23 04:22:07 +00:00
|
|
|
return regexec((regex_t *) reg, str.c_str(), 0, NULL, 0) != REG_NOMATCH;
|
|
|
|
|
}
|
2003-01-05 02:48:15 +00:00
|
|
|
|
2003-01-23 04:22:07 +00:00
|
|
|
bool Regex::Compare(const CString &str, vector<CString> &matches)
|
|
|
|
|
{
|
|
|
|
|
matches.clear();
|
2003-01-05 02:48:15 +00:00
|
|
|
|
|
|
|
|
regmatch_t mat[128];
|
2003-01-23 04:22:07 +00:00
|
|
|
int ret = regexec((regex_t *) reg, str.c_str(), 128, mat, 0);
|
2003-01-05 02:48:15 +00:00
|
|
|
|
2003-01-23 04:22:07 +00:00
|
|
|
if(ret == REG_NOMATCH)
|
2003-01-05 02:48:15 +00:00
|
|
|
return false;
|
|
|
|
|
|
2003-01-23 04:22:07 +00:00
|
|
|
for(unsigned i = 1; i < backrefs+1; ++i)
|
2003-01-05 02:48:15 +00:00
|
|
|
{
|
|
|
|
|
if(mat[i].rm_so == -1)
|
|
|
|
|
matches.push_back(""); /* no match */
|
|
|
|
|
else
|
|
|
|
|
matches.push_back(str.substr(mat[i].rm_so, mat[i].rm_eo - mat[i].rm_so));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2003-06-27 20:09:29 +00:00
|
|
|
#endif
|
2003-01-05 02:48:15 +00:00
|
|
|
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2003-01-06 01:19:22 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
|
|
|
|
|
/* Given a UTF-8 byte, return the length of the codepoint (if a start code)
|
|
|
|
|
* or 0 if it's a continuation byte. */
|
|
|
|
|
int utf8_get_char_len( char p )
|
|
|
|
|
{
|
|
|
|
|
if( !(p & 0x80) ) return 1; /* 0xxxxxxx - 1 */
|
|
|
|
|
if( !(p & 0x40) ) return 1; /* 10xxxxxx - continuation */
|
|
|
|
|
if( !(p & 0x20) ) return 2; /* 110xxxxx */
|
|
|
|
|
if( !(p & 0x10) ) return 3; /* 1110xxxx */
|
|
|
|
|
if( !(p & 0x08) ) return 4; /* 11110xxx */
|
|
|
|
|
if( !(p & 0x04) ) return 5; /* 111110xx */
|
|
|
|
|
if( !(p & 0x02) ) return 6; /* 1111110x */
|
|
|
|
|
return 1; /* 1111111x */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool is_utf8_continuation_byte( char c )
|
2003-01-06 01:19:22 +00:00
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
return (c & 0xC0) == 0x80;
|
2003-05-18 04:36:03 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
/* Decode one codepoint at start; advance start and place the result in ch. If
|
|
|
|
|
* the encoded string is invalid, false is returned. */
|
|
|
|
|
bool utf8_to_wchar_ec( const CString &s, unsigned &start, wchar_t &ch )
|
2003-05-18 04:36:03 +00:00
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
if( start >= s.size() )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if( is_utf8_continuation_byte( s[start] ) || /* misplaced continuation byte */
|
|
|
|
|
(s[start] & 0xFE) == 0xFE ) /* 0xFE, 0xFF */
|
|
|
|
|
{
|
|
|
|
|
start += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int len = utf8_get_char_len( s[start] );
|
|
|
|
|
|
|
|
|
|
const int first_byte_mask[] = { -1, 0x7F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
|
|
|
|
|
|
2004-05-18 03:15:13 +00:00
|
|
|
ch = wchar_t(s[start] & first_byte_mask[len]);
|
2004-05-17 21:28:26 +00:00
|
|
|
|
|
|
|
|
for( int i = 1; i < len; ++i )
|
2003-05-18 04:36:03 +00:00
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
if( start+i >= s.size() )
|
|
|
|
|
{
|
|
|
|
|
/* We expected a continuation byte, but didn't get one. Return error, and point
|
|
|
|
|
* start at the unexpected byte; it's probably a new sequence. */
|
|
|
|
|
start += i;
|
2003-05-18 04:36:03 +00:00
|
|
|
return false;
|
2004-05-17 21:28:26 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-18 03:15:13 +00:00
|
|
|
char byte = s[start+i];
|
2004-05-17 21:28:26 +00:00
|
|
|
if( !is_utf8_continuation_byte(byte) )
|
|
|
|
|
{
|
|
|
|
|
/* We expected a continuation byte, but didn't get one. Return error, and point
|
|
|
|
|
* start at the unexpected byte; it's probably a new sequence. */
|
|
|
|
|
start += i;
|
2003-06-30 08:38:24 +00:00
|
|
|
return false;
|
2004-05-17 21:28:26 +00:00
|
|
|
}
|
|
|
|
|
ch = (ch << 6) | byte & 0x3F;
|
|
|
|
|
}
|
2003-05-18 04:36:03 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
bool bValid = true;
|
|
|
|
|
{
|
|
|
|
|
unsigned c1 = (unsigned) s[start] & 0xFF;
|
|
|
|
|
unsigned c2 = (unsigned) s[start+1] & 0xFF;
|
|
|
|
|
int c = (c1 << 8) + c2;
|
|
|
|
|
if( (c & 0xFE00) == 0xC000 ||
|
|
|
|
|
(c & 0xFFE0) == 0xE080 ||
|
|
|
|
|
(c & 0xFFF0) == 0xF080 ||
|
|
|
|
|
(c & 0xFFF8) == 0xF880 ||
|
|
|
|
|
(c & 0xFFFC) == 0xFC80 )
|
|
|
|
|
{
|
|
|
|
|
bValid = false;
|
|
|
|
|
}
|
2003-05-18 04:36:03 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
if( ch == 0xFFFE || ch == 0xFFFF )
|
|
|
|
|
bValid = false;
|
|
|
|
|
|
|
|
|
|
start += len;
|
|
|
|
|
return bValid;
|
2003-01-06 01:19:22 +00:00
|
|
|
}
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
/* Like utf8_to_wchar_ec, but only does enough error checking to prevent crashing. */
|
|
|
|
|
bool utf8_to_wchar( const CString &s, unsigned &start, wchar_t &ch )
|
2003-01-05 05:31:59 +00:00
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
if( start >= s.size() )
|
|
|
|
|
return false;
|
2003-01-06 01:19:22 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
int len = utf8_get_char_len( s[start] );
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
if( start+len > s.size() )
|
|
|
|
|
{
|
|
|
|
|
/* We don't have room for enough continuation bytes. Return error. */
|
|
|
|
|
start += len;
|
|
|
|
|
ch = L'?';
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
switch( len )
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
ch = (s[start+0] & 0x7F);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
ch = ( (s[start+0] & 0x1F) << 6 ) |
|
|
|
|
|
(s[start+1] & 0x3F);
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
ch = ( (s[start+0] & 0x0F) << 12 ) |
|
|
|
|
|
( (s[start+1] & 0x3F) << 6 ) |
|
|
|
|
|
(s[start+2] & 0x3F);
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
ch = ( (s[start+0] & 0x07) << 18 ) |
|
|
|
|
|
( (s[start+1] & 0x3F) << 12 ) |
|
|
|
|
|
( (s[start+2] & 0x3F) << 6 ) |
|
|
|
|
|
(s[start+3] & 0x3F);
|
|
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
ch = ( (s[start+0] & 0x03) << 24 ) |
|
|
|
|
|
( (s[start+1] & 0x3F) << 18 ) |
|
|
|
|
|
( (s[start+2] & 0x3F) << 12 ) |
|
|
|
|
|
( (s[start+3] & 0x3F) << 6 ) |
|
|
|
|
|
(s[start+4] & 0x3F);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
|
ch = ( (s[start+0] & 0x01) << 30 ) |
|
|
|
|
|
( (s[start+1] & 0x3F) << 24 ) |
|
|
|
|
|
( (s[start+2] & 0x3F) << 18 ) |
|
|
|
|
|
( (s[start+3] & 0x3F) << 12) |
|
|
|
|
|
( (s[start+4] & 0x3F) << 6 ) |
|
|
|
|
|
(s[start+5] & 0x3F);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
start += len;
|
|
|
|
|
return true;
|
2003-01-05 05:31:59 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-06 04:23:41 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
/* UTF-8 encode ch and append to out. */
|
|
|
|
|
void wchar_to_utf8( wchar_t ch, CString &out )
|
2003-01-05 05:31:59 +00:00
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
if( ch < 0x80 ) { out.append( 1, (char) ch ); return; }
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
int cbytes = 0;
|
|
|
|
|
if( ch < 0x800 ) cbytes = 1;
|
|
|
|
|
else if( ch < 0x10000 ) cbytes = 2;
|
|
|
|
|
else if( ch < 0x200000 ) cbytes = 3;
|
|
|
|
|
else if( ch < 0x4000000 ) cbytes = 4;
|
|
|
|
|
else cbytes = 5;
|
2003-01-05 05:31:59 +00:00
|
|
|
|
|
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
int shift = cbytes*6;
|
|
|
|
|
const int init_masks[] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
|
|
|
|
out.append( 1, (char) (init_masks[cbytes-1] | (ch>>shift)) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for( int i = 0; i < cbytes; ++i )
|
|
|
|
|
{
|
|
|
|
|
int shift = (cbytes-i-1)*6;
|
|
|
|
|
out.append( 1, (char) (0x80 | ((ch>>shift)&0x3F)) );
|
2003-01-05 05:31:59 +00:00
|
|
|
}
|
2004-05-17 21:28:26 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-05 05:31:59 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
wchar_t utf8_get_char( const CString &s )
|
|
|
|
|
{
|
|
|
|
|
unsigned start = 0;
|
|
|
|
|
wchar_t ret;
|
|
|
|
|
if( !utf8_to_wchar_ec( s, start, ret ) )
|
|
|
|
|
return INVALID_CHAR;
|
2003-01-05 05:31:59 +00:00
|
|
|
return ret;
|
|
|
|
|
}
|
2003-01-06 01:35:05 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Replace invalid sequences in s. */
|
|
|
|
|
void utf8_sanitize( CString &s )
|
2003-01-06 07:43:14 +00:00
|
|
|
{
|
|
|
|
|
CString ret;
|
2004-05-17 21:28:26 +00:00
|
|
|
for( unsigned start = 0; start < s.size(); )
|
|
|
|
|
{
|
|
|
|
|
wchar_t ch;
|
|
|
|
|
if( !utf8_to_wchar_ec( s, start, ch ) )
|
2004-05-18 03:15:13 +00:00
|
|
|
ch = INVALID_CHAR;
|
2003-01-06 07:43:14 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
wchar_to_utf8( ch, ret );
|
|
|
|
|
}
|
2003-01-06 07:43:14 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
s = ret;
|
2003-01-06 07:43:14 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
|
|
|
|
|
bool utf8_is_valid( const CString &s )
|
|
|
|
|
{
|
|
|
|
|
for( unsigned start = 0; start < s.size(); )
|
|
|
|
|
{
|
|
|
|
|
wchar_t ch;
|
|
|
|
|
if( !utf8_to_wchar_ec( s, start, ch ) )
|
|
|
|
|
return false;
|
2003-01-06 01:35:05 +00:00
|
|
|
}
|
2004-05-17 21:28:26 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-03 01:02:45 +00:00
|
|
|
/* Windows tends to drop garbage BOM characters at the start of UTF-8 text files.
|
|
|
|
|
* Remove them. */
|
|
|
|
|
void utf8_remove_bom( CString &sLine )
|
|
|
|
|
{
|
|
|
|
|
if( !sLine.compare(0, 3, "\xef\xbb\xbf") )
|
|
|
|
|
sLine.erase(0, 3);
|
|
|
|
|
}
|
|
|
|
|
|
2004-05-17 21:33:35 +00:00
|
|
|
const wchar_t INVALID_CHAR = 0xFFFD; /* U+FFFD REPLACEMENT CHARACTER */
|
2003-01-06 01:35:05 +00:00
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
wstring CStringToWstring( const CString &s )
|
|
|
|
|
{
|
|
|
|
|
wstring ret;
|
2004-10-03 14:41:28 +00:00
|
|
|
ret.reserve( s.size() );
|
2004-05-17 21:28:26 +00:00
|
|
|
for( unsigned start = 0; start < s.size(); )
|
2003-01-06 01:35:05 +00:00
|
|
|
{
|
2004-10-03 14:41:28 +00:00
|
|
|
char c = s[start];
|
|
|
|
|
if( !(c&0x80) )
|
2003-01-06 01:35:05 +00:00
|
|
|
{
|
2004-10-03 14:41:28 +00:00
|
|
|
/* ASCII fast path */
|
|
|
|
|
ret += c;
|
|
|
|
|
++start;
|
|
|
|
|
continue;
|
2003-01-06 01:35:05 +00:00
|
|
|
}
|
2004-10-03 14:41:28 +00:00
|
|
|
|
|
|
|
|
wchar_t ch;
|
|
|
|
|
if( !utf8_to_wchar( s, start, ch ) )
|
|
|
|
|
ch = INVALID_CHAR;
|
|
|
|
|
ret += ch;
|
2003-01-06 01:35:05 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
return ret;
|
2003-01-06 01:35:05 +00:00
|
|
|
}
|
|
|
|
|
|
2004-05-17 21:28:26 +00:00
|
|
|
CString WStringToCString(const wstring &str)
|
|
|
|
|
{
|
|
|
|
|
CString ret;
|
|
|
|
|
|
|
|
|
|
for(unsigned i = 0; i < str.size(); ++i)
|
|
|
|
|
wchar_to_utf8( str[i], ret );
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-01-28 00:45:01 +00:00
|
|
|
CString WcharToUTF8( wchar_t c )
|
2003-01-06 01:35:05 +00:00
|
|
|
{
|
2004-05-17 21:28:26 +00:00
|
|
|
CString ret;
|
|
|
|
|
wchar_to_utf8( c, ret );
|
|
|
|
|
return ret;
|
2003-01-06 01:35:05 +00:00
|
|
|
}
|
2003-01-06 02:48:14 +00:00
|
|
|
|
2003-02-15 00:21:40 +00:00
|
|
|
|
2003-01-06 02:48:14 +00:00
|
|
|
/* Replace &#nnnn; (decimal) &xnnnn; (hex) with corresponding UTF-8 characters. */
|
|
|
|
|
void Replace_Unicode_Markers( CString &Text )
|
|
|
|
|
{
|
|
|
|
|
unsigned start = 0;
|
|
|
|
|
while(start < Text.size())
|
|
|
|
|
{
|
|
|
|
|
/* Look for &#digits; */
|
|
|
|
|
bool hex = false;
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t pos = Text.find("&#", start);
|
2003-01-06 02:48:14 +00:00
|
|
|
if(pos == Text.npos) {
|
|
|
|
|
hex = true;
|
|
|
|
|
pos = Text.find("&x", start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(pos == Text.npos) break;
|
|
|
|
|
start = pos+1;
|
|
|
|
|
|
|
|
|
|
unsigned p = pos;
|
|
|
|
|
p += 2;
|
|
|
|
|
|
|
|
|
|
/* Found &# or &x. Is it followed by digits and a semicolon? */
|
|
|
|
|
if(p >= Text.size()) continue;
|
|
|
|
|
|
|
|
|
|
int numdigits = 0;
|
|
|
|
|
while(p < Text.size() &&
|
|
|
|
|
(hex && isxdigit(Text[p])) || (!hex && isdigit(Text[p])))
|
|
|
|
|
{
|
|
|
|
|
p++;
|
|
|
|
|
numdigits++;
|
|
|
|
|
}
|
|
|
|
|
if(!numdigits) continue; /* must have at least one digit */
|
|
|
|
|
if(p >= Text.size() || Text[p] != ';') continue;
|
|
|
|
|
p++;
|
|
|
|
|
|
|
|
|
|
int num;
|
|
|
|
|
if(hex) sscanf(Text.c_str()+pos, "&x%x;", &num);
|
|
|
|
|
else sscanf(Text.c_str()+pos, "&#%i;", &num);
|
2003-01-28 00:45:01 +00:00
|
|
|
if(num > 0xFFFF)
|
|
|
|
|
num = INVALID_CHAR;
|
|
|
|
|
|
|
|
|
|
Text.replace(pos, p-pos, WcharToUTF8(wchar_t(num)));
|
2003-01-06 02:48:14 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-09 23:53:14 +00:00
|
|
|
/* Form a string to identify a wchar_t with ASCII. */
|
|
|
|
|
CString WcharDisplayText(wchar_t c)
|
|
|
|
|
{
|
|
|
|
|
CString chr;
|
|
|
|
|
chr = ssprintf("U+%4.4x", c);
|
|
|
|
|
if(c < 128) chr += ssprintf(" ('%c')", char(c));
|
|
|
|
|
return chr;
|
|
|
|
|
}
|
2003-01-23 04:22:07 +00:00
|
|
|
|
|
|
|
|
/* Return the last named component of dir:
|
|
|
|
|
* a/b/c -> c
|
|
|
|
|
* a/b/c/ -> c
|
|
|
|
|
*/
|
2003-10-29 20:18:04 +00:00
|
|
|
CString Basename( const CString &dir )
|
2003-01-23 04:22:07 +00:00
|
|
|
{
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t end = dir.find_last_not_of( "/\\" );
|
2003-10-29 20:18:04 +00:00
|
|
|
if( end == dir.npos )
|
|
|
|
|
return "";
|
|
|
|
|
|
2004-06-16 07:01:12 +00:00
|
|
|
size_t start = dir.find_last_of( "/\\", end );
|
2003-10-29 20:18:04 +00:00
|
|
|
if( start == dir.npos )
|
|
|
|
|
start = 0;
|
|
|
|
|
else
|
|
|
|
|
++start;
|
|
|
|
|
|
|
|
|
|
return dir.substr( start, end-start+1 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return all but the last named component of dir:
|
|
|
|
|
* a/b/c -> a/b/
|
|
|
|
|
* a/b/c/ -> a/b/
|
|
|
|
|
* c/ -> ./
|
|
|
|
|
* /foo -> /
|
|
|
|
|
* / -> /
|
|
|
|
|
*/
|
|
|
|
|
CString Dirname( const CString &dir )
|
|
|
|
|
{
|
|
|
|
|
/* Special case: "/" -> "/". */
|
2003-12-10 09:47:36 +00:00
|
|
|
if( dir.size() == 1 && dir[0] == '/' )
|
|
|
|
|
return "/";
|
2003-10-29 20:18:04 +00:00
|
|
|
|
|
|
|
|
int pos = dir.size()-1;
|
|
|
|
|
/* Skip trailing slashes. */
|
2003-12-10 09:47:36 +00:00
|
|
|
while( pos >= 0 && dir[pos] == '/' )
|
2003-10-29 20:18:04 +00:00
|
|
|
--pos;
|
|
|
|
|
|
|
|
|
|
/* Skip the last component. */
|
2003-12-10 09:47:36 +00:00
|
|
|
while( pos >= 0 && dir[pos] != '/' )
|
2003-10-29 20:18:04 +00:00
|
|
|
--pos;
|
2003-01-23 04:22:07 +00:00
|
|
|
|
2003-10-29 20:18:04 +00:00
|
|
|
if( pos < 0 )
|
2003-12-10 09:47:36 +00:00
|
|
|
return "./";
|
2003-01-23 04:22:07 +00:00
|
|
|
|
2003-10-29 20:18:04 +00:00
|
|
|
return dir.substr(0, pos+1);
|
2003-01-23 04:22:07 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-07 22:54:50 +00:00
|
|
|
CString Capitalize( const CString &s )
|
2003-04-13 04:50:08 +00:00
|
|
|
{
|
|
|
|
|
if( s.GetLength()==0 )
|
|
|
|
|
return "";
|
2004-01-02 08:43:14 +00:00
|
|
|
CString s2 = s;
|
2004-01-03 04:30:34 +00:00
|
|
|
/* XXX: utf-8 */
|
2004-01-27 21:03:43 +00:00
|
|
|
if( !(s2[0] & 0x80) )
|
2004-01-03 04:30:34 +00:00
|
|
|
s2[0] = (char) toupper( s2[0] );
|
2004-01-02 08:43:14 +00:00
|
|
|
return s2;
|
2003-04-13 04:50:08 +00:00
|
|
|
}
|
2003-10-20 03:57:02 +00:00
|
|
|
|
|
|
|
|
char char_traits_char_nocase::uptab[256] =
|
|
|
|
|
{
|
|
|
|
|
'\x00','\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x0A','\x0B','\x0C','\x0D','\x0E','\x0F',
|
|
|
|
|
'\x10','\x11','\x12','\x13','\x14','\x15','\x16','\x17','\x18','\x19','\x1A','\x1B','\x1C','\x1D','\x1E','\x1F',
|
|
|
|
|
'\x20','\x21','\x22','\x23','\x24','\x25','\x26','\x27','\x28','\x29','\x2A','\x2B','\x2C','\x2D','\x2E','\x2F',
|
|
|
|
|
'\x30','\x31','\x32','\x33','\x34','\x35','\x36','\x37','\x38','\x39','\x3A','\x3B','\x3C','\x3D','\x3E','\x3F',
|
|
|
|
|
'\x40','\x41','\x42','\x43','\x44','\x45','\x46','\x47','\x48','\x49','\x4A','\x4B','\x4C','\x4D','\x4E','\x4F',
|
|
|
|
|
'\x50','\x51','\x52','\x53','\x54','\x55','\x56','\x57','\x58','\x59','\x5A','\x5B','\x5C','\x5D','\x5E','\x5F',
|
|
|
|
|
'\x60','\x41','\x42','\x43','\x44','\x45','\x46','\x47','\x48','\x49','\x4A','\x4B','\x4C','\x4D','\x4E','\x4F',
|
|
|
|
|
'\x50','\x51','\x52','\x53','\x54','\x55','\x56','\x57','\x58','\x59','\x5A','\x7B','\x7C','\x7D','\x7E','\x7F',
|
|
|
|
|
'\x80','\x81','\x82','\x83','\x84','\x85','\x86','\x87','\x88','\x89','\x8A','\x8B','\x8C','\x8D','\x8E','\x8F',
|
|
|
|
|
'\x90','\x91','\x92','\x93','\x94','\x95','\x96','\x97','\x98','\x99','\x9A','\x9B','\x9C','\x9D','\x9E','\x9F',
|
|
|
|
|
'\xA0','\xA1','\xA2','\xA3','\xA4','\xA5','\xA6','\xA7','\xA8','\xA9','\xAA','\xAB','\xAC','\xAD','\xAE','\xAF',
|
|
|
|
|
'\xB0','\xB1','\xB2','\xB3','\xB4','\xB5','\xB6','\xB7','\xB8','\xB9','\xBA','\xBB','\xBC','\xBD','\xBE','\xBF',
|
|
|
|
|
'\xC0','\xC1','\xC2','\xC3','\xC4','\xC5','\xC6','\xC7','\xC8','\xC9','\xCA','\xCB','\xCC','\xCD','\xCE','\xCF',
|
|
|
|
|
'\xD0','\xD1','\xD2','\xD3','\xD4','\xD5','\xD6','\xD7','\xD8','\xD9','\xDA','\xDB','\xDC','\xDD','\xDE','\xDF',
|
|
|
|
|
'\xE0','\xE1','\xE2','\xE3','\xE4','\xE5','\xE6','\xE7','\xE8','\xE9','\xEA','\xEB','\xEC','\xED','\xEE','\xEF',
|
|
|
|
|
'\xF0','\xF1','\xF2','\xF3','\xF4','\xF5','\xF6','\xF7','\xF8','\xF9','\xFA','\xFB','\xFC','\xFD','\xFE','\xFF',
|
|
|
|
|
};
|
2003-12-08 04:02:43 +00:00
|
|
|
|
2003-12-21 02:20:53 +00:00
|
|
|
void FixSlashesInPlace( CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
for( unsigned i = 0; i < sPath.size(); ++i )
|
|
|
|
|
if( sPath[i] == '\\' )
|
|
|
|
|
sPath[i] = '/';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString FixSlashes( CString sPath )
|
|
|
|
|
{
|
|
|
|
|
FixSlashesInPlace( sPath );
|
|
|
|
|
return sPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Keep trailing slashes, since that can be used to illustrate that a path always
|
2004-02-27 05:50:57 +00:00
|
|
|
* represents a directory.
|
2003-12-21 02:20:53 +00:00
|
|
|
*
|
|
|
|
|
* foo/bar -> foo/bar
|
|
|
|
|
* foo/bar/ -> foo/bar/
|
|
|
|
|
* foo///bar/// -> foo/bar/
|
|
|
|
|
* foo/bar/./baz -> foo/bar/baz
|
|
|
|
|
* foo/bar/../baz -> foo/baz
|
2004-04-06 04:21:06 +00:00
|
|
|
* ../foo -> ../foo
|
|
|
|
|
* ../../foo -> ../../foo
|
2004-02-27 05:50:57 +00:00
|
|
|
* ./foo -> foo (if bRemoveLeadingDot), ./foo (if !bRemoveLeadingDot)
|
2003-12-21 02:20:53 +00:00
|
|
|
* ./ -> .
|
|
|
|
|
* ./// -> .
|
|
|
|
|
*/
|
|
|
|
|
|
2004-02-27 05:50:57 +00:00
|
|
|
void CollapsePath( CString &sPath, bool bRemoveLeadingDot )
|
2003-12-21 02:20:53 +00:00
|
|
|
{
|
|
|
|
|
/* Don't ignore empty: we do want to keep trailing slashes. */
|
|
|
|
|
CStringArray as;
|
|
|
|
|
split( sPath, "/", as, false );
|
|
|
|
|
|
|
|
|
|
for( unsigned i=0; i<as.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
if( as[i] == ".." && i != 0 )
|
|
|
|
|
{
|
2004-04-06 04:21:06 +00:00
|
|
|
/* If the previous element is also "..", then we have a path beginning
|
|
|
|
|
* with multiple "../"--one .. can't eat another .., since that would
|
|
|
|
|
* cause "../../foo" to become "foo". */
|
|
|
|
|
if( as[i-1] != ".." )
|
|
|
|
|
{
|
|
|
|
|
as.erase( as.begin()+i-1 );
|
|
|
|
|
as.erase( as.begin()+i-1 );
|
|
|
|
|
i -= 2;
|
|
|
|
|
}
|
2003-12-21 02:20:53 +00:00
|
|
|
}
|
2004-03-03 00:35:00 +00:00
|
|
|
else if( as[i] == "" && i != 0 && i+1 < as.size() )
|
2003-12-21 02:20:53 +00:00
|
|
|
{
|
2004-03-03 00:35:00 +00:00
|
|
|
/* Remove empty parts that aren't at the beginning or end;
|
|
|
|
|
* "foo//bar/" -> "foo/bar/", but "/foo" -> "/foo" and "foo/"
|
|
|
|
|
* to "foo/". */
|
2003-12-21 02:20:53 +00:00
|
|
|
as.erase( as.begin()+i );
|
|
|
|
|
i -= 1;
|
|
|
|
|
}
|
2004-02-27 05:50:57 +00:00
|
|
|
else if( as[i] == "." && (bRemoveLeadingDot || i != 0) )
|
2003-12-21 02:20:53 +00:00
|
|
|
{
|
|
|
|
|
as.erase( as.begin()+i );
|
|
|
|
|
i -= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sPath = join( "/", as );
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-08 04:02:43 +00:00
|
|
|
|
2004-12-07 01:43:06 +00:00
|
|
|
bool FromString( const CString &sValue, int &out )
|
|
|
|
|
{
|
|
|
|
|
if( sscanf( sValue.c_str(), "%d", &out ) == 1 )
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
out = 0;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FromString( const CString &sValue, unsigned &out )
|
|
|
|
|
{
|
|
|
|
|
if( sscanf( sValue.c_str(), "%u", &out ) == 1 )
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
out = 0;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FromString( const CString &sValue, float &out )
|
|
|
|
|
{
|
|
|
|
|
const char *endptr = sValue.data() + sValue.size();
|
|
|
|
|
out = strtof( sValue, (char **) &endptr );
|
|
|
|
|
return endptr != sValue.data();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FromString( const CString &sValue, bool &out )
|
|
|
|
|
{
|
|
|
|
|
if( sValue.size() == 0 )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
out = (atoi(sValue) != 0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString ToString( int value )
|
|
|
|
|
{
|
|
|
|
|
return ssprintf( "%i", value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString ToString( unsigned value )
|
|
|
|
|
{
|
|
|
|
|
return ssprintf( "%u", value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString ToString( float value )
|
|
|
|
|
{
|
|
|
|
|
return ssprintf( "%f", value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString ToString( bool value )
|
|
|
|
|
{
|
|
|
|
|
return ssprintf( "%i", value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-12-08 04:02:43 +00:00
|
|
|
//
|
|
|
|
|
// Helper function for reading/writing scores
|
|
|
|
|
//
|
2005-05-21 06:51:55 +00:00
|
|
|
bool FileRead(RageFileBasic& f, CString& sOut)
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
|
|
|
|
if (f.AtEOF())
|
|
|
|
|
return false;
|
2004-06-06 20:56:46 +00:00
|
|
|
if( f.GetLine(sOut) == -1 )
|
|
|
|
|
return false;
|
2003-12-08 04:02:43 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-21 06:51:55 +00:00
|
|
|
bool FileRead(RageFileBasic& f, int& iOut)
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
|
|
|
|
CString s;
|
|
|
|
|
if (!FileRead(f, s))
|
|
|
|
|
return false;
|
|
|
|
|
iOut = atoi(s);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-21 06:51:55 +00:00
|
|
|
bool FileRead(RageFileBasic& f, unsigned& uOut)
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
|
|
|
|
CString s;
|
|
|
|
|
if (!FileRead(f, s))
|
|
|
|
|
return false;
|
|
|
|
|
uOut = atoi(s);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-21 06:51:55 +00:00
|
|
|
bool FileRead(RageFileBasic& f, float& fOut)
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
|
|
|
|
CString s;
|
|
|
|
|
if (!FileRead(f, s))
|
|
|
|
|
return false;
|
2004-08-10 20:57:59 +00:00
|
|
|
fOut = strtof( s, NULL );
|
2003-12-08 04:02:43 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-21 06:51:55 +00:00
|
|
|
void FileWrite(RageFileBasic& f, const CString& sWrite)
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
|
|
|
|
f.PutLine( sWrite );
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-21 06:51:55 +00:00
|
|
|
void FileWrite(RageFileBasic& f, int iWrite)
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
|
|
|
|
f.PutLine( ssprintf("%d", iWrite) );
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-21 06:51:55 +00:00
|
|
|
void FileWrite(RageFileBasic& f, size_t uWrite)
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
2004-01-27 21:03:43 +00:00
|
|
|
f.PutLine( ssprintf("%i", (int)uWrite) );
|
2003-12-08 04:02:43 +00:00
|
|
|
}
|
|
|
|
|
|
2005-05-21 06:51:55 +00:00
|
|
|
void FileWrite(RageFileBasic& f, float fWrite)
|
2003-12-08 04:02:43 +00:00
|
|
|
{
|
|
|
|
|
f.PutLine( ssprintf("%f", fWrite) );
|
|
|
|
|
}
|
2003-12-08 10:27:45 +00:00
|
|
|
|
2003-12-21 05:59:52 +00:00
|
|
|
bool FileCopy( CString sSrcFile, CString sDstFile )
|
2003-12-08 10:27:45 +00:00
|
|
|
{
|
2004-02-17 22:12:39 +00:00
|
|
|
if( !sSrcFile.CompareNoCase(sDstFile) )
|
|
|
|
|
{
|
|
|
|
|
LOG->Warn( "Tried to copy \"%s\" over itself", sSrcFile.c_str() );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-08 10:27:45 +00:00
|
|
|
RageFile in;
|
|
|
|
|
if( !in.Open(sSrcFile, RageFile::READ) )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
RageFile out;
|
|
|
|
|
if( !out.Open(sDstFile, RageFile::WRITE) )
|
|
|
|
|
return false;
|
|
|
|
|
|
2005-05-21 06:51:55 +00:00
|
|
|
CString sError;
|
|
|
|
|
if( !FileCopy(in, out, sError) )
|
|
|
|
|
{
|
|
|
|
|
LOG->Warn( "FileCopy(%s,%s): %s",
|
|
|
|
|
sSrcFile.c_str(), sDstFile.c_str(), sError.c_str() );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileCopy( RageFileBasic &in, RageFileBasic &out, CString &sError, bool *bReadError )
|
|
|
|
|
{
|
2005-04-25 21:05:29 +00:00
|
|
|
while(1)
|
|
|
|
|
{
|
|
|
|
|
CString data;
|
|
|
|
|
if( in.Read(data, 1024*32) == -1 )
|
|
|
|
|
{
|
2005-05-21 06:51:55 +00:00
|
|
|
sError = ssprintf( "read error: %s", in.GetError().c_str() );
|
|
|
|
|
if( bReadError != NULL )
|
|
|
|
|
*bReadError = true;
|
2005-04-25 21:05:29 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if( data.empty() )
|
|
|
|
|
break;
|
|
|
|
|
int i = out.Write(data);
|
|
|
|
|
if( i == -1 )
|
|
|
|
|
{
|
2005-05-21 06:51:55 +00:00
|
|
|
sError = ssprintf( "write error: %s", out.GetError().c_str() );
|
|
|
|
|
if( bReadError != NULL )
|
|
|
|
|
*bReadError = false;
|
2005-04-25 21:05:29 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-05-21 06:51:55 +00:00
|
|
|
|
2003-12-21 09:18:06 +00:00
|
|
|
if( out.Flush() == -1 )
|
2005-05-21 06:51:55 +00:00
|
|
|
{
|
|
|
|
|
sError = ssprintf( "write error: %s", out.GetError().c_str() );
|
|
|
|
|
if( bReadError != NULL )
|
|
|
|
|
*bReadError = false;
|
2003-12-21 09:18:06 +00:00
|
|
|
return false;
|
2005-05-21 06:51:55 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-21 09:18:06 +00:00
|
|
|
return true;
|
2003-12-08 10:27:45 +00:00
|
|
|
}
|
2004-05-06 02:40:33 +00:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2001-2004 Chris Danford, Glenn Maynard
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
|
|
|
* whom the Software is furnished to do so, provided that the above
|
|
|
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
|
|
|
* the Software and that both the above copyright notice(s) and this
|
|
|
|
|
* permission notice appear in supporting documentation.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
|
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
|
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
|
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
|
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*/
|