fsTinyXml.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "fsTinyXml.h"
  23. #include <cassert>
  24. #include "console/console.h"
  25. bool VfsXMLDocument::LoadFile(const char* pFilename)
  26. {
  27. // Expand the file-path.
  28. char filenameBuffer[1024];
  29. Con::expandScriptFilename(filenameBuffer, sizeof(filenameBuffer), pFilename);
  30. FileStream stream;
  31. #ifdef TORQUE_OS_ANDROID
  32. if (strlen(pFilename) > strlen(filenameBuffer)) {
  33. dStrcpy(filenameBuffer, pFilename, 1024);
  34. }
  35. #endif
  36. // File open for read?
  37. if (!stream.open(filenameBuffer, Torque::FS::File::Read))
  38. {
  39. // No, so warn.
  40. Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for parse.", filenameBuffer);
  41. return false;
  42. }
  43. // Load document from stream.
  44. if (!LoadFile(stream))
  45. {
  46. // Warn!
  47. Con::warnf("TamlXmlParser: Could not load Taml XML file from stream.");
  48. return false;
  49. }
  50. // Close the stream.
  51. stream.close();
  52. return true;
  53. }
  54. bool VfsXMLDocument::SaveFile(const char* pFilename)
  55. {
  56. // Expand the file-name into the file-path buffer.
  57. char filenameBuffer[1024];
  58. Con::expandScriptFilename(filenameBuffer, sizeof(filenameBuffer), pFilename);
  59. FileStream stream;
  60. // File opened?
  61. if (!stream.open(filenameBuffer, Torque::FS::File::Write))
  62. {
  63. // No, so warn.
  64. Con::warnf("Taml::writeFile() - Could not open filename '%s' for write.", filenameBuffer);
  65. return false;
  66. }
  67. bool ret = SaveFile(stream);
  68. stream.close();
  69. return ret;
  70. }
  71. void VfsXMLDocument::ClearError()
  72. {
  73. _errorID = tinyxml2::XML_SUCCESS;
  74. _errorLineNum = 0;
  75. _errorStr.Reset();
  76. tinyxml2::XMLDocument::ClearError();
  77. }
  78. void VfsXMLDocument::SetError(tinyxml2::XMLError error, int lineNum, const char* format, ...)
  79. {
  80. TIXMLASSERT(error >= 0 && error < tinyxml2::XML_ERROR_COUNT);
  81. _errorID = error;
  82. _errorLineNum = lineNum;
  83. _errorStr.Reset();
  84. const size_t BUFFER_SIZE = 1000;
  85. char* buffer = new char[BUFFER_SIZE];
  86. TIXMLASSERT(sizeof(error) <= sizeof(int));
  87. dSprintf(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum);
  88. if (format) {
  89. size_t len = strlen(buffer);
  90. dSprintf(buffer + len, BUFFER_SIZE - len, ": ");
  91. len = strlen(buffer);
  92. va_list va;
  93. va_start(va, format);
  94. dSprintf(buffer + len, BUFFER_SIZE - len, format, va);
  95. va_end(va);
  96. }
  97. _errorStr.SetStr(buffer);
  98. delete[] buffer;
  99. }
  100. VfsXMLPrinter::VfsXMLPrinter(FileStream& stream, bool compact, int depth)
  101. : XMLPrinter(NULL, compact, depth),
  102. m_Stream(stream)
  103. {
  104. }
  105. VfsXMLPrinter::~VfsXMLPrinter()
  106. {
  107. m_Stream.flush();
  108. m_Stream.close();
  109. }
  110. void VfsXMLPrinter::Print(const char* format, ...)
  111. {
  112. va_list va;
  113. va_start(va, format);
  114. m_Stream.writeFormattedBuffer(format, va);
  115. va_end(va);
  116. }
  117. void VfsXMLPrinter::Write(const char* data, size_t size)
  118. {
  119. m_Stream.write(size, data);
  120. }
  121. void VfsXMLPrinter::Putc(char ch)
  122. {
  123. m_Stream.write(static_cast<U8>(ch));
  124. }
  125. bool VfsXMLDocument::LoadFile(FileStream& stream)
  126. {
  127. // Delete the existing data:
  128. Clear();
  129. // Clear shadowed error
  130. ClearError();
  131. //TODO: Can't clear location, investigate if this gives issues.
  132. //doc.location.Clear();
  133. // Get the file size, so we can pre-allocate the string. HUGE speed impact.
  134. long length = stream.getStreamSize();
  135. // Strange case, but good to handle up front.
  136. if (length <= 0)
  137. {
  138. SetError(tinyxml2::XML_ERROR_EMPTY_DOCUMENT, 0, 0);
  139. return false;
  140. }
  141. // Subtle bug here. TinyXml did use fgets. But from the XML spec:
  142. // 2.11 End-of-Line Handling
  143. // <snip>
  144. // <quote>
  145. // ...the XML processor MUST behave as if it normalized all line breaks in external
  146. // parsed entities (including the document entity) on input, before parsing, by translating
  147. // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
  148. // a single #xA character.
  149. // </quote>
  150. //
  151. // It is not clear fgets does that, and certainly isn't clear it works cross platform.
  152. // Generally, you expect fgets to translate from the convention of the OS to the c/unix
  153. // convention, and not work generally.
  154. /*
  155. while( fgets( buf, sizeof(buf), file ) )
  156. {
  157. data += buf;
  158. }
  159. */
  160. char* buf = new char[length + 1];
  161. buf[0] = 0;
  162. if (!stream.read(length, buf))
  163. {
  164. delete [] buf;
  165. SetError(tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, 0);
  166. return false;
  167. }
  168. // Process the buffer in place to normalize new lines. (See comment above.)
  169. // Copies from the 'p' to 'q' pointer, where p can advance faster if
  170. // a newline-carriage return is hit.
  171. //
  172. // Wikipedia:
  173. // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
  174. // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
  175. // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
  176. // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS
  177. // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
  178. const char* p = buf; // the read head
  179. char* q = buf; // the write head
  180. const char CR = 0x0d;
  181. const char LF = 0x0a;
  182. buf[length] = 0;
  183. while (*p)
  184. {
  185. assert(p < (buf+length));
  186. assert(q <= (buf+length));
  187. assert(q <= p);
  188. if (*p == CR)
  189. {
  190. *q++ = LF;
  191. p++;
  192. if (*p == LF)
  193. {
  194. // check for CR+LF (and skip LF)
  195. p++;
  196. }
  197. }
  198. else
  199. {
  200. *q++ = *p++;
  201. }
  202. }
  203. assert(q <= (buf+length));
  204. *q = 0;
  205. Parse(buf, length);
  206. delete [] buf;
  207. return !Error();
  208. }
  209. bool VfsXMLDocument::SaveFile(FileStream& stream)
  210. {
  211. // Clear any error from the last save, otherwise it will get reported
  212. // for *this* call.
  213. ClearError();
  214. VfsXMLPrinter printer(stream, false, 0);
  215. Print(&printer);
  216. return !Error();
  217. }