2004-02-09 08:10:01 +00:00
|
|
|
#include "global.h"
|
|
|
|
|
#include "XmlFile.h"
|
2004-02-10 09:42:01 +00:00
|
|
|
#include "RageFile.h"
|
2004-04-02 05:06:32 +00:00
|
|
|
#include "RageLog.h"
|
2004-07-18 21:55:49 +00:00
|
|
|
#include "RageUtil.h"
|
|
|
|
|
#include "DateTime.h"
|
2005-01-07 09:09:23 +00:00
|
|
|
#include "Foreach.h"
|
2005-05-29 01:05:23 +00:00
|
|
|
#include "arch/Dialog/Dialog.h"
|
2005-06-16 03:08:45 +00:00
|
|
|
#include "RageFileDriverMemory.h"
|
2004-02-09 08:10:01 +00:00
|
|
|
|
2005-09-03 08:28:43 +00:00
|
|
|
static inline long XStr2Int( const char* str, long default_value = 0 )
|
|
|
|
|
{
|
|
|
|
|
return str ? atol(str) : default_value;
|
|
|
|
|
}
|
2004-02-09 08:10:01 +00:00
|
|
|
|
2004-08-18 22:09:23 +00:00
|
|
|
static const char chXMLTagOpen = '<';
|
2005-02-18 13:05:17 +00:00
|
|
|
static const char chXMLTagClose = '>';
|
2005-02-18 14:03:50 +00:00
|
|
|
static const char chXMLQuestion = '?'; // used in checking for meta tags: "<?TAG ... ?/>"
|
2005-02-18 13:05:17 +00:00
|
|
|
static const char chXMLTagPre = '/';
|
|
|
|
|
static const char chXMLEscape = '\\'; // for value field escape
|
2005-02-18 14:03:50 +00:00
|
|
|
static const char chXMLExclamation = '!';
|
|
|
|
|
static const char chXMLDash = '-';
|
2004-02-09 08:10:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
static const XENTITY x_EntityTable[] = {
|
2005-09-04 21:59:00 +00:00
|
|
|
{ '&', "&", 5 } ,
|
|
|
|
|
{ '\"', """, 6 } ,
|
|
|
|
|
{ '\'', "'", 6 } ,
|
|
|
|
|
{ '<', "<", 4 } ,
|
|
|
|
|
{ '>', ">", 4 }
|
2004-02-09 08:10:01 +00:00
|
|
|
};
|
|
|
|
|
|
2005-09-04 21:59:00 +00:00
|
|
|
XENTITYS entityDefault(x_EntityTable, sizeof(x_EntityTable)/sizeof(x_EntityTable[0]) );
|
2004-02-09 08:10:01 +00:00
|
|
|
|
2004-05-23 02:28:36 +00:00
|
|
|
// skip spaces
|
2005-07-15 22:58:11 +00:00
|
|
|
static char* tcsskip( const char* psz )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 21:52:59 +00:00
|
|
|
while( psz && isspace(*psz) ) psz++;
|
2004-02-09 08:10:01 +00:00
|
|
|
|
2004-02-25 01:09:17 +00:00
|
|
|
return (char*)psz;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
2004-02-25 01:09:17 +00:00
|
|
|
// Desc : similar with strchr with escape process
|
2004-02-09 08:10:01 +00:00
|
|
|
// Param : escape - will be escape character
|
2005-09-03 08:28:43 +00:00
|
|
|
static const char* tcsechr( const char* pch, int ch, char escape )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
bool bInEscape = false;
|
2004-02-09 08:10:01 +00:00
|
|
|
while( pch && *pch )
|
|
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
if( *pch == escape && !bInEscape )
|
|
|
|
|
bInEscape = true;
|
2004-02-09 08:10:01 +00:00
|
|
|
else
|
|
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
bInEscape = false;
|
|
|
|
|
if( *pch == ch )
|
|
|
|
|
return pch;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
pch++;
|
|
|
|
|
}
|
|
|
|
|
return pch;
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-25 01:09:17 +00:00
|
|
|
// Desc : similar with strlen with escape process
|
2004-02-09 08:10:01 +00:00
|
|
|
// Param : escape - will be escape character
|
2005-09-03 08:28:43 +00:00
|
|
|
static int tcselen( char escape, const char *start, const char *end )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
int len = 0;
|
2005-09-03 08:28:43 +00:00
|
|
|
bool bInEscape = false;
|
2004-07-22 19:25:17 +00:00
|
|
|
while( start && *start && start<end )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
if( *start == escape && !bInEscape )
|
|
|
|
|
bInEscape = true;
|
2004-02-09 08:10:01 +00:00
|
|
|
else
|
|
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
bInEscape = false;
|
2004-02-09 08:10:01 +00:00
|
|
|
len++;
|
|
|
|
|
}
|
2004-07-22 19:25:17 +00:00
|
|
|
++start;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Desc : similar with _tcscpy with escape process
|
|
|
|
|
// Param : escape - will be escape character
|
2005-09-03 08:28:43 +00:00
|
|
|
static void unescape( char *psz, int escape, const char* srt, const char* end = NULL )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2004-07-22 19:25:17 +00:00
|
|
|
const char* pch = srt;
|
2004-02-25 01:09:17 +00:00
|
|
|
if( end==NULL ) end = (char*)sizeof(long);
|
2004-07-22 19:25:17 +00:00
|
|
|
const char* prev_escape = NULL;
|
2004-02-09 08:10:01 +00:00
|
|
|
while( pch && *pch && pch<end )
|
|
|
|
|
{
|
|
|
|
|
if( *pch == escape && prev_escape == NULL )
|
|
|
|
|
prev_escape = pch;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
prev_escape = NULL;
|
|
|
|
|
*psz++ = *pch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pch++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*psz = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-25 01:09:17 +00:00
|
|
|
// Desc : similar with strpbrk with escape process
|
2004-02-09 08:10:01 +00:00
|
|
|
// Param : escape - will be escape character
|
2005-07-15 22:58:11 +00:00
|
|
|
static char* tcsepbrk( const char* psz, const char* chset, int escape )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2004-02-25 01:09:17 +00:00
|
|
|
char* pch = (char*)psz;
|
|
|
|
|
char* prev_escape = NULL;
|
2004-02-09 08:10:01 +00:00
|
|
|
while( pch && *pch )
|
|
|
|
|
{
|
|
|
|
|
if( *pch == escape && prev_escape == NULL )
|
|
|
|
|
prev_escape = pch;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
prev_escape = NULL;
|
2004-02-25 01:09:17 +00:00
|
|
|
if( strchr( chset, *pch ) )
|
|
|
|
|
return (char*)pch;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
pch++;
|
|
|
|
|
}
|
|
|
|
|
return pch;
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-26 19:13:08 +00:00
|
|
|
static bool XIsEmptyString( const CString &s )
|
|
|
|
|
{
|
|
|
|
|
return s.find_first_not_of( "\r\n\t " ) == s.npos;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-03 08:28:43 +00:00
|
|
|
// put string of (psz~end) on ps string
|
|
|
|
|
static void SetString( const char* psz, const char* end, CString* ps, bool trim = false, char escape = 0 )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
if( trim )
|
|
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
while( psz < end && isspace(*psz) )
|
|
|
|
|
psz++;
|
|
|
|
|
while( end-1 >= psz && isspace(end[-1]) )
|
|
|
|
|
end--;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
2005-09-03 08:28:43 +00:00
|
|
|
|
2004-02-09 08:10:01 +00:00
|
|
|
int len = end - psz;
|
2005-09-03 08:28:43 +00:00
|
|
|
if( len <= 0 )
|
|
|
|
|
return;
|
|
|
|
|
|
2004-02-09 08:10:01 +00:00
|
|
|
if( escape )
|
|
|
|
|
{
|
2004-07-22 20:14:26 +00:00
|
|
|
len = tcselen( escape, psz, end );
|
2004-07-22 18:25:58 +00:00
|
|
|
char* szTemp = new char[len];
|
2004-07-22 19:25:17 +00:00
|
|
|
unescape( szTemp, escape, psz, end );
|
2004-02-09 08:10:01 +00:00
|
|
|
*ps = szTemp;
|
2004-07-22 18:25:58 +00:00
|
|
|
delete [] szTemp;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2004-07-22 19:31:54 +00:00
|
|
|
ps->assign( psz, len );
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-04-18 10:41:54 +00:00
|
|
|
XNode::~XNode()
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 14:28:00 +00:00
|
|
|
Clear();
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-07 14:28:00 +00:00
|
|
|
void XNode::Clear()
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 09:09:23 +00:00
|
|
|
FOREACH_Child( this, p )
|
|
|
|
|
SAFE_DELETE( p );
|
2005-01-07 14:28:00 +00:00
|
|
|
m_childs.clear();
|
2004-02-09 08:10:01 +00:00
|
|
|
|
2005-01-07 09:09:23 +00:00
|
|
|
FOREACH_Attr( this, p2 )
|
|
|
|
|
SAFE_DELETE( p2 );
|
2005-01-07 14:28:00 +00:00
|
|
|
m_attrs.clear();
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// attr1="value1" attr2='value2' attr3=value3 />
|
|
|
|
|
// ^- return pointer
|
|
|
|
|
// Desc : loading attribute plain xml text
|
|
|
|
|
// Param : pszAttrs - xml of attributes
|
|
|
|
|
// pi = parser information
|
|
|
|
|
// Return : advanced string pointer. (error return NULL)
|
2005-09-03 08:28:43 +00:00
|
|
|
const char* XNode::LoadAttributes( const char* xml, PARSEINFO *pi /*= &piDefault*/)
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
while( xml && *xml )
|
|
|
|
|
{
|
2004-07-22 20:14:26 +00:00
|
|
|
xml = tcsskip( xml );
|
2004-02-09 19:33:56 +00:00
|
|
|
if( !xml )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// close tag
|
2005-02-18 14:03:50 +00:00
|
|
|
if( *xml == chXMLTagClose || *xml == chXMLTagPre || *xml == chXMLQuestion || *xml == chXMLDash )
|
2005-09-04 21:59:00 +00:00
|
|
|
return xml; // well-formed tag
|
2004-02-09 19:33:56 +00:00
|
|
|
|
|
|
|
|
// XML Attr Name
|
2005-09-03 08:28:43 +00:00
|
|
|
const char* pEnd = strpbrk( xml, " =" );
|
2004-02-09 19:33:56 +00:00
|
|
|
if( pEnd == NULL )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2004-02-09 19:33:56 +00:00
|
|
|
// error
|
2005-02-18 11:55:34 +00:00
|
|
|
if( !pi->error_occur )
|
2004-02-09 19:33:56 +00:00
|
|
|
{
|
2005-02-18 11:55:34 +00:00
|
|
|
pi->error_occur = true;
|
2004-02-09 19:33:56 +00:00
|
|
|
pi->error_pointer = xml;
|
|
|
|
|
pi->error_code = PIE_ATTR_NO_VALUE;
|
2005-01-07 14:28:00 +00:00
|
|
|
pi->error_string = ssprintf( ("<%s> attribute has error "), m_sName.c_str() );
|
2004-02-09 19:33:56 +00:00
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2004-07-23 02:27:37 +00:00
|
|
|
XAttr *attr = new XAttr;
|
2004-02-09 08:10:01 +00:00
|
|
|
|
2004-02-09 19:33:56 +00:00
|
|
|
// XML Attr Name
|
2005-01-07 14:28:00 +00:00
|
|
|
SetString( xml, pEnd, &attr->m_sName );
|
2004-02-09 19:33:56 +00:00
|
|
|
|
|
|
|
|
// add new attribute
|
2005-01-07 14:28:00 +00:00
|
|
|
DEBUG_ASSERT( attr->m_sName.size() );
|
2005-06-27 05:24:24 +00:00
|
|
|
m_attrs.insert( make_pair(attr->m_sName, attr) );
|
2004-02-09 19:33:56 +00:00
|
|
|
xml = pEnd;
|
|
|
|
|
|
|
|
|
|
// XML Attr Value
|
2004-07-22 20:14:26 +00:00
|
|
|
xml = tcsskip( xml );
|
2004-02-09 19:33:56 +00:00
|
|
|
if( !xml )
|
|
|
|
|
continue;
|
|
|
|
|
|
2004-02-25 01:09:17 +00:00
|
|
|
//if( xml = strchr( xml, '=' ) )
|
2004-02-09 19:33:56 +00:00
|
|
|
if( *xml == '=' )
|
|
|
|
|
{
|
2004-07-22 20:14:26 +00:00
|
|
|
xml = tcsskip( ++xml );
|
2004-02-09 19:33:56 +00:00
|
|
|
if( !xml )
|
|
|
|
|
continue;
|
|
|
|
|
// if " or '
|
|
|
|
|
// or none quote
|
|
|
|
|
int quote = *xml;
|
|
|
|
|
if( quote == '"' || quote == '\'' )
|
2005-01-07 14:28:00 +00:00
|
|
|
{
|
2004-07-22 20:14:26 +00:00
|
|
|
pEnd = tcsechr( ++xml, quote, chXMLEscape );
|
2005-01-07 14:28:00 +00:00
|
|
|
}
|
2004-02-09 19:33:56 +00:00
|
|
|
else
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2004-02-09 19:33:56 +00:00
|
|
|
//attr= value>
|
|
|
|
|
// none quote mode
|
2004-07-22 20:14:26 +00:00
|
|
|
//pEnd = tcsechr( xml, ' ', '\\' );
|
|
|
|
|
pEnd = tcsepbrk( xml, (" >"), chXMLEscape );
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
2004-02-09 19:33:56 +00:00
|
|
|
bool trim = pi->trim_value;
|
2004-08-18 22:09:23 +00:00
|
|
|
char escape = pi->escape_value;
|
2005-01-07 14:28:00 +00:00
|
|
|
//SetString( xml, pEnd, &attr->m_sValue, trim, chXMLEscape );
|
|
|
|
|
SetString( xml, pEnd, &attr->m_sValue, trim, escape );
|
2004-02-09 08:10:01 +00:00
|
|
|
xml = pEnd;
|
2004-02-09 19:33:56 +00:00
|
|
|
// ATTRVALUE
|
|
|
|
|
if( pi->entity_value && pi->entitys )
|
2005-01-07 14:28:00 +00:00
|
|
|
attr->m_sValue = pi->entitys->Ref2Entity(attr->m_sValue);
|
2004-02-09 08:10:01 +00:00
|
|
|
|
2004-02-09 19:33:56 +00:00
|
|
|
if( quote == '"' || quote == '\'' )
|
|
|
|
|
xml++;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-18 14:03:50 +00:00
|
|
|
// not well-formed tag
|
2004-02-09 08:10:01 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// <TAG attr1="value1" attr2='value2' attr3=value3 >
|
|
|
|
|
// </TAG>
|
|
|
|
|
// or
|
|
|
|
|
// <TAG />
|
|
|
|
|
// ^- return pointer
|
|
|
|
|
// Desc : load xml plain text
|
|
|
|
|
// Param : pszXml - plain xml text
|
|
|
|
|
// pi = parser information
|
|
|
|
|
// Return : advanced string pointer (error return NULL)
|
2005-09-03 08:28:43 +00:00
|
|
|
const char* XNode::Load( const char* xml, PARSEINFO *pi /*= &piDefault*/ )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 14:28:00 +00:00
|
|
|
Clear();
|
2004-02-09 08:10:01 +00:00
|
|
|
|
2005-02-25 06:13:21 +00:00
|
|
|
// <
|
2004-02-25 01:09:17 +00:00
|
|
|
xml = strchr( xml, chXMLTagOpen );
|
2004-02-09 08:10:01 +00:00
|
|
|
if( xml == NULL )
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2005-02-25 06:13:21 +00:00
|
|
|
// </
|
|
|
|
|
if( xml[1] == chXMLTagPre )
|
2004-02-09 08:10:01 +00:00
|
|
|
return xml;
|
|
|
|
|
|
2005-02-25 06:13:21 +00:00
|
|
|
/* <!-- */
|
|
|
|
|
if( !strncmp(xml+1, "!--", 3) )
|
|
|
|
|
{
|
|
|
|
|
xml += 4;
|
|
|
|
|
|
|
|
|
|
/* Find the close tag. */
|
|
|
|
|
char *pEnd = strstr( xml, "-->" );
|
|
|
|
|
if( pEnd == NULL )
|
|
|
|
|
{
|
|
|
|
|
if( !pi->error_occur )
|
|
|
|
|
{
|
|
|
|
|
pi->error_occur = true;
|
|
|
|
|
pi->error_pointer = xml;
|
|
|
|
|
pi->error_code = PIE_ALONE_NOT_CLOSED;
|
|
|
|
|
pi->error_string = "Unterminated comment";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Skip -->.
|
|
|
|
|
xml = pEnd + 3;
|
|
|
|
|
|
|
|
|
|
return Load( xml, pi );
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-09 08:10:01 +00:00
|
|
|
// XML Node Tag Name Open
|
|
|
|
|
xml++;
|
2005-09-03 08:28:43 +00:00
|
|
|
const char* pTagEnd = strpbrk( xml, " \t\r\n/>" );
|
2005-01-07 14:28:00 +00:00
|
|
|
SetString( xml, pTagEnd, &m_sName );
|
2005-02-18 14:03:50 +00:00
|
|
|
|
2004-02-09 08:10:01 +00:00
|
|
|
xml = pTagEnd;
|
|
|
|
|
// Generate XML Attributte List
|
2004-02-09 19:33:56 +00:00
|
|
|
xml = LoadAttributes( xml, pi );
|
|
|
|
|
if( xml == NULL )
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2005-02-18 14:03:50 +00:00
|
|
|
// alone tag <TAG ... /> or <?TAG ... ?> or <!-- ... -->
|
|
|
|
|
// current pointer: ^ ^ ^
|
|
|
|
|
|
|
|
|
|
if( *xml == chXMLTagPre || *xml == chXMLQuestion || *xml == chXMLDash )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2004-02-09 19:33:56 +00:00
|
|
|
xml++;
|
2005-02-18 14:03:50 +00:00
|
|
|
|
|
|
|
|
// skip over 2nd dash
|
|
|
|
|
if( *xml == chXMLDash )
|
|
|
|
|
xml++;
|
|
|
|
|
|
2005-09-03 08:28:43 +00:00
|
|
|
if( *xml != chXMLTagClose )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2004-02-09 19:33:56 +00:00
|
|
|
// error: <TAG ... / >
|
2005-02-18 11:55:34 +00:00
|
|
|
if( !pi->error_occur )
|
2004-02-09 19:33:56 +00:00
|
|
|
{
|
2005-02-18 11:55:34 +00:00
|
|
|
pi->error_occur = true;
|
2004-02-09 19:33:56 +00:00
|
|
|
pi->error_pointer = xml;
|
|
|
|
|
pi->error_code = PIE_ALONE_NOT_CLOSED;
|
2005-09-03 08:28:43 +00:00
|
|
|
pi->error_string = "Element must be closed.";
|
2004-02-09 19:33:56 +00:00
|
|
|
}
|
2005-09-03 08:28:43 +00:00
|
|
|
|
|
|
|
|
// ill-formed tag
|
2004-02-09 19:33:56 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
2005-09-03 08:28:43 +00:00
|
|
|
|
|
|
|
|
// well-formed tag
|
|
|
|
|
++xml;
|
|
|
|
|
|
|
|
|
|
// UGLY: We want to ignore all XML meta tags. So, since the Node we
|
|
|
|
|
// just loaded is a meta tag, then Load ourself again using the rest
|
|
|
|
|
// of the file until we reach a non-meta tag.
|
|
|
|
|
if( !m_sName.empty() && (m_sName[0] == chXMLQuestion || m_sName[0] == chXMLExclamation) )
|
|
|
|
|
xml = Load( xml, pi );
|
|
|
|
|
|
|
|
|
|
return xml;
|
2004-02-09 19:33:56 +00:00
|
|
|
}
|
2005-09-03 08:28:43 +00:00
|
|
|
|
2004-02-09 19:33:56 +00:00
|
|
|
// open/close tag <TAG ..> ... </TAG>
|
|
|
|
|
// ^- current pointer
|
2005-09-03 08:28:43 +00:00
|
|
|
if( XIsEmptyString( m_sValue ) )
|
|
|
|
|
{
|
|
|
|
|
// Text Value
|
|
|
|
|
const char* pEnd = tcsechr( ++xml, chXMLTagOpen, chXMLEscape );
|
|
|
|
|
if( pEnd == NULL )
|
|
|
|
|
{
|
|
|
|
|
if( !pi->error_occur )
|
|
|
|
|
{
|
|
|
|
|
pi->error_occur = true;
|
|
|
|
|
pi->error_pointer = xml;
|
|
|
|
|
pi->error_code = PIE_NOT_CLOSED;
|
|
|
|
|
pi->error_string = ssprintf( "%s must be closed with </%s>", m_sName.c_str(), m_sName.c_str() );
|
|
|
|
|
}
|
|
|
|
|
// error cos not exist CloseTag </TAG>
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool trim = pi->trim_value;
|
|
|
|
|
char escape = pi->escape_value;
|
|
|
|
|
SetString( xml, pEnd, &m_sValue, trim, escape );
|
|
|
|
|
|
|
|
|
|
xml = pEnd;
|
|
|
|
|
// TEXTVALUE reference
|
|
|
|
|
if( pi->entity_value && pi->entitys )
|
|
|
|
|
m_sValue = pi->entitys->Ref2Entity(m_sValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generate child nodes
|
|
|
|
|
while( xml && *xml )
|
2004-02-09 19:33:56 +00:00
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
XNode *node = new XNode;
|
|
|
|
|
|
|
|
|
|
xml = node->Load( xml,pi );
|
|
|
|
|
if( !node->m_sName.empty() )
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ASSERT( node->m_sName.size() );
|
|
|
|
|
m_childs.insert( make_pair(node->m_sName, node) );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
delete node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// open/close tag <TAG ..> ... </TAG>
|
|
|
|
|
// ^- current pointer
|
|
|
|
|
// CloseTag case
|
|
|
|
|
if( xml && *xml && *(xml+1) && *xml == chXMLTagOpen && *(xml+1) == chXMLTagPre )
|
2004-02-09 19:33:56 +00:00
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
// </Close>
|
|
|
|
|
xml+=2; // C
|
|
|
|
|
|
|
|
|
|
xml = tcsskip( xml );
|
|
|
|
|
if( xml == NULL )
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
CString closename;
|
|
|
|
|
const char* pEnd = strpbrk( xml, " >" );
|
2004-02-09 19:33:56 +00:00
|
|
|
if( pEnd == NULL )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-02-18 11:55:34 +00:00
|
|
|
if( !pi->error_occur )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-02-18 11:55:34 +00:00
|
|
|
pi->error_occur = true;
|
2004-02-09 08:10:01 +00:00
|
|
|
pi->error_pointer = xml;
|
2004-02-09 19:33:56 +00:00
|
|
|
pi->error_code = PIE_NOT_CLOSED;
|
2005-09-03 08:28:43 +00:00
|
|
|
pi->error_string = ssprintf( "it must be closed with </%s>", m_sName.c_str() );
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
2005-09-03 08:28:43 +00:00
|
|
|
// error
|
2004-02-09 08:10:01 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
2005-09-03 08:28:43 +00:00
|
|
|
SetString( xml, pEnd, &closename );
|
|
|
|
|
if( closename == this->m_sName )
|
2004-02-09 19:33:56 +00:00
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
// wel-formed open/close
|
|
|
|
|
xml = pEnd+1;
|
|
|
|
|
// return '>' or ' ' after pointer
|
|
|
|
|
return xml;
|
2004-02-09 19:33:56 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
xml = pEnd+1;
|
|
|
|
|
// not welformed open/close
|
|
|
|
|
if( !pi->error_occur )
|
|
|
|
|
{
|
|
|
|
|
pi->error_occur = true;
|
|
|
|
|
pi->error_pointer = xml;
|
|
|
|
|
pi->error_code = PIE_NOT_NESTED;
|
|
|
|
|
pi->error_string = ssprintf( "'<%s> ... </%s>' is not well-formed.", m_sName.c_str(), closename.c_str() );
|
2004-02-09 19:33:56 +00:00
|
|
|
|
2005-09-03 08:28:43 +00:00
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // Alone child Tag Loaded
|
|
|
|
|
{
|
|
|
|
|
if( xml && XIsEmptyString( m_sValue ) && *xml !=chXMLTagOpen )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
// Text Value
|
|
|
|
|
const char* pEnd = tcsechr( xml, chXMLTagOpen, chXMLEscape );
|
2004-02-09 08:10:01 +00:00
|
|
|
if( pEnd == NULL )
|
|
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
// error cos not exist CloseTag </TAG>
|
|
|
|
|
if( !pi->error_occur )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-02-18 11:55:34 +00:00
|
|
|
pi->error_occur = true;
|
2004-02-09 08:10:01 +00:00
|
|
|
pi->error_pointer = xml;
|
|
|
|
|
pi->error_code = PIE_NOT_CLOSED;
|
2005-01-07 14:28:00 +00:00
|
|
|
pi->error_string = ssprintf( "it must be closed with </%s>", m_sName.c_str() );
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
2004-02-09 19:33:56 +00:00
|
|
|
return NULL;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
2004-02-09 19:33:56 +00:00
|
|
|
|
2005-09-03 08:28:43 +00:00
|
|
|
bool trim = pi->trim_value;
|
|
|
|
|
char escape = pi->escape_value;
|
|
|
|
|
SetString( xml, pEnd, &m_sValue, trim, escape );
|
|
|
|
|
|
|
|
|
|
xml = pEnd;
|
|
|
|
|
//TEXTVALUE
|
|
|
|
|
if( pi->entity_value && pi->entitys )
|
|
|
|
|
m_sValue = pi->entitys->Ref2Entity(m_sValue);
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return xml;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Desc : convert plain xml text from parsed xml attirbute
|
|
|
|
|
// Return : converted plain string
|
2005-02-18 13:05:17 +00:00
|
|
|
bool XAttr::GetXML( RageFileBasic &f, DISP_OPT *opt ) const
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-06-14 05:21:48 +00:00
|
|
|
return f.Write(m_sName + "='" + (opt && opt->reference_value && opt->entitys ? opt->entitys->Entity2Ref(m_sValue) : m_sValue) + "' ") != -1;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Desc : convert plain xml text from parsed xml node
|
|
|
|
|
// Return : converted plain string
|
2005-02-18 13:05:17 +00:00
|
|
|
bool XNode::GetXML( RageFileBasic &f, DISP_OPT *opt ) const
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
// tab
|
|
|
|
|
if( opt && opt->newline )
|
|
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
if( f.Write("\r\n") == -1 )
|
|
|
|
|
return false;
|
2004-08-28 21:37:57 +00:00
|
|
|
if( opt->write_tabs )
|
|
|
|
|
for( int i = 0 ; i < opt->tab_base ; i++)
|
|
|
|
|
if( f.Write("\t") == -1 )
|
|
|
|
|
return false;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// <TAG
|
2005-01-07 14:28:00 +00:00
|
|
|
if( f.Write("<" + m_sName) == -1 )
|
2004-06-06 20:47:48 +00:00
|
|
|
return false;
|
2004-02-09 08:10:01 +00:00
|
|
|
|
|
|
|
|
// <TAG Attr1="Val1"
|
2005-01-07 14:28:00 +00:00
|
|
|
if( !m_attrs.empty() )
|
2004-06-06 20:47:48 +00:00
|
|
|
if( f.Write(" ") == -1 )
|
|
|
|
|
return false;
|
2005-02-18 03:07:22 +00:00
|
|
|
FOREACH_CONST_Attr( this, p )
|
2005-01-07 09:09:23 +00:00
|
|
|
if( !p->GetXML(f, opt) )
|
2004-06-06 20:47:48 +00:00
|
|
|
return false;
|
2004-02-09 08:10:01 +00:00
|
|
|
|
2005-01-07 14:28:00 +00:00
|
|
|
if( m_childs.empty() && m_sValue.empty() )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
// <TAG Attr1="Val1"/> alone tag
|
2004-06-06 20:47:48 +00:00
|
|
|
if( f.Write("/>") == -1 )
|
|
|
|
|
return false;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// <TAG Attr1="Val1"> and get child
|
2004-06-06 20:47:48 +00:00
|
|
|
if( f.Write(">") == -1 )
|
|
|
|
|
return false;
|
|
|
|
|
|
2005-01-07 14:28:00 +00:00
|
|
|
if( opt && opt->newline && !m_childs.empty() )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
opt->tab_base++;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-18 03:07:22 +00:00
|
|
|
FOREACH_CONST_Child( this, p )
|
2005-01-07 09:09:23 +00:00
|
|
|
if( !p->GetXML( f, opt ) )
|
2004-06-06 20:47:48 +00:00
|
|
|
return false;
|
2004-02-09 08:10:01 +00:00
|
|
|
|
|
|
|
|
// Text Value
|
2005-01-07 14:28:00 +00:00
|
|
|
if( m_sValue != ("") )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 14:28:00 +00:00
|
|
|
if( opt && opt->newline && !m_childs.empty() )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
if( opt && opt->newline )
|
2004-06-06 20:47:48 +00:00
|
|
|
if( f.Write("\r\n") == -1 )
|
|
|
|
|
return false;
|
2004-08-28 21:37:57 +00:00
|
|
|
if( opt->write_tabs )
|
|
|
|
|
for( int i = 0 ; i < opt->tab_base ; i++)
|
|
|
|
|
if( f.Write("\t") == -1 )
|
|
|
|
|
return false;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
2005-06-16 02:49:39 +00:00
|
|
|
if( f.Write((opt && opt->reference_value && opt->entitys ? opt->entitys->Entity2Ref(m_sValue) : m_sValue)) == -1 )
|
2004-06-06 20:47:48 +00:00
|
|
|
return false;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// </TAG> CloseTag
|
2005-01-07 14:28:00 +00:00
|
|
|
if( opt && opt->newline && !m_childs.empty() )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2004-06-06 20:47:48 +00:00
|
|
|
if( f.Write("\r\n") == -1 )
|
|
|
|
|
return false;
|
2004-08-28 21:37:57 +00:00
|
|
|
if( opt->write_tabs )
|
|
|
|
|
for( int i = 0 ; i < opt->tab_base-1 ; i++)
|
|
|
|
|
if( f.Write("\t") == -1 )
|
|
|
|
|
return false;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
2005-01-07 14:28:00 +00:00
|
|
|
if( f.Write("</" + m_sName + ">") == -1 )
|
2004-06-06 20:47:48 +00:00
|
|
|
return false;
|
2004-02-09 08:10:01 +00:00
|
|
|
|
|
|
|
|
if( opt && opt->newline )
|
|
|
|
|
{
|
2005-01-07 14:28:00 +00:00
|
|
|
if( !m_childs.empty() )
|
2004-02-09 08:10:01 +00:00
|
|
|
opt->tab_base--;
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-06-06 20:47:48 +00:00
|
|
|
return true;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
2005-06-16 03:08:45 +00:00
|
|
|
// Desc : convert plain xml text from parsed xml node
|
|
|
|
|
// Return : converted plain string
|
|
|
|
|
CString XNode::GetXML() const
|
|
|
|
|
{
|
|
|
|
|
RageFileObjMem f;
|
|
|
|
|
GetXML( f, NULL );
|
|
|
|
|
return f.GetString();
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-03 08:28:43 +00:00
|
|
|
void XNode::GetValue( CString &out ) const { out = m_sValue; }
|
|
|
|
|
void XNode::GetValue( int &out ) const { out = atoi(m_sValue); }
|
|
|
|
|
void XNode::GetValue( float &out ) const { out = strtof(m_sValue, NULL); }
|
|
|
|
|
void XNode::GetValue( bool &out ) const { out = atoi(m_sValue) != 0; }
|
|
|
|
|
void XNode::GetValue( unsigned &out ) const { out = 0; sscanf(m_sValue,"%u",&out); }
|
|
|
|
|
void XNode::GetValue( DateTime &out ) const { out.FromString( m_sValue ); }
|
2004-07-18 21:55:49 +00:00
|
|
|
|
2005-09-03 08:28:43 +00:00
|
|
|
void XAttr::GetValue( CString &out ) const { out = m_sValue; }
|
|
|
|
|
void XAttr::GetValue( int &out ) const { out = atoi(m_sValue); }
|
|
|
|
|
void XAttr::GetValue( float &out ) const { out = strtof(m_sValue, NULL); }
|
|
|
|
|
void XAttr::GetValue( bool &out ) const { out = atoi(m_sValue) != 0; }
|
|
|
|
|
void XAttr::GetValue( unsigned &out ) const { out = 0; sscanf(m_sValue,"%u",&out); }
|
|
|
|
|
void XAttr::GetValue( DateTime &out ) const { out.FromString( m_sValue ); }
|
2004-07-18 21:55:49 +00:00
|
|
|
|
2005-09-03 08:28:43 +00:00
|
|
|
void XNode::SetValue( int v ) { m_sValue = ssprintf("%d",v); }
|
|
|
|
|
void XNode::SetValue( float v ) { m_sValue = ssprintf("%f",v); }
|
|
|
|
|
void XNode::SetValue( bool v ) { m_sValue = ssprintf("%d",v); }
|
|
|
|
|
void XNode::SetValue( unsigned v ) { m_sValue = ssprintf("%u",v); }
|
|
|
|
|
void XNode::SetValue( const DateTime &v ) { m_sValue = v.GetString(); }
|
2004-07-17 21:49:32 +00:00
|
|
|
|
2005-09-03 08:28:43 +00:00
|
|
|
const XAttr *XNode::GetAttr( const CString &attrname ) const
|
2004-02-22 05:12:23 +00:00
|
|
|
{
|
2005-01-07 14:28:00 +00:00
|
|
|
multimap<CString, XAttr*>::const_iterator it = m_attrs.find( attrname );
|
|
|
|
|
if( it != m_attrs.end() )
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ASSERT( attrname == it->second->m_sName );
|
2005-01-07 09:09:23 +00:00
|
|
|
return it->second;
|
2005-01-07 14:28:00 +00:00
|
|
|
}
|
2004-02-22 05:12:23 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-03 08:28:43 +00:00
|
|
|
XAttr *XNode::GetAttr( const CString &attrname )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 14:28:00 +00:00
|
|
|
multimap<CString, XAttr*>::iterator it = m_attrs.find( attrname );
|
|
|
|
|
if( it != m_attrs.end() )
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ASSERT( attrname == it->second->m_sName );
|
2005-01-07 09:09:23 +00:00
|
|
|
return it->second;
|
2005-01-07 14:28:00 +00:00
|
|
|
}
|
2004-02-09 08:10:01 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Desc : Find child with name and return child
|
|
|
|
|
// Return : NULL return if no child.
|
2005-09-04 05:12:48 +00:00
|
|
|
XNode *XNode::GetChild( const CString &sName )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-09-04 05:12:48 +00:00
|
|
|
multimap<CString, XNode*>::iterator it = m_childs.find( sName );
|
2005-01-07 14:28:00 +00:00
|
|
|
if( it != m_childs.end() )
|
|
|
|
|
{
|
2005-09-04 16:45:38 +00:00
|
|
|
DEBUG_ASSERT( sName == it->second->m_sName );
|
2005-01-07 09:09:23 +00:00
|
|
|
return it->second;
|
2005-01-07 14:28:00 +00:00
|
|
|
}
|
2004-02-09 08:10:01 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-04 05:12:48 +00:00
|
|
|
const XNode *XNode::GetChild( const CString &sName ) const
|
2004-02-19 03:19:41 +00:00
|
|
|
{
|
2005-09-04 05:12:48 +00:00
|
|
|
multimap<CString, XNode*>::const_iterator it = m_childs.find( sName );
|
2005-01-07 14:28:00 +00:00
|
|
|
if( it != m_childs.end() )
|
|
|
|
|
{
|
2005-09-04 16:45:38 +00:00
|
|
|
DEBUG_ASSERT( sName == it->second->m_sName );
|
2005-01-07 09:09:23 +00:00
|
|
|
return it->second;
|
2005-01-07 14:28:00 +00:00
|
|
|
}
|
2004-02-19 03:19:41 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-04 21:59:00 +00:00
|
|
|
XNode *XNode::AppendChild( const CString &sName, const CString &value ) { XNode *p = new XNode; p->m_sName = sName; p->m_sValue = value; return AppendChild( p ); }
|
2005-09-04 05:12:48 +00:00
|
|
|
XNode *XNode::AppendChild( const CString &sName, float value ) { XNode *p = new XNode; p->m_sName = sName; p->SetValue( value ); return AppendChild( p ); }
|
|
|
|
|
XNode *XNode::AppendChild( const CString &sName, int value ) { XNode *p = new XNode; p->m_sName = sName; p->SetValue( value ); return AppendChild( p ); }
|
|
|
|
|
XNode *XNode::AppendChild( const CString &sName, unsigned value ) { XNode *p = new XNode; p->m_sName = sName; p->SetValue( value ); return AppendChild( p ); }
|
|
|
|
|
XNode *XNode::AppendChild( const CString &sName, const DateTime &value ) { XNode *p = new XNode; p->m_sName = sName; p->SetValue( value ); return AppendChild( p ); }
|
2004-04-23 00:53:29 +00:00
|
|
|
|
2004-07-23 02:27:37 +00:00
|
|
|
XNode *XNode::AppendChild( XNode *node )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 14:28:00 +00:00
|
|
|
DEBUG_ASSERT( node->m_sName.size() );
|
|
|
|
|
m_childs.insert( pair<CString,XNode*>(node->m_sName,node) );
|
2004-02-09 08:10:01 +00:00
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-15 22:58:11 +00:00
|
|
|
// detach node and delete object
|
2004-07-23 02:27:37 +00:00
|
|
|
bool XNode::RemoveChild( XNode *node )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 14:28:00 +00:00
|
|
|
FOREACHMM( CString, XNode*, m_childs, p )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 09:09:23 +00:00
|
|
|
if( p->second == node )
|
|
|
|
|
{
|
|
|
|
|
SAFE_DELETE( p->second );
|
2005-01-07 14:28:00 +00:00
|
|
|
m_childs.erase( p );
|
2005-01-07 09:09:23 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-07-15 22:58:11 +00:00
|
|
|
// add attribute
|
2004-07-23 02:27:37 +00:00
|
|
|
XAttr *XNode::AppendAttr( XAttr *attr )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 14:28:00 +00:00
|
|
|
DEBUG_ASSERT( attr->m_sName.size() );
|
2005-06-27 05:24:24 +00:00
|
|
|
m_attrs.insert( make_pair(attr->m_sName,attr) );
|
2004-02-09 08:10:01 +00:00
|
|
|
return attr;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-03 21:28:34 +00:00
|
|
|
// detach attribute and delete object
|
2004-07-23 02:27:37 +00:00
|
|
|
bool XNode::RemoveAttr( XAttr *attr )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 14:28:00 +00:00
|
|
|
FOREACHMM( CString, XAttr*, m_attrs, p )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-01-07 09:09:23 +00:00
|
|
|
if( p->second == attr )
|
|
|
|
|
{
|
|
|
|
|
SAFE_DELETE( p->second );
|
2005-01-07 14:28:00 +00:00
|
|
|
m_attrs.erase( p );
|
2005-01-07 09:09:23 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-04 05:12:48 +00:00
|
|
|
XAttr *XNode::AppendAttr( const CString &sName, const CString &sValue )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-09-04 05:12:48 +00:00
|
|
|
XAttr *pAttr = new XAttr;
|
|
|
|
|
pAttr->m_sName = sName;
|
|
|
|
|
pAttr->m_sValue = sValue;
|
|
|
|
|
return AppendAttr( pAttr );
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
2005-09-04 05:12:48 +00:00
|
|
|
XAttr *XNode::AppendAttr( const CString &sName, float value ){ return AppendAttr(sName,ssprintf("%f",value)); }
|
|
|
|
|
XAttr *XNode::AppendAttr( const CString &sName, int value ) { return AppendAttr(sName,ssprintf("%d",value)); }
|
|
|
|
|
XAttr *XNode::AppendAttr( const CString &sName, unsigned value ) { return AppendAttr(sName,ssprintf("%u",value)); }
|
2005-01-07 14:28:00 +00:00
|
|
|
|
2005-09-04 05:12:48 +00:00
|
|
|
void XNode::SetAttrValue( const CString &sName, const CString &sValue )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-09-04 05:12:48 +00:00
|
|
|
XAttr* pAttr = GetAttr( sName );
|
2005-01-07 14:28:00 +00:00
|
|
|
if( pAttr )
|
2005-09-04 05:12:48 +00:00
|
|
|
pAttr->m_sValue = sValue;
|
2005-01-07 14:28:00 +00:00
|
|
|
else
|
2005-09-04 05:12:48 +00:00
|
|
|
AppendAttr( sName, sValue );
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-09-04 21:59:00 +00:00
|
|
|
XENTITYS::XENTITYS( const XENTITY *entities, int count )
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
for( int i = 0; i < count; i++)
|
|
|
|
|
push_back( entities[i] );
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-04 21:59:00 +00:00
|
|
|
const XENTITY *XENTITYS::GetEntity( char entity ) const
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
for( unsigned i = 0 ; i < size(); i ++ )
|
|
|
|
|
{
|
|
|
|
|
if( at(i).entity == entity )
|
2005-09-04 21:59:00 +00:00
|
|
|
return &at(i);
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-04 21:59:00 +00:00
|
|
|
const XENTITY *XENTITYS::GetEntity( const char* entity ) const
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
for( unsigned i = 0 ; i < size(); i ++ )
|
|
|
|
|
{
|
2005-09-03 08:28:43 +00:00
|
|
|
const char* ref = at(i).ref;
|
|
|
|
|
const char* ps = entity;
|
2004-02-09 08:10:01 +00:00
|
|
|
while( ref && *ref )
|
|
|
|
|
if( *ref++ != *ps++ )
|
|
|
|
|
break;
|
|
|
|
|
if( ref && !*ref ) // found!
|
2005-09-04 21:59:00 +00:00
|
|
|
return &at(i);
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-04 21:59:00 +00:00
|
|
|
int XENTITYS::GetEntityCount( const char* str ) const
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
int nCount = 0;
|
2005-09-03 08:28:43 +00:00
|
|
|
while( str && *str )
|
|
|
|
|
if( GetEntity(*str++) )
|
|
|
|
|
++nCount;
|
2004-02-09 08:10:01 +00:00
|
|
|
return nCount;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-04 21:59:00 +00:00
|
|
|
int XENTITYS::Ref2Entity( const char* pes, char* str, int len ) const
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2004-02-25 01:09:17 +00:00
|
|
|
char* ps = str;
|
2005-09-03 08:28:43 +00:00
|
|
|
char* ps_end = ps+len;
|
2004-02-09 08:10:01 +00:00
|
|
|
while( pes && *pes && ps < ps_end )
|
|
|
|
|
{
|
2005-09-04 21:59:00 +00:00
|
|
|
const XENTITY *ent = GetEntity( pes );
|
2004-02-09 08:10:01 +00:00
|
|
|
if( ent )
|
|
|
|
|
{
|
|
|
|
|
// copy entity meanning char
|
|
|
|
|
*ps = ent->entity;
|
|
|
|
|
pes += ent->ref_len;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
*ps = *pes++; // default character copy
|
|
|
|
|
ps++;
|
|
|
|
|
}
|
|
|
|
|
*ps = '\0';
|
|
|
|
|
|
|
|
|
|
// total copied characters
|
|
|
|
|
return ps-str;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-04 21:59:00 +00:00
|
|
|
int XENTITYS::Entity2Ref( const char* ps, char* estr, int estrlen ) const
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
2005-09-04 21:59:00 +00:00
|
|
|
char* pes = estr;
|
2004-02-25 01:09:17 +00:00
|
|
|
char* pes_end = pes+estrlen;
|
2004-02-09 08:10:01 +00:00
|
|
|
while( ps && *ps && pes < pes_end )
|
|
|
|
|
{
|
2005-09-04 21:59:00 +00:00
|
|
|
const XENTITY *ent = GetEntity( *ps );
|
2004-02-09 08:10:01 +00:00
|
|
|
if( ent )
|
|
|
|
|
{
|
|
|
|
|
// copy entity string
|
2005-09-03 08:28:43 +00:00
|
|
|
const char* ref = ent->ref;
|
2004-02-09 08:10:01 +00:00
|
|
|
while( ref && *ref )
|
|
|
|
|
*pes++ = *ref++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
*pes++ = *ps; // default character copy
|
|
|
|
|
ps++;
|
|
|
|
|
}
|
|
|
|
|
*pes = '\0';
|
|
|
|
|
|
|
|
|
|
// total copied characters
|
|
|
|
|
return pes-estr;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-04 21:59:00 +00:00
|
|
|
CString XENTITYS::Ref2Entity( const char* estr ) const
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
CString es;
|
|
|
|
|
if( estr )
|
|
|
|
|
{
|
2004-02-25 01:09:17 +00:00
|
|
|
int len = strlen(estr);
|
2004-07-22 18:25:58 +00:00
|
|
|
char* szTemp = new char[len+1];
|
2005-09-03 21:28:34 +00:00
|
|
|
int iLen = Ref2Entity( estr, szTemp, len );
|
|
|
|
|
es.assign( szTemp, iLen );
|
2004-07-22 18:25:58 +00:00
|
|
|
delete [] szTemp;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
return es;
|
|
|
|
|
}
|
|
|
|
|
|
2005-09-04 21:59:00 +00:00
|
|
|
CString XENTITYS::Entity2Ref( const char* str ) const
|
2004-02-09 08:10:01 +00:00
|
|
|
{
|
|
|
|
|
CString s;
|
|
|
|
|
if( str )
|
|
|
|
|
{
|
|
|
|
|
int nEntityCount = GetEntityCount(str);
|
|
|
|
|
if( nEntityCount == 0 )
|
|
|
|
|
return CString(str);
|
2005-09-03 21:28:34 +00:00
|
|
|
int len = strlen(str) + nEntityCount*10;
|
2004-07-22 18:25:58 +00:00
|
|
|
char* szTemp = new char[len+1];
|
2005-09-03 21:28:34 +00:00
|
|
|
int iLen = Entity2Ref( str, szTemp, len );
|
|
|
|
|
s.assign( szTemp, iLen );
|
2004-07-22 18:25:58 +00:00
|
|
|
delete [] szTemp;
|
2004-02-09 08:10:01 +00:00
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-29 01:05:23 +00:00
|
|
|
bool XNode::LoadFromFile( const CString &sFile )
|
2004-02-10 09:42:01 +00:00
|
|
|
{
|
|
|
|
|
RageFile f;
|
|
|
|
|
if( !f.Open(sFile, RageFile::READ) )
|
2004-04-02 05:06:32 +00:00
|
|
|
{
|
|
|
|
|
LOG->Warn("Couldn't open %s for reading: %s", sFile.c_str(), f.GetError().c_str() );
|
2004-02-10 09:42:01 +00:00
|
|
|
return false;
|
2004-04-02 05:06:32 +00:00
|
|
|
}
|
2005-01-08 19:12:44 +00:00
|
|
|
|
2005-05-29 01:05:23 +00:00
|
|
|
bool bSuccess = LoadFromFile( f );
|
|
|
|
|
if( !bSuccess )
|
|
|
|
|
{
|
|
|
|
|
CString sWarning = ssprintf( "XML: LoadFromFile failed for file: %s", sFile.c_str() );
|
|
|
|
|
LOG->Warn( sWarning );
|
|
|
|
|
Dialog::OK( sWarning, "XML_PARSE_ERROR" );
|
|
|
|
|
}
|
|
|
|
|
return bSuccess;
|
2005-01-08 19:12:44 +00:00
|
|
|
}
|
|
|
|
|
|
2005-05-29 01:05:23 +00:00
|
|
|
bool XNode::LoadFromFile( RageFileBasic &f )
|
2005-01-08 19:12:44 +00:00
|
|
|
{
|
2005-05-29 01:05:23 +00:00
|
|
|
PARSEINFO pi;
|
2004-02-10 09:42:01 +00:00
|
|
|
CString s;
|
2004-06-06 20:47:48 +00:00
|
|
|
if( f.Read( s ) == -1 )
|
|
|
|
|
{
|
2005-05-29 01:05:23 +00:00
|
|
|
pi.error_occur = true;
|
|
|
|
|
pi.error_pointer = NULL;
|
|
|
|
|
pi.error_code = PIE_READ_ERROR;
|
|
|
|
|
pi.error_string = f.GetError();
|
2004-06-06 20:47:48 +00:00
|
|
|
|
2005-05-29 01:05:23 +00:00
|
|
|
goto error;
|
2004-06-06 20:47:48 +00:00
|
|
|
}
|
2005-05-29 01:05:23 +00:00
|
|
|
this->Load( s, &pi );
|
|
|
|
|
if( pi.error_occur )
|
|
|
|
|
goto error;
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
CString sWarning = ssprintf( "XML: LoadFromFile failed: %s", pi.error_string.c_str() );
|
|
|
|
|
LOG->Warn( sWarning );
|
|
|
|
|
Dialog::OK( sWarning, "XML_PARSE_ERROR" );
|
|
|
|
|
return false;
|
2004-02-10 09:42:01 +00:00
|
|
|
}
|
|
|
|
|
|
2005-02-18 03:07:22 +00:00
|
|
|
bool XNode::SaveToFile( RageFileBasic &f, DISP_OPT *opt ) const
|
2004-02-10 09:42:01 +00:00
|
|
|
{
|
2004-07-11 01:58:55 +00:00
|
|
|
f.PutLine( "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" );
|
|
|
|
|
if( !opt->stylesheet.empty() )
|
|
|
|
|
f.PutLine( "<?xml-stylesheet type=\"text/xsl\" href=\"" + opt->stylesheet + "\"?>" );
|
2004-06-06 20:47:48 +00:00
|
|
|
if( !this->GetXML(f, opt) )
|
|
|
|
|
return false;
|
|
|
|
|
if( f.Flush() == -1 )
|
|
|
|
|
return false;
|
2004-02-10 09:42:01 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2004-07-18 21:55:49 +00:00
|
|
|
|
2005-02-18 03:07:22 +00:00
|
|
|
bool XNode::SaveToFile( const CString &sFile, DISP_OPT *opt ) const
|
2004-12-18 05:37:53 +00:00
|
|
|
{
|
|
|
|
|
RageFile f;
|
|
|
|
|
if( !f.Open(sFile, RageFile::WRITE) )
|
|
|
|
|
{
|
|
|
|
|
LOG->Warn("Couldn't open %s for writing: %s", sFile.c_str(), f.GetError().c_str() );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return SaveToFile( f, opt );
|
|
|
|
|
}
|