diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp
index 4499be3071..d73a1363d0 100755
--- a/source/graphics/MapReader.cpp
+++ b/source/graphics/MapReader.cpp
@@ -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 );
diff --git a/source/graphics/ObjectEntry.cpp b/source/graphics/ObjectEntry.cpp
index df561b9649..aa926d60a1 100755
--- a/source/graphics/ObjectEntry.cpp
+++ b/source/graphics/ObjectEntry.cpp
@@ -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,"\n\n");
- fprintf(fp,"\n\n");
-
- // write the object itself
- fprintf(fp,"\n");
- fprintf(fp,"\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,"\n\n");
- vfs_printf(h,"\n\n");
+ XML_Start("iso-8859-1");
+ XML_Doctype("Object", "/art/actors/object.dtd");
- // write the object itself
- vfs_printf(h,"\n");
- vfs_printf(h,"\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;
}
diff --git a/source/ps/XMLWriter.cpp b/source/ps/XMLWriter.cpp
new file mode 100644
index 0000000000..2d5654a69c
--- /dev/null
+++ b/source/ps/XMLWriter.cpp
@@ -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 = "\n";
+}
+
+void XMLWriter_File::Doctype(const char* type, const char* 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(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(const char* name, T& value, bool newelement) \
+{ \
+ ElementAttribute(name, CStr(value), newelement); \
+}
+
+TYPE(int)
+TYPE(float)
+TYPE(double)
+TYPE(const char*)
\ No newline at end of file
diff --git a/source/ps/XMLWriter.h b/source/ps/XMLWriter.h
new file mode 100644
index 0000000000..9c7a27fb68
--- /dev/null
+++ b/source/ps/XMLWriter.h
@@ -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 " ... " -- 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:
+#define XML_Comment(text) xml_file_.Comment(text)
+
+// Start a new element:
+#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': value
+#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 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 void Attribute(const char* name, T value) { m_File->ElementAttribute(name, value, false); }
+ template 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
\ No newline at end of file