2011-03-26 21:17:21 +01:00
|
|
|
/* Copyright (C) 2011 Wildfire Games.
|
2009-04-18 19:00:33 +02:00
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. 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.
|
|
|
|
*
|
|
|
|
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2006-04-24 01:14:18 +02:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
#include "XMLWriter.h"
|
|
|
|
|
|
|
|
#include "ps/CLogger.h"
|
2007-12-20 21:21:45 +01:00
|
|
|
#include "ps/Filesystem.h"
|
2011-03-26 21:17:21 +01:00
|
|
|
#include "ps/XML/Xeromyces.h"
|
2010-03-01 15:55:34 +01:00
|
|
|
#include "lib/utf8.h"
|
2010-01-09 20:20:14 +01:00
|
|
|
#include "lib/sysdep/cpu.h"
|
2010-08-04 23:15:41 +02:00
|
|
|
#include "maths/Fixed.h"
|
2006-04-24 01:14:18 +02:00
|
|
|
|
|
|
|
|
2007-12-20 21:21:45 +01:00
|
|
|
// TODO (maybe): Write to the file frequently, instead of buffering
|
2006-04-24 01:14:18 +02:00
|
|
|
// the entire file, so that large files get written faster.
|
|
|
|
|
2006-12-03 18:13:22 +01:00
|
|
|
namespace
|
2006-04-24 01:14:18 +02:00
|
|
|
{
|
2006-12-03 18:13:22 +01:00
|
|
|
CStr escapeAttributeValue(const char* input)
|
|
|
|
{
|
|
|
|
// Spec says:
|
|
|
|
// AttValue ::= '"' ([^<&"] | Reference)* '"'
|
|
|
|
// so > is allowed in attribute values, so we don't bother escaping it.
|
|
|
|
|
|
|
|
CStr ret = input;
|
|
|
|
ret.Replace("&", "&");
|
|
|
|
ret.Replace("<", "<");
|
|
|
|
ret.Replace("\"", """);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
CStr escapeCharacterData(const char* input)
|
|
|
|
{
|
|
|
|
// CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
|
|
|
|
|
|
|
|
CStr ret = input;
|
|
|
|
ret.Replace("&", "&");
|
|
|
|
ret.Replace("<", "<");
|
|
|
|
ret.Replace("]]>", "]]>");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-04-30 01:22:18 +02:00
|
|
|
CStr escapeCDATA(const char* input)
|
|
|
|
{
|
|
|
|
CStr ret = input;
|
|
|
|
ret.Replace("]]>", "]]>]]><![CDATA[");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-12-03 18:13:22 +01:00
|
|
|
CStr escapeComment(const char* input)
|
|
|
|
{
|
|
|
|
// Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
|
|
|
|
// This just avoids double-hyphens, and doesn't enforce the no-hyphen-at-end
|
|
|
|
// rule, since it's only used in contexts where there's already a space
|
|
|
|
// between this data and the -->.
|
|
|
|
CStr ret = input;
|
|
|
|
ret.Replace("--", "\xE2\x80\x90\xE2\x80\x90");
|
|
|
|
// replace with U+2010 HYPHEN, because it's close enough and it's
|
|
|
|
// probably nicer than inserting spaces or deleting hyphens or
|
|
|
|
// any alternative
|
|
|
|
return ret;
|
|
|
|
}
|
2006-04-24 01:14:18 +02:00
|
|
|
}
|
|
|
|
|
2006-12-03 18:13:22 +01:00
|
|
|
enum { EL_ATTR, EL_TEXT, EL_SUBEL };
|
|
|
|
|
|
|
|
XMLWriter_File::XMLWriter_File()
|
|
|
|
: m_Indent(0), m_LastElement(NULL),
|
|
|
|
m_PrettyPrint(true)
|
2006-04-24 01:14:18 +02:00
|
|
|
{
|
2006-12-03 18:13:22 +01:00
|
|
|
// Encoding is always UTF-8 - that's one of the only two guaranteed to be
|
|
|
|
// supported by XML parsers (along with UTF-16), and there's not much need
|
|
|
|
// to let people choose another.
|
2008-04-15 15:45:17 +02:00
|
|
|
m_Data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
2006-04-24 01:14:18 +02:00
|
|
|
}
|
|
|
|
|
2010-07-04 12:15:53 +02:00
|
|
|
bool XMLWriter_File::StoreVFS(const PIVFS& vfs, const VfsPath& pathname)
|
2006-04-24 01:14:18 +02:00
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
if (m_LastElement) debug_warn(L"ERROR: Saving XML while an element is still open");
|
2006-04-24 01:14:18 +02:00
|
|
|
|
2007-12-20 21:21:45 +01:00
|
|
|
const size_t size = m_Data.length();
|
|
|
|
shared_ptr<u8> data = io_Allocate(size);
|
2010-11-01 12:09:03 +01:00
|
|
|
memcpy(data.get(), m_Data.data(), size);
|
2010-07-04 12:15:53 +02:00
|
|
|
LibError ret = vfs->CreateFile(pathname, data, size);
|
2007-12-20 21:21:45 +01:00
|
|
|
if (ret < 0)
|
2006-04-24 01:14:18 +02:00
|
|
|
{
|
2010-12-05 09:41:55 +01:00
|
|
|
LOGERROR(L"Error saving XML data through VFS: %ld", ret);
|
2006-04-24 01:14:18 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-12-03 18:13:22 +01:00
|
|
|
const CStr& XMLWriter_File::GetOutput()
|
|
|
|
{
|
|
|
|
return m_Data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-26 21:17:21 +01:00
|
|
|
void XMLWriter_File::XMB(const XMBFile& file)
|
|
|
|
{
|
|
|
|
ElementXMB(file, file.GetRoot());
|
|
|
|
}
|
|
|
|
|
|
|
|
void XMLWriter_File::ElementXMB(const XMBFile& file, XMBElement el)
|
|
|
|
{
|
|
|
|
XMLWriter_Element writer(*this, file.GetElementString(el.GetNodeName()).c_str());
|
|
|
|
|
|
|
|
XERO_ITER_ATTR(el, attr)
|
|
|
|
writer.Attribute(file.GetAttributeString(attr.Name).c_str(), attr.Value);
|
|
|
|
|
|
|
|
XERO_ITER_EL(el, child)
|
|
|
|
ElementXMB(file, child);
|
|
|
|
}
|
|
|
|
|
2006-04-24 01:14:18 +02:00
|
|
|
void XMLWriter_File::Comment(const char* text)
|
|
|
|
{
|
|
|
|
ElementStart(NULL, "!-- ");
|
2006-12-03 18:13:22 +01:00
|
|
|
m_Data += escapeComment(text);
|
2006-04-24 01:14:18 +02:00
|
|
|
m_Data += " -->";
|
|
|
|
--m_Indent;
|
|
|
|
}
|
|
|
|
|
|
|
|
CStr XMLWriter_File::Indent()
|
|
|
|
{
|
|
|
|
return std::string(m_Indent, '\t');
|
|
|
|
}
|
|
|
|
|
|
|
|
void XMLWriter_File::ElementStart(XMLWriter_Element* element, const char* name)
|
|
|
|
{
|
|
|
|
if (m_LastElement) m_LastElement->Close(EL_SUBEL);
|
|
|
|
m_LastElement = element;
|
2006-12-03 18:13:22 +01:00
|
|
|
|
|
|
|
if (m_PrettyPrint)
|
|
|
|
{
|
|
|
|
m_Data += "\n";
|
|
|
|
m_Data += Indent();
|
|
|
|
}
|
2006-04-24 01:14:18 +02:00
|
|
|
m_Data += "<";
|
|
|
|
m_Data += name;
|
|
|
|
|
|
|
|
++m_Indent;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XMLWriter_File::ElementClose()
|
|
|
|
{
|
|
|
|
m_Data += ">";
|
|
|
|
}
|
|
|
|
|
|
|
|
void XMLWriter_File::ElementEnd(const char* name, int type)
|
|
|
|
{
|
|
|
|
--m_Indent;
|
|
|
|
m_LastElement = NULL;
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case EL_ATTR:
|
2006-06-11 09:03:59 +02:00
|
|
|
m_Data += "/>";
|
2006-04-24 01:14:18 +02:00
|
|
|
break;
|
|
|
|
case EL_TEXT:
|
|
|
|
m_Data += "</";
|
|
|
|
m_Data += name;
|
|
|
|
m_Data += ">";
|
|
|
|
break;
|
|
|
|
case EL_SUBEL:
|
2006-12-03 18:13:22 +01:00
|
|
|
if (m_PrettyPrint)
|
|
|
|
{
|
|
|
|
m_Data += "\n";
|
|
|
|
m_Data += Indent();
|
|
|
|
}
|
2006-04-24 01:14:18 +02:00
|
|
|
m_Data += "</";
|
|
|
|
m_Data += name;
|
|
|
|
m_Data += ">";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
debug_assert(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-30 01:22:18 +02:00
|
|
|
void XMLWriter_File::ElementText(const char* text, bool cdata)
|
2006-04-24 01:14:18 +02:00
|
|
|
{
|
2010-04-30 01:22:18 +02:00
|
|
|
if (cdata)
|
|
|
|
{
|
|
|
|
m_Data += "<![CDATA[";
|
|
|
|
m_Data += escapeCDATA(text);
|
|
|
|
m_Data += "]]>";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_Data += escapeCharacterData(text);
|
|
|
|
}
|
2006-04-24 01:14:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
XMLWriter_Element::XMLWriter_Element(XMLWriter_File& file, const char* name)
|
|
|
|
: m_File(&file), m_Name(name), m_Type(EL_ATTR)
|
|
|
|
{
|
|
|
|
m_File->ElementStart(this, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
XMLWriter_Element::~XMLWriter_Element()
|
|
|
|
{
|
2011-02-17 21:08:20 +01:00
|
|
|
m_File->ElementEnd(m_Name.c_str(), m_Type);
|
2006-04-24 01:14:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void XMLWriter_Element::Close(int type)
|
|
|
|
{
|
2006-12-03 18:13:22 +01:00
|
|
|
if (m_Type == type)
|
|
|
|
return;
|
|
|
|
|
2006-04-24 01:14:18 +02:00
|
|
|
m_File->ElementClose();
|
|
|
|
m_Type = type;
|
|
|
|
}
|
|
|
|
|
2006-12-03 18:13:22 +01:00
|
|
|
|
|
|
|
// Template specialisations for various string types:
|
|
|
|
|
2010-04-30 01:22:18 +02:00
|
|
|
template <> void XMLWriter_Element::Text<const char*>(const char* text, bool cdata)
|
2006-04-24 01:14:18 +02:00
|
|
|
{
|
|
|
|
Close(EL_TEXT);
|
2010-04-30 01:22:18 +02:00
|
|
|
m_File->ElementText(text, cdata);
|
2006-04-24 01:14:18 +02:00
|
|
|
}
|
|
|
|
|
2010-04-30 01:22:18 +02:00
|
|
|
template <> void XMLWriter_Element::Text<const wchar_t*>(const wchar_t* text, bool cdata)
|
2006-12-03 18:13:22 +01:00
|
|
|
{
|
2010-04-30 01:22:18 +02:00
|
|
|
Text( CStrW(text).ToUTF8().c_str(), cdata );
|
2006-12-03 18:13:22 +01:00
|
|
|
}
|
2006-04-24 01:14:18 +02:00
|
|
|
|
2006-12-03 18:13:22 +01:00
|
|
|
//
|
2006-04-24 01:14:18 +02:00
|
|
|
|
2006-12-03 18:13:22 +01:00
|
|
|
template <> void XMLWriter_File::ElementAttribute<const char*>(const char* name, const char* const& value, bool newelement)
|
2006-04-24 01:14:18 +02:00
|
|
|
{
|
|
|
|
if (newelement)
|
|
|
|
{
|
|
|
|
ElementStart(NULL, name);
|
|
|
|
m_Data += ">";
|
2010-04-30 01:22:18 +02:00
|
|
|
ElementText(value, false);
|
2006-04-24 01:14:18 +02:00
|
|
|
ElementEnd(name, EL_TEXT);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
debug_assert(m_LastElement && m_LastElement->m_Type == EL_ATTR);
|
|
|
|
m_Data += " ";
|
|
|
|
m_Data += name;
|
|
|
|
m_Data += "=\"";
|
2006-12-03 18:13:22 +01:00
|
|
|
m_Data += escapeAttributeValue(value);
|
2006-04-24 01:14:18 +02:00
|
|
|
m_Data += "\"";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attribute/setting value-to-string template specialisations.
|
|
|
|
//
|
|
|
|
// These only deal with basic types. Anything more complicated should
|
|
|
|
// be converted into a basic type by whatever is making use of XMLWriter,
|
|
|
|
// to keep game-related logic out of the not-directly-game-related code here.
|
|
|
|
|
2006-12-03 18:13:22 +01:00
|
|
|
template <> void XMLWriter_File::ElementAttribute<CStr>(const char* name, const CStr& value, bool newelement)
|
|
|
|
{
|
|
|
|
ElementAttribute(name, value.c_str(), newelement);
|
|
|
|
}
|
2010-05-25 20:17:12 +02:00
|
|
|
template <> void XMLWriter_File::ElementAttribute<std::string>(const char* name, const std::string& value, bool newelement)
|
|
|
|
{
|
|
|
|
ElementAttribute(name, value.c_str(), newelement);
|
|
|
|
}
|
|
|
|
// Encode Unicode strings as UTF-8
|
|
|
|
template <> void XMLWriter_File::ElementAttribute<CStrW>(const char* name, const CStrW& value, bool newelement)
|
|
|
|
{
|
|
|
|
ElementAttribute(name, value.ToUTF8(), newelement);
|
|
|
|
}
|
|
|
|
template <> void XMLWriter_File::ElementAttribute<std::wstring>(const char* name, const std::wstring& value, bool newelement)
|
|
|
|
{
|
|
|
|
ElementAttribute(name, utf8_from_wstring(value).c_str(), newelement);
|
|
|
|
}
|
2006-12-03 18:13:22 +01:00
|
|
|
|
2010-08-04 23:15:41 +02:00
|
|
|
template <> void XMLWriter_File::ElementAttribute<fixed>(const char* name, const fixed& value, bool newelement)
|
|
|
|
{
|
|
|
|
ElementAttribute(name, value.ToString().c_str(), newelement);
|
|
|
|
}
|
|
|
|
|
2011-02-17 21:08:20 +01:00
|
|
|
template <> void XMLWriter_File::ElementAttribute<int>(const char* name, const int& value, bool newelement)
|
|
|
|
{
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << value;
|
|
|
|
ElementAttribute(name, ss.str().c_str(), newelement);
|
2006-04-24 01:14:18 +02:00
|
|
|
}
|
|
|
|
|
2011-02-17 21:08:20 +01:00
|
|
|
template <> void XMLWriter_File::ElementAttribute<unsigned int>(const char* name, const unsigned int& value, bool newelement)
|
|
|
|
{
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << value;
|
|
|
|
ElementAttribute(name, ss.str().c_str(), newelement);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> void XMLWriter_File::ElementAttribute<float>(const char* name, const float& value, bool newelement)
|
|
|
|
{
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << value;
|
|
|
|
ElementAttribute(name, ss.str().c_str(), newelement);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <> void XMLWriter_File::ElementAttribute<double>(const char* name, const double& value, bool newelement)
|
|
|
|
{
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << value;
|
|
|
|
ElementAttribute(name, ss.str().c_str(), newelement);
|
|
|
|
}
|