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"); - fprintf(fp,"\t%s\n",(const char*) m_Name); - fprintf(fp,"\t%s\n",(const char*) m_ModelName); - fprintf(fp,"\t%s\n",(const char*) m_TextureName); - if(m_Material.Trim(PS_TRIM_BOTH).Length()) - fprintf(fp,"\t%s\n", (const char*)m_Material); - - fprintf(fp,"\t\n"); - if (m_Animations.size()>0) { - fprintf(fp,"\t\n"); - for (uint i=0;i\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\n"); - } - if (m_Props.size()>0) { - fprintf(fp,"\t\n"); - for (uint i=0;i\n",(const char*) m_Props[i].m_PropPointName,(const char*) m_Props[i].m_ModelName); - } - fprintf(fp,"\t\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"); - vfs_printf(h,"\t%s\n",(const char*) m_Name); - vfs_printf(h,"\t%s\n",(const char*) m_ModelName); - vfs_printf(h,"\t%s\n",(const char*) m_TextureName); + XML_Comment("File automatically generated by ScEd"); - if(m_Material.Trim(PS_TRIM_BOTH).Length()) - vfs_printf(h,"\t%s\n", (const char*)m_Material); + { + XML_Element("Object"); - 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\n"); - for (uint i=0;i\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\n"); - } - if (m_Props.size()>0) { - vfs_printf(h,"\t\n"); - for (uint i=0;i\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;i0) + { + XML_Element("Props"); + + for (uint i=0;i\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 += ""; + break; + case EL_SUBEL: + m_Data += "\n" + Indent() + ""; + 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