stk-code_catmod/src/io/xml_node.cpp
2015-03-30 11:42:50 +11:00

634 lines
21 KiB
C++

//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2009-2015 Joerg Henrichs
//
// This program 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 3
// of the License, or (at your option) any later version.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "utils/string_utils.hpp"
#include "utils/interpolation_array.hpp"
#include "utils/vec3.hpp"
#include <stdexcept>
XMLNode::XMLNode(io::IXMLReader *xml)
{
m_file_name = "[unknown]";
while(xml->getNodeType()!=io::EXN_ELEMENT && xml->read());
readXML(xml);
} // XMLNode
// ----------------------------------------------------------------------------
/** Reads a XML file and convert it into a XMLNode tree.
* \param filename Name of the XML file to read.
*/
XMLNode::XMLNode(const std::string &filename)
{
m_file_name = filename;
io::IXMLReader *xml = file_manager->createXMLReader(filename);
if (xml == NULL)
{
throw std::runtime_error("Cannot find file "+filename);
}
bool is_first_element = true;
while(xml->read())
{
switch (xml->getNodeType())
{
case io::EXN_ELEMENT:
{
if(!is_first_element)
{
Log::warn("[XMLNode]",
"More than one root element in '%s' - ignored.",
filename.c_str());
}
readXML(xml);
is_first_element = false;
break;
}
case io::EXN_ELEMENT_END: break; // Ignore all other types
case io::EXN_UNKNOWN: break;
case io::EXN_COMMENT: break;
case io::EXN_TEXT: break;
default: break;
} // switch
} // while
xml->drop();
} // XMLNode
// ----------------------------------------------------------------------------
/** Destructor. */
XMLNode::~XMLNode()
{
for(unsigned int i=0; i<m_nodes.size(); i++)
{
delete m_nodes[i];
}
m_nodes.clear();
} // ~XMLNode
// ----------------------------------------------------------------------------
/** Stores all attributes, and reads in all children.
* \param xml The XML reader.
*/
void XMLNode::readXML(io::IXMLReader *xml)
{
m_name = std::string(core::stringc(xml->getNodeName()).c_str());
for(unsigned int i=0; i<xml->getAttributeCount(); i++)
{
std::string name = core::stringc(xml->getAttributeName(i)).c_str();
core::stringw value = xml->getAttributeValue(i);
m_attributes[name] = value;
} // for i
// If no children, we are done
if(xml->isEmptyElement())
return;
/** Read all children elements. */
while(xml->read())
{
switch (xml->getNodeType())
{
case io::EXN_ELEMENT:
{
XMLNode* n = new XMLNode(xml);
n->m_file_name = m_file_name;
m_nodes.push_back(n);
break;
}
case io::EXN_ELEMENT_END:
// End of this element found.
return;
break;
case io::EXN_UNKNOWN: break;
case io::EXN_COMMENT: break;
case io::EXN_TEXT: break;
default: break;
} // switch
} // while
} // readXML
// ----------------------------------------------------------------------------
/** Returns the i.th node.
* \param i Number of node to return.
*/
const XMLNode *XMLNode::getNode(unsigned int i) const
{
return m_nodes[i];
} // getNode
// ----------------------------------------------------------------------------
/** Returns the node with the given name.
* \param s Name of the node to return.
*/
const XMLNode *XMLNode::getNode(const std::string &s) const
{
for(unsigned int i=0; i<m_nodes.size(); i++)
{
if(m_nodes[i]->getName()==s) return m_nodes[i];
}
return NULL;
} // getNode
// ----------------------------------------------------------------------------
/** Returns all nodes with the given name.
* \param s Name of the nodes to return.
* \param s Vector that will be filled with output values.
*/
const void XMLNode::getNodes(const std::string &s, std::vector<XMLNode*>& out) const
{
for(unsigned int i=0; i<m_nodes.size(); i++)
{
if(m_nodes[i]->getName()==s)
{
out.push_back(m_nodes[i]);
}
}
} // getNode
// ----------------------------------------------------------------------------
/** If 'attribute' was defined, set 'value' to the value of the
* attribute and return 1, otherwise return 0 and do not change value.
* \param attribute Name of the attribute.
* \param value Value of the attribute.
*/
int XMLNode::get(const std::string &attribute, std::string *value) const
{
if(m_attributes.empty()) return 0;
std::map<std::string, core::stringw>::const_iterator o;
o = m_attributes.find(attribute);
if(o==m_attributes.end()) return 0;
*value=core::stringc(o->second).c_str();
return 1;
} // get
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, core::stringw *value) const
{
if(m_attributes.empty()) return 0;
std::map<std::string, core::stringw>::const_iterator o;
o = m_attributes.find(attribute);
if(o==m_attributes.end()) return 0;
*value = o->second;
return 1;
} // get
// ----------------------------------------------------------------------------
int XMLNode::getAndDecode(const std::string &attribute, core::stringw *value) const
{
if (m_attributes.empty()) return 0;
std::map<std::string, core::stringw>::const_iterator o;
o = m_attributes.find(attribute);
if (o == m_attributes.end()) return 0;
std::string raw_value = core::stringc(o->second).c_str();
*value = StringUtils::xmlDecode(raw_value);
return 1;
} // get
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, core::vector2df *value) const
{
std::string s = "";
if(!get(attribute, &s)) return 0;
std::vector<std::string> v = StringUtils::split(s,' ');
if(v.size()!=2) return 0;
value->X = (float)atof(v[0].c_str());
value->Y = (float)atof(v[1].c_str());
return 1;
} // get(vector2df)
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, core::vector3df *value) const
{
Vec3 xyz;
if(!get(attribute, &xyz)) return 0;
*value = xyz.toIrrVector();
return 1;
} // get(vector3df)
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, Vec3 *value) const
{
std::string s = "";
if(!get(attribute, &s)) return 0;
std::vector<std::string> v = StringUtils::split(s,' ');
if (v.size() != 3)
{
Log::warn("[XMLNode]", "WARNING: Expected 3 floating-point values, but found '%s' in file %s",
s.c_str(), m_file_name.c_str());
return 0;
}
float x, y, z;
if (StringUtils::parseString<float>(v[0], &x) &&
StringUtils::parseString<float>(v[1], &y) &&
StringUtils::parseString<float>(v[2], &z) )
{
value->setX(x);
value->setY(y);
value->setZ(z);
}
else
{
Log::warn("[XMLNode]", "WARNING: Expected 3 floating-point values, but found '%s' in file %s",
s.c_str(), m_file_name.c_str());
return 0;
}
return 1;
} // get(Vec3)
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, video::SColor *color) const
{
std::string s;
if(!get(attribute, &s)) return 0;
std::vector<std::string> v = StringUtils::split(s,' ');
if (v.size()<3 || v.size()>4) return 0;
if (v.size()==3)
{
color->setRed (atoi(v[0].c_str()));
color->setGreen(atoi(v[1].c_str()));
color->setBlue (atoi(v[2].c_str()));
}
else
{
color->set(atoi(v[3].c_str()), // irrLicht expects ARGB, and we use RGBA in XML files
atoi(v[0].c_str()),
atoi(v[1].c_str()),
atoi(v[2].c_str()));
}
return 1;
} // get(SColor)
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, video::SColorf *color) const
{
std::string s;
if(!get(attribute, &s)) return 0;
std::vector<std::string> v = StringUtils::split(s,' ');
if(v.size()==3)
{
color->set((float)atof(v[0].c_str())/255.0f,
(float)atof(v[1].c_str())/255.0f,
(float)atof(v[2].c_str())/255.0f);
}
else if(v.size()==4)
{
color->set((float)atof(v[3].c_str())/255.0f, // set takes ARGB, but we use RGBA
(float)atof(v[0].c_str())/255.0f,
(float)atof(v[1].c_str())/255.0f,
(float)atof(v[2].c_str())/255.0f);
}
else
return 0;
return 1;
} // get(SColor)
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, int32_t *value) const
{
std::string s;
if(!get(attribute, &s)) return 0;
if (!StringUtils::parseString<int>(s, value))
{
Log::warn("[XMLNode]", "WARNING: Expected int but found '%s' for attribute '%s' of node '%s' in file %s",
s.c_str(), attribute.c_str(), m_name.c_str(), m_file_name.c_str());
return 0;
}
return 1;
} // get(int32_t)
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, int64_t *value) const
{
std::string s;
if(!get(attribute, &s)) return 0;
if (!StringUtils::parseString<int64_t>(s, value))
{
Log::warn("[XMLNode]", "WARNING: Expected int but found '%s' for attribute '%s' of node '%s' in file %s",
s.c_str(), attribute.c_str(), m_name.c_str(), m_file_name.c_str());
return 0;
}
return 1;
} // get(int64_t)
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, uint16_t *value) const
{
std::string s;
if(!get(attribute, &s)) return 0;
if (!StringUtils::parseString<uint16_t>(s, value))
{
Log::warn("[XMLNode]", "WARNING: Expected uint but found '%s' for attribute '%s' of node '%s' in file %s",
s.c_str(), attribute.c_str(), m_name.c_str(), m_file_name.c_str());
return 0;
}
return 1;
} // get(uint32_t)
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, uint32_t *value) const
{
std::string s;
if(!get(attribute, &s)) return 0;
if (!StringUtils::parseString<unsigned int>(s, value))
{
Log::warn("[XMLNode]", "WARNING: Expected uint but found '%s' for attribute '%s' of node '%s' in file %s",
s.c_str(), attribute.c_str(), m_name.c_str(), m_file_name.c_str());
return 0;
}
return 1;
} // get(uint32_t)
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, float *value) const
{
std::string s;
if(!get(attribute, &s)) return 0;
if (!StringUtils::parseString<float>(s, value))
{
Log::warn("[XMLNode]", "WARNING: Expected float but found '%s' for attribute '%s' of node '%s' in file %s",
s.c_str(), attribute.c_str(), m_name.c_str(), m_file_name.c_str());
return 0;
}
return 1;
} // get(int)
// ----------------------------------------------------------------------------
int XMLNode::get(const std::string &attribute, bool *value) const
{
std::string s;
// FIXME: for some reason, missing attributes don't trigger that if???
if(!get(attribute, &s)) return 0;
*value = s[0]=='T' || s[0]=='t' || s[0]=='Y' || s[0]=='y' ||
s=="#t" || s =="#T" || s=="1";
return 1;
} // get(bool)
// ----------------------------------------------------------------------------
/** If 'attribute' was defined, split the value of the attribute by spaces,
* set value to this vector array and return the number of elements. Otherwise
* return 0 and do not change value.
* \param attribute Name of the attribute.
* \param value Value of the attribute.
*/
int XMLNode::get(const std::string &attribute,
std::vector<std::string> *value) const
{
std::string s;
if(!get(attribute, &s)) return 0;
*value = StringUtils::split(s,' ');
return (int) value->size();
} // get(vector<string>)
// ----------------------------------------------------------------------------
/** If 'attribute' was defined, split the value of the attribute by spaces,
* set value to this vector array and return the number of elements. Otherwise
* return 0 and do not change value.
* \param attribute Name of the attribute.
* \param value Value of the attribute.
*/
int XMLNode::get(const std::string &attribute,
std::vector<float> *value) const
{
std::string s;
if(!get(attribute, &s)) return 0;
std::vector<std::string> v = StringUtils::split(s,' ');
value->clear();
const unsigned int count = (unsigned int)v.size();
for (unsigned int i=0; i<count; i++)
{
float curr;
if (!StringUtils::parseString<float>(v[i], &curr))
{
Log::warn("[XMLNode]", "WARNING: Expected float but found '%s' for attribute '%s' of node '%s' in file %s",
v[i].c_str(), attribute.c_str(), m_name.c_str(), m_file_name.c_str());
return 0;
}
value->push_back(curr);
}
return (int) value->size();
} // get(vector<float>)
// ----------------------------------------------------------------------------
/** If 'attribute' was defined, split the value of the attribute by spaces,
* set value to this vector array and return the number of elements. Otherwise
* return 0 and do not change value.
* \param attribute Name of the attribute.
* \param value Value of the attribute.
*/
int XMLNode::get(const std::string &attribute, std::vector<int> *value) const
{
std::string s;
if(!get(attribute, &s)) return 0;
std::vector<std::string> v = StringUtils::split(s,' ');
value->clear();
const unsigned int count = (unsigned int)v.size();
for (unsigned int i=0; i<count; i++)
{
int val;
if (!StringUtils::parseString<int>(v[i], &val))
{
Log::warn("[XMLNode]", "WARNING: Expected int but found '%s' for attribute '%s' of node '%s'",
v[i].c_str(), attribute.c_str(), m_name.c_str());
return 0;
}
value->push_back(val);
}
return (int) value->size();
} // get(vector<int>)
// ----------------------------------------------------------------------------
/** Reads an InterpolatioARray. The values must be specified as:
* x0:y0 x1:y1 x2:y2 ...
* and the X values must be sorted. The function will abort (exit) with
* an error message in case of incorrectly formed x:y pairs.
* \param attribute Name of the attribute.
* \param value The InterpolationArray.
* \returns 0 in case of an error, !=0 otherwise
*/
int XMLNode::get(const std::string &attribute, InterpolationArray *value) const
{
std::string s;
if(!get(attribute, &s)) return 0;
std::vector<std::string> pairs = StringUtils::split(s, ' ');
for(unsigned int i=0; i<pairs.size(); i++)
{
std::vector<std::string> pair = StringUtils::split(pairs[i],':');
if(pair.size()!=2)
{
Log::fatal("[XMLNode]", "Incorrect interpolation pair '%s' in '%s'.",
pairs[i].c_str(), attribute.c_str());
Log::fatal("[XMLNode]", "Must be x:y.");
exit(-1);
}
float x;
if(!StringUtils::fromString(pair[0], x))
{
Log::fatal("[XMLNode]", "Incorrect x in pair '%s' of '%s'.",
pairs[i].c_str(), attribute.c_str());
exit(-1);
}
float y;
if(!StringUtils::fromString(pair[1], y))
{
Log::fatal("[XMLNode]", "Incorrect y in pair '%s' in '%s'.",
pair[1].c_str(), attribute.c_str());
exit(-1);
}
if(!value->push_back(x, y))
{
return 0;
}
} // for i
return 1;
} // get(InterpolationArray)
// ----------------------------------------------------------------------------
/** Interprets the attributes 'x', 'y', 'z' or 'h', 'p', 'r' as a 3d vector
* and set the corresponding elements of value. Not all values need to be
* defined as attributes (and the correspnding elements of the vector will
* not be changed). It returns a bit field for each defined value, i.e. if x
* and y are defined, 3 is returned.
* \param value Vector to return the values in.
*/
int XMLNode::get(core::vector3df *value) const
{
float f;
int bits=0;
if(get("x", &f)) { value->X = f; bits |= 1; }
if(get("h", &f)) { value->X = f; bits |= 1; }
if(get("y", &f)) { value->Y = f; bits |= 2; }
if(get("p", &f)) { value->Y = f; bits |= 2; }
if(get("z", &f)) { value->Z = f; bits |= 4; }
if(get("r", &f)) { value->Z = f; bits |= 4; }
return bits;
} // core::vector3df
// ----------------------------------------------------------------------------
/** Interprets the attributes 'x', 'y', 'z' as a 3d vector and set the
* corresponding elements of value. Not all values need to be defined as
* attributes (and the correspnding elements of the vector will not be
* changed). It returns a bit field for each defined value, i.e. if x
* and y are defined, 3 is returned.
* \param value Vector to return the values in.
*/
int XMLNode::getXYZ(core::vector3df *value) const
{
float f;
int bits=0;
if(get("x", &f)) { value->X = f; bits |= 1; }
if(get("y", &f)) { value->Y = f; bits |= 2; }
if(get("z", &f)) { value->Z = f; bits |= 4; }
return bits;
} // getXYZ vector3df
// ----------------------------------------------------------------------------
/** Interprets the attributes 'x', 'y', 'z' as a 3d vector and set the
* corresponding elements of value. Not all values need to be defined as
* attributes (and the correspnding elements of the vector will not be
* changed). It returns a bit field for each defined value, i.e. if x
* and y are defined, 3 is returned.
* \param value Vector to return the values in.
*/
int XMLNode::getXYZ(Vec3 *value) const
{
float f;
int bits=0;
if(get("x", &f)) { value->setX(f); bits |= 1; }
if(get("y", &f)) { value->setY(f); bits |= 2; }
if(get("z", &f)) { value->setZ(f); bits |= 4; }
return bits;
} // getXYZ Vec3
// ----------------------------------------------------------------------------
/** Interprets the attributes 'h', 'p', 'r' as a 3d vector and set the
* corresponding elements of value. Not all values need to be defined as
* attributes (and the correspnding elements of the vector will not be
* changed). It returns a bit field for each defined value, i.e. if x and y
* are defined, 3 is returned.
* \param value Vector to return the values in.
*/
int XMLNode::getHPR(core::vector3df *value) const
{
float f;
int bits=0;
if(get("h", &f)) { value->X = f; bits |= 1; }
if(get("p", &f)) { value->Y = f; bits |= 2; }
if(get("r", &f)) { value->Z = f; bits |= 4; }
return bits;
} // getHPR vector3df
// ----------------------------------------------------------------------------
/** Interprets the attributes 'h', 'p', 'r' as a 3d vector and set the
* corresponding elements of value. Not all values need to be defined as
* attributes (and the correspnding elements of the vector will not be
* changed). It returns a bit field for each defined value, i.e. if x and y
* are defined, 3 is returned.
* \param value Vector to return the values in.
*/
int XMLNode::getHPR(Vec3 *value) const
{
float f;
int bits=0;
if(get("h", &f)) { value->setX(f); bits |= 1; }
if(get("p", &f)) { value->setY(f); bits |= 2; }
if(get("r", &f)) { value->setZ(f); bits |= 4; }
return bits;
} // getHPR Vec3
// ----------------------------------------------------------------------------
bool XMLNode::hasChildNamed(const char* name) const
{
for (unsigned int i = 0; i < m_nodes.size(); i++)
{
if (m_nodes[i]->getName() == name) return true;
}
return false;
}