593 lines
15 KiB
C++
593 lines
15 KiB
C++
/*
|
|
Copyright (C) 2006, Stefan Greven.
|
|
All Rights Reserved.
|
|
|
|
This file is part of GtkRadiant.
|
|
|
|
GtkRadiant is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
GtkRadiant is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GtkRadiant; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "xmltextags.h"
|
|
|
|
#include <string>
|
|
|
|
#include "qerplugin.h"
|
|
#include "stream/stringstream.h"
|
|
|
|
XmlTagBuilder::XmlTagBuilder(){
|
|
}
|
|
|
|
XmlTagBuilder::~XmlTagBuilder(){
|
|
// clean up
|
|
xmlFreeDoc( doc );
|
|
xmlXPathFreeContext( context );
|
|
}
|
|
|
|
bool XmlTagBuilder::CreateXmlDocument(){
|
|
/* Creates an XML file
|
|
|
|
returns TRUE if the file was created successfully or FALSE when failed
|
|
*/
|
|
|
|
xmlTextWriterPtr writer;
|
|
|
|
writer = xmlNewTextWriterDoc( &doc, 0 );
|
|
|
|
// begin a new UTF-8 formatted xml document
|
|
xmlTextWriterStartDocument( writer, NULL, "UTF-8", NULL );
|
|
|
|
// create the root node with stock and custom elements
|
|
xmlTextWriterStartElement( writer, (xmlChar*)"root" );
|
|
xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
|
|
xmlTextWriterStartElement( writer, (xmlChar*)"stock" );
|
|
xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
|
|
xmlTextWriterEndElement( writer );
|
|
xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
|
|
xmlTextWriterStartElement( writer, (xmlChar*)"custom" );
|
|
xmlTextWriterWriteString( writer, (xmlChar*)"\n " );
|
|
xmlTextWriterEndElement( writer );
|
|
xmlTextWriterWriteString( writer, (xmlChar*)"\n" );
|
|
xmlTextWriterEndElement( writer );
|
|
|
|
// end of the xml document
|
|
xmlTextWriterEndDocument( writer );
|
|
xmlFreeTextWriter( writer );
|
|
|
|
if ( !doc ) {
|
|
return false;
|
|
}
|
|
else {
|
|
context = xmlXPathNewContext( doc );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool XmlTagBuilder::OpenXmlDoc( const char* file, const char* savefile ){
|
|
/* Reads a XML document from a file
|
|
|
|
returns TRUE if the document was read successfully or FALSE when failed
|
|
*/
|
|
|
|
if ( savefile ) {
|
|
m_savefilename = savefile;
|
|
}
|
|
else{
|
|
m_savefilename = file;
|
|
}
|
|
|
|
doc = xmlParseFile( file ); // TODO error checking!
|
|
|
|
if ( !doc ) {
|
|
return false;
|
|
}
|
|
else {
|
|
context = xmlXPathNewContext( doc );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool XmlTagBuilder::SaveXmlDoc( void ){
|
|
return SaveXmlDoc( m_savefilename.c_str() );
|
|
}
|
|
|
|
bool XmlTagBuilder::SaveXmlDoc( const char* file ){
|
|
/* Writes the XML document
|
|
|
|
returns TRUE if the document was saved successfully or FALSE when saving failed
|
|
*/
|
|
|
|
xmlSaveNoEmptyTags = 1;
|
|
|
|
if ( xmlSaveFile( file, doc ) != -1 ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool XmlTagBuilder::AddShaderNode( const char* shader, TextureType textureType, NodeShaderType nodeShaderType ){
|
|
/* Adds a shader node
|
|
|
|
char* shader - the name of the shader or texture (without trailing .tga or something)
|
|
|
|
returns TRUE if the node was added successfully or FALSE when failed
|
|
*/
|
|
|
|
xmlNodeSetPtr nodePtr = NULL;
|
|
xmlXPathObjectPtr xpathPtr = NULL;
|
|
|
|
switch ( textureType )
|
|
{
|
|
case STOCK:
|
|
xpathPtr = XpathEval( "/root/stock" );
|
|
break;
|
|
case CUSTOM:
|
|
xpathPtr = XpathEval( "/root/custom" );
|
|
};
|
|
|
|
if ( xpathPtr ) {
|
|
nodePtr = xpathPtr->nodesetval;
|
|
}
|
|
else{
|
|
return false;
|
|
}
|
|
|
|
if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
|
|
xmlNodePtr newnode, newtext;
|
|
xmlNodePtr nodeParent = nodePtr->nodeTab[0];
|
|
|
|
// create a new node and set the node attribute (shader path)
|
|
switch ( nodeShaderType )
|
|
{
|
|
case SHADER:
|
|
newnode = xmlNewNode( NULL, (xmlChar*)"shader" );
|
|
break;
|
|
case TEXTURE:
|
|
newnode = xmlNewNode( NULL, (xmlChar*)"texture" );
|
|
};
|
|
|
|
newnode = xmlDocCopyNode( newnode, doc, 1 );
|
|
xmlSetProp( newnode, (xmlChar*)"path", (xmlChar*)shader );
|
|
xmlNodeSetContent( newnode, (xmlChar*)"\n " );
|
|
|
|
if ( nodePtr->nodeTab[0]->children->next == NULL ) { // there are no shaders yet
|
|
// add spaces
|
|
newtext = xmlNewText( (xmlChar*)" " );
|
|
xmlAddChild( nodeParent->children, newtext );
|
|
|
|
// add the new node
|
|
xmlAddNextSibling( nodeParent->children, newnode );
|
|
|
|
// append a new line
|
|
newtext = xmlNewText( (xmlChar*)"\n " );
|
|
xmlAddNextSibling( nodeParent->children->next, newtext );
|
|
}
|
|
else {
|
|
// add the node
|
|
xmlAddNextSibling( nodeParent->children, newnode );
|
|
|
|
// append a new line and spaces
|
|
newtext = xmlNewText( (xmlChar*)"\n " );
|
|
xmlAddNextSibling( nodeParent->children->next, newtext );
|
|
}
|
|
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return true;
|
|
}
|
|
else {
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool XmlTagBuilder::DeleteShaderNode( const char* shader ){
|
|
/* Deletes a shader node
|
|
|
|
char* shader - the name of the shader or texture (without trailing .tga or something)
|
|
|
|
returns TRUE if the node was deleted successfully or FALSE when failed
|
|
*/
|
|
|
|
char buffer[256];
|
|
char* expression = GetTagsXpathExpression( buffer, shader, EMPTY );
|
|
xmlXPathObjectPtr xpathPtr = XpathEval( expression );
|
|
|
|
xmlNodeSetPtr nodePtr;
|
|
if ( xpathPtr ) {
|
|
nodePtr = xpathPtr->nodesetval;
|
|
}
|
|
else{
|
|
return false;
|
|
}
|
|
|
|
if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
|
|
xmlNodePtr ptrContent = nodePtr->nodeTab[0];
|
|
xmlNodePtr ptrWhitespace = nodePtr->nodeTab[0]->prev;
|
|
|
|
// delete the node
|
|
xmlUnlinkNode( ptrContent );
|
|
xmlFreeNode( ptrContent );
|
|
|
|
// delete leading whitespace node
|
|
xmlUnlinkNode( ptrWhitespace );
|
|
xmlFreeNode( ptrWhitespace );
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return true;
|
|
}
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return false;
|
|
}
|
|
|
|
bool XmlTagBuilder::CheckShaderTag( const char* shader ){
|
|
/* Checks whether there exists an entry for a shader/texture with at least one tag
|
|
|
|
char* shader - the name of the shader or texture (without trailing .tga or something)
|
|
|
|
returns TRUE if the shader is already stored in the XML tag file and has at least one tag
|
|
*/
|
|
|
|
// build the XPath expression to search for
|
|
char buffer[256];
|
|
strcpy( buffer, "/root/*/*[@path='" );
|
|
strcat( buffer, shader );
|
|
strcat( buffer, "']" );
|
|
|
|
char* expression = buffer;
|
|
|
|
xmlXPathObjectPtr xpathPtr = XpathEval( expression );
|
|
xmlNodeSetPtr nodePtr;
|
|
if ( xpathPtr ) {
|
|
nodePtr = xpathPtr->nodesetval;
|
|
}
|
|
else{
|
|
return false;
|
|
}
|
|
|
|
if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return true;
|
|
}
|
|
else {
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool XmlTagBuilder::CheckShaderTag( const char* shader, const char* content ){
|
|
/* Checks whether a tag with content already exists
|
|
|
|
char* shader - the name of the shader or texture (without trailing .tga or something)
|
|
char* content - the node content (a tag name)
|
|
|
|
returns TRUE if the tag with content already exists or FALSE if not
|
|
*/
|
|
|
|
// build the XPath expression to search for
|
|
// example expression: "/stock/*[@path='textures/alpha/barb_wire'][child::tag='Alpha']";
|
|
|
|
char buffer[256];
|
|
strcpy( buffer, "/root/*/*[@path='" );
|
|
strcat( buffer, shader );
|
|
strcat( buffer, "'][child::tag='" );
|
|
strcat( buffer, content );
|
|
strcat( buffer, "']" );
|
|
|
|
char* expression = buffer;
|
|
|
|
xmlXPathObjectPtr xpathPtr = XpathEval( expression );
|
|
xmlNodeSetPtr nodePtr;
|
|
if ( xpathPtr ) {
|
|
nodePtr = xpathPtr->nodesetval;
|
|
}
|
|
else{
|
|
return false;
|
|
}
|
|
|
|
if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return true;
|
|
}
|
|
else {
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool XmlTagBuilder::AddShaderTag( const char* shader, const char* content, NodeTagType nodeTagType ){
|
|
/* Adds a tag node to an existing shader/texture node if there's no tag with the same content yet
|
|
|
|
char* shader - the name of the shader or texture (without trailing .tga or something)
|
|
char* content - the node content (a tag name)
|
|
|
|
returns TRUE if the node was added successfully or FALSE when failed
|
|
*/
|
|
|
|
// build the XPath expression
|
|
char buffer[256];
|
|
char* expression = GetTagsXpathExpression( buffer, shader, EMPTY );
|
|
|
|
xmlXPathObjectPtr xpathPtr = XpathEval( expression );
|
|
xmlNodeSetPtr nodePtr;
|
|
if ( xpathPtr ) {
|
|
nodePtr = xpathPtr->nodesetval;
|
|
}
|
|
else{
|
|
return false;
|
|
}
|
|
|
|
if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { // node was found
|
|
xmlNodePtr newnode = xmlNewNode( NULL, (xmlChar*)"tag" );
|
|
xmlNodePtr nodeParent = nodePtr->nodeTab[0];
|
|
newnode = xmlDocCopyNode( newnode, doc, 1 );
|
|
xmlNodeSetContent( newnode, (xmlChar*)content );
|
|
|
|
if ( nodePtr->nodeTab[0]->children->next == NULL ) { // shader node has NO children
|
|
// add spaces
|
|
xmlNodePtr newtext = xmlNewText( (xmlChar*)" " );
|
|
xmlAddChild( nodeParent->children, newtext );
|
|
|
|
// add new node
|
|
xmlAddNextSibling( nodeParent->children, newnode );
|
|
|
|
// append a new line + spaces
|
|
newtext = xmlNewText( (xmlChar*)"\n " );
|
|
xmlAddNextSibling( nodeParent->children->next, newtext );
|
|
}
|
|
else { // shader node has children already - the new node will be the first sibling
|
|
xmlAddNextSibling( nodeParent->children, newnode );
|
|
xmlNodePtr newtext = xmlNewText( (xmlChar*)"\n " );
|
|
xmlAddNextSibling( nodeParent->children->next, newtext );
|
|
}
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return true;
|
|
}
|
|
else {
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//int XmlTagBuilder::RenameShaderTag(const char* oldtag, const char* newtag)
|
|
int XmlTagBuilder::RenameShaderTag( const char* oldtag, CopiedString newtag ){
|
|
/* Replaces tag node contents
|
|
|
|
char* oldtag - the <tag></tag> node content that sould be changed
|
|
char* newtag - the new <tag></tag> node content
|
|
|
|
returns the number of renamed shaders
|
|
*/
|
|
|
|
int num = 0;
|
|
|
|
// build the XPath expression
|
|
char expression[256];
|
|
strcpy( expression, "/root/*/*[child::tag='" );
|
|
strcat( expression, oldtag );
|
|
strcat( expression, "']/*" );
|
|
|
|
xmlXPathObjectPtr result = xmlXPathEvalExpression( (xmlChar*)expression, context );
|
|
if ( !result ) {
|
|
return 0;
|
|
}
|
|
xmlNodeSetPtr nodePtr = result->nodesetval;
|
|
|
|
for ( int i = 0; i < nodePtr->nodeNr; i++ )
|
|
{
|
|
xmlNodePtr ptrContent = nodePtr->nodeTab[i];
|
|
char* content = (char*)xmlNodeGetContent( ptrContent );
|
|
|
|
if ( strcmp( content, oldtag ) == 0 ) { // found a node with old content?
|
|
xmlNodeSetContent( ptrContent, (xmlChar*)newtag.c_str() );
|
|
num++;
|
|
}
|
|
}
|
|
|
|
SaveXmlDoc();
|
|
xmlXPathFreeObject( result ); // CHANGED
|
|
return num;
|
|
}
|
|
|
|
bool XmlTagBuilder::DeleteShaderTag( const char* shader, const char* tag ){
|
|
/* Deletes a child node of a shader
|
|
|
|
char* shader - the name of the shader or texture (without trailing .tga or something)
|
|
char* tag - the tag being deleted
|
|
|
|
returns TRUE if the node was deleted successfully or FALSE when failed
|
|
*/
|
|
|
|
char buffer[256];
|
|
char* expression = GetTagsXpathExpression( buffer, shader, TAG );
|
|
xmlXPathObjectPtr xpathPtr = XpathEval( expression );
|
|
xmlNodeSetPtr nodePtr;
|
|
if ( xpathPtr ) {
|
|
nodePtr = xpathPtr->nodesetval;
|
|
}
|
|
else{
|
|
return false;
|
|
}
|
|
|
|
if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
|
|
for ( int i = 0; i < nodePtr->nodeNr; i++ )
|
|
{
|
|
xmlNodePtr ptrContent = nodePtr->nodeTab[i];
|
|
char* content = (char*)(xmlChar*)xmlNodeGetContent( ptrContent );
|
|
|
|
if ( strcmp( content, tag ) == 0 ) { // find the node
|
|
xmlNodePtr ptrWhitespace = nodePtr->nodeTab[i]->prev;
|
|
// delete the node
|
|
xmlUnlinkNode( ptrContent );
|
|
xmlFreeNode( ptrContent );
|
|
|
|
// delete leading whitespace node
|
|
xmlUnlinkNode( ptrWhitespace );
|
|
xmlFreeNode( ptrWhitespace );
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
xmlXPathFreeObject( xpathPtr );
|
|
return false;
|
|
}
|
|
|
|
bool XmlTagBuilder::DeleteTag( const char* tag ){
|
|
/* Deletes a tag from all shaders
|
|
|
|
char* tag - the tag being deleted from all shaders
|
|
|
|
returns TRUE if the tag was deleted successfully or FALSE when failed
|
|
*/
|
|
|
|
char expression[256];
|
|
strcpy( expression, "/root/*/*[child::tag='" );
|
|
strcat( expression, tag );
|
|
strcat( expression, "']" );
|
|
|
|
std::set<CopiedString> dellist;
|
|
TagSearch( expression, dellist );
|
|
std::set<CopiedString>::iterator iter;
|
|
|
|
for ( iter = dellist.begin(); iter != dellist.end(); iter++ )
|
|
{
|
|
DeleteShaderTag( iter->c_str(), tag );
|
|
}
|
|
SaveXmlDoc();
|
|
|
|
return true;
|
|
}
|
|
|
|
void XmlTagBuilder::GetShaderTags( const char* shader, std::vector<CopiedString>& tags ){
|
|
/* Gets the tags from a shader
|
|
|
|
char* shader - the name of the shader
|
|
|
|
returns a vector containing the tags
|
|
*/
|
|
|
|
char const *expression;
|
|
|
|
if ( shader == NULL ) { // get all tags from all shaders
|
|
expression = "/root/*/*/tag";
|
|
}
|
|
else {
|
|
char buffer[256];
|
|
expression = GetTagsXpathExpression( buffer, shader, TAG );
|
|
}
|
|
|
|
xmlXPathObjectPtr xpathPtr = XpathEval( expression );
|
|
xmlNodeSetPtr nodePtr;
|
|
if ( xpathPtr ) {
|
|
nodePtr = xpathPtr->nodesetval;
|
|
}
|
|
else{
|
|
return;
|
|
}
|
|
|
|
if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
|
|
for ( int i = 0; i < nodePtr->nodeNr; i++ )
|
|
{
|
|
tags.push_back( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
|
|
}
|
|
}
|
|
xmlXPathFreeObject( xpathPtr );
|
|
}
|
|
|
|
void XmlTagBuilder::GetUntagged( std::set<CopiedString>& shaders ){
|
|
/* Gets all textures and shaders listed in the xml file that don't have any tag
|
|
|
|
returns a set containing the shaders (with path)
|
|
*/
|
|
|
|
char const *expression = "/root/*/*[not(child::tag)]";
|
|
|
|
xmlXPathObjectPtr xpathPtr = XpathEval( expression );
|
|
xmlNodeSetPtr nodePtr;
|
|
if ( xpathPtr ) {
|
|
nodePtr = xpathPtr->nodesetval;
|
|
}
|
|
else{
|
|
return;
|
|
}
|
|
|
|
if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
|
|
xmlNodePtr ptr;
|
|
|
|
for ( int i = 0; i < nodePtr->nodeNr; i++ )
|
|
{
|
|
ptr = nodePtr->nodeTab[i];
|
|
shaders.insert( (char*)xmlGetProp( ptr, (xmlChar*)"path" ) );
|
|
}
|
|
}
|
|
|
|
xmlXPathFreeObject( xpathPtr );
|
|
}
|
|
|
|
void XmlTagBuilder::GetAllTags( std::set<CopiedString>& tags ){
|
|
/* Gets a list of all tags that are used (assigned to any shader)
|
|
|
|
returns a set containing all used tags
|
|
*/
|
|
|
|
char const *expression = "/root/*/*/tag";
|
|
|
|
xmlXPathObjectPtr xpathPtr = XpathEval( expression );
|
|
xmlNodeSetPtr nodePtr;
|
|
if ( xpathPtr ) {
|
|
nodePtr = xpathPtr->nodesetval;
|
|
}
|
|
else{
|
|
return;
|
|
}
|
|
|
|
if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
|
|
for ( int i = 0; i < nodePtr->nodeNr; i++ )
|
|
{
|
|
tags.insert( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
|
|
}
|
|
}
|
|
|
|
xmlXPathFreeObject( xpathPtr );
|
|
}
|
|
|
|
void XmlTagBuilder::TagSearch( const char* expression, std::set<CopiedString>& paths ){
|
|
/* Searches shaders by tags
|
|
|
|
char* expression - the XPath expression to search
|
|
|
|
returns a set containing the found shaders
|
|
*/
|
|
|
|
xmlXPathObjectPtr xpathPtr = XpathEval( expression );
|
|
xmlNodeSetPtr nodePtr;
|
|
if ( xpathPtr ) {
|
|
nodePtr = xpathPtr->nodesetval;
|
|
}
|
|
else{
|
|
return;
|
|
}
|
|
|
|
if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
|
|
xmlNodePtr ptr;
|
|
xmlChar* xmlattrib;
|
|
for ( int i = 0; i < nodePtr->nodeNr; i++ )
|
|
{
|
|
ptr = nodePtr->nodeTab[i];
|
|
xmlattrib = xmlGetProp( ptr, (xmlChar*)"path" );
|
|
paths.insert( (CopiedString)(char*)xmlattrib );
|
|
}
|
|
}
|
|
xmlXPathFreeObject( xpathPtr );
|
|
}
|