|
@@ -26,111 +26,126 @@
|
|
|
|
|
|
#include "console/console.h"
|
|
|
|
|
|
-bool VfsXMLDocument::LoadFile(const char* pFilename)
|
|
|
-{
|
|
|
- // Expand the file-path.
|
|
|
- char filenameBuffer[1024];
|
|
|
- Con::expandScriptFilename(filenameBuffer, sizeof(filenameBuffer), pFilename);
|
|
|
-
|
|
|
- FileStream stream;
|
|
|
-
|
|
|
-#ifdef TORQUE_OS_ANDROID
|
|
|
- if (strlen(pFilename) > strlen(filenameBuffer)) {
|
|
|
- dStrcpy(filenameBuffer, pFilename, 1024);
|
|
|
- }
|
|
|
-#endif
|
|
|
|
|
|
- // File open for read?
|
|
|
- if (!stream.open(filenameBuffer, Torque::FS::File::Read))
|
|
|
- {
|
|
|
- // No, so warn.
|
|
|
- Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for parse.", filenameBuffer);
|
|
|
- return false;
|
|
|
- }
|
|
|
+// Re-implement private functionality in TinyXML2
|
|
|
+
|
|
|
+static const char LINE_FEED = static_cast<char>(0x0a); // all line endings are normalized to LF
|
|
|
+static const char LF = LINE_FEED;
|
|
|
+static const char CARRIAGE_RETURN = static_cast<char>(0x0d); // CR gets filtered out
|
|
|
+static const char CR = CARRIAGE_RETURN;
|
|
|
+static const char SINGLE_QUOTE = '\'';
|
|
|
+static const char DOUBLE_QUOTE = '\"';
|
|
|
+
|
|
|
+struct Entity {
|
|
|
+ const char* pattern;
|
|
|
+ int length;
|
|
|
+ char value;
|
|
|
+};
|
|
|
+
|
|
|
+static const int NUM_ENTITIES = 5;
|
|
|
+static const Entity entities[NUM_ENTITIES] = {
|
|
|
+ { "quot", 4, DOUBLE_QUOTE },
|
|
|
+ { "amp", 3, '&' },
|
|
|
+ { "apos", 4, SINGLE_QUOTE },
|
|
|
+ { "lt", 2, '<' },
|
|
|
+ { "gt", 2, '>' }
|
|
|
+};
|
|
|
|
|
|
- // Load document from stream.
|
|
|
- if (!LoadFile(stream))
|
|
|
- {
|
|
|
- // Warn!
|
|
|
- Con::warnf("TamlXmlParser: Could not load Taml XML file from stream.");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // Close the stream.
|
|
|
- stream.close();
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-bool VfsXMLDocument::SaveFile(const char* pFilename)
|
|
|
+VfsXMLPrinter::VfsXMLPrinter(FileStream& stream, bool compact, int depth)
|
|
|
+ : XMLPrinter(NULL, compact, depth),
|
|
|
+ m_Stream(stream),
|
|
|
+ _depth(depth)
|
|
|
{
|
|
|
- // Expand the file-name into the file-path buffer.
|
|
|
- char filenameBuffer[1024];
|
|
|
- Con::expandScriptFilename(filenameBuffer, sizeof(filenameBuffer), pFilename);
|
|
|
-
|
|
|
- FileStream stream;
|
|
|
-
|
|
|
- // File opened?
|
|
|
- if (!stream.open(filenameBuffer, Torque::FS::File::Write))
|
|
|
- {
|
|
|
- // No, so warn.
|
|
|
- Con::warnf("Taml::writeFile() - Could not open filename '%s' for write.", filenameBuffer);
|
|
|
- return false;
|
|
|
+ for (int i = 0; i < ENTITY_RANGE; ++i) {
|
|
|
+ _entityFlag[i] = false;
|
|
|
+ _restrictedEntityFlag[i] = false;
|
|
|
}
|
|
|
-
|
|
|
- bool ret = SaveFile(stream);
|
|
|
-
|
|
|
- stream.close();
|
|
|
- return ret;
|
|
|
+ for (int i = 0; i < NUM_ENTITIES; ++i) {
|
|
|
+ const char entityValue = entities[i].value;
|
|
|
+ const unsigned char flagIndex = static_cast<unsigned char>(entityValue);
|
|
|
+ TIXMLASSERT(flagIndex < ENTITY_RANGE);
|
|
|
+ _entityFlag[flagIndex] = true;
|
|
|
+ }
|
|
|
+ _restrictedEntityFlag[static_cast<unsigned char>('&')] = true;
|
|
|
+ _restrictedEntityFlag[static_cast<unsigned char>('<')] = true;
|
|
|
+ _restrictedEntityFlag[static_cast<unsigned char>('>')] = true; // not required, but consistency is nice
|
|
|
}
|
|
|
|
|
|
-void VfsXMLDocument::ClearError()
|
|
|
+VfsXMLPrinter::~VfsXMLPrinter()
|
|
|
{
|
|
|
- _errorID = tinyxml2::XML_SUCCESS;
|
|
|
- _errorLineNum = 0;
|
|
|
- _errorStr.Reset();
|
|
|
-
|
|
|
- tinyxml2::XMLDocument::ClearError();
|
|
|
+ m_Stream.flush();
|
|
|
+ m_Stream.close();
|
|
|
}
|
|
|
|
|
|
-void VfsXMLDocument::SetError(tinyxml2::XMLError error, int lineNum, const char* format, ...)
|
|
|
+void VfsXMLPrinter::PrintString(const char* p, bool restricted)
|
|
|
{
|
|
|
- TIXMLASSERT(error >= 0 && error < tinyxml2::XML_ERROR_COUNT);
|
|
|
- _errorID = error;
|
|
|
- _errorLineNum = lineNum;
|
|
|
- _errorStr.Reset();
|
|
|
-
|
|
|
- const size_t BUFFER_SIZE = 1000;
|
|
|
- char* buffer = new char[BUFFER_SIZE];
|
|
|
-
|
|
|
- TIXMLASSERT(sizeof(error) <= sizeof(int));
|
|
|
- dSprintf(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
|
|
|
-
|
|
|
- if (format) {
|
|
|
- size_t len = strlen(buffer);
|
|
|
- dSprintf(buffer + len, BUFFER_SIZE - len, ": ");
|
|
|
- len = strlen(buffer);
|
|
|
-
|
|
|
- va_list va;
|
|
|
- va_start(va, format);
|
|
|
- dSprintf(buffer + len, BUFFER_SIZE - len, format, va);
|
|
|
- va_end(va);
|
|
|
+ // Look for runs of bytes between entities to print.
|
|
|
+ const char* q = p;
|
|
|
+
|
|
|
+ if (_processEntities) {
|
|
|
+ const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
|
|
|
+ while (*q) {
|
|
|
+ TIXMLASSERT(p <= q);
|
|
|
+ // Remember, char is sometimes signed. (How many times has that bitten me?)
|
|
|
+ if (*q > 0 && *q < ENTITY_RANGE) {
|
|
|
+ // Check for entities. If one is found, flush
|
|
|
+ // the stream up until the entity, write the
|
|
|
+ // entity, and keep looking.
|
|
|
+ if (flag[static_cast<unsigned char>(*q)]) {
|
|
|
+ while (p < q) {
|
|
|
+ const size_t delta = q - p;
|
|
|
+ const int toPrint = (INT_MAX < delta) ? INT_MAX : static_cast<int>(delta);
|
|
|
+ Write(p, toPrint);
|
|
|
+ p += toPrint;
|
|
|
+ }
|
|
|
+ bool entityPatternPrinted = false;
|
|
|
+ for (int i = 0; i < NUM_ENTITIES; ++i) {
|
|
|
+ if (entities[i].value == *q) {
|
|
|
+ Putc('&');
|
|
|
+ Write(entities[i].pattern, entities[i].length);
|
|
|
+ Putc(';');
|
|
|
+ entityPatternPrinted = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!entityPatternPrinted) {
|
|
|
+ // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
|
|
|
+ TIXMLASSERT(false);
|
|
|
+ }
|
|
|
+ ++p;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ++q;
|
|
|
+ TIXMLASSERT(p <= q);
|
|
|
+ }
|
|
|
+ // Flush the remaining string. This will be the entire
|
|
|
+ // string if an entity wasn't found.
|
|
|
+ if (p < q) {
|
|
|
+ const size_t delta = q - p;
|
|
|
+ const int toPrint = (INT_MAX < delta) ? INT_MAX : static_cast<int>(delta);
|
|
|
+ Write(p, toPrint);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ Write(p);
|
|
|
}
|
|
|
- _errorStr.SetStr(buffer);
|
|
|
- delete[] buffer;
|
|
|
}
|
|
|
|
|
|
-VfsXMLPrinter::VfsXMLPrinter(FileStream& stream, bool compact, int depth)
|
|
|
- : XMLPrinter(NULL, compact, depth),
|
|
|
- m_Stream(stream)
|
|
|
+bool VfsXMLPrinter::VisitEnter(const tinyxml2::XMLDocument& doc)
|
|
|
{
|
|
|
+ _processEntities = doc.ProcessEntities();
|
|
|
+ return XMLPrinter::VisitEnter(doc);
|
|
|
}
|
|
|
|
|
|
-VfsXMLPrinter::~VfsXMLPrinter()
|
|
|
+bool VfsXMLPrinter::VisitExit(const tinyxml2::XMLElement& element)
|
|
|
{
|
|
|
- m_Stream.flush();
|
|
|
- m_Stream.close();
|
|
|
+ _depth--;
|
|
|
+ return XMLPrinter::VisitExit(element);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+// Add VFS friendly implementations of output functions
|
|
|
+
|
|
|
void VfsXMLPrinter::Print(const char* format, ...)
|
|
|
{
|
|
|
va_list va;
|
|
@@ -151,6 +166,77 @@ void VfsXMLPrinter::Putc(char ch)
|
|
|
m_Stream.write(static_cast<U8>(ch));
|
|
|
}
|
|
|
|
|
|
+// Overwrite Visitation of elements to add newlines before attributes
|
|
|
+
|
|
|
+bool VfsXMLPrinter::VisitEnter(const tinyxml2::XMLElement& element, const tinyxml2::XMLAttribute* attribute)
|
|
|
+{
|
|
|
+ const tinyxml2::XMLElement* parentElem = 0;
|
|
|
+ if (element.Parent()) {
|
|
|
+ parentElem = element.Parent()->ToElement();
|
|
|
+ }
|
|
|
+ const bool compactMode = parentElem ? CompactMode(*parentElem) : CompactMode(element);
|
|
|
+ OpenElement(element.Name(), compactMode);
|
|
|
+ _depth++;
|
|
|
+ while (attribute) {
|
|
|
+ PushAttribute(attribute->Name(), attribute->Value(), compactMode);
|
|
|
+ attribute = attribute->Next();
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+void VfsXMLPrinter::PushAttribute(const char* name, const char* value, bool compactMode)
|
|
|
+{
|
|
|
+ TIXMLASSERT(_elementJustOpened);
|
|
|
+ if (compactMode)
|
|
|
+ {
|
|
|
+ Putc(' ');
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Putc('\n');
|
|
|
+ PrintSpace(_depth);
|
|
|
+ }
|
|
|
+ Write(name);
|
|
|
+ Write("=\"");
|
|
|
+ PrintString(value, false);
|
|
|
+ Putc('\"');
|
|
|
+}
|
|
|
+
|
|
|
+bool VfsXMLDocument::LoadFile(const char* pFilename)
|
|
|
+{
|
|
|
+ // Expand the file-path.
|
|
|
+ char filenameBuffer[1024];
|
|
|
+ Con::expandScriptFilename(filenameBuffer, sizeof(filenameBuffer), pFilename);
|
|
|
+
|
|
|
+ FileStream stream;
|
|
|
+
|
|
|
+#ifdef TORQUE_OS_ANDROID
|
|
|
+ if (strlen(pFilename) > strlen(filenameBuffer)) {
|
|
|
+ dStrcpy(filenameBuffer, pFilename, 1024);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ // File open for read?
|
|
|
+ if (!stream.open(filenameBuffer, Torque::FS::File::Read))
|
|
|
+ {
|
|
|
+ // No, so warn.
|
|
|
+ Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for parse.", filenameBuffer);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Load document from stream.
|
|
|
+ if (!LoadFile(stream))
|
|
|
+ {
|
|
|
+ // Warn!
|
|
|
+ Con::warnf("TamlXmlParser: Could not load Taml XML file from stream.");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Close the stream.
|
|
|
+ stream.close();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
bool VfsXMLDocument::LoadFile(FileStream& stream)
|
|
|
{
|
|
|
// Delete the existing data:
|
|
@@ -196,7 +282,7 @@ bool VfsXMLDocument::LoadFile(FileStream& stream)
|
|
|
|
|
|
if (!stream.read(length, buf))
|
|
|
{
|
|
|
- delete [] buf;
|
|
|
+ delete[] buf;
|
|
|
SetError(tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, 0);
|
|
|
return false;
|
|
|
}
|
|
@@ -220,8 +306,8 @@ bool VfsXMLDocument::LoadFile(FileStream& stream)
|
|
|
buf[length] = 0;
|
|
|
while (*p)
|
|
|
{
|
|
|
- assert(p < (buf+length));
|
|
|
- assert(q <= (buf+length));
|
|
|
+ assert(p < (buf + length));
|
|
|
+ assert(q <= (buf + length));
|
|
|
assert(q <= p);
|
|
|
|
|
|
if (*p == CR)
|
|
@@ -239,12 +325,12 @@ bool VfsXMLDocument::LoadFile(FileStream& stream)
|
|
|
*q++ = *p++;
|
|
|
}
|
|
|
}
|
|
|
- assert(q <= (buf+length));
|
|
|
+ assert(q <= (buf + length));
|
|
|
*q = 0;
|
|
|
|
|
|
Parse(buf, length);
|
|
|
|
|
|
- delete [] buf;
|
|
|
+ delete[] buf;
|
|
|
return !Error();
|
|
|
}
|
|
|
|
|
@@ -257,3 +343,61 @@ bool VfsXMLDocument::SaveFile(FileStream& stream)
|
|
|
Print(&printer);
|
|
|
return !Error();
|
|
|
}
|
|
|
+
|
|
|
+bool VfsXMLDocument::SaveFile(const char* pFilename)
|
|
|
+{
|
|
|
+ // Expand the file-name into the file-path buffer.
|
|
|
+ char filenameBuffer[1024];
|
|
|
+ Con::expandScriptFilename(filenameBuffer, sizeof(filenameBuffer), pFilename);
|
|
|
+
|
|
|
+ FileStream stream;
|
|
|
+
|
|
|
+ // File opened?
|
|
|
+ if (!stream.open(filenameBuffer, Torque::FS::File::Write))
|
|
|
+ {
|
|
|
+ // No, so warn.
|
|
|
+ Con::warnf("Taml::writeFile() - Could not open filename '%s' for write.", filenameBuffer);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool ret = SaveFile(stream);
|
|
|
+
|
|
|
+ stream.close();
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+void VfsXMLDocument::ClearError()
|
|
|
+{
|
|
|
+ _errorID = tinyxml2::XML_SUCCESS;
|
|
|
+ _errorLineNum = 0;
|
|
|
+ _errorStr.Reset();
|
|
|
+
|
|
|
+ tinyxml2::XMLDocument::ClearError();
|
|
|
+}
|
|
|
+
|
|
|
+void VfsXMLDocument::SetError(tinyxml2::XMLError error, int lineNum, const char* format, ...)
|
|
|
+{
|
|
|
+ TIXMLASSERT(error >= 0 && error < tinyxml2::XML_ERROR_COUNT);
|
|
|
+ _errorID = error;
|
|
|
+ _errorLineNum = lineNum;
|
|
|
+ _errorStr.Reset();
|
|
|
+
|
|
|
+ const size_t BUFFER_SIZE = 1000;
|
|
|
+ char* buffer = new char[BUFFER_SIZE];
|
|
|
+
|
|
|
+ TIXMLASSERT(sizeof(error) <= sizeof(int));
|
|
|
+ dSprintf(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
|
|
|
+
|
|
|
+ if (format) {
|
|
|
+ size_t len = strlen(buffer);
|
|
|
+ dSprintf(buffer + len, BUFFER_SIZE - len, ": ");
|
|
|
+ len = strlen(buffer);
|
|
|
+
|
|
|
+ va_list va;
|
|
|
+ va_start(va, format);
|
|
|
+ dSprintf(buffer + len, BUFFER_SIZE - len, format, va);
|
|
|
+ va_end(va);
|
|
|
+ }
|
|
|
+ _errorStr.SetStr(buffer);
|
|
|
+ delete[] buffer;
|
|
|
+}
|