# Started using libxml2

This was SVN commit r6763.
This commit is contained in:
Ykkrosh 2009-03-22 20:51:35 +00:00
parent 6768858cf1
commit 7610d4361c
5 changed files with 105 additions and 78 deletions

View File

@ -601,6 +601,10 @@ function setup_atlas_package(package_name, target_type, rel_source_dirs, rel_inc
package_setup_pch(nil, "precompiled.h", "precompiled.cpp"); package_setup_pch(nil, "precompiled.h", "precompiled.cpp");
end end
if options["aoe3ed"] then
tinsert(package.defines, "USE_AOE3ED")
end
-- Platform Specifics -- Platform Specifics
if OS == "windows" then if OS == "windows" then
@ -638,6 +642,7 @@ function setup_atlas_packages()
"" ""
},{ -- include },{ -- include
},{ -- extern_libs },{ -- extern_libs
"libxml2",
"xerces", "xerces",
"wxwidgets" "wxwidgets"
},{ -- extra_params },{ -- extra_params
@ -723,6 +728,7 @@ function setup_atlas_packages()
"boost", "boost",
"devil", "devil",
--"ffmpeg", -- disabled for now because it causes too many build difficulties --"ffmpeg", -- disabled for now because it causes too many build difficulties
"libxml2",
"spidermonkey", "spidermonkey",
"wxwidgets", "wxwidgets",
"xerces", "xerces",

View File

@ -8,6 +8,7 @@
#define INCLUDED_ATLASOBJECT #define INCLUDED_ATLASOBJECT
#include <wchar.h> // for wchar_t #include <wchar.h> // for wchar_t
#include <string>
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Mostly-private bits: // Mostly-private bits:
@ -142,8 +143,9 @@ namespace AtlasObject
// Returns AtObj() on failure - test with AtObj::defined() // Returns AtObj() on failure - test with AtObj::defined()
AtObj LoadFromXML(const wchar_t* filename); AtObj LoadFromXML(const wchar_t* filename);
// Returns false on failure // Returns UTF-8-encoded XML document string.
bool SaveToXML(AtObj& obj, const wchar_t* filename); // Returns empty string on failure.
std::string SaveToXML(AtObj& obj);
AtObj TrimEmptyChildren(AtObj& obj); AtObj TrimEmptyChildren(AtObj& obj);
} }

View File

@ -4,6 +4,7 @@
#include <assert.h> #include <assert.h>
#include <memory> #include <memory>
#include <fstream>
#ifdef _MSC_VER #ifdef _MSC_VER
# ifndef NDEBUG # ifndef NDEBUG
@ -27,6 +28,8 @@
#include <xercesc/sax/SAXParseException.hpp> #include <xercesc/sax/SAXParseException.hpp>
#include <xercesc/sax/ErrorHandler.hpp> #include <xercesc/sax/ErrorHandler.hpp>
#include <libxml/parser.h>
XERCES_CPP_NAMESPACE_USE XERCES_CPP_NAMESPACE_USE
class XercesInitialiser class XercesInitialiser
@ -96,6 +99,45 @@ private:
T* data; T* data;
}; };
// UTF conversion code adapted from http://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
class toXmlChar
{
public:
toXmlChar(const std::wstring& str)
{
for (size_t i = 0; i < str.length(); ++i)
{
unsigned short bytesToWrite;
wchar_t ch = str[i];
if (ch < 0x80) bytesToWrite = 1;
else if (ch < 0x800) bytesToWrite = 2;
else if (ch < 0x10000) bytesToWrite = 3;
else if (ch < 0x110000) bytesToWrite = 4;
else bytesToWrite = 3, ch = 0xFFFD; // replacement character
char buf[4];
char* target = &buf[bytesToWrite];
switch (bytesToWrite)
{
case 4: *--target = ((ch | 0x80) & 0xBF); ch >>= 6;
case 3: *--target = ((ch | 0x80) & 0xBF); ch >>= 6;
case 2: *--target = ((ch | 0x80) & 0xBF); ch >>= 6;
case 1: *--target = (ch | firstByteMark[bytesToWrite]);
}
data += std::string(buf, bytesToWrite);
}
}
operator const xmlChar*()
{
return (const xmlChar*)data.c_str();
}
private:
std::string data;
};
// TODO: replace most of the asserts below (e.g. for when it fails to load // TODO: replace most of the asserts below (e.g. for when it fails to load
// a file) with some proper logging/reporting system // a file) with some proper logging/reporting system
@ -219,106 +261,64 @@ static AtSmartPtr<AtNode> ConvertNode(DOMElement* element)
return obj; return obj;
} }
// Build a DOM node from a given AtNode // Build a DOM node from a given AtNode
static DOMAttr* BuildDOMAttr(DOMDocument* doc, const XMLCh* name, AtNode::Ptr p) static void BuildDOMNode(xmlDocPtr doc, xmlNodePtr node, AtNode::Ptr p)
{ {
assert(p); // attributes must contain some data
assert(p->children.size() == 0); // attributes mustn't contain nested data
if (!p || p->children.size() != 0)
{
// Oops - invalid data
return NULL;
}
DOMAttr* attr = doc->createAttribute(name);
attr->setValue(StrConv<XMLCh>(p->value).c_str());
return attr;
}
// Build a DOM node from a given AtNode
static DOMNode* BuildDOMNode(DOMDocument* doc, const XMLCh* name, AtNode::Ptr p)
{
DOMElement* node = doc->createElement(name);
if (p) if (p)
{ {
if (p->value.length()) if (p->value.length())
node->setTextContent(StrConv<XMLCh>(p->value).c_str()); xmlNodeAddContent(node, toXmlChar(p->value));
XMLCh tempStr[256]; // urgh, nasty fixed-size buffer
for (AtNode::child_maptype::const_iterator it = p->children.begin(); it != p->children.end(); ++it) for (AtNode::child_maptype::const_iterator it = p->children.begin(); it != p->children.end(); ++it)
{ {
// Test for attribute nodes (whose names start with @) // Test for attribute nodes (whose names start with @)
if (it->first.length() && it->first[0] == '@') if (it->first.length() && it->first[0] == '@')
{ {
XMLString::transcode(it->first.c_str()+1, tempStr, 255); assert(it->second);
node->setAttributeNode(BuildDOMAttr(doc, tempStr, it->second)); assert(it->second->children.empty());
xmlNewProp(node, (const xmlChar*)it->first.c_str()+1, toXmlChar(it->second->value));
} }
else else
{ {
XMLString::transcode(it->first.c_str(), tempStr, 255); if (node == NULL) // first node in the document - needs to be made the root node
node->appendChild(BuildDOMNode(doc, tempStr, it->second)); {
xmlNodePtr root = xmlNewNode(NULL, (const xmlChar*)it->first.c_str());
xmlDocSetRootElement(doc, root);
BuildDOMNode(doc, root, it->second);
}
else
{
xmlNodePtr child = xmlNewChild(node, NULL, (const xmlChar*)it->first.c_str(), NULL);
BuildDOMNode(doc, child, it->second);
}
} }
} }
} }
return node;
} }
bool AtlasObject::SaveToXML(AtObj& obj, const wchar_t* filename) std::string AtlasObject::SaveToXML(AtObj& obj)
{ {
XercesInitialiser::enable();
// Why does it take so much work just to create a standard DOMWriter? :-(
XMLCh domFeatures[100] = { 0 };
XMLString::transcode("LS", domFeatures, 99); // maybe "LS" means "load/save", but I really don't know
DOMImplementation* impl = DOMImplementationRegistry::getDOMImplementation(domFeatures);
DOMWriter* writer = ((DOMImplementationLS*)impl)->createDOMWriter();
if (writer->canSetFeature(XMLUni::fgDOMWRTDiscardDefaultContent, true))
writer->setFeature(XMLUni::fgDOMWRTDiscardDefaultContent, true);
if (writer->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true))
writer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true);
// Find the root element of the object:
if (!obj.p || obj.p->children.size() != 1) if (!obj.p || obj.p->children.size() != 1)
{ {
assert(! "SaveToXML: root must only have one child"); assert(! "SaveToXML: root must only have one child");
return false; return "";
} }
XMLCh rootName[255];
XMLString::transcode(obj.p->children.begin()->first.c_str(), rootName, 255);
AtNode::Ptr firstChild (obj.p->children.begin()->second); AtNode::Ptr firstChild (obj.p->children.begin()->second);
try xmlDocPtr doc = xmlNewDoc((const xmlChar*)"1.0");
{ BuildDOMNode(doc, NULL, obj.p);
std::auto_ptr<DOMDocument> doc (impl->createDocument());
doc->appendChild(BuildDOMNode(doc.get(), rootName, firstChild));
LocalFileFormatTarget formatTarget (StrConv<XMLCh>(filename).c_str()); xmlChar* buf;
writer->writeNode(&formatTarget, *doc); int size;
} xmlDocDumpFormatMemoryEnc(doc, &buf, &size, "utf-8", 1);
catch (const XMLException& e) {
char* message = XMLString::transcode(e.getMessage());
assert(! "XML exception - maybe failed while writing the file");
XMLString::release(&message);
return false;
}
catch (const DOMException& e) {
char* message = XMLString::transcode(e.msg);
assert(! "DOM exception");
XMLString::release(&message);
return false;
}
writer->release(); std::string ret((const char*)buf, size);
return true; xmlFree(buf);
xmlFreeDoc(doc);
// TODO: handle errors better
return ret;
} }

View File

@ -263,7 +263,12 @@ bool AtlasWindow::SaveChanges(bool forceSaveAs)
AtObj file (ExportData()); AtObj file (ExportData());
// TODO: Make sure it succeeded. Back up .xml file in case it didn't. // TODO: Make sure it succeeded. Back up .xml file in case it didn't.
AtlasObject::SaveToXML(file, GetCurrentFilename().GetFullPath()); std::string xml = AtlasObject::SaveToXML(file);
wxCHECK(!xml.empty(), false);
wxFile outfile (GetCurrentFilename().GetFullPath(), wxFile::write);
outfile.Write(xml.c_str(), xml.length());
outfile.Close();
sig_FileSaved(); sig_FileSaved();

View File

@ -22,6 +22,12 @@
#include "wx/debugrpt.h" #include "wx/debugrpt.h"
#include "wx/file.h" #include "wx/file.h"
#include <libxml/parser.h>
#ifndef LIBXML_THREAD_ENABLED
#error libxml2 must have threading support enabled
#endif
#ifdef __WXGTK__ #ifdef __WXGTK__
#include <X11/Xlib.h> #include <X11/Xlib.h>
#endif #endif
@ -85,6 +91,10 @@ ATLASDLLIMPEXP void Atlas_SetMessagePasser(MessagePasser* passer)
ATLASDLLIMPEXP void Atlas_StartWindow(const wchar_t* type) ATLASDLLIMPEXP void Atlas_StartWindow(const wchar_t* type)
{ {
// Initialise libxml2
// (If we're executed from the game instead, it has the responsibility to initialise libxml2)
xmlInitParser();
g_InitialWindowType = type; g_InitialWindowType = type;
#ifdef __WXMSW__ #ifdef __WXMSW__
wxEntry(g_Module); wxEntry(g_Module);
@ -99,7 +109,8 @@ ATLASDLLIMPEXP void Atlas_StartWindow(const wchar_t* type)
} }
#endif #endif
int argc = 1; int argc = 1;
char *argv[] = {"atlas", NULL}; char atlas[] = "atlas";
char *argv[] = {atlas, NULL};
wxEntry(argc, argv); wxEntry(argc, argv);
#endif #endif
} }
@ -161,9 +172,11 @@ public:
#define MAYBE(t) if (g_InitialWindowType == _T(#t)) frame = new t(NULL); else #define MAYBE(t) if (g_InitialWindowType == _T(#t)) frame = new t(NULL); else
MAYBE(ActorEditor) MAYBE(ActorEditor)
MAYBE(ActorViewer) MAYBE(ActorViewer)
MAYBE(ArchiveViewer)
MAYBE(ColourTester) MAYBE(ColourTester)
#ifdef USE_AOE3ED
MAYBE(ArchiveViewer)
MAYBE(FileConverter) MAYBE(FileConverter)
#endif
#undef MAYBE #undef MAYBE
// else // else
if (g_InitialWindowType == _T("ScenarioEditor")) if (g_InitialWindowType == _T("ScenarioEditor"))
@ -278,6 +291,7 @@ private:
#else #else
// Figure out what goes for "default browser" on unix/linux/whatever // Figure out what goes for "default browser" on unix/linux/whatever
// open an xterm perhaps? :) // open an xterm perhaps? :)
(void)dir;
return false; return false;
#endif #endif
} }