mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-07-24 17:13:08 +00:00
Nicer XML-writing system
This was SVN commit r1711.
This commit is contained in:
@@ -149,7 +149,7 @@ void CMapReader::ApplyData(CFileUnpacker& unpacker, CTerrain *pTerrain, CUnitMan
|
||||
CObjectEntry* objentry=m_ObjectTypes[m_Objects[i].m_ObjectIndex];
|
||||
if (objentry && objentry->m_Model) {
|
||||
// Hijack the standard actor instantiation for actors that correspond to entities.
|
||||
// Not an ideal solution; we'll have to figure out a map format that can define entities seperately or somesuch.
|
||||
// Not an ideal solution; we'll have to figure out a map format that can define entities separately or somesuch.
|
||||
|
||||
CBaseEntity* templateObject = g_EntityTemplateCollection.getTemplateByActor( objentry );
|
||||
|
||||
|
||||
+60
-100
@@ -12,6 +12,7 @@
|
||||
#include "Unit.h"
|
||||
|
||||
#include "ps/Xeromyces.h"
|
||||
#include "ps/XMLWriter.h"
|
||||
|
||||
#include "lib/res/vfs.h"
|
||||
|
||||
@@ -201,7 +202,7 @@ bool CObjectEntry::Load(const char* filename)
|
||||
XMBElementList children = root.getChildNodes();
|
||||
|
||||
for (int i = 0; i < children.Count; ++i) {
|
||||
// Get node
|
||||
|
||||
XMBElement child = children.item(i);
|
||||
|
||||
int element_name = child.getNodeName();
|
||||
@@ -220,15 +221,19 @@ bool CObjectEntry::Load(const char* filename)
|
||||
m_Material = element_value;
|
||||
|
||||
else if (element_name == el_properties) {
|
||||
|
||||
XMBAttributeList attributes=child.getAttributes();
|
||||
for (int j = 0; j < attributes.Count; ++j) {
|
||||
XMBAttribute attrib=attributes.item(j);
|
||||
|
||||
int attrib_name = attrib.Name;
|
||||
|
||||
if (attrib_name == at_autoflatten) {
|
||||
CStr str= (CStr) attrib.Value;
|
||||
CStr str (attrib.Value);
|
||||
m_Properties.m_AutoFlatten=str.ToInt() ? true : false;
|
||||
|
||||
} else if (attrib_name == at_castshadows) {
|
||||
CStr str= (CStr) attrib.Value;
|
||||
CStr str = (attrib.Value);
|
||||
m_Properties.m_CastShadows=str.ToInt() ? true : false;
|
||||
}
|
||||
}
|
||||
@@ -244,9 +249,9 @@ bool CObjectEntry::Load(const char* filename)
|
||||
if (attributes.Count) {
|
||||
Anim anim;
|
||||
|
||||
anim.m_AnimName = (CStr)attributes.getNamedItem(at_name);
|
||||
anim.m_FileName = (CStr)attributes.getNamedItem(at_file);
|
||||
CStr speedstr = (CStr)attributes.getNamedItem(at_speed);
|
||||
anim.m_AnimName = attributes.getNamedItem(at_name);
|
||||
anim.m_FileName = attributes.getNamedItem(at_file);
|
||||
CStr speedstr = attributes.getNamedItem(at_speed);
|
||||
|
||||
anim.m_Speed=float(speedstr.ToInt())/100.0f;
|
||||
if (anim.m_Speed<=0.0) anim.m_Speed=1.0f;
|
||||
@@ -265,8 +270,8 @@ bool CObjectEntry::Load(const char* filename)
|
||||
if (attributes.Count) {
|
||||
Prop prop;
|
||||
|
||||
prop.m_PropPointName = (CStr)attributes.getNamedItem(at_attachpoint);
|
||||
prop.m_ModelName = (CStr)attributes.getNamedItem(at_model);
|
||||
prop.m_PropPointName = attributes.getNamedItem(at_attachpoint);
|
||||
prop.m_ModelName = attributes.getNamedItem(at_model);
|
||||
|
||||
m_Props.push_back(prop);
|
||||
}
|
||||
@@ -278,72 +283,8 @@ bool CObjectEntry::Load(const char* filename)
|
||||
|
||||
}
|
||||
|
||||
// TODO: Make this less hacky, and maybe make it part of the VFS
|
||||
void vfs_printf(Handle h, const char* fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
|
||||
char buf[1024];
|
||||
int r = vsnprintf(buf, sizeof(buf), fmt, va);
|
||||
if (r < 0 || r >= sizeof(buf))
|
||||
{
|
||||
// TODO: Handle this properly
|
||||
debug_warn("Argh - too much stuff in vfs_printf");
|
||||
return;
|
||||
}
|
||||
char *bufp = buf;
|
||||
if (vfs_io(h, strlen(buf), (void**)&bufp) < 0)
|
||||
{
|
||||
debug_warn("vfs_io failed");
|
||||
}
|
||||
}
|
||||
|
||||
bool CObjectEntry::Save(const char* filename)
|
||||
{
|
||||
/*
|
||||
|
||||
FILE* fp=fopen(filename,"w");
|
||||
if (!fp) return false;
|
||||
|
||||
// write XML header
|
||||
fprintf(fp,"<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?>\n\n");
|
||||
fprintf(fp,"<!DOCTYPE Object SYSTEM \"/art/actors/object.dtd\">\n\n");
|
||||
|
||||
// write the object itself
|
||||
fprintf(fp,"<!-- File automatically generated by ScEd -->\n");
|
||||
fprintf(fp,"<Object>\n");
|
||||
fprintf(fp,"\t<Name>%s</Name>\n",(const char*) m_Name);
|
||||
fprintf(fp,"\t<ModelName>%s</ModelName>\n",(const char*) m_ModelName);
|
||||
fprintf(fp,"\t<TextureName>%s</TextureName>\n",(const char*) m_TextureName);
|
||||
if(m_Material.Trim(PS_TRIM_BOTH).Length())
|
||||
fprintf(fp,"\t<Material>%s</Material>\n", (const char*)m_Material);
|
||||
|
||||
fprintf(fp,"\t<Properties\n");
|
||||
fprintf(fp,"\t\tautoflatten=\"%d\"\n",m_Properties.m_AutoFlatten ? 1 : 0);
|
||||
fprintf(fp,"\t\tcastshadows=\"%d\"\n",m_Properties.m_CastShadows ? 1 : 0);
|
||||
fprintf(fp,"\t/>\n");
|
||||
if (m_Animations.size()>0) {
|
||||
fprintf(fp,"\t<Animations>\n");
|
||||
for (uint i=0;i<m_Animations.size();i++) {
|
||||
fprintf(fp,"\t\t<Animation name=\"%s\" file=\"%s\" speed=\"%d\"/>\n",(const char*) m_Animations[i].m_AnimName,(const char*) m_Animations[i].m_FileName,int(m_Animations[i].m_Speed*100));
|
||||
}
|
||||
fprintf(fp,"\t</Animations>\n");
|
||||
}
|
||||
if (m_Props.size()>0) {
|
||||
fprintf(fp,"\t<Props>\n");
|
||||
for (uint i=0;i<m_Props.size();i++) {
|
||||
fprintf(fp,"\t\t<Prop attachpoint=\"%s\" model=\"%s\"/>\n",(const char*) m_Props[i].m_PropPointName,(const char*) m_Props[i].m_ModelName);
|
||||
}
|
||||
fprintf(fp,"\t</Props>\n");
|
||||
}
|
||||
|
||||
fprintf(fp,"</Object>\n");
|
||||
fclose(fp);
|
||||
|
||||
return true;
|
||||
*/
|
||||
|
||||
Handle h = vfs_open(filename, FILE_WRITE|FILE_NO_AIO);
|
||||
if (h <= 0)
|
||||
{
|
||||
@@ -351,43 +292,62 @@ bool CObjectEntry::Save(const char* filename)
|
||||
return false;
|
||||
}
|
||||
|
||||
// write XML header
|
||||
vfs_printf(h,"<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?>\n\n");
|
||||
vfs_printf(h,"<!DOCTYPE Object SYSTEM \"/art/actors/object.dtd\">\n\n");
|
||||
XML_Start("iso-8859-1");
|
||||
XML_Doctype("Object", "/art/actors/object.dtd");
|
||||
|
||||
// write the object itself
|
||||
vfs_printf(h,"<!-- File automatically generated by ScEd -->\n");
|
||||
vfs_printf(h,"<Object>\n");
|
||||
vfs_printf(h,"\t<Name>%s</Name>\n",(const char*) m_Name);
|
||||
vfs_printf(h,"\t<ModelName>%s</ModelName>\n",(const char*) m_ModelName);
|
||||
vfs_printf(h,"\t<TextureName>%s</TextureName>\n",(const char*) m_TextureName);
|
||||
XML_Comment("File automatically generated by ScEd");
|
||||
|
||||
if(m_Material.Trim(PS_TRIM_BOTH).Length())
|
||||
vfs_printf(h,"\t<Material>%s</Material>\n", (const char*)m_Material);
|
||||
{
|
||||
XML_Element("Object");
|
||||
|
||||
vfs_printf(h,"\t<Properties\n");
|
||||
vfs_printf(h,"\t\tautoflatten=\"%d\"\n",m_Properties.m_AutoFlatten ? 1 : 0);
|
||||
vfs_printf(h,"\t\tcastshadows=\"%d\"\n",m_Properties.m_CastShadows ? 1 : 0);
|
||||
vfs_printf(h,"\t/>\n");
|
||||
XML_Setting("Name", m_Name);
|
||||
XML_Setting("ModelName", m_ModelName);
|
||||
XML_Setting("TextureName", m_TextureName);
|
||||
|
||||
if (m_Animations.size()>0) {
|
||||
vfs_printf(h,"\t<Animations>\n");
|
||||
for (uint i=0;i<m_Animations.size();i++) {
|
||||
vfs_printf(h,"\t\t<Animation name=\"%s\" file=\"%s\" speed=\"%d\"/>\n",(const char*) m_Animations[i].m_AnimName,(const char*) m_Animations[i].m_FileName,int(m_Animations[i].m_Speed*100));
|
||||
if(m_Material.Trim(PS_TRIM_BOTH).Length())
|
||||
XML_Setting("Material", m_Material);
|
||||
|
||||
{
|
||||
XML_Element("Properties");
|
||||
XML_Attribute("autoflatten", m_Properties.m_AutoFlatten ? 1 : 0);
|
||||
XML_Attribute("castshadows", m_Properties.m_CastShadows ? 1 : 0);
|
||||
}
|
||||
vfs_printf(h,"\t</Animations>\n");
|
||||
}
|
||||
if (m_Props.size()>0) {
|
||||
vfs_printf(h,"\t<Props>\n");
|
||||
for (uint i=0;i<m_Props.size();i++) {
|
||||
vfs_printf(h,"\t\t<Prop attachpoint=\"%s\" model=\"%s\"/>\n",(const char*) m_Props[i].m_PropPointName,(const char*) m_Props[i].m_ModelName);
|
||||
|
||||
if (m_Animations.size()>0)
|
||||
{
|
||||
XML_Element("Animations");
|
||||
|
||||
for (uint i=0;i<m_Animations.size();i++)
|
||||
{
|
||||
XML_Element("Animation");
|
||||
XML_Attribute("name", m_Animations[i].m_AnimName);
|
||||
XML_Attribute("file", m_Animations[i].m_FileName);
|
||||
XML_Attribute("speed", int(m_Animations[i].m_Speed*100));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Props.size()>0)
|
||||
{
|
||||
XML_Element("Props");
|
||||
|
||||
for (uint i=0;i<m_Props.size();i++)
|
||||
{
|
||||
XML_Element("Prop");
|
||||
XML_Attribute("attachpoint", m_Props[i].m_PropPointName);
|
||||
XML_Attribute("model", m_Props[i].m_ModelName);
|
||||
}
|
||||
}
|
||||
vfs_printf(h,"\t</Props>\n");
|
||||
}
|
||||
|
||||
vfs_printf(h,"</Object>\n");
|
||||
if (! XML_StoreVFS(h))
|
||||
{
|
||||
// Error occurred while saving data
|
||||
debug_warn("actor save failed");
|
||||
vfs_close(h);
|
||||
return false;
|
||||
}
|
||||
|
||||
vfs_close(h);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "XMLWriter.h"
|
||||
|
||||
#include "ps/CLogger.h"
|
||||
#include "lib/res/vfs.h"
|
||||
|
||||
enum { EL_ATTR, EL_TEXT, EL_SUBEL };
|
||||
|
||||
XMLWriter_File::XMLWriter_File(const char* encoding)
|
||||
: m_LastElement(NULL), m_Indent(0)
|
||||
{
|
||||
m_Data = "<?xml version=\"1.0\" encoding=\"";
|
||||
m_Data += encoding;
|
||||
m_Data += "\" standalone=\"no\"?>\n";
|
||||
}
|
||||
|
||||
void XMLWriter_File::Doctype(const char* type, const char* dtd)
|
||||
{
|
||||
m_Data += "<!DOCTYPE ";
|
||||
m_Data += type;
|
||||
m_Data += " SYSTEM \"";
|
||||
m_Data += dtd;
|
||||
m_Data += "\">\n";
|
||||
}
|
||||
|
||||
bool XMLWriter_File::StoreVFS(Handle h)
|
||||
{
|
||||
if (m_LastElement) debug_warn("ERROR: Saving XML while an element is still open");
|
||||
|
||||
void* data = (void*)m_Data.data();
|
||||
int err = vfs_io(h, m_Data.Length(), &data);
|
||||
if (err < 0)
|
||||
{
|
||||
LOG(ERROR, "xml", "Error saving XML data through VFS: %lld", h);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void XMLWriter_File::Comment(const char* text)
|
||||
{
|
||||
ElementStart(NULL, "!-- ");
|
||||
m_Data += text;
|
||||
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;
|
||||
m_Data += "\n" + Indent() + "<" + 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:
|
||||
m_Data += " />";
|
||||
break;
|
||||
case EL_TEXT:
|
||||
m_Data += "</";
|
||||
m_Data += name;
|
||||
m_Data += ">";
|
||||
break;
|
||||
case EL_SUBEL:
|
||||
m_Data += "\n" + Indent() + "</" + name += ">";
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void XMLWriter_File::ElementText(const char* text)
|
||||
{
|
||||
m_Data += text;
|
||||
}
|
||||
|
||||
|
||||
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()
|
||||
{
|
||||
m_File->ElementEnd(m_Name, m_Type);
|
||||
}
|
||||
|
||||
|
||||
void XMLWriter_Element::Close(int type)
|
||||
{
|
||||
m_File->ElementClose();
|
||||
m_Type = type;
|
||||
}
|
||||
|
||||
void XMLWriter_Element::Text(const char* text)
|
||||
{
|
||||
Close(EL_TEXT);
|
||||
m_File->ElementText(text);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template <> void XMLWriter_File::ElementAttribute<CStr>(const char* name, CStr& value, bool newelement)
|
||||
{
|
||||
if (newelement)
|
||||
{
|
||||
ElementStart(NULL, name);
|
||||
m_Data += ">";
|
||||
ElementText(value);
|
||||
ElementEnd(name, EL_TEXT);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(m_LastElement && m_LastElement->m_Type == EL_ATTR);
|
||||
m_Data += " ";
|
||||
m_Data += name;
|
||||
m_Data += "=\"" + value + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
// Attribute/setting value-to-string template specialisations:
|
||||
|
||||
// Use CStr's conversion for most types:
|
||||
#define TYPE(T) \
|
||||
template <> void XMLWriter_File::ElementAttribute<T>(const char* name, T& value, bool newelement) \
|
||||
{ \
|
||||
ElementAttribute(name, CStr(value), newelement); \
|
||||
}
|
||||
|
||||
TYPE(int)
|
||||
TYPE(float)
|
||||
TYPE(double)
|
||||
TYPE(const char*)
|
||||
@@ -0,0 +1,132 @@
|
||||
#ifndef XMLWRITER_H
|
||||
#define XMLWRITER_H
|
||||
|
||||
/*
|
||||
|
||||
System for writing simple XML files, with human-readable formatting.
|
||||
|
||||
Example usage:
|
||||
|
||||
XML_Start("utf-8");
|
||||
XML_Doctype("Scenario", "/maps/scenario.dtd");
|
||||
|
||||
{
|
||||
XML_Element("Scenario");
|
||||
{
|
||||
XML_Element("Entities");
|
||||
for (...)
|
||||
{
|
||||
XML_Element("Entity");
|
||||
|
||||
{
|
||||
XML_Element("Template");
|
||||
XML_Text(entity.name);
|
||||
}
|
||||
// Or equivalently:
|
||||
XML_Setting("Template", entity.name);
|
||||
|
||||
{
|
||||
XML_Element("Position");
|
||||
XML_Attribute("x", entity.x);
|
||||
XML_Attribute("y", entity.y);
|
||||
XML_Attribute("z", entity.z);
|
||||
}
|
||||
|
||||
{
|
||||
XML_Element("Orientation");
|
||||
XML_Attribute("angle", entity.angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Handle h = vfs_open("/test.xml", FILE_WRITE|FILE_NO_AIO);
|
||||
XML_StoreVFS(h);
|
||||
|
||||
In general, "{ XML_Element(name); ... }" means "<name> ... </name>" -- the
|
||||
scoping braces are important to indicate where an element ends.
|
||||
|
||||
XML_Attribute/XML_Setting are templated. To support more types, alter the
|
||||
end of XMLWriter.cpp.
|
||||
|
||||
*/
|
||||
|
||||
// Encoding should usually be "iso-8859-1" or "utf-8"
|
||||
#define XML_Start(encoding) XMLWriter_File xml_file_ (encoding)
|
||||
|
||||
// Set the doctype/DTD (e.g. "Object", "/art/actors/object.dtd")
|
||||
#define XML_Doctype(type, dtd) xml_file_.Doctype(type, dtd)
|
||||
|
||||
// Add a comment to the XML file: <!-- text -->
|
||||
#define XML_Comment(text) xml_file_.Comment(text)
|
||||
|
||||
// Start a new element: <name ...>
|
||||
#define XML_Element(name) XMLWriter_Element xml_element_ (xml_file_, name)
|
||||
|
||||
// Add text to the interior of the current element: ...>text</...>
|
||||
#define XML_Text(text) xml_element_.Text(text)
|
||||
|
||||
// Add an attribute to the current element: <... name="value" ...>
|
||||
#define XML_Attribute(name, value) xml_element_.Attribute(name, value)
|
||||
|
||||
// Add a 'setting': <name>value</name>
|
||||
#define XML_Setting(name, value) xml_element_.Setting(name, value)
|
||||
|
||||
// Output the XML file to a VFS handle, which must be opened with FILE_WRITE|FILE_NO_AIO.
|
||||
// Returns true on success, false (and logs an error) on failure.
|
||||
#define XML_StoreVFS(handle) xml_file_.StoreVFS(handle)
|
||||
|
||||
|
||||
#include "ps/CStr.h"
|
||||
#include "lib/res/handle.h"
|
||||
|
||||
class XMLWriter_Element;
|
||||
|
||||
class XMLWriter_File
|
||||
{
|
||||
public:
|
||||
XMLWriter_File(const char* encoding);
|
||||
|
||||
void Doctype(const char* type, const char* dtd);
|
||||
void Comment(const char* text);
|
||||
|
||||
bool StoreVFS(Handle h);
|
||||
|
||||
private:
|
||||
|
||||
friend class XMLWriter_Element;
|
||||
|
||||
void ElementStart(XMLWriter_Element* element, const char* name);
|
||||
void ElementText(const char* text);
|
||||
template <typename T> void ElementAttribute(const char* name, T& value, bool newelement);
|
||||
void ElementClose();
|
||||
void ElementEnd(const char* name, int type);
|
||||
|
||||
CStr Indent();
|
||||
|
||||
CStr m_Data;
|
||||
int m_Indent;
|
||||
XMLWriter_Element* m_LastElement;
|
||||
};
|
||||
|
||||
class XMLWriter_Element
|
||||
{
|
||||
public:
|
||||
XMLWriter_Element(XMLWriter_File& file, const char* name);
|
||||
~XMLWriter_Element();
|
||||
|
||||
void Text(const char* text);
|
||||
template <typename T> void Attribute(const char* name, T value) { m_File->ElementAttribute(name, value, false); }
|
||||
template <typename T> void Setting(const char* name, T value) { m_File->ElementAttribute(name, value, true); }
|
||||
void Close(int type);
|
||||
|
||||
private:
|
||||
|
||||
friend class XMLWriter_File;
|
||||
|
||||
XMLWriter_File* m_File;
|
||||
CStr m_Name;
|
||||
int m_Type;
|
||||
};
|
||||
|
||||
#endif // XMLWRITER_H
|
||||
Reference in New Issue
Block a user