mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-20 20:05:33 +00:00
Initial Implementation of the Taml, Asset and Modules systems.
Only has example and shape assets currently.
This commit is contained in:
parent
2044b2691e
commit
7a3b40a86d
123 changed files with 30435 additions and 181 deletions
111
Engine/source/persistence/_tinyXML/tinystr.cpp
Normal file
111
Engine/source/persistence/_tinyXML/tinystr.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
www.sourceforge.net/projects/tinyxml
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TIXML_USE_STL
|
||||
|
||||
#include "tinystr.h"
|
||||
|
||||
// Error value for find primitive
|
||||
const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1);
|
||||
|
||||
|
||||
// Null rep.
|
||||
TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
|
||||
|
||||
|
||||
void TiXmlString::reserve (size_type cap)
|
||||
{
|
||||
if (cap > capacity())
|
||||
{
|
||||
TiXmlString tmp;
|
||||
tmp.init(length(), cap);
|
||||
memcpy(tmp.start(), data(), length());
|
||||
swap(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TiXmlString& TiXmlString::assign(const char* str, size_type len)
|
||||
{
|
||||
size_type cap = capacity();
|
||||
if (len > cap || cap > 3*(len + 8))
|
||||
{
|
||||
TiXmlString tmp;
|
||||
tmp.init(len);
|
||||
memcpy(tmp.start(), str, len);
|
||||
swap(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(start(), str, len);
|
||||
set_size(len);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
TiXmlString& TiXmlString::append(const char* str, size_type len)
|
||||
{
|
||||
size_type newsize = length() + len;
|
||||
if (newsize > capacity())
|
||||
{
|
||||
reserve (newsize + capacity());
|
||||
}
|
||||
memmove(finish(), str, len);
|
||||
set_size(newsize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
TiXmlString operator + (const TiXmlString & a, const TiXmlString & b)
|
||||
{
|
||||
TiXmlString tmp;
|
||||
tmp.reserve(a.length() + b.length());
|
||||
tmp += a;
|
||||
tmp += b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
TiXmlString operator + (const TiXmlString & a, const char* b)
|
||||
{
|
||||
TiXmlString tmp;
|
||||
TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) );
|
||||
tmp.reserve(a.length() + b_len);
|
||||
tmp += a;
|
||||
tmp.append(b, b_len);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
TiXmlString operator + (const char* a, const TiXmlString & b)
|
||||
{
|
||||
TiXmlString tmp;
|
||||
TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) );
|
||||
tmp.reserve(a_len + b.length());
|
||||
tmp.append(a, a_len);
|
||||
tmp += b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
#endif // TIXML_USE_STL
|
||||
305
Engine/source/persistence/_tinyXML/tinystr.h
Normal file
305
Engine/source/persistence/_tinyXML/tinystr.h
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
www.sourceforge.net/projects/tinyxml
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TIXML_USE_STL
|
||||
|
||||
#ifndef TIXML_STRING_INCLUDED
|
||||
#define TIXML_STRING_INCLUDED
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/* The support for explicit isn't that universal, and it isn't really
|
||||
required - it is used to check that the TiXmlString class isn't incorrectly
|
||||
used. Be nice to old compilers and macro it here:
|
||||
*/
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200 )
|
||||
// Microsoft visual studio, version 6 and higher.
|
||||
#define TIXML_EXPLICIT explicit
|
||||
#elif defined(__GNUC__) && (__GNUC__ >= 3 )
|
||||
// GCC version 3 and higher.s
|
||||
#define TIXML_EXPLICIT explicit
|
||||
#else
|
||||
#define TIXML_EXPLICIT
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
TiXmlString is an emulation of a subset of the std::string template.
|
||||
Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
|
||||
Only the member functions relevant to the TinyXML project have been implemented.
|
||||
The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
|
||||
a string and there's no more room, we allocate a buffer twice as big as we need.
|
||||
*/
|
||||
class TiXmlString
|
||||
{
|
||||
public :
|
||||
// The size type used
|
||||
typedef size_t size_type;
|
||||
|
||||
// Error value for find primitive
|
||||
static const size_type npos; // = -1;
|
||||
|
||||
|
||||
// TiXmlString empty constructor
|
||||
TiXmlString () : rep_(&nullrep_)
|
||||
{
|
||||
}
|
||||
|
||||
// TiXmlString copy constructor
|
||||
TiXmlString ( const TiXmlString & copy) : rep_(0)
|
||||
{
|
||||
init(copy.length());
|
||||
memcpy(start(), copy.data(), length());
|
||||
}
|
||||
|
||||
// TiXmlString constructor, based on a string
|
||||
TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0)
|
||||
{
|
||||
init( static_cast<size_type>( strlen(copy) ));
|
||||
memcpy(start(), copy, length());
|
||||
}
|
||||
|
||||
// TiXmlString constructor, based on a string
|
||||
TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0)
|
||||
{
|
||||
init(len);
|
||||
memcpy(start(), str, len);
|
||||
}
|
||||
|
||||
// TiXmlString destructor
|
||||
~TiXmlString ()
|
||||
{
|
||||
quit();
|
||||
}
|
||||
|
||||
TiXmlString& operator = (const char * copy)
|
||||
{
|
||||
return assign( copy, (size_type)strlen(copy));
|
||||
}
|
||||
|
||||
TiXmlString& operator = (const TiXmlString & copy)
|
||||
{
|
||||
return assign(copy.start(), copy.length());
|
||||
}
|
||||
|
||||
|
||||
// += operator. Maps to append
|
||||
TiXmlString& operator += (const char * suffix)
|
||||
{
|
||||
return append(suffix, static_cast<size_type>( strlen(suffix) ));
|
||||
}
|
||||
|
||||
// += operator. Maps to append
|
||||
TiXmlString& operator += (char single)
|
||||
{
|
||||
return append(&single, 1);
|
||||
}
|
||||
|
||||
// += operator. Maps to append
|
||||
TiXmlString& operator += (const TiXmlString & suffix)
|
||||
{
|
||||
return append(suffix.data(), suffix.length());
|
||||
}
|
||||
|
||||
|
||||
// Convert a TiXmlString into a null-terminated char *
|
||||
const char * c_str () const { return rep_->str; }
|
||||
|
||||
// Convert a TiXmlString into a char * (need not be null terminated).
|
||||
const char * data () const { return rep_->str; }
|
||||
|
||||
// Return the length of a TiXmlString
|
||||
size_type length () const { return rep_->size; }
|
||||
|
||||
// Alias for length()
|
||||
size_type size () const { return rep_->size; }
|
||||
|
||||
// Checks if a TiXmlString is empty
|
||||
bool empty () const { return rep_->size == 0; }
|
||||
|
||||
// Return capacity of string
|
||||
size_type capacity () const { return rep_->capacity; }
|
||||
|
||||
|
||||
// single char extraction
|
||||
const char& at (size_type index) const
|
||||
{
|
||||
assert( index < length() );
|
||||
return rep_->str[ index ];
|
||||
}
|
||||
|
||||
// [] operator
|
||||
char& operator [] (size_type index) const
|
||||
{
|
||||
assert( index < length() );
|
||||
return rep_->str[ index ];
|
||||
}
|
||||
|
||||
// find a char in a string. Return TiXmlString::npos if not found
|
||||
size_type find (char lookup) const
|
||||
{
|
||||
return find(lookup, 0);
|
||||
}
|
||||
|
||||
// find a char in a string from an offset. Return TiXmlString::npos if not found
|
||||
size_type find (char tofind, size_type offset) const
|
||||
{
|
||||
if (offset >= length()) return npos;
|
||||
|
||||
for (const char* p = c_str() + offset; *p != '\0'; ++p)
|
||||
{
|
||||
if (*p == tofind) return static_cast< size_type >( p - c_str() );
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
//Lee:
|
||||
//The original was just too strange, though correct:
|
||||
// TiXmlString().swap(*this);
|
||||
//Instead use the quit & re-init:
|
||||
quit();
|
||||
init(0,0);
|
||||
}
|
||||
|
||||
/* Function to reserve a big amount of data when we know we'll need it. Be aware that this
|
||||
function DOES NOT clear the content of the TiXmlString if any exists.
|
||||
*/
|
||||
void reserve (size_type cap);
|
||||
|
||||
TiXmlString& assign (const char* str, size_type len);
|
||||
|
||||
TiXmlString& append (const char* str, size_type len);
|
||||
|
||||
void swap (TiXmlString& other)
|
||||
{
|
||||
Rep* r = rep_;
|
||||
rep_ = other.rep_;
|
||||
other.rep_ = r;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void init(size_type sz) { init(sz, sz); }
|
||||
void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; }
|
||||
char* start() const { return rep_->str; }
|
||||
char* finish() const { return rep_->str + rep_->size; }
|
||||
|
||||
struct Rep
|
||||
{
|
||||
size_type size, capacity;
|
||||
char str[1];
|
||||
};
|
||||
|
||||
void init(size_type sz, size_type cap)
|
||||
{
|
||||
if (cap)
|
||||
{
|
||||
// Lee: the original form:
|
||||
// rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
|
||||
// doesn't work in some cases of new being overloaded. Switching
|
||||
// to the normal allocation, although use an 'int' for systems
|
||||
// that are overly picky about structure alignment.
|
||||
const size_type bytesNeeded = sizeof(Rep) + cap;
|
||||
const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int );
|
||||
rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] );
|
||||
|
||||
rep_->str[ rep_->size = sz ] = '\0';
|
||||
rep_->capacity = cap;
|
||||
}
|
||||
else
|
||||
{
|
||||
rep_ = &nullrep_;
|
||||
}
|
||||
}
|
||||
|
||||
void quit()
|
||||
{
|
||||
if (rep_ != &nullrep_)
|
||||
{
|
||||
// The rep_ is really an array of ints. (see the allocator, above).
|
||||
// Cast it back before delete, so the compiler won't incorrectly call destructors.
|
||||
delete [] ( reinterpret_cast<int*>( rep_ ) );
|
||||
}
|
||||
}
|
||||
|
||||
Rep * rep_;
|
||||
static Rep nullrep_;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
inline bool operator == (const TiXmlString & a, const TiXmlString & b)
|
||||
{
|
||||
return ( a.length() == b.length() ) // optimization on some platforms
|
||||
&& ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare
|
||||
}
|
||||
inline bool operator < (const TiXmlString & a, const TiXmlString & b)
|
||||
{
|
||||
return strcmp(a.c_str(), b.c_str()) < 0;
|
||||
}
|
||||
|
||||
inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }
|
||||
inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; }
|
||||
inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }
|
||||
inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }
|
||||
|
||||
inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }
|
||||
inline bool operator == (const char* a, const TiXmlString & b) { return b == a; }
|
||||
inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }
|
||||
inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }
|
||||
|
||||
TiXmlString operator + (const TiXmlString & a, const TiXmlString & b);
|
||||
TiXmlString operator + (const TiXmlString & a, const char* b);
|
||||
TiXmlString operator + (const char* a, const TiXmlString & b);
|
||||
|
||||
|
||||
/*
|
||||
TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
|
||||
Only the operators that we need for TinyXML have been developped.
|
||||
*/
|
||||
class TiXmlOutStream : public TiXmlString
|
||||
{
|
||||
public :
|
||||
|
||||
// TiXmlOutStream << operator.
|
||||
TiXmlOutStream & operator << (const TiXmlString & in)
|
||||
{
|
||||
*this += in;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TiXmlOutStream << operator.
|
||||
TiXmlOutStream & operator << (const char * in)
|
||||
{
|
||||
*this += in;
|
||||
return *this;
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
#endif // TIXML_STRING_INCLUDED
|
||||
#endif // TIXML_USE_STL
|
||||
1725
Engine/source/persistence/_tinyXML/tinyxml.cpp
Normal file
1725
Engine/source/persistence/_tinyXML/tinyxml.cpp
Normal file
File diff suppressed because it is too large
Load diff
1725
Engine/source/persistence/_tinyXML/tinyxml.h
Normal file
1725
Engine/source/persistence/_tinyXML/tinyxml.h
Normal file
File diff suppressed because it is too large
Load diff
52
Engine/source/persistence/_tinyXML/tinyxmlerror.cpp
Normal file
52
Engine/source/persistence/_tinyXML/tinyxmlerror.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
www.sourceforge.net/projects/tinyxml
|
||||
Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#include "tinyxml.h"
|
||||
|
||||
// The goal of the seperate error file is to make the first
|
||||
// step towards localization. tinyxml (currently) only supports
|
||||
// english error messages, but the could now be translated.
|
||||
//
|
||||
// It also cleans up the code a bit.
|
||||
//
|
||||
|
||||
const char* TiXmlBase::errorString[ TiXmlBase::TIXML_ERROR_STRING_COUNT ] =
|
||||
{
|
||||
"No error",
|
||||
"Error",
|
||||
"Failed to open file",
|
||||
"Error parsing Element.",
|
||||
"Failed to read Element name",
|
||||
"Error reading Element value.",
|
||||
"Error reading Attributes.",
|
||||
"Error: empty tag.",
|
||||
"Error reading end tag.",
|
||||
"Error parsing Unknown.",
|
||||
"Error parsing Comment.",
|
||||
"Error parsing Declaration.",
|
||||
"Error document empty.",
|
||||
"Error null (0) or unexpected EOF found in input stream.",
|
||||
"Error parsing CDATA.",
|
||||
"Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
|
||||
};
|
||||
1638
Engine/source/persistence/_tinyXML/tinyxmlparser.cpp
Normal file
1638
Engine/source/persistence/_tinyXML/tinyxmlparser.cpp
Normal file
File diff suppressed because it is too large
Load diff
221
Engine/source/persistence/rapidjson/allocators.h
Normal file
221
Engine/source/persistence/rapidjson/allocators.h
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
#ifndef RAPIDJSON_ALLOCATORS_H_
|
||||
#define RAPIDJSON_ALLOCATORS_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Allocator
|
||||
|
||||
/*! \class rapidjson::Allocator
|
||||
\brief Concept for allocating, resizing and freeing memory block.
|
||||
|
||||
Note that Malloc() and Realloc() are non-static but Free() is static.
|
||||
|
||||
So if an allocator need to support Free(), it needs to put its pointer in
|
||||
the header of memory block.
|
||||
|
||||
\code
|
||||
concept Allocator {
|
||||
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
|
||||
|
||||
// Allocate a memory block.
|
||||
// \param size of the memory block in bytes.
|
||||
// \returns pointer to the memory block.
|
||||
void* Malloc(size_t size);
|
||||
|
||||
// Resize a memory block.
|
||||
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
|
||||
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
|
||||
// \param newSize the new size in bytes.
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
|
||||
|
||||
// Free a memory block.
|
||||
// \param pointer to the memory block. Null pointer is permitted.
|
||||
static void Free(void *ptr);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CrtAllocator
|
||||
|
||||
//! C-runtime library allocator.
|
||||
/*! This class is just wrapper for standard C library memory routines.
|
||||
\implements Allocator
|
||||
*/
|
||||
class CrtAllocator {
|
||||
public:
|
||||
static const bool kNeedFree = true;
|
||||
void* Malloc(size_t size) { return malloc(size); }
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; return realloc(originalPtr, newSize); }
|
||||
static void Free(void *ptr) { free(ptr); }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MemoryPoolAllocator
|
||||
|
||||
//! Default memory allocator used by the parser and DOM.
|
||||
/*! This allocator allocate memory blocks from pre-allocated memory chunks.
|
||||
|
||||
It does not free memory blocks. And Realloc() only allocate new memory.
|
||||
|
||||
The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
|
||||
|
||||
User may also supply a buffer as the first chunk.
|
||||
|
||||
If the user-buffer is full then additional chunks are allocated by BaseAllocator.
|
||||
|
||||
The user-buffer is not deallocated by this allocator.
|
||||
|
||||
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
|
||||
\implements Allocator
|
||||
*/
|
||||
template <typename BaseAllocator = CrtAllocator>
|
||||
class MemoryPoolAllocator {
|
||||
public:
|
||||
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
|
||||
|
||||
//! Constructor with chunkSize.
|
||||
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||
\param baseAllocator The allocator for allocating memory chunks.
|
||||
*/
|
||||
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
||||
{
|
||||
if (!baseAllocator_)
|
||||
ownBaseAllocator_ = baseAllocator_ = new BaseAllocator();
|
||||
AddChunk(chunk_capacity_);
|
||||
}
|
||||
|
||||
//! Constructor with user-supplied buffer.
|
||||
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
|
||||
|
||||
The user buffer will not be deallocated when this allocator is destructed.
|
||||
|
||||
\param buffer User supplied buffer.
|
||||
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
|
||||
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||
\param baseAllocator The allocator for allocating memory chunks.
|
||||
*/
|
||||
MemoryPoolAllocator(char *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
||||
{
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
|
||||
chunkHead_ = (ChunkHeader*)buffer;
|
||||
chunkHead_->capacity = size - sizeof(ChunkHeader);
|
||||
chunkHead_->size = 0;
|
||||
chunkHead_->next = 0;
|
||||
}
|
||||
|
||||
//! Destructor.
|
||||
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
|
||||
*/
|
||||
~MemoryPoolAllocator() {
|
||||
Clear();
|
||||
delete ownBaseAllocator_;
|
||||
}
|
||||
|
||||
//! Deallocates all memory chunks, excluding the user-supplied buffer.
|
||||
void Clear() {
|
||||
while(chunkHead_ != 0 && chunkHead_ != (ChunkHeader *)userBuffer_) {
|
||||
ChunkHeader* next = chunkHead_->next;
|
||||
baseAllocator_->Free(chunkHead_);
|
||||
chunkHead_ = next;
|
||||
}
|
||||
}
|
||||
|
||||
//! Computes the total capacity of allocated memory chunks.
|
||||
/*! \return total capacity in bytes.
|
||||
*/
|
||||
size_t Capacity() {
|
||||
size_t capacity = 0;
|
||||
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
||||
capacity += c->capacity;
|
||||
return capacity;
|
||||
}
|
||||
|
||||
//! Computes the memory blocks allocated.
|
||||
/*! \return total used bytes.
|
||||
*/
|
||||
size_t Size() {
|
||||
size_t size = 0;
|
||||
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
||||
size += c->size;
|
||||
return size;
|
||||
}
|
||||
|
||||
//! Allocates a memory block. (concept Allocator)
|
||||
void* Malloc(size_t size) {
|
||||
size = RAPIDJSON_ALIGN(size);
|
||||
if (chunkHead_->size + size > chunkHead_->capacity)
|
||||
AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size);
|
||||
|
||||
char *buffer = (char *)(chunkHead_ + 1) + chunkHead_->size;
|
||||
chunkHead_->size += size;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//! Resizes a memory block (concept Allocator)
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
|
||||
if (originalPtr == 0)
|
||||
return Malloc(newSize);
|
||||
|
||||
// Do not shrink if new size is smaller than original
|
||||
if (originalSize >= newSize)
|
||||
return originalPtr;
|
||||
|
||||
// Simply expand it if it is the last allocation and there is sufficient space
|
||||
if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) {
|
||||
size_t increment = newSize - originalSize;
|
||||
increment = RAPIDJSON_ALIGN(increment);
|
||||
if (chunkHead_->size + increment <= chunkHead_->capacity) {
|
||||
chunkHead_->size += increment;
|
||||
return originalPtr;
|
||||
}
|
||||
}
|
||||
|
||||
// Realloc process: allocate and copy memory, do not free original buffer.
|
||||
void* newBuffer = Malloc(newSize);
|
||||
RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly.
|
||||
return memcpy(newBuffer, originalPtr, originalSize);
|
||||
}
|
||||
|
||||
//! Frees a memory block (concept Allocator)
|
||||
static void Free(void *ptr) { (void)ptr; } // Do nothing
|
||||
|
||||
private:
|
||||
//! Creates a new chunk.
|
||||
/*! \param capacity Capacity of the chunk in bytes.
|
||||
*/
|
||||
void AddChunk(size_t capacity) {
|
||||
ChunkHeader* chunk = (ChunkHeader*)baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity);
|
||||
chunk->capacity = capacity;
|
||||
chunk->size = 0;
|
||||
chunk->next = chunkHead_;
|
||||
chunkHead_ = chunk;
|
||||
}
|
||||
|
||||
static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity.
|
||||
|
||||
//! Chunk header for perpending to each chunk.
|
||||
/*! Chunks are stored as a singly linked list.
|
||||
*/
|
||||
struct ChunkHeader {
|
||||
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
|
||||
size_t size; //!< Current size of allocated memory in bytes.
|
||||
ChunkHeader *next; //!< Next chunk in the linked list.
|
||||
};
|
||||
|
||||
ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
|
||||
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
|
||||
char *userBuffer_; //!< User supplied buffer.
|
||||
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
|
||||
BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
|
||||
};
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_ENCODINGS_H_
|
||||
843
Engine/source/persistence/rapidjson/document.h
Normal file
843
Engine/source/persistence/rapidjson/document.h
Normal file
|
|
@ -0,0 +1,843 @@
|
|||
#ifndef RAPIDJSON_DOCUMENT_H_
|
||||
#define RAPIDJSON_DOCUMENT_H_
|
||||
|
||||
#include "reader.h"
|
||||
#include "internal/strfunc.h"
|
||||
#include <new> // placement new
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GenericValue
|
||||
|
||||
//! Represents a JSON value. Use Value for UTF8 encoding and default allocator.
|
||||
/*!
|
||||
A JSON value can be one of 7 types. This class is a variant type supporting
|
||||
these types.
|
||||
|
||||
Use the Value if UTF8 and default allocator
|
||||
|
||||
\tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document)
|
||||
\tparam Allocator Allocator type for allocating memory of object, array and string.
|
||||
*/
|
||||
#pragma pack (push, 4)
|
||||
template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
|
||||
class GenericValue {
|
||||
public:
|
||||
//! Name-value pair in an object.
|
||||
struct Member {
|
||||
GenericValue<Encoding, Allocator> name; //!< name of member (must be a string)
|
||||
GenericValue<Encoding, Allocator> value; //!< value of member.
|
||||
};
|
||||
|
||||
typedef Encoding EncodingType; //!< Encoding type from template parameter.
|
||||
typedef Allocator AllocatorType; //!< Allocator type from template parameter.
|
||||
typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding.
|
||||
typedef Member* MemberIterator; //!< Member iterator for iterating in object.
|
||||
typedef const Member* ConstMemberIterator; //!< Constant member iterator for iterating in object.
|
||||
typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array.
|
||||
typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array.
|
||||
|
||||
//!@name Constructors and destructor.
|
||||
//@{
|
||||
|
||||
//! Default constructor creates a null value.
|
||||
GenericValue() : flags_(kNullFlag) {}
|
||||
|
||||
//! Copy constructor is not permitted.
|
||||
private:
|
||||
GenericValue(const GenericValue& rhs);
|
||||
|
||||
public:
|
||||
|
||||
//! Constructor with JSON value type.
|
||||
/*! This creates a Value of specified type with default content.
|
||||
\param type Type of the value.
|
||||
\note Default content for number is zero.
|
||||
*/
|
||||
GenericValue(Type type) {
|
||||
static const unsigned defaultFlags[7] = {
|
||||
kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kConstStringFlag,
|
||||
kNumberFlag | kIntFlag | kUintFlag | kInt64Flag | kUint64Flag | kDoubleFlag
|
||||
};
|
||||
RAPIDJSON_ASSERT(type <= kNumberType);
|
||||
flags_ = defaultFlags[type];
|
||||
memset(&data_, 0, sizeof(data_));
|
||||
}
|
||||
|
||||
//! Constructor for boolean value.
|
||||
GenericValue(bool b) : flags_(b ? kTrueFlag : kFalseFlag) {}
|
||||
|
||||
//! Constructor for int value.
|
||||
GenericValue(int i) : flags_(kNumberIntFlag) {
|
||||
data_.n.i64 = i;
|
||||
if (i >= 0)
|
||||
flags_ |= kUintFlag | kUint64Flag;
|
||||
}
|
||||
|
||||
//! Constructor for unsigned value.
|
||||
GenericValue(unsigned u) : flags_(kNumberUintFlag) {
|
||||
data_.n.u64 = u;
|
||||
if (!(u & 0x80000000))
|
||||
flags_ |= kIntFlag | kInt64Flag;
|
||||
}
|
||||
|
||||
//! Constructor for int64_t value.
|
||||
GenericValue(int64_t i64) : flags_(kNumberInt64Flag) {
|
||||
data_.n.i64 = i64;
|
||||
if (i64 >= 0) {
|
||||
flags_ |= kNumberUint64Flag;
|
||||
if (!(i64 & 0xFFFFFFFF00000000LL))
|
||||
flags_ |= kUintFlag;
|
||||
if (!(i64 & 0xFFFFFFFF80000000LL))
|
||||
flags_ |= kIntFlag;
|
||||
}
|
||||
else if (i64 >= -2147483648LL)
|
||||
flags_ |= kIntFlag;
|
||||
}
|
||||
|
||||
//! Constructor for uint64_t value.
|
||||
GenericValue(uint64_t u64) : flags_(kNumberUint64Flag) {
|
||||
data_.n.u64 = u64;
|
||||
if (!(u64 & 0x8000000000000000ULL))
|
||||
flags_ |= kInt64Flag;
|
||||
if (!(u64 & 0xFFFFFFFF00000000ULL))
|
||||
flags_ |= kUintFlag;
|
||||
if (!(u64 & 0xFFFFFFFF80000000ULL))
|
||||
flags_ |= kIntFlag;
|
||||
}
|
||||
|
||||
//! Constructor for double value.
|
||||
GenericValue(double d) : flags_(kNumberDoubleFlag) { data_.n.d = d; }
|
||||
|
||||
//! Constructor for constant string (i.e. do not make a copy of string)
|
||||
GenericValue(const Ch* s, SizeType length) {
|
||||
RAPIDJSON_ASSERT(s != NULL);
|
||||
flags_ = kConstStringFlag;
|
||||
data_.s.str = s;
|
||||
data_.s.length = length;
|
||||
}
|
||||
|
||||
//! Constructor for constant string (i.e. do not make a copy of string)
|
||||
GenericValue(const Ch* s) { SetStringRaw(s, internal::StrLen(s)); }
|
||||
|
||||
//! Constructor for copy-string (i.e. do make a copy of string)
|
||||
GenericValue(const Ch* s, SizeType length, Allocator& allocator) { SetStringRaw(s, length, allocator); }
|
||||
|
||||
//! Constructor for copy-string (i.e. do make a copy of string)
|
||||
GenericValue(const Ch*s, Allocator& allocator) { SetStringRaw(s, internal::StrLen(s), allocator); }
|
||||
|
||||
//! Destructor.
|
||||
/*! Need to destruct elements of array, members of object, or copy-string.
|
||||
*/
|
||||
~GenericValue() {
|
||||
if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
|
||||
switch(flags_) {
|
||||
case kArrayFlag:
|
||||
for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v)
|
||||
v->~GenericValue();
|
||||
Allocator::Free(data_.a.elements);
|
||||
break;
|
||||
|
||||
case kObjectFlag:
|
||||
for (Member* m = data_.o.members; m != data_.o.members + data_.o.size; ++m) {
|
||||
m->name.~GenericValue();
|
||||
m->value.~GenericValue();
|
||||
}
|
||||
Allocator::Free(data_.o.members);
|
||||
break;
|
||||
|
||||
case kCopyStringFlag:
|
||||
Allocator::Free(const_cast<Ch*>(data_.s.str));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
//!@name Assignment operators
|
||||
//@{
|
||||
|
||||
//! Assignment with move semantics.
|
||||
/*! \param rhs Source of the assignment. It will become a null value after assignment.
|
||||
*/
|
||||
GenericValue& operator=(GenericValue& rhs) {
|
||||
RAPIDJSON_ASSERT(this != &rhs);
|
||||
this->~GenericValue();
|
||||
memcpy(this, &rhs, sizeof(GenericValue));
|
||||
rhs.flags_ = kNullFlag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Assignment with primitive types.
|
||||
/*! \tparam T Either Type, int, unsigned, int64_t, uint64_t, const Ch*
|
||||
\param value The value to be assigned.
|
||||
*/
|
||||
template <typename T>
|
||||
GenericValue& operator=(T value) {
|
||||
this->~GenericValue();
|
||||
new (this) GenericValue(value);
|
||||
return *this;
|
||||
}
|
||||
//@}
|
||||
|
||||
//!@name Type
|
||||
//@{
|
||||
|
||||
Type GetType() const { return static_cast<Type>(flags_ & kTypeMask); }
|
||||
bool IsNull() const { return flags_ == kNullFlag; }
|
||||
bool IsFalse() const { return flags_ == kFalseFlag; }
|
||||
bool IsTrue() const { return flags_ == kTrueFlag; }
|
||||
bool IsBool() const { return (flags_ & kBoolFlag) != 0; }
|
||||
bool IsObject() const { return flags_ == kObjectFlag; }
|
||||
bool IsArray() const { return flags_ == kArrayFlag; }
|
||||
bool IsNumber() const { return (flags_ & kNumberFlag) != 0; }
|
||||
bool IsInt() const { return (flags_ & kIntFlag) != 0; }
|
||||
bool IsUint() const { return (flags_ & kUintFlag) != 0; }
|
||||
bool IsInt64() const { return (flags_ & kInt64Flag) != 0; }
|
||||
bool IsUint64() const { return (flags_ & kUint64Flag) != 0; }
|
||||
bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; }
|
||||
bool IsString() const { return (flags_ & kStringFlag) != 0; }
|
||||
|
||||
//@}
|
||||
|
||||
//!@name Null
|
||||
//@{
|
||||
|
||||
GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; }
|
||||
|
||||
//@}
|
||||
|
||||
//!@name Bool
|
||||
//@{
|
||||
|
||||
bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; }
|
||||
GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; }
|
||||
|
||||
//@}
|
||||
|
||||
//!@name Object
|
||||
//@{
|
||||
|
||||
//! Set this value as an empty object.
|
||||
GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; }
|
||||
|
||||
//! Get the value associated with the name.
|
||||
/*!
|
||||
\note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7.
|
||||
Since 0.2, if the name is not correct, it will assert.
|
||||
If user is unsure whether a member exists, user should use HasMember() first.
|
||||
A better approach is to use the now public FindMember().
|
||||
*/
|
||||
GenericValue& operator[](const Ch* name) {
|
||||
if (Member* member = FindMember(name))
|
||||
return member->value;
|
||||
else {
|
||||
RAPIDJSON_ASSERT(false); // see above note
|
||||
static GenericValue NullValue;
|
||||
return NullValue;
|
||||
}
|
||||
}
|
||||
const GenericValue& operator[](const Ch* name) const { return const_cast<GenericValue&>(*this)[name]; }
|
||||
|
||||
//! Member iterators.
|
||||
ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.members; }
|
||||
ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.members + data_.o.size; }
|
||||
MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return data_.o.members; }
|
||||
MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return data_.o.members + data_.o.size; }
|
||||
|
||||
//! Check whether a member exists in the object.
|
||||
/*!
|
||||
\note It is better to use FindMember() directly if you need the obtain the value as well.
|
||||
*/
|
||||
bool HasMember(const Ch* name) const { return FindMember(name) != 0; }
|
||||
|
||||
//! Find member by name.
|
||||
/*!
|
||||
\return Return the member if exists. Otherwise returns null pointer.
|
||||
*/
|
||||
Member* FindMember(const Ch* name) {
|
||||
RAPIDJSON_ASSERT(name);
|
||||
RAPIDJSON_ASSERT(IsObject());
|
||||
|
||||
Object& o = data_.o;
|
||||
for (Member* member = o.members; member != data_.o.members + data_.o.size; ++member)
|
||||
if (name[member->name.data_.s.length] == '\0' && memcmp(member->name.data_.s.str, name, member->name.data_.s.length * sizeof(Ch)) == 0)
|
||||
return member;
|
||||
|
||||
return 0;
|
||||
}
|
||||
const Member* FindMember(const Ch* name) const { return const_cast<GenericValue&>(*this).FindMember(name); }
|
||||
|
||||
//! Add a member (name-value pair) to the object.
|
||||
/*! \param name A string value as name of member.
|
||||
\param value Value of any type.
|
||||
\param allocator Allocator for reallocating memory.
|
||||
\return The value itself for fluent API.
|
||||
\note The ownership of name and value will be transfered to this object if success.
|
||||
*/
|
||||
GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
|
||||
RAPIDJSON_ASSERT(IsObject());
|
||||
RAPIDJSON_ASSERT(name.IsString());
|
||||
Object& o = data_.o;
|
||||
if (o.size >= o.capacity) {
|
||||
if (o.capacity == 0) {
|
||||
o.capacity = kDefaultObjectCapacity;
|
||||
o.members = (Member*)allocator.Malloc(o.capacity * sizeof(Member));
|
||||
}
|
||||
else {
|
||||
SizeType oldCapacity = o.capacity;
|
||||
o.capacity *= 2;
|
||||
o.members = (Member*)allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member));
|
||||
}
|
||||
}
|
||||
o.members[o.size].name.RawAssign(name);
|
||||
o.members[o.size].value.RawAssign(value);
|
||||
o.size++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GenericValue& AddMember(const Ch* name, Allocator& nameAllocator, GenericValue& value, Allocator& allocator) {
|
||||
GenericValue n(name, internal::StrLen(name), nameAllocator);
|
||||
return AddMember(n, value, allocator);
|
||||
}
|
||||
|
||||
GenericValue& AddMember(const Ch* name, GenericValue& value, Allocator& allocator) {
|
||||
GenericValue n(name, internal::StrLen(name));
|
||||
return AddMember(n, value, allocator);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
GenericValue& AddMember(const Ch* name, T value, Allocator& allocator) {
|
||||
GenericValue n(name, internal::StrLen(name));
|
||||
GenericValue v(value);
|
||||
return AddMember(n, v, allocator);
|
||||
}
|
||||
|
||||
//! Remove a member in object by its name.
|
||||
/*! \param name Name of member to be removed.
|
||||
\return Whether the member existed.
|
||||
\note Removing member is implemented by moving the last member. So the ordering of members is changed.
|
||||
*/
|
||||
bool RemoveMember(const Ch* name) {
|
||||
RAPIDJSON_ASSERT(IsObject());
|
||||
if (Member* m = FindMember(name)) {
|
||||
RAPIDJSON_ASSERT(data_.o.size > 0);
|
||||
RAPIDJSON_ASSERT(data_.o.members != 0);
|
||||
|
||||
Member* last = data_.o.members + (data_.o.size - 1);
|
||||
if (data_.o.size > 1 && m != last) {
|
||||
// Move the last one to this place
|
||||
m->name = last->name;
|
||||
m->value = last->value;
|
||||
}
|
||||
else {
|
||||
// Only one left, just destroy
|
||||
m->name.~GenericValue();
|
||||
m->value.~GenericValue();
|
||||
}
|
||||
--data_.o.size;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
//!@name Array
|
||||
//@{
|
||||
|
||||
//! Set this value as an empty array.
|
||||
GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; }
|
||||
|
||||
//! Get the number of elements in array.
|
||||
SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; }
|
||||
|
||||
//! Get the capacity of array.
|
||||
SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; }
|
||||
|
||||
//! Check whether the array is empty.
|
||||
bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; }
|
||||
|
||||
//! Remove all elements in the array.
|
||||
/*! This function do not deallocate memory in the array, i.e. the capacity is unchanged.
|
||||
*/
|
||||
void Clear() {
|
||||
RAPIDJSON_ASSERT(IsArray());
|
||||
for (SizeType i = 0; i < data_.a.size; ++i)
|
||||
data_.a.elements[i].~GenericValue();
|
||||
data_.a.size = 0;
|
||||
}
|
||||
|
||||
//! Get an element from array by index.
|
||||
/*! \param index Zero-based index of element.
|
||||
\note
|
||||
\code
|
||||
Value a(kArrayType);
|
||||
a.PushBack(123);
|
||||
int x = a[0].GetInt(); // Error: operator[ is ambiguous, as 0 also mean a null pointer of const char* type.
|
||||
int y = a[SizeType(0)].GetInt(); // Cast to SizeType will work.
|
||||
int z = a[0u].GetInt(); // This works too.
|
||||
\endcode
|
||||
*/
|
||||
GenericValue& operator[](SizeType index) {
|
||||
RAPIDJSON_ASSERT(IsArray());
|
||||
RAPIDJSON_ASSERT(index < data_.a.size);
|
||||
return data_.a.elements[index];
|
||||
}
|
||||
const GenericValue& operator[](SizeType index) const { return const_cast<GenericValue&>(*this)[index]; }
|
||||
|
||||
//! Element iterator
|
||||
ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; }
|
||||
ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; }
|
||||
ConstValueIterator Begin() const { return const_cast<GenericValue&>(*this).Begin(); }
|
||||
ConstValueIterator End() const { return const_cast<GenericValue&>(*this).End(); }
|
||||
|
||||
//! Request the array to have enough capacity to store elements.
|
||||
/*! \param newCapacity The capacity that the array at least need to have.
|
||||
\param allocator The allocator for allocating memory. It must be the same one use previously.
|
||||
\return The value itself for fluent API.
|
||||
*/
|
||||
GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) {
|
||||
RAPIDJSON_ASSERT(IsArray());
|
||||
if (newCapacity > data_.a.capacity) {
|
||||
data_.a.elements = (GenericValue*)allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue));
|
||||
data_.a.capacity = newCapacity;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Append a value at the end of the array.
|
||||
/*! \param value The value to be appended.
|
||||
\param allocator The allocator for allocating memory. It must be the same one use previously.
|
||||
\return The value itself for fluent API.
|
||||
\note The ownership of the value will be transfered to this object if success.
|
||||
\note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
|
||||
*/
|
||||
GenericValue& PushBack(GenericValue& value, Allocator& allocator) {
|
||||
RAPIDJSON_ASSERT(IsArray());
|
||||
if (data_.a.size >= data_.a.capacity)
|
||||
Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : data_.a.capacity * 2, allocator);
|
||||
data_.a.elements[data_.a.size++].RawAssign(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
GenericValue& PushBack(T value, Allocator& allocator) {
|
||||
GenericValue v(value);
|
||||
return PushBack(v, allocator);
|
||||
}
|
||||
|
||||
//! Remove the last element in the array.
|
||||
GenericValue& PopBack() {
|
||||
RAPIDJSON_ASSERT(IsArray());
|
||||
RAPIDJSON_ASSERT(!Empty());
|
||||
data_.a.elements[--data_.a.size].~GenericValue();
|
||||
return *this;
|
||||
}
|
||||
//@}
|
||||
|
||||
//!@name Number
|
||||
//@{
|
||||
|
||||
int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; }
|
||||
unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; }
|
||||
int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; }
|
||||
uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; }
|
||||
|
||||
double GetDouble() const {
|
||||
RAPIDJSON_ASSERT(IsNumber());
|
||||
if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion.
|
||||
if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double
|
||||
if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double
|
||||
if ((flags_ & kInt64Flag) != 0) return (double)data_.n.i64; // int64_t -> double (may lose precision)
|
||||
RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return (double)data_.n.u64; // uint64_t -> double (may lose precision)
|
||||
}
|
||||
|
||||
GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; }
|
||||
GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; }
|
||||
GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; }
|
||||
GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; }
|
||||
GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; }
|
||||
|
||||
//@}
|
||||
|
||||
//!@name String
|
||||
//@{
|
||||
|
||||
const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return data_.s.str; }
|
||||
|
||||
//! Get the length of string.
|
||||
/*! Since rapidjson permits "\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength().
|
||||
*/
|
||||
SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return data_.s.length; }
|
||||
|
||||
//! Set this value as a string without copying source string.
|
||||
/*! This version has better performance with supplied length, and also support string containing null character.
|
||||
\param s source string pointer.
|
||||
\param length The length of source string, excluding the trailing null terminator.
|
||||
\return The value itself for fluent API.
|
||||
*/
|
||||
GenericValue& SetString(const Ch* s, SizeType length) { this->~GenericValue(); SetStringRaw(s, length); return *this; }
|
||||
|
||||
//! Set this value as a string without copying source string.
|
||||
/*! \param s source string pointer.
|
||||
\return The value itself for fluent API.
|
||||
*/
|
||||
GenericValue& SetString(const Ch* s) { return SetString(s, internal::StrLen(s)); }
|
||||
|
||||
//! Set this value as a string by copying from source string.
|
||||
/*! This version has better performance with supplied length, and also support string containing null character.
|
||||
\param s source string.
|
||||
\param length The length of source string, excluding the trailing null terminator.
|
||||
\param allocator Allocator for allocating copied buffer. Commonly use document.GetAllocator().
|
||||
\return The value itself for fluent API.
|
||||
*/
|
||||
GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, length, allocator); return *this; }
|
||||
|
||||
//! Set this value as a string by copying from source string.
|
||||
/*! \param s source string.
|
||||
\param allocator Allocator for allocating copied buffer. Commonly use document.GetAllocator().
|
||||
\return The value itself for fluent API.
|
||||
*/
|
||||
GenericValue& SetString(const Ch* s, Allocator& allocator) { SetString(s, internal::StrLen(s), allocator); return *this; }
|
||||
|
||||
//@}
|
||||
|
||||
//! Generate events of this value to a Handler.
|
||||
/*! This function adopts the GoF visitor pattern.
|
||||
Typical usage is to output this JSON value as JSON text via Writer, which is a Handler.
|
||||
It can also be used to deep clone this value via GenericDocument, which is also a Handler.
|
||||
\tparam Handler type of handler.
|
||||
\param handler An object implementing concept Handler.
|
||||
*/
|
||||
template <typename Handler>
|
||||
const GenericValue& Accept(Handler& handler) const {
|
||||
switch(GetType()) {
|
||||
case kNullType: handler.Null(); break;
|
||||
case kFalseType: handler.Bool(false); break;
|
||||
case kTrueType: handler.Bool(true); break;
|
||||
|
||||
case kObjectType:
|
||||
handler.StartObject();
|
||||
for (Member* m = data_.o.members; m != data_.o.members + data_.o.size; ++m) {
|
||||
handler.String(m->name.data_.s.str, m->name.data_.s.length, false);
|
||||
m->value.Accept(handler);
|
||||
}
|
||||
handler.EndObject(data_.o.size);
|
||||
break;
|
||||
|
||||
case kArrayType:
|
||||
handler.StartArray();
|
||||
for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v)
|
||||
v->Accept(handler);
|
||||
handler.EndArray(data_.a.size);
|
||||
break;
|
||||
|
||||
case kStringType:
|
||||
handler.String(data_.s.str, data_.s.length, false);
|
||||
break;
|
||||
|
||||
case kNumberType:
|
||||
if (IsInt()) handler.Int(data_.n.i.i);
|
||||
else if (IsUint()) handler.Uint(data_.n.u.u);
|
||||
else if (IsInt64()) handler.Int64(data_.n.i64);
|
||||
else if (IsUint64()) handler.Uint64(data_.n.u64);
|
||||
else handler.Double(data_.n.d);
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename, typename>
|
||||
friend class GenericDocument;
|
||||
|
||||
enum {
|
||||
kBoolFlag = 0x100,
|
||||
kNumberFlag = 0x200,
|
||||
kIntFlag = 0x400,
|
||||
kUintFlag = 0x800,
|
||||
kInt64Flag = 0x1000,
|
||||
kUint64Flag = 0x2000,
|
||||
kDoubleFlag = 0x4000,
|
||||
kStringFlag = 0x100000,
|
||||
kCopyFlag = 0x200000,
|
||||
|
||||
// Initial flags of different types.
|
||||
kNullFlag = kNullType,
|
||||
kTrueFlag = kTrueType | kBoolFlag,
|
||||
kFalseFlag = kFalseType | kBoolFlag,
|
||||
kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag,
|
||||
kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag,
|
||||
kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag,
|
||||
kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag,
|
||||
kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag,
|
||||
kConstStringFlag = kStringType | kStringFlag,
|
||||
kCopyStringFlag = kStringType | kStringFlag | kCopyFlag,
|
||||
kObjectFlag = kObjectType,
|
||||
kArrayFlag = kArrayType,
|
||||
|
||||
kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler
|
||||
};
|
||||
|
||||
static const SizeType kDefaultArrayCapacity = 16;
|
||||
static const SizeType kDefaultObjectCapacity = 16;
|
||||
|
||||
struct String {
|
||||
const Ch* str;
|
||||
SizeType length;
|
||||
unsigned hashcode; //!< reserved
|
||||
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
|
||||
|
||||
// By using proper binary layout, retrieval of different integer types do not need conversions.
|
||||
union Number {
|
||||
#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN
|
||||
struct I {
|
||||
int i;
|
||||
char padding[4];
|
||||
}i;
|
||||
struct U {
|
||||
unsigned u;
|
||||
char padding2[4];
|
||||
}u;
|
||||
#else
|
||||
struct I {
|
||||
char padding[4];
|
||||
int i;
|
||||
}i;
|
||||
struct U {
|
||||
char padding2[4];
|
||||
unsigned u;
|
||||
}u;
|
||||
#endif
|
||||
int64_t i64;
|
||||
uint64_t u64;
|
||||
double d;
|
||||
}; // 8 bytes
|
||||
|
||||
struct Object {
|
||||
Member* members;
|
||||
SizeType size;
|
||||
SizeType capacity;
|
||||
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
|
||||
|
||||
struct Array {
|
||||
GenericValue<Encoding, Allocator>* elements;
|
||||
SizeType size;
|
||||
SizeType capacity;
|
||||
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
|
||||
|
||||
union Data {
|
||||
String s;
|
||||
Number n;
|
||||
Object o;
|
||||
Array a;
|
||||
}; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
|
||||
|
||||
// Initialize this value as array with initial data, without calling destructor.
|
||||
void SetArrayRaw(GenericValue* values, SizeType count, Allocator& alloctaor) {
|
||||
flags_ = kArrayFlag;
|
||||
data_.a.elements = (GenericValue*)alloctaor.Malloc(count * sizeof(GenericValue));
|
||||
memcpy(data_.a.elements, values, count * sizeof(GenericValue));
|
||||
data_.a.size = data_.a.capacity = count;
|
||||
}
|
||||
|
||||
//! Initialize this value as object with initial data, without calling destructor.
|
||||
void SetObjectRaw(Member* members, SizeType count, Allocator& alloctaor) {
|
||||
flags_ = kObjectFlag;
|
||||
data_.o.members = (Member*)alloctaor.Malloc(count * sizeof(Member));
|
||||
memcpy(data_.o.members, members, count * sizeof(Member));
|
||||
data_.o.size = data_.o.capacity = count;
|
||||
}
|
||||
|
||||
//! Initialize this value as constant string, without calling destructor.
|
||||
void SetStringRaw(const Ch* s, SizeType length) {
|
||||
RAPIDJSON_ASSERT(s != NULL);
|
||||
flags_ = kConstStringFlag;
|
||||
data_.s.str = s;
|
||||
data_.s.length = length;
|
||||
}
|
||||
|
||||
//! Initialize this value as copy string with initial data, without calling destructor.
|
||||
void SetStringRaw(const Ch* s, SizeType length, Allocator& allocator) {
|
||||
RAPIDJSON_ASSERT(s != NULL);
|
||||
flags_ = kCopyStringFlag;
|
||||
data_.s.str = (Ch *)allocator.Malloc((length + 1) * sizeof(Ch));
|
||||
data_.s.length = length;
|
||||
memcpy(const_cast<Ch*>(data_.s.str), s, length * sizeof(Ch));
|
||||
const_cast<Ch*>(data_.s.str)[length] = '\0';
|
||||
}
|
||||
|
||||
//! Assignment without calling destructor
|
||||
void RawAssign(GenericValue& rhs) {
|
||||
memcpy(this, &rhs, sizeof(GenericValue));
|
||||
rhs.flags_ = kNullFlag;
|
||||
}
|
||||
|
||||
Data data_;
|
||||
unsigned flags_;
|
||||
};
|
||||
#pragma pack (pop)
|
||||
|
||||
//! Value with UTF8 encoding.
|
||||
typedef GenericValue<UTF8<> > Value;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GenericDocument
|
||||
|
||||
//! A document for parsing JSON text as DOM.
|
||||
/*!
|
||||
\implements Handler
|
||||
\tparam Encoding encoding for both parsing and string storage.
|
||||
\tparam Alloactor allocator for allocating memory for the DOM, and the stack during parsing.
|
||||
*/
|
||||
template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
|
||||
class GenericDocument : public GenericValue<Encoding, Allocator> {
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding.
|
||||
typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of the document.
|
||||
typedef Allocator AllocatorType; //!< Allocator type from template parameter.
|
||||
|
||||
//! Constructor
|
||||
/*! \param allocator Optional allocator for allocating stack memory.
|
||||
\param stackCapacity Initial capacity of stack in bytes.
|
||||
*/
|
||||
GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseError_(0), errorOffset_(0) {}
|
||||
|
||||
//! Parse JSON text from an input stream.
|
||||
/*! \tparam parseFlags Combination of ParseFlag.
|
||||
\param stream Input stream to be parsed.
|
||||
\return The document itself for fluent API.
|
||||
*/
|
||||
template <unsigned parseFlags, typename SourceEncoding, typename InputStream>
|
||||
GenericDocument& ParseStream(InputStream& is) {
|
||||
ValueType::SetNull(); // Remove existing root if exist
|
||||
GenericReader<SourceEncoding, Encoding, Allocator> reader;
|
||||
if (reader.template Parse<parseFlags>(is, *this)) {
|
||||
RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
|
||||
this->RawAssign(*stack_.template Pop<ValueType>(1)); // Add this-> to prevent issue 13.
|
||||
parseError_ = 0;
|
||||
errorOffset_ = 0;
|
||||
}
|
||||
else {
|
||||
parseError_ = reader.GetParseError();
|
||||
errorOffset_ = reader.GetErrorOffset();
|
||||
ClearStack();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Parse JSON text from a mutable string.
|
||||
/*! \tparam parseFlags Combination of ParseFlag.
|
||||
\param str Mutable zero-terminated string to be parsed.
|
||||
\return The document itself for fluent API.
|
||||
*/
|
||||
template <unsigned parseFlags, typename SourceEncoding>
|
||||
GenericDocument& ParseInsitu(Ch* str) {
|
||||
GenericInsituStringStream<Encoding> s(str);
|
||||
return ParseStream<parseFlags | kParseInsituFlag, SourceEncoding>(s);
|
||||
}
|
||||
|
||||
template <unsigned parseFlags>
|
||||
GenericDocument& ParseInsitu(Ch* str) {
|
||||
return ParseInsitu<parseFlags, Encoding>(str);
|
||||
}
|
||||
|
||||
//! Parse JSON text from a read-only string.
|
||||
/*! \tparam parseFlags Combination of ParseFlag (must not contain kParseInsituFlag).
|
||||
\param str Read-only zero-terminated string to be parsed.
|
||||
*/
|
||||
template <unsigned parseFlags, typename SourceEncoding>
|
||||
GenericDocument& Parse(const Ch* str) {
|
||||
RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
|
||||
GenericStringStream<SourceEncoding> s(str);
|
||||
return ParseStream<parseFlags, SourceEncoding>(s);
|
||||
}
|
||||
|
||||
template <unsigned parseFlags>
|
||||
GenericDocument& Parse(const Ch* str) {
|
||||
return Parse<parseFlags, Encoding>(str);
|
||||
}
|
||||
|
||||
//! Whether a parse error was occured in the last parsing.
|
||||
bool HasParseError() const { return parseError_ != 0; }
|
||||
|
||||
//! Get the message of parsing error.
|
||||
const char* GetParseError() const { return parseError_; }
|
||||
|
||||
//! Get the offset in character of the parsing error.
|
||||
size_t GetErrorOffset() const { return errorOffset_; }
|
||||
|
||||
//! Get the allocator of this document.
|
||||
Allocator& GetAllocator() { return stack_.GetAllocator(); }
|
||||
|
||||
//! Get the capacity of stack in bytes.
|
||||
size_t GetStackCapacity() const { return stack_.GetCapacity(); }
|
||||
|
||||
//private:
|
||||
//friend class GenericReader<Encoding>; // for Reader to call the following private handler functions
|
||||
|
||||
// Implementation of Handler
|
||||
void Null() { new (stack_.template Push<ValueType>()) ValueType(); }
|
||||
void Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); }
|
||||
void Int(int i) { new (stack_.template Push<ValueType>()) ValueType(i); }
|
||||
void Uint(unsigned i) { new (stack_.template Push<ValueType>()) ValueType(i); }
|
||||
void Int64(int64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); }
|
||||
void Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); }
|
||||
void Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); }
|
||||
|
||||
void String(const Ch* str, SizeType length, bool copy) {
|
||||
if (copy)
|
||||
new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
|
||||
else
|
||||
new (stack_.template Push<ValueType>()) ValueType(str, length);
|
||||
}
|
||||
|
||||
void StartObject() { new (stack_.template Push<ValueType>()) ValueType(kObjectType); }
|
||||
|
||||
void EndObject(SizeType memberCount) {
|
||||
typename ValueType::Member* members = stack_.template Pop<typename ValueType::Member>(memberCount);
|
||||
stack_.template Top<ValueType>()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator());
|
||||
}
|
||||
|
||||
void StartArray() { new (stack_.template Push<ValueType>()) ValueType(kArrayType); }
|
||||
|
||||
void EndArray(SizeType elementCount) {
|
||||
ValueType* elements = stack_.template Pop<ValueType>(elementCount);
|
||||
stack_.template Top<ValueType>()->SetArrayRaw(elements, elementCount, GetAllocator());
|
||||
}
|
||||
|
||||
private:
|
||||
// Prohibit assignment
|
||||
GenericDocument& operator=(const GenericDocument&);
|
||||
|
||||
void ClearStack() {
|
||||
if (Allocator::kNeedFree)
|
||||
while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects)
|
||||
(stack_.template Pop<ValueType>(1))->~ValueType();
|
||||
else
|
||||
stack_.Clear();
|
||||
}
|
||||
|
||||
static const size_t kDefaultStackCapacity = 1024;
|
||||
internal::Stack<Allocator> stack_;
|
||||
const char* parseError_;
|
||||
size_t errorOffset_;
|
||||
};
|
||||
|
||||
typedef GenericDocument<UTF8<> > Document;
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_DOCUMENT_H_
|
||||
250
Engine/source/persistence/rapidjson/encodedstream.h
Normal file
250
Engine/source/persistence/rapidjson/encodedstream.h
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
#ifndef RAPIDJSON_ENCODEDSTREAM_H_
|
||||
#define RAPIDJSON_ENCODEDSTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
//! Input byte stream wrapper with a statically bound encoding.
|
||||
/*!
|
||||
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||
\tparam InputByteStream Type of input byte stream. For example, FileReadStream.
|
||||
*/
|
||||
template <typename Encoding, typename InputByteStream>
|
||||
class EncodedInputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
EncodedInputStream(InputByteStream& is) : is_(is) {
|
||||
current_ = Encoding::TakeBOM(is_);
|
||||
}
|
||||
|
||||
Ch Peek() const { return current_; }
|
||||
Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
|
||||
size_t Tell() const { return is_.Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch c) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
// Prohibit assignment for VC C4512 warning
|
||||
EncodedInputStream& operator=(const EncodedInputStream&);
|
||||
|
||||
InputByteStream& is_;
|
||||
Ch current_;
|
||||
};
|
||||
|
||||
//! Output byte stream wrapper with statically bound encoding.
|
||||
/*!
|
||||
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||
\tparam InputByteStream Type of input byte stream. For example, FileWriteStream.
|
||||
*/
|
||||
template <typename Encoding, typename OutputByteStream>
|
||||
class EncodedOutputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
|
||||
if (putBOM)
|
||||
Encoding::PutBOM(os_);
|
||||
}
|
||||
|
||||
void Put(Ch c) { Encoding::Put(os_, c); }
|
||||
void Flush() { os_.Flush(); }
|
||||
|
||||
// Not implemented
|
||||
Ch Peek() const { RAPIDJSON_ASSERT(false); }
|
||||
Ch Take() { RAPIDJSON_ASSERT(false); }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
// Prohibit assignment for VC C4512 warning
|
||||
EncodedOutputStream& operator=(const EncodedOutputStream&);
|
||||
|
||||
OutputByteStream& os_;
|
||||
};
|
||||
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||
/*!
|
||||
\tparam CharType Type of character for reading.
|
||||
\tparam InputByteStream type of input byte stream to be wrapped.
|
||||
*/
|
||||
template <typename CharType, typename InputByteStream>
|
||||
class AutoUTFInputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef CharType Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param is input stream to be wrapped.
|
||||
\param type UTF encoding type if it is not detected from the stream.
|
||||
*/
|
||||
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
|
||||
DetectType();
|
||||
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
|
||||
takeFunc_ = f[type_];
|
||||
current_ = takeFunc_(*is_);
|
||||
}
|
||||
|
||||
UTFType GetType() const { return type_; }
|
||||
bool HasBOM() const { return hasBOM_; }
|
||||
|
||||
Ch Peek() const { return current_; }
|
||||
Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
|
||||
size_t Tell() const { return is_->Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
// Detect encoding type with BOM or RFC 4627
|
||||
void DetectType() {
|
||||
// BOM (Byte Order Mark):
|
||||
// 00 00 FE FF UTF-32BE
|
||||
// FF FE 00 00 UTF-32LE
|
||||
// FE FF UTF-16BE
|
||||
// FF FE UTF-16LE
|
||||
// EF BB BF UTF-8
|
||||
|
||||
const unsigned char* c = (const unsigned char *)is_->Peek4();
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
|
||||
hasBOM_ = false;
|
||||
if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||
else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
|
||||
|
||||
// RFC 4627: Section 3
|
||||
// "Since the first two characters of a JSON text will always be ASCII
|
||||
// characters [RFC0020], it is possible to determine whether an octet
|
||||
// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
|
||||
// at the pattern of nulls in the first four octets."
|
||||
// 00 00 00 xx UTF-32BE
|
||||
// 00 xx 00 xx UTF-16BE
|
||||
// xx 00 00 00 UTF-32LE
|
||||
// xx 00 xx 00 UTF-16LE
|
||||
// xx xx xx xx UTF-8
|
||||
|
||||
if (!hasBOM_) {
|
||||
unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
|
||||
switch (pattern) {
|
||||
case 0x08: type_ = kUTF32BE; break;
|
||||
case 0x0A: type_ = kUTF16BE; break;
|
||||
case 0x01: type_ = kUTF32LE; break;
|
||||
case 0x05: type_ = kUTF16LE; break;
|
||||
case 0x0F: type_ = kUTF8; break;
|
||||
}
|
||||
}
|
||||
|
||||
// RUntime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||
switch (type_) {
|
||||
case kUTF8:
|
||||
// Do nothing
|
||||
break;
|
||||
case kUTF16LE:
|
||||
case kUTF16BE:
|
||||
RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||
break;
|
||||
case kUTF32LE:
|
||||
case kUTF32BE:
|
||||
RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
typedef Ch (*TakeFunc)(InputByteStream& is);
|
||||
InputByteStream* is_;
|
||||
UTFType type_;
|
||||
Ch current_;
|
||||
TakeFunc takeFunc_;
|
||||
bool hasBOM_;
|
||||
};
|
||||
|
||||
//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||
/*!
|
||||
\tparam CharType Type of character for writing.
|
||||
\tparam InputByteStream type of output byte stream to be wrapped.
|
||||
*/
|
||||
template <typename CharType, typename OutputByteStream>
|
||||
class AutoUTFOutputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef CharType Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param os output stream to be wrapped.
|
||||
\param type UTF encoding type.
|
||||
\param putBOM Whether to write BOM at the beginning of the stream.
|
||||
*/
|
||||
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
|
||||
// RUntime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||
switch (type_) {
|
||||
case kUTF16LE:
|
||||
case kUTF16BE:
|
||||
RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||
break;
|
||||
case kUTF32LE:
|
||||
case kUTF32BE:
|
||||
RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||
break;
|
||||
case kUTF8:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
|
||||
putFunc_ = f[type_];
|
||||
|
||||
if (putBOM)
|
||||
PutBOM();
|
||||
}
|
||||
|
||||
UTFType GetType() const { return type_; }
|
||||
|
||||
void Put(Ch c) { putFunc_(*os_, c); }
|
||||
void Flush() { os_->Flush(); }
|
||||
|
||||
// Not implemented
|
||||
Ch Peek() const { RAPIDJSON_ASSERT(false); }
|
||||
Ch Take() { RAPIDJSON_ASSERT(false); }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
void PutBOM() {
|
||||
typedef void (*PutBOMFunc)(OutputByteStream&);
|
||||
static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
|
||||
f[type_](*os_);
|
||||
}
|
||||
|
||||
typedef void (*PutFunc)(OutputByteStream&, Ch);
|
||||
|
||||
OutputByteStream* os_;
|
||||
UTFType type_;
|
||||
PutFunc putFunc_;
|
||||
};
|
||||
|
||||
#undef RAPIDJSON_ENCODINGS_FUNC
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
527
Engine/source/persistence/rapidjson/encodings.h
Normal file
527
Engine/source/persistence/rapidjson/encodings.h
Normal file
|
|
@ -0,0 +1,527 @@
|
|||
#ifndef RAPIDJSON_ENCODINGS_H_
|
||||
#define RAPIDJSON_ENCODINGS_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Encoding
|
||||
|
||||
/*! \class rapidjson::Encoding
|
||||
\brief Concept for encoding of Unicode characters.
|
||||
|
||||
\code
|
||||
concept Encoding {
|
||||
typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition.
|
||||
|
||||
//! \brief Encode a Unicode codepoint to an output stream.
|
||||
//! \param os Output stream.
|
||||
//! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint);
|
||||
|
||||
//! \brief Decode a Unicode codepoint from an input stream.
|
||||
//! \param is Input stream.
|
||||
//! \param codepoint Output of the unicode codepoint.
|
||||
//! \return true if a valid codepoint can be decoded from the stream.
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint);
|
||||
|
||||
//! \brief Validate one Unicode codepoint from an encoded stream.
|
||||
//! \param is Input stream to obtain codepoint.
|
||||
//! \param os Output for copying one codepoint.
|
||||
//! \return true if it is valid.
|
||||
//! \note This function just validating and copying the codepoint without actually decode it.
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os);
|
||||
|
||||
// The following functions are deal with byte streams.
|
||||
|
||||
//! Take a character from input byte stream, skip BOM if exist.
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is);
|
||||
|
||||
//! Take a character from input byte stream.
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is);
|
||||
|
||||
//! Put BOM to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os);
|
||||
|
||||
//! Put a character to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF8
|
||||
|
||||
//! UTF-8 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-8
|
||||
http://tools.ietf.org/html/rfc3629
|
||||
\tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
|
||||
\implements Encoding
|
||||
*/
|
||||
template<typename CharType = char>
|
||||
struct UTF8 {
|
||||
typedef CharType Ch;
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
if (codepoint <= 0x7F)
|
||||
os.Put(codepoint & 0xFF);
|
||||
else if (codepoint <= 0x7FF) {
|
||||
os.Put(0xC0 | ((codepoint >> 6) & 0xFF));
|
||||
os.Put(0x80 | ((codepoint & 0x3F)));
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
os.Put(0xE0 | ((codepoint >> 12) & 0xFF));
|
||||
os.Put(0x80 | ((codepoint >> 6) & 0x3F));
|
||||
os.Put(0x80 | (codepoint & 0x3F));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(0xF0 | ((codepoint >> 18) & 0xFF));
|
||||
os.Put(0x80 | ((codepoint >> 12) & 0x3F));
|
||||
os.Put(0x80 | ((codepoint >> 6) & 0x3F));
|
||||
os.Put(0x80 | (codepoint & 0x3F));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | ((unsigned char)c & 0x3Fu)
|
||||
#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0)
|
||||
#define TAIL() COPY(); TRANS(0x70)
|
||||
Ch c = is.Take();
|
||||
if (!(c & 0x80)) {
|
||||
*codepoint = (unsigned char)c;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char type = GetRange((unsigned char)c);
|
||||
*codepoint = (0xFF >> type) & (unsigned char)c;
|
||||
bool result = true;
|
||||
switch (type) {
|
||||
case 2: TAIL(); return result;
|
||||
case 3: TAIL(); TAIL(); return result;
|
||||
case 4: COPY(); TRANS(0x50); TAIL(); return result;
|
||||
case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
|
||||
case 6: TAIL(); TAIL(); TAIL(); return result;
|
||||
case 10: COPY(); TRANS(0x20); TAIL(); return result;
|
||||
case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
|
||||
default: return false;
|
||||
}
|
||||
#undef COPY
|
||||
#undef TRANS
|
||||
#undef TAIL
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
#define COPY() os.Put(c = is.Take())
|
||||
#define TRANS(mask) result &= ((GetRange((unsigned char)c) & mask) != 0)
|
||||
#define TAIL() COPY(); TRANS(0x70)
|
||||
Ch c;
|
||||
COPY();
|
||||
if (!(c & 0x80))
|
||||
return true;
|
||||
|
||||
bool result = true;
|
||||
switch (GetRange((unsigned char)c)) {
|
||||
case 2: TAIL(); return result;
|
||||
case 3: TAIL(); TAIL(); return result;
|
||||
case 4: COPY(); TRANS(0x50); TAIL(); return result;
|
||||
case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
|
||||
case 6: TAIL(); TAIL(); TAIL(); return result;
|
||||
case 10: COPY(); TRANS(0x20); TAIL(); return result;
|
||||
case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
|
||||
default: return false;
|
||||
}
|
||||
#undef COPY
|
||||
#undef TRANS
|
||||
#undef TAIL
|
||||
}
|
||||
|
||||
static unsigned char GetRange(unsigned char c) {
|
||||
// Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||
// With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
|
||||
static const unsigned char type[] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
|
||||
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
|
||||
};
|
||||
return type[c];
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
Ch c = Take(is);
|
||||
if ((unsigned char)c != 0xEFu) return c;
|
||||
c = is.Take();
|
||||
if ((unsigned char)c != 0xBBu) return c;
|
||||
c = is.Take();
|
||||
if ((unsigned char)c != 0xBFu) return c;
|
||||
c = is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return is.Take();
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xEFu); os.Put(0xBBu); os.Put(0xBFu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF16
|
||||
|
||||
//! UTF-16 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-16
|
||||
http://tools.ietf.org/html/rfc2781
|
||||
\tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
|
||||
\implements Encoding
|
||||
|
||||
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||
For streaming, use UTF16LE and UTF16BE, which handle endianness.
|
||||
*/
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
if (codepoint <= 0xFFFF) {
|
||||
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
|
||||
os.Put(static_cast<typename OutputStream::Ch>(codepoint));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
unsigned v = codepoint - 0x10000;
|
||||
os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
||||
os.Put((v & 0x3FF) | 0xDC00);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||
Ch c = is.Take();
|
||||
if (c < 0xD800 || c > 0xDFFF) {
|
||||
*codepoint = c;
|
||||
return true;
|
||||
}
|
||||
else if (c <= 0xDBFF) {
|
||||
*codepoint = (c & 0x3FF) << 10;
|
||||
c = is.Take();
|
||||
*codepoint |= (c & 0x3FF);
|
||||
*codepoint += 0x10000;
|
||||
return c >= 0xDC00 && c <= 0xDFFF;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
Ch c;
|
||||
os.Put(c = is.Take());
|
||||
if (c < 0xD800 || c > 0xDFFF)
|
||||
return true;
|
||||
else if (c <= 0xDBFF) {
|
||||
os.Put(c = is.Take());
|
||||
return c >= 0xDC00 && c <= 0xDFFF;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-16 little endian encoding.
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16LE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned short)c == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take();
|
||||
c |= (unsigned char)is.Take() << 8;
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xFFu); os.Put(0xFEu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(c & 0xFFu);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-16 big endian encoding.
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16BE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned short)c == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take() << 8;
|
||||
c |= (unsigned char)is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xFEu); os.Put(0xFFu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
os.Put(c & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF32
|
||||
|
||||
//! UTF-32 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-32
|
||||
\tparam Ch Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
|
||||
\implements Encoding
|
||||
|
||||
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||
For streaming, use UTF32LE and UTF32BE, which handle endianness.
|
||||
*/
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||
Ch c = is.Take();
|
||||
*codepoint = c;
|
||||
return c <= 0x10FFFF;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||
Ch c;
|
||||
os.Put(c = is.Take());
|
||||
return c <= 0x10FFFF;
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 little endian enocoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32LE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned)c == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take();
|
||||
c |= (unsigned char)is.Take() << 8;
|
||||
c |= (unsigned char)is.Take() << 16;
|
||||
c |= (unsigned char)is.Take() << 24;
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0xFFu); os.Put(0xFEu); os.Put(0x00u); os.Put(0x00u);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(c & 0xFFu);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
os.Put((c >> 16) & 0xFFu);
|
||||
os.Put((c >> 24) & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 big endian encoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32BE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return (unsigned)c == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = (unsigned char)is.Take() << 24;
|
||||
c |= (unsigned char)is.Take() << 16;
|
||||
c |= (unsigned char)is.Take() << 8;
|
||||
c |= (unsigned char)is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(0x00u); os.Put(0x00u); os.Put(0xFEu); os.Put(0xFFu);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put((c >> 24) & 0xFFu);
|
||||
os.Put((c >> 16) & 0xFFu);
|
||||
os.Put((c >> 8) & 0xFFu);
|
||||
os.Put(c & 0xFFu);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AutoUTF
|
||||
|
||||
//! Runtime-specified UTF encoding type of a stream.
|
||||
enum UTFType {
|
||||
kUTF8 = 0, //!< UTF-8.
|
||||
kUTF16LE = 1, //!< UTF-16 little endian.
|
||||
kUTF16BE = 2, //!< UTF-16 big endian.
|
||||
kUTF32LE = 3, //!< UTF-32 little endian.
|
||||
kUTF32BE = 4, //!< UTF-32 big endian.
|
||||
};
|
||||
|
||||
//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
|
||||
/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
|
||||
*/
|
||||
template<typename CharType>
|
||||
struct AutoUTF {
|
||||
typedef CharType Ch;
|
||||
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
template<typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
typedef void (*EncodeFunc)(OutputStream&, unsigned);
|
||||
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) };
|
||||
(*f[os.GetType()])(os, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
typedef bool (*DecodeFunc)(InputStream&, unsigned*);
|
||||
static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) };
|
||||
return (*f[is.GetType()])(is, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||
typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
|
||||
static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) };
|
||||
return (*f[is.GetType()])(is, os);
|
||||
}
|
||||
|
||||
#undef RAPIDJSON_ENCODINGS_FUNC
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Transcoder
|
||||
|
||||
//! Encoding conversion.
|
||||
template<typename SourceEncoding, typename TargetEncoding>
|
||||
struct Transcoder {
|
||||
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
|
||||
unsigned codepoint;
|
||||
if (!SourceEncoding::Decode(is, &codepoint))
|
||||
return false;
|
||||
TargetEncoding::Encode(os, codepoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Validate one Unicode codepoint from an encoded stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Transcode(is, os); // Since source/target encoding is different, must transcode.
|
||||
}
|
||||
};
|
||||
|
||||
//! Specialization of Transcoder with same source and target encoding.
|
||||
template<typename Encoding>
|
||||
struct Transcoder<Encoding, Encoding> {
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
|
||||
os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class.
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Encoding::Validate(is, os); // source/target encoding are the same
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_ENCODINGS_H_
|
||||
74
Engine/source/persistence/rapidjson/filereadstream.h
Normal file
74
Engine/source/persistence/rapidjson/filereadstream.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#ifndef RAPIDJSON_FILEREADSTREAM_H_
|
||||
#define RAPIDJSON_FILEREADSTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include <cstdio>
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
//! File byte stream for input using fread().
|
||||
/*!
|
||||
\implements Stream
|
||||
*/
|
||||
class FileReadStream {
|
||||
public:
|
||||
typedef char Ch; //!< Character type (byte).
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param fp File pointer opened for read.
|
||||
\param buffer user-supplied buffer.
|
||||
\param bufferSize size of buffer in bytes. Must >=4 bytes.
|
||||
*/
|
||||
FileReadStream(FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
||||
RAPIDJSON_ASSERT(fp_ != 0);
|
||||
RAPIDJSON_ASSERT(bufferSize >= 4);
|
||||
Read();
|
||||
}
|
||||
|
||||
Ch Peek() const { return *current_; }
|
||||
Ch Take() { Ch c = *current_; Read(); return c; }
|
||||
size_t Tell() const { return count_ + (current_ - buffer_); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
// For encoding detection only.
|
||||
const Ch* Peek4() const {
|
||||
return (current_ + 4 <= bufferLast_) ? current_ : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void Read() {
|
||||
if (current_ < bufferLast_)
|
||||
++current_;
|
||||
else if (!eof_) {
|
||||
count_ += readCount_;
|
||||
readCount_ = fread(buffer_, 1, bufferSize_, fp_);
|
||||
bufferLast_ = buffer_ + readCount_ - 1;
|
||||
current_ = buffer_;
|
||||
|
||||
if (readCount_ < bufferSize_) {
|
||||
buffer_[readCount_] = '\0';
|
||||
++bufferLast_;
|
||||
eof_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FILE* fp_;
|
||||
Ch *buffer_;
|
||||
size_t bufferSize_;
|
||||
Ch *bufferLast_;
|
||||
Ch *current_;
|
||||
size_t readCount_;
|
||||
size_t count_; //!< Number of characters read
|
||||
bool eof_;
|
||||
};
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
49
Engine/source/persistence/rapidjson/filestream.h
Normal file
49
Engine/source/persistence/rapidjson/filestream.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef RAPIDJSON_FILESTREAM_H_
|
||||
#define RAPIDJSON_FILESTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include <cstdio>
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
//! (Depreciated) Wrapper of C file stream for input or output.
|
||||
/*!
|
||||
This simple wrapper does not check the validity of the stream.
|
||||
\implements Stream
|
||||
\deprecated { This was only for basic testing in version 0.1, it is found that the performance is very low by using fgetc(). Use FileReadStream instead. }
|
||||
*/
|
||||
class FileStream {
|
||||
public:
|
||||
typedef char Ch; //!< Character type. Only support char.
|
||||
|
||||
FileStream(FILE* fp) : fp_(fp), count_(0) { Read(); }
|
||||
char Peek() const { return current_; }
|
||||
char Take() { char c = current_; Read(); return c; }
|
||||
size_t Tell() const { return count_; }
|
||||
void Put(char c) { fputc(c, fp_); }
|
||||
void Flush() { fflush(fp_); }
|
||||
|
||||
// Not implemented
|
||||
char* PutBegin() { return 0; }
|
||||
size_t PutEnd(char*) { return 0; }
|
||||
|
||||
private:
|
||||
void Read() {
|
||||
RAPIDJSON_ASSERT(fp_ != 0);
|
||||
int c = fgetc(fp_);
|
||||
if (c != EOF) {
|
||||
current_ = (char)c;
|
||||
count_++;
|
||||
}
|
||||
else if (current_ != '\0')
|
||||
current_ = '\0';
|
||||
}
|
||||
|
||||
FILE* fp_;
|
||||
char current_;
|
||||
size_t count_;
|
||||
};
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
73
Engine/source/persistence/rapidjson/filewritestream.h
Normal file
73
Engine/source/persistence/rapidjson/filewritestream.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#ifndef RAPIDJSON_FILEWRITESTREAM_H_
|
||||
#define RAPIDJSON_FILEWRITESTREAM_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include <cstdio>
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
//! Wrapper of C file stream for input using fread().
|
||||
/*!
|
||||
\implements Stream
|
||||
*/
|
||||
class FileWriteStream {
|
||||
public:
|
||||
typedef char Ch; //!< Character type. Only support char.
|
||||
|
||||
FileWriteStream(FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) {
|
||||
RAPIDJSON_ASSERT(fp_ != 0);
|
||||
}
|
||||
|
||||
void Put(char c) {
|
||||
if (current_ >= bufferEnd_)
|
||||
Flush();
|
||||
|
||||
*current_++ = c;
|
||||
}
|
||||
|
||||
void PutN(char c, size_t n) {
|
||||
size_t avail = bufferEnd_ - current_;
|
||||
while (n > avail) {
|
||||
memset(current_, c, avail);
|
||||
current_ += avail;
|
||||
Flush();
|
||||
n -= avail;
|
||||
avail = bufferEnd_ - current_;
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
memset(current_, c, n);
|
||||
current_ += n;
|
||||
}
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
if (current_ != buffer_) {
|
||||
fwrite(buffer_, 1, current_ - buffer_, fp_);
|
||||
current_ = buffer_;
|
||||
}
|
||||
}
|
||||
|
||||
// Not implemented
|
||||
char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char Take() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
FILE* fp_;
|
||||
char *buffer_;
|
||||
char *bufferEnd_;
|
||||
char *current_;
|
||||
};
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(FileWriteStream& stream, char c, size_t n) {
|
||||
stream.PutN(c, n);
|
||||
}
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
54
Engine/source/persistence/rapidjson/internal/pow10.h
Normal file
54
Engine/source/persistence/rapidjson/internal/pow10.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef RAPIDJSON_POW10_
|
||||
#define RAPIDJSON_POW10_
|
||||
|
||||
namespace rapidjson {
|
||||
namespace internal {
|
||||
|
||||
//! Computes integer powers of 10 in double (10.0^n).
|
||||
/*! This function uses lookup table for fast and accurate results.
|
||||
\param n positive/negative exponent. Must <= 308.
|
||||
\return 10.0^n
|
||||
*/
|
||||
inline double Pow10(int n) {
|
||||
static const double e[] = { // 1e-308...1e308: 617 * 8 bytes = 4936 bytes
|
||||
1e-308,1e-307,1e-306,1e-305,1e-304,1e-303,1e-302,1e-301,1e-300,
|
||||
1e-299,1e-298,1e-297,1e-296,1e-295,1e-294,1e-293,1e-292,1e-291,1e-290,1e-289,1e-288,1e-287,1e-286,1e-285,1e-284,1e-283,1e-282,1e-281,1e-280,
|
||||
1e-279,1e-278,1e-277,1e-276,1e-275,1e-274,1e-273,1e-272,1e-271,1e-270,1e-269,1e-268,1e-267,1e-266,1e-265,1e-264,1e-263,1e-262,1e-261,1e-260,
|
||||
1e-259,1e-258,1e-257,1e-256,1e-255,1e-254,1e-253,1e-252,1e-251,1e-250,1e-249,1e-248,1e-247,1e-246,1e-245,1e-244,1e-243,1e-242,1e-241,1e-240,
|
||||
1e-239,1e-238,1e-237,1e-236,1e-235,1e-234,1e-233,1e-232,1e-231,1e-230,1e-229,1e-228,1e-227,1e-226,1e-225,1e-224,1e-223,1e-222,1e-221,1e-220,
|
||||
1e-219,1e-218,1e-217,1e-216,1e-215,1e-214,1e-213,1e-212,1e-211,1e-210,1e-209,1e-208,1e-207,1e-206,1e-205,1e-204,1e-203,1e-202,1e-201,1e-200,
|
||||
1e-199,1e-198,1e-197,1e-196,1e-195,1e-194,1e-193,1e-192,1e-191,1e-190,1e-189,1e-188,1e-187,1e-186,1e-185,1e-184,1e-183,1e-182,1e-181,1e-180,
|
||||
1e-179,1e-178,1e-177,1e-176,1e-175,1e-174,1e-173,1e-172,1e-171,1e-170,1e-169,1e-168,1e-167,1e-166,1e-165,1e-164,1e-163,1e-162,1e-161,1e-160,
|
||||
1e-159,1e-158,1e-157,1e-156,1e-155,1e-154,1e-153,1e-152,1e-151,1e-150,1e-149,1e-148,1e-147,1e-146,1e-145,1e-144,1e-143,1e-142,1e-141,1e-140,
|
||||
1e-139,1e-138,1e-137,1e-136,1e-135,1e-134,1e-133,1e-132,1e-131,1e-130,1e-129,1e-128,1e-127,1e-126,1e-125,1e-124,1e-123,1e-122,1e-121,1e-120,
|
||||
1e-119,1e-118,1e-117,1e-116,1e-115,1e-114,1e-113,1e-112,1e-111,1e-110,1e-109,1e-108,1e-107,1e-106,1e-105,1e-104,1e-103,1e-102,1e-101,1e-100,
|
||||
1e-99, 1e-98, 1e-97, 1e-96, 1e-95, 1e-94, 1e-93, 1e-92, 1e-91, 1e-90, 1e-89, 1e-88, 1e-87, 1e-86, 1e-85, 1e-84, 1e-83, 1e-82, 1e-81, 1e-80,
|
||||
1e-79, 1e-78, 1e-77, 1e-76, 1e-75, 1e-74, 1e-73, 1e-72, 1e-71, 1e-70, 1e-69, 1e-68, 1e-67, 1e-66, 1e-65, 1e-64, 1e-63, 1e-62, 1e-61, 1e-60,
|
||||
1e-59, 1e-58, 1e-57, 1e-56, 1e-55, 1e-54, 1e-53, 1e-52, 1e-51, 1e-50, 1e-49, 1e-48, 1e-47, 1e-46, 1e-45, 1e-44, 1e-43, 1e-42, 1e-41, 1e-40,
|
||||
1e-39, 1e-38, 1e-37, 1e-36, 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30, 1e-29, 1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20,
|
||||
1e-19, 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e+0,
|
||||
1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
|
||||
1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
|
||||
1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
|
||||
1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
|
||||
1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
|
||||
1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
|
||||
1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
|
||||
1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
|
||||
1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
|
||||
1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
|
||||
1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
|
||||
1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
|
||||
1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
|
||||
1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
|
||||
1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
|
||||
1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
|
||||
};
|
||||
RAPIDJSON_ASSERT(n <= 308);
|
||||
return n < -308 ? 0.0 : e[n + 308];
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_POW10_
|
||||
83
Engine/source/persistence/rapidjson/internal/stack.h
Normal file
83
Engine/source/persistence/rapidjson/internal/stack.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#ifndef RAPIDJSON_INTERNAL_STACK_H_
|
||||
#define RAPIDJSON_INTERNAL_STACK_H_
|
||||
|
||||
namespace rapidjson {
|
||||
namespace internal {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Stack
|
||||
|
||||
//! A type-unsafe stack for storing different types of data.
|
||||
/*! \tparam Allocator Allocator for allocating stack memory.
|
||||
*/
|
||||
template <typename Allocator>
|
||||
class Stack {
|
||||
public:
|
||||
Stack(Allocator* allocator, size_t stack_capacity) : allocator_(allocator), own_allocator_(0), stack_(0), stack_top_(0), stack_end_(0), stack_capacity_(stack_capacity) {
|
||||
RAPIDJSON_ASSERT(stack_capacity_ > 0);
|
||||
if (!allocator_)
|
||||
own_allocator_ = allocator_ = new Allocator();
|
||||
stack_top_ = stack_ = (char*)allocator_->Malloc(stack_capacity_);
|
||||
stack_end_ = stack_ + stack_capacity_;
|
||||
}
|
||||
|
||||
~Stack() {
|
||||
Allocator::Free(stack_);
|
||||
delete own_allocator_; // Only delete if it is owned by the stack
|
||||
}
|
||||
|
||||
void Clear() { /*stack_top_ = 0;*/ stack_top_ = stack_; }
|
||||
|
||||
template<typename T>
|
||||
T* Push(size_t count = 1) {
|
||||
// Expand the stack if needed
|
||||
if (stack_top_ + sizeof(T) * count >= stack_end_) {
|
||||
size_t new_capacity = stack_capacity_ * 2;
|
||||
size_t size = GetSize();
|
||||
size_t new_size = GetSize() + sizeof(T) * count;
|
||||
if (new_capacity < new_size)
|
||||
new_capacity = new_size;
|
||||
stack_ = (char*)allocator_->Realloc(stack_, stack_capacity_, new_capacity);
|
||||
stack_capacity_ = new_capacity;
|
||||
stack_top_ = stack_ + size;
|
||||
stack_end_ = stack_ + stack_capacity_;
|
||||
}
|
||||
T* ret = (T*)stack_top_;
|
||||
stack_top_ += sizeof(T) * count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Pop(size_t count) {
|
||||
RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
|
||||
stack_top_ -= count * sizeof(T);
|
||||
return (T*)stack_top_;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Top() {
|
||||
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
|
||||
return (T*)(stack_top_ - sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Bottom() { return (T*)stack_; }
|
||||
|
||||
Allocator& GetAllocator() { return *allocator_; }
|
||||
bool Empty() const { return stack_top_ == stack_; }
|
||||
size_t GetSize() const { return stack_top_ - stack_; }
|
||||
size_t GetCapacity() const { return stack_capacity_; }
|
||||
|
||||
private:
|
||||
Allocator* allocator_;
|
||||
Allocator* own_allocator_;
|
||||
char *stack_;
|
||||
char *stack_top_;
|
||||
char *stack_end_;
|
||||
size_t stack_capacity_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_STACK_H_
|
||||
24
Engine/source/persistence/rapidjson/internal/strfunc.h
Normal file
24
Engine/source/persistence/rapidjson/internal/strfunc.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
#define RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
|
||||
namespace rapidjson {
|
||||
namespace internal {
|
||||
|
||||
//! Custom strlen() which works on different character types.
|
||||
/*! \tparam Ch Character type (e.g. char, wchar_t, short)
|
||||
\param s Null-terminated input string.
|
||||
\return Number of characters in the string.
|
||||
\note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
|
||||
*/
|
||||
template <typename Ch>
|
||||
inline SizeType StrLen(const Ch* s) {
|
||||
const Ch* p = s;
|
||||
while (*p != '\0')
|
||||
++p;
|
||||
return SizeType(p - s);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
160
Engine/source/persistence/rapidjson/prettywriter.h
Normal file
160
Engine/source/persistence/rapidjson/prettywriter.h
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
#ifndef RAPIDJSON_PRETTYWRITER_H_
|
||||
#define RAPIDJSON_PRETTYWRITER_H_
|
||||
|
||||
#include "writer.h"
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
//! Writer with indentation and spacing.
|
||||
/*!
|
||||
\tparam OutputStream Type of ouptut os.
|
||||
\tparam Encoding Encoding of both source strings and output.
|
||||
\tparam Allocator Type of allocator for allocating memory of stack.
|
||||
*/
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> >
|
||||
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, Allocator> {
|
||||
public:
|
||||
typedef Writer<OutputStream, SourceEncoding, TargetEncoding, Allocator> Base;
|
||||
typedef typename Base::Ch Ch;
|
||||
|
||||
//! Constructor
|
||||
/*! \param os Output os.
|
||||
\param allocator User supplied allocator. If it is null, it will create a private one.
|
||||
\param levelDepth Initial capacity of
|
||||
*/
|
||||
PrettyWriter(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
|
||||
Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
|
||||
|
||||
//! Set custom indentation.
|
||||
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\t', '\n', '\r').
|
||||
\param indentCharCount Number of indent characters for each indentation level.
|
||||
\note The default indentation is 4 spaces.
|
||||
*/
|
||||
PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
|
||||
RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
|
||||
indentChar_ = indentChar;
|
||||
indentCharCount_ = indentCharCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//@name Implementation of Handler.
|
||||
//@{
|
||||
|
||||
PrettyWriter& Null() { PrettyPrefix(kNullType); Base::WriteNull(); return *this; }
|
||||
PrettyWriter& Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); Base::WriteBool(b); return *this; }
|
||||
PrettyWriter& Int(int i) { PrettyPrefix(kNumberType); Base::WriteInt(i); return *this; }
|
||||
PrettyWriter& Uint(unsigned u) { PrettyPrefix(kNumberType); Base::WriteUint(u); return *this; }
|
||||
PrettyWriter& Int64(int64_t i64) { PrettyPrefix(kNumberType); Base::WriteInt64(i64); return *this; }
|
||||
PrettyWriter& Uint64(uint64_t u64) { PrettyPrefix(kNumberType); Base::WriteUint64(u64); return *this; }
|
||||
PrettyWriter& Double(double d) { PrettyPrefix(kNumberType); Base::WriteDouble(d); return *this; }
|
||||
|
||||
PrettyWriter& String(const Ch* str, SizeType length, bool copy = false) {
|
||||
(void)copy;
|
||||
PrettyPrefix(kStringType);
|
||||
Base::WriteString(str, length);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PrettyWriter& StartObject() {
|
||||
PrettyPrefix(kObjectType);
|
||||
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
|
||||
Base::WriteStartObject();
|
||||
return *this;
|
||||
}
|
||||
|
||||
PrettyWriter& EndObject(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||
RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
||||
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
|
||||
|
||||
if (!empty) {
|
||||
Base::os_.Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
Base::WriteEndObject();
|
||||
if (Base::level_stack_.Empty()) // end of json text
|
||||
Base::os_.flush();
|
||||
return *this;
|
||||
}
|
||||
|
||||
PrettyWriter& StartArray() {
|
||||
PrettyPrefix(kArrayType);
|
||||
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
|
||||
Base::WriteStartArray();
|
||||
return *this;
|
||||
}
|
||||
|
||||
PrettyWriter& EndArray(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
||||
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
|
||||
|
||||
if (!empty) {
|
||||
Base::os_.Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
Base::WriteEndArray();
|
||||
if (Base::level_stack_.Empty()) // end of json text
|
||||
Base::os_.flush();
|
||||
return *this;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
//! Simpler but slower overload.
|
||||
PrettyWriter& String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||
|
||||
protected:
|
||||
void PrettyPrefix(Type type) {
|
||||
(void)type;
|
||||
if (Base::level_stack_.GetSize() != 0) { // this value is not at root
|
||||
typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
|
||||
|
||||
if (level->inArray) {
|
||||
if (level->valueCount > 0) {
|
||||
Base::os_.Put(','); // add comma if it is not the first element in array
|
||||
Base::os_.Put('\n');
|
||||
}
|
||||
else
|
||||
Base::os_.Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
else { // in object
|
||||
if (level->valueCount > 0) {
|
||||
if (level->valueCount % 2 == 0) {
|
||||
Base::os_.Put(',');
|
||||
Base::os_.Put('\n');
|
||||
}
|
||||
else {
|
||||
Base::os_.Put(':');
|
||||
Base::os_.Put(' ');
|
||||
}
|
||||
}
|
||||
else
|
||||
Base::os_.Put('\n');
|
||||
|
||||
if (level->valueCount % 2 == 0)
|
||||
WriteIndent();
|
||||
}
|
||||
if (!level->inArray && level->valueCount % 2 == 0)
|
||||
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
|
||||
level->valueCount++;
|
||||
}
|
||||
else
|
||||
RAPIDJSON_ASSERT(type == kObjectType || type == kArrayType);
|
||||
}
|
||||
|
||||
void WriteIndent() {
|
||||
size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
|
||||
PutN(Base::os_, indentChar_, count);
|
||||
}
|
||||
|
||||
Ch indentChar_;
|
||||
unsigned indentCharCount_;
|
||||
};
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
||||
256
Engine/source/persistence/rapidjson/rapidjson.h
Normal file
256
Engine/source/persistence/rapidjson/rapidjson.h
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
#ifndef RAPIDJSON_RAPIDJSON_H_
|
||||
#define RAPIDJSON_RAPIDJSON_H_
|
||||
|
||||
// Copyright (c) 2011 Milo Yip (miloyip@gmail.com)
|
||||
// Version 0.1
|
||||
|
||||
#include <cstdlib> // malloc(), realloc(), free()
|
||||
#include <cstring> // memcpy()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NO_INT64DEFINE
|
||||
|
||||
// Here defines int64_t and uint64_t types in global namespace.
|
||||
// If user have their own definition, can define RAPIDJSON_NO_INT64DEFINE to disable this.
|
||||
#ifndef RAPIDJSON_NO_INT64DEFINE
|
||||
#ifdef _MSC_VER
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#define RAPIDJSON_FORCEINLINE __forceinline
|
||||
#else
|
||||
#include <inttypes.h>
|
||||
#define RAPIDJSON_FORCEINLINE
|
||||
#endif
|
||||
#endif // RAPIDJSON_NO_INT64TYPEDEF
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ENDIAN
|
||||
#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine
|
||||
#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine
|
||||
|
||||
//! Endianness of the machine.
|
||||
/*! GCC provided macro for detecting endianness of the target machine. But other
|
||||
compilers may not have this. User can define RAPIDJSON_ENDIAN to either
|
||||
RAPIDJSON_LITTLEENDIAN or RAPIDJSON_BIGENDIAN.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ENDIAN
|
||||
#ifdef __BYTE_ORDER__
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
#else
|
||||
#define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
#endif // __BYTE_ORDER__
|
||||
#else
|
||||
#define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN // Assumes little endian otherwise.
|
||||
#endif
|
||||
#endif // RAPIDJSON_ENDIAN
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ALIGNSIZE
|
||||
|
||||
//! Data alignment of the machine.
|
||||
/*!
|
||||
Some machine requires strict data alignment.
|
||||
Currently the default uses 4 bytes alignment. User can customize this.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ALIGN
|
||||
#define RAPIDJSON_ALIGN(x) ((x + 3) & ~3)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD
|
||||
|
||||
// Enable SSE2 optimization.
|
||||
//#define RAPIDJSON_SSE2
|
||||
|
||||
// Enable SSE4.2 optimization.
|
||||
//#define RAPIDJSON_SSE42
|
||||
|
||||
#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
|
||||
#define RAPIDJSON_SIMD
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
|
||||
#ifndef RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
namespace rapidjson {
|
||||
//! Use 32-bit array/string indices even for 64-bit platform, instead of using size_t.
|
||||
/*! User may override the SizeType by defining RAPIDJSON_NO_SIZETYPEDEFINE.
|
||||
*/
|
||||
typedef unsigned SizeType;
|
||||
} // namespace rapidjson
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ASSERT
|
||||
|
||||
//! Assertion.
|
||||
/*! By default, rapidjson uses C assert() for assertion.
|
||||
User can override it by defining RAPIDJSON_ASSERT(x) macro.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ASSERT
|
||||
#include <cassert>
|
||||
#define RAPIDJSON_ASSERT(x) assert(x)
|
||||
#endif // RAPIDJSON_ASSERT
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_STATIC_ASSERT
|
||||
|
||||
// Adopt from boost
|
||||
#ifndef RAPIDJSON_STATIC_ASSERT
|
||||
namespace rapidjson {
|
||||
template <bool x> struct STATIC_ASSERTION_FAILURE;
|
||||
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
|
||||
template<int x> struct StaticAssertTest {};
|
||||
} // namespace rapidjson
|
||||
|
||||
#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y)
|
||||
#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y)
|
||||
#define RAPIDJSON_DO_JOIN2(X, Y) X##Y
|
||||
|
||||
#define RAPIDJSON_STATIC_ASSERT(x) typedef ::rapidjson::StaticAssertTest<\
|
||||
sizeof(::rapidjson::STATIC_ASSERTION_FAILURE<bool(x) >)>\
|
||||
RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
|
||||
#define RAPIDJSON_MULTILINEMACRO_BEGIN do {
|
||||
#define RAPIDJSON_MULTILINEMACRO_END \
|
||||
} while((void)0, 0)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Allocators and Encodings
|
||||
|
||||
#include "allocators.h"
|
||||
#include "encodings.h"
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Stream
|
||||
|
||||
/*! \class rapidjson::Stream
|
||||
\brief Concept for reading and writing characters.
|
||||
|
||||
For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
|
||||
|
||||
For write-only stream, only need to implement Put() and Flush().
|
||||
|
||||
\code
|
||||
concept Stream {
|
||||
typename Ch; //!< Character type of the stream.
|
||||
|
||||
//! Read the current character from stream without moving the read cursor.
|
||||
Ch Peek() const;
|
||||
|
||||
//! Read the current character from stream and moving the read cursor to next character.
|
||||
Ch Take();
|
||||
|
||||
//! Get the current read cursor.
|
||||
//! \return Number of characters read from start.
|
||||
size_t Tell();
|
||||
|
||||
//! Begin writing operation at the current read pointer.
|
||||
//! \return The begin writer pointer.
|
||||
Ch* PutBegin();
|
||||
|
||||
//! Write a character.
|
||||
void Put(Ch c);
|
||||
|
||||
//! Flush the buffer.
|
||||
void Flush();
|
||||
|
||||
//! End the writing operation.
|
||||
//! \param begin The begin write pointer returned by PutBegin().
|
||||
//! \return Number of characters written.
|
||||
size_t PutEnd(Ch* begin);
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
|
||||
//! Put N copies of a character to a stream.
|
||||
template<typename Stream, typename Ch>
|
||||
inline void PutN(Stream& stream, Ch c, size_t n) {
|
||||
for (size_t i = 0; i < n; i++)
|
||||
stream.Put(c);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// StringStream
|
||||
|
||||
//! Read-only string stream.
|
||||
/*! \implements Stream
|
||||
*/
|
||||
template <typename Encoding>
|
||||
struct GenericStringStream {
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericStringStream(const Ch *src) : src_(src), head_(src) {}
|
||||
|
||||
Ch Peek() const { return *src_; }
|
||||
Ch Take() { return *src_++; }
|
||||
size_t Tell() const { return src_ - head_; }
|
||||
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
const Ch* src_; //!< Current read position.
|
||||
const Ch* head_; //!< Original head of the string.
|
||||
};
|
||||
|
||||
typedef GenericStringStream<UTF8<> > StringStream;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// InsituStringStream
|
||||
|
||||
//! A read-write string stream.
|
||||
/*! This string stream is particularly designed for in-situ parsing.
|
||||
\implements Stream
|
||||
*/
|
||||
template <typename Encoding>
|
||||
struct GenericInsituStringStream {
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
|
||||
|
||||
// Read
|
||||
Ch Peek() { return *src_; }
|
||||
Ch Take() { return *src_++; }
|
||||
size_t Tell() { return src_ - head_; }
|
||||
|
||||
// Write
|
||||
Ch* PutBegin() { return dst_ = src_; }
|
||||
void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
|
||||
void Flush() {}
|
||||
size_t PutEnd(Ch* begin) { return dst_ - begin; }
|
||||
|
||||
Ch* src_;
|
||||
Ch* dst_;
|
||||
Ch* head_;
|
||||
};
|
||||
|
||||
typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Type
|
||||
|
||||
//! Type of JSON value
|
||||
enum Type {
|
||||
kNullType = 0, //!< null
|
||||
kFalseType = 1, //!< false
|
||||
kTrueType = 2, //!< true
|
||||
kObjectType = 3, //!< object
|
||||
kArrayType = 4, //!< array
|
||||
kStringType = 5, //!< string
|
||||
kNumberType = 6, //!< number
|
||||
};
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
||||
669
Engine/source/persistence/rapidjson/reader.h
Normal file
669
Engine/source/persistence/rapidjson/reader.h
Normal file
|
|
@ -0,0 +1,669 @@
|
|||
#ifndef RAPIDJSON_READER_H_
|
||||
#define RAPIDJSON_READER_H_
|
||||
|
||||
// Copyright (c) 2011 Milo Yip (miloyip@gmail.com)
|
||||
// Version 0.1
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "encodings.h"
|
||||
#include "internal/pow10.h"
|
||||
#include "internal/stack.h"
|
||||
#include <csetjmp>
|
||||
|
||||
#ifdef RAPIDJSON_SSE42
|
||||
#include <nmmintrin.h>
|
||||
#elif defined(RAPIDJSON_SSE2)
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
#ifndef RAPIDJSON_PARSE_ERROR
|
||||
#define RAPIDJSON_PARSE_ERROR(msg, offset) \
|
||||
RAPIDJSON_MULTILINEMACRO_BEGIN \
|
||||
parseError_ = msg; \
|
||||
errorOffset_ = offset; \
|
||||
longjmp(jmpbuf_, 1); \
|
||||
RAPIDJSON_MULTILINEMACRO_END
|
||||
#endif
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ParseFlag
|
||||
|
||||
//! Combination of parseFlags
|
||||
enum ParseFlag {
|
||||
kParseDefaultFlags = 0, //!< Default parse flags. Non-destructive parsing. Text strings are decoded into allocated buffer.
|
||||
kParseInsituFlag = 1, //!< In-situ(destructive) parsing.
|
||||
kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings.
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Handler
|
||||
|
||||
/*! \class rapidjson::Handler
|
||||
\brief Concept for receiving events from GenericReader upon parsing.
|
||||
\code
|
||||
concept Handler {
|
||||
typename Ch;
|
||||
|
||||
void Null();
|
||||
void Bool(bool b);
|
||||
void Int(int i);
|
||||
void Uint(unsigned i);
|
||||
void Int64(int64_t i);
|
||||
void Uint64(uint64_t i);
|
||||
void Double(double d);
|
||||
void String(const Ch* str, SizeType length, bool copy);
|
||||
void StartObject();
|
||||
void EndObject(SizeType memberCount);
|
||||
void StartArray();
|
||||
void EndArray(SizeType elementCount);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// BaseReaderHandler
|
||||
|
||||
//! Default implementation of Handler.
|
||||
/*! This can be used as base class of any reader handler.
|
||||
\implements Handler
|
||||
*/
|
||||
template<typename Encoding = UTF8<> >
|
||||
struct BaseReaderHandler {
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
void Default() {}
|
||||
void Null() { Default(); }
|
||||
void Bool(bool) { Default(); }
|
||||
void Int(int) { Default(); }
|
||||
void Uint(unsigned) { Default(); }
|
||||
void Int64(int64_t) { Default(); }
|
||||
void Uint64(uint64_t) { Default(); }
|
||||
void Double(double) { Default(); }
|
||||
void String(const Ch*, SizeType, bool) { Default(); }
|
||||
void StartObject() { Default(); }
|
||||
void EndObject(SizeType) { Default(); }
|
||||
void StartArray() { Default(); }
|
||||
void EndArray(SizeType) { Default(); }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SkipWhitespace
|
||||
|
||||
//! Skip the JSON white spaces in a stream.
|
||||
/*! \param stream A input stream for skipping white spaces.
|
||||
\note This function has SSE2/SSE4.2 specialization.
|
||||
*/
|
||||
template<typename InputStream>
|
||||
void SkipWhitespace(InputStream& is) {
|
||||
InputStream s = is; // Use a local copy for optimization
|
||||
while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t')
|
||||
s.Take();
|
||||
is = s;
|
||||
}
|
||||
|
||||
#ifdef RAPIDJSON_SSE42
|
||||
//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once.
|
||||
inline const char *SkipWhitespace_SIMD(const char* p) {
|
||||
static const char whitespace[16] = " \n\r\t";
|
||||
__m128i w = _mm_loadu_si128((const __m128i *)&whitespace[0]);
|
||||
|
||||
for (;;) {
|
||||
__m128i s = _mm_loadu_si128((const __m128i *)p);
|
||||
unsigned r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY));
|
||||
if (r == 0) // all 16 characters are whitespace
|
||||
p += 16;
|
||||
else { // some of characters may be non-whitespace
|
||||
#ifdef _MSC_VER // Find the index of first non-whitespace
|
||||
unsigned long offset;
|
||||
if (_BitScanForward(&offset, r))
|
||||
return p + offset;
|
||||
#else
|
||||
if (r != 0)
|
||||
return p + __builtin_ffs(r) - 1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(RAPIDJSON_SSE2)
|
||||
|
||||
//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once.
|
||||
inline const char *SkipWhitespace_SIMD(const char* p) {
|
||||
static const char whitespaces[4][17] = {
|
||||
" ",
|
||||
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
|
||||
"\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r",
|
||||
"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"};
|
||||
|
||||
__m128i w0 = _mm_loadu_si128((const __m128i *)&whitespaces[0][0]);
|
||||
__m128i w1 = _mm_loadu_si128((const __m128i *)&whitespaces[1][0]);
|
||||
__m128i w2 = _mm_loadu_si128((const __m128i *)&whitespaces[2][0]);
|
||||
__m128i w3 = _mm_loadu_si128((const __m128i *)&whitespaces[3][0]);
|
||||
|
||||
for (;;) {
|
||||
__m128i s = _mm_loadu_si128((const __m128i *)p);
|
||||
__m128i x = _mm_cmpeq_epi8(s, w0);
|
||||
x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1));
|
||||
x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2));
|
||||
x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3));
|
||||
unsigned short r = ~_mm_movemask_epi8(x);
|
||||
if (r == 0) // all 16 characters are whitespace
|
||||
p += 16;
|
||||
else { // some of characters may be non-whitespace
|
||||
#ifdef _MSC_VER // Find the index of first non-whitespace
|
||||
unsigned long offset;
|
||||
if (_BitScanForward(&offset, r))
|
||||
return p + offset;
|
||||
#else
|
||||
if (r != 0)
|
||||
return p + __builtin_ffs(r) - 1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // RAPIDJSON_SSE2
|
||||
|
||||
#ifdef RAPIDJSON_SIMD
|
||||
//! Template function specialization for InsituStringStream
|
||||
template<> inline void SkipWhitespace(InsituStringStream& is) {
|
||||
is.src_ = const_cast<char*>(SkipWhitespace_SIMD(is.src_));
|
||||
}
|
||||
|
||||
//! Template function specialization for StringStream
|
||||
template<> inline void SkipWhitespace(StringStream& is) {
|
||||
is.src_ = SkipWhitespace_SIMD(is.src_);
|
||||
}
|
||||
#endif // RAPIDJSON_SIMD
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GenericReader
|
||||
|
||||
//! SAX-style JSON parser. Use Reader for UTF8 encoding and default allocator.
|
||||
/*! GenericReader parses JSON text from a stream, and send events synchronously to an
|
||||
object implementing Handler concept.
|
||||
|
||||
It needs to allocate a stack for storing a single decoded string during
|
||||
non-destructive parsing.
|
||||
|
||||
For in-situ parsing, the decoded string is directly written to the source
|
||||
text string, no temporary buffer is required.
|
||||
|
||||
A GenericReader object can be reused for parsing multiple JSON text.
|
||||
|
||||
\tparam SourceEncoding Encoding of the input stream.
|
||||
\tparam TargetEncoding Encoding of the parse output.
|
||||
\tparam Allocator Allocator type for stack.
|
||||
*/
|
||||
template <typename SourceEncoding, typename TargetEncoding, typename Allocator = MemoryPoolAllocator<> >
|
||||
class GenericReader {
|
||||
public:
|
||||
typedef typename SourceEncoding::Ch Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
|
||||
\param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing)
|
||||
*/
|
||||
GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseError_(0), errorOffset_(0) {}
|
||||
|
||||
//! Parse JSON text.
|
||||
/*! \tparam parseFlags Combination of ParseFlag.
|
||||
\tparam InputStream Type of input stream.
|
||||
\tparam Handler Type of handler which must implement Handler concept.
|
||||
\param stream Input stream to be parsed.
|
||||
\param handler The handler to receive events.
|
||||
\return Whether the parsing is successful.
|
||||
*/
|
||||
template <unsigned parseFlags, typename InputStream, typename Handler>
|
||||
bool Parse(InputStream& is, Handler& handler) {
|
||||
parseError_ = 0;
|
||||
errorOffset_ = 0;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4611) // interaction between '_setjmp' and C++ object destruction is non-portable
|
||||
#endif
|
||||
if (setjmp(jmpbuf_)) {
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
stack_.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
SkipWhitespace(is);
|
||||
|
||||
if (is.Peek() == '\0')
|
||||
RAPIDJSON_PARSE_ERROR("Text only contains white space(s)", is.Tell());
|
||||
else {
|
||||
switch (is.Peek()) {
|
||||
case '{': ParseObject<parseFlags>(is, handler); break;
|
||||
case '[': ParseArray<parseFlags>(is, handler); break;
|
||||
default: RAPIDJSON_PARSE_ERROR("Expect either an object or array at root", is.Tell());
|
||||
}
|
||||
SkipWhitespace(is);
|
||||
|
||||
if (is.Peek() != '\0')
|
||||
RAPIDJSON_PARSE_ERROR("Nothing should follow the root object or array.", is.Tell());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HasParseError() const { return parseError_ != 0; }
|
||||
const char* GetParseError() const { return parseError_; }
|
||||
size_t GetErrorOffset() const { return errorOffset_; }
|
||||
|
||||
private:
|
||||
// Parse object: { string : value, ... }
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
void ParseObject(InputStream& is, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(is.Peek() == '{');
|
||||
is.Take(); // Skip '{'
|
||||
handler.StartObject();
|
||||
SkipWhitespace(is);
|
||||
|
||||
if (is.Peek() == '}') {
|
||||
is.Take();
|
||||
handler.EndObject(0); // empty object
|
||||
return;
|
||||
}
|
||||
|
||||
for (SizeType memberCount = 0;;) {
|
||||
if (is.Peek() != '"')
|
||||
RAPIDJSON_PARSE_ERROR("Name of an object member must be a string", is.Tell());
|
||||
|
||||
ParseString<parseFlags>(is, handler);
|
||||
SkipWhitespace(is);
|
||||
|
||||
if (is.Take() != ':')
|
||||
RAPIDJSON_PARSE_ERROR("There must be a colon after the name of object member", is.Tell());
|
||||
|
||||
SkipWhitespace(is);
|
||||
|
||||
ParseValue<parseFlags>(is, handler);
|
||||
SkipWhitespace(is);
|
||||
|
||||
++memberCount;
|
||||
|
||||
switch(is.Take()) {
|
||||
case ',': SkipWhitespace(is); break;
|
||||
case '}': handler.EndObject(memberCount); return;
|
||||
default: RAPIDJSON_PARSE_ERROR("Must be a comma or '}' after an object member", is.Tell());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse array: [ value, ... ]
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
void ParseArray(InputStream& is, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(is.Peek() == '[');
|
||||
is.Take(); // Skip '['
|
||||
handler.StartArray();
|
||||
SkipWhitespace(is);
|
||||
|
||||
if (is.Peek() == ']') {
|
||||
is.Take();
|
||||
handler.EndArray(0); // empty array
|
||||
return;
|
||||
}
|
||||
|
||||
for (SizeType elementCount = 0;;) {
|
||||
ParseValue<parseFlags>(is, handler);
|
||||
++elementCount;
|
||||
SkipWhitespace(is);
|
||||
|
||||
switch (is.Take()) {
|
||||
case ',': SkipWhitespace(is); break;
|
||||
case ']': handler.EndArray(elementCount); return;
|
||||
default: RAPIDJSON_PARSE_ERROR("Must be a comma or ']' after an array element.", is.Tell());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
void ParseNull(InputStream& is, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(is.Peek() == 'n');
|
||||
is.Take();
|
||||
|
||||
if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l')
|
||||
handler.Null();
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell() - 1);
|
||||
}
|
||||
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
void ParseTrue(InputStream& is, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(is.Peek() == 't');
|
||||
is.Take();
|
||||
|
||||
if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e')
|
||||
handler.Bool(true);
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell());
|
||||
}
|
||||
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
void ParseFalse(InputStream& is, Handler& handler) {
|
||||
RAPIDJSON_ASSERT(is.Peek() == 'f');
|
||||
is.Take();
|
||||
|
||||
if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e')
|
||||
handler.Bool(false);
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell() - 1);
|
||||
}
|
||||
|
||||
// Helper function to parse four hexidecimal digits in \uXXXX in ParseString().
|
||||
template<typename InputStream>
|
||||
unsigned ParseHex4(InputStream& is) {
|
||||
InputStream s = is; // Use a local copy for optimization
|
||||
unsigned codepoint = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Ch c = s.Take();
|
||||
codepoint <<= 4;
|
||||
codepoint += c;
|
||||
if (c >= '0' && c <= '9')
|
||||
codepoint -= '0';
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
codepoint -= 'A' - 10;
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
codepoint -= 'a' - 10;
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR("Incorrect hex digit after \\u escape", s.Tell() - 1);
|
||||
}
|
||||
is = s; // Restore is
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
class StackStream {
|
||||
public:
|
||||
typedef typename TargetEncoding::Ch Ch;
|
||||
|
||||
StackStream(internal::Stack<Allocator>& stack) : stack_(stack), length_(0) {}
|
||||
void Put(Ch c) {
|
||||
*stack_.template Push<Ch>() = c;
|
||||
++length_;
|
||||
}
|
||||
internal::Stack<Allocator>& stack_;
|
||||
SizeType length_;
|
||||
|
||||
private:
|
||||
// Prohibit assignment for VC C4512 warning
|
||||
StackStream& operator=(const StackStream&);
|
||||
};
|
||||
|
||||
// Parse string and generate String event. Different code paths for kParseInsituFlag.
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
void ParseString(InputStream& is, Handler& handler) {
|
||||
InputStream s = is; // Local copy for optimization
|
||||
if (parseFlags & kParseInsituFlag) {
|
||||
Ch *head = s.PutBegin();
|
||||
ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s);
|
||||
size_t length = s.PutEnd(head) - 1;
|
||||
RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
|
||||
handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false);
|
||||
}
|
||||
else {
|
||||
StackStream stackStream(stack_);
|
||||
ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream);
|
||||
handler.String(stack_.template Pop<typename TargetEncoding::Ch>(stackStream.length_), stackStream.length_ - 1, true);
|
||||
}
|
||||
is = s; // Restore is
|
||||
}
|
||||
|
||||
// Parse string to an output is
|
||||
// This function handles the prefix/suffix double quotes, escaping, and optional encoding validation.
|
||||
template<unsigned parseFlags, typename SEncoding, typename TEncoding, typename InputStream, typename OutputStream>
|
||||
RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) {
|
||||
#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
static const char escape[256] = {
|
||||
Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/',
|
||||
Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0,
|
||||
0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0,
|
||||
0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16
|
||||
};
|
||||
#undef Z16
|
||||
|
||||
RAPIDJSON_ASSERT(is.Peek() == '\"');
|
||||
is.Take(); // Skip '\"'
|
||||
|
||||
for (;;) {
|
||||
Ch c = is.Peek();
|
||||
if (c == '\\') { // Escape
|
||||
is.Take();
|
||||
Ch e = is.Take();
|
||||
if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e])
|
||||
os.Put(escape[(unsigned char)e]);
|
||||
else if (e == 'u') { // Unicode
|
||||
unsigned codepoint = ParseHex4(is);
|
||||
if (codepoint >= 0xD800 && codepoint <= 0xDBFF) {
|
||||
// Handle UTF-16 surrogate pair
|
||||
if (is.Take() != '\\' || is.Take() != 'u')
|
||||
RAPIDJSON_PARSE_ERROR("Missing the second \\u in surrogate pair", is.Tell() - 2);
|
||||
unsigned codepoint2 = ParseHex4(is);
|
||||
if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)
|
||||
RAPIDJSON_PARSE_ERROR("The second \\u in surrogate pair is invalid", is.Tell() - 2);
|
||||
codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000;
|
||||
}
|
||||
TEncoding::Encode(os, codepoint);
|
||||
}
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR("Unknown escape character", is.Tell() - 1);
|
||||
}
|
||||
else if (c == '"') { // Closing double quote
|
||||
is.Take();
|
||||
os.Put('\0'); // null-terminate the string
|
||||
return;
|
||||
}
|
||||
else if (c == '\0')
|
||||
RAPIDJSON_PARSE_ERROR("lacks ending quotation before the end of string", is.Tell() - 1);
|
||||
else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
|
||||
RAPIDJSON_PARSE_ERROR("Incorrect unescaped character in string", is.Tell() - 1);
|
||||
else {
|
||||
if (parseFlags & kParseValidateEncodingFlag ?
|
||||
!Transcoder<SEncoding, TEncoding>::Validate(is, os) :
|
||||
!Transcoder<SEncoding, TEncoding>::Transcode(is, os))
|
||||
RAPIDJSON_PARSE_ERROR("Invalid encoding", is.Tell());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
void ParseNumber(InputStream& is, Handler& handler) {
|
||||
InputStream s = is; // Local copy for optimization
|
||||
// Parse minus
|
||||
bool minus = false;
|
||||
if (s.Peek() == '-') {
|
||||
minus = true;
|
||||
s.Take();
|
||||
}
|
||||
|
||||
// Parse int: zero / ( digit1-9 *DIGIT )
|
||||
unsigned i;
|
||||
bool try64bit = false;
|
||||
if (s.Peek() == '0') {
|
||||
i = 0;
|
||||
s.Take();
|
||||
}
|
||||
else if (s.Peek() >= '1' && s.Peek() <= '9') {
|
||||
i = s.Take() - '0';
|
||||
|
||||
if (minus)
|
||||
while (s.Peek() >= '0' && s.Peek() <= '9') {
|
||||
if (i >= 214748364) { // 2^31 = 2147483648
|
||||
if (i != 214748364 || s.Peek() > '8') {
|
||||
try64bit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i = i * 10 + (s.Take() - '0');
|
||||
}
|
||||
else
|
||||
while (s.Peek() >= '0' && s.Peek() <= '9') {
|
||||
if (i >= 429496729) { // 2^32 - 1 = 4294967295
|
||||
if (i != 429496729 || s.Peek() > '5') {
|
||||
try64bit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i = i * 10 + (s.Take() - '0');
|
||||
}
|
||||
}
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR("Expect a value here.", is.Tell());
|
||||
|
||||
// Parse 64bit int
|
||||
uint64_t i64 = 0;
|
||||
bool useDouble = false;
|
||||
if (try64bit) {
|
||||
i64 = i;
|
||||
if (minus)
|
||||
while (s.Peek() >= '0' && s.Peek() <= '9') {
|
||||
if (i64 >= 922337203685477580uLL) // 2^63 = 9223372036854775808
|
||||
if (i64 != 922337203685477580uLL || s.Peek() > '8') {
|
||||
useDouble = true;
|
||||
break;
|
||||
}
|
||||
i64 = i64 * 10 + (s.Take() - '0');
|
||||
}
|
||||
else
|
||||
while (s.Peek() >= '0' && s.Peek() <= '9') {
|
||||
if (i64 >= 1844674407370955161uLL) // 2^64 - 1 = 18446744073709551615
|
||||
if (i64 != 1844674407370955161uLL || s.Peek() > '5') {
|
||||
useDouble = true;
|
||||
break;
|
||||
}
|
||||
i64 = i64 * 10 + (s.Take() - '0');
|
||||
}
|
||||
}
|
||||
|
||||
// Force double for big integer
|
||||
double d = 0.0;
|
||||
if (useDouble) {
|
||||
d = (double)i64;
|
||||
while (s.Peek() >= '0' && s.Peek() <= '9') {
|
||||
if (d >= 1E307)
|
||||
RAPIDJSON_PARSE_ERROR("Number too big to store in double", is.Tell());
|
||||
d = d * 10 + (s.Take() - '0');
|
||||
}
|
||||
}
|
||||
|
||||
// Parse frac = decimal-point 1*DIGIT
|
||||
int expFrac = 0;
|
||||
if (s.Peek() == '.') {
|
||||
if (!useDouble) {
|
||||
d = try64bit ? (double)i64 : (double)i;
|
||||
useDouble = true;
|
||||
}
|
||||
s.Take();
|
||||
|
||||
if (s.Peek() >= '0' && s.Peek() <= '9') {
|
||||
d = d * 10 + (s.Take() - '0');
|
||||
--expFrac;
|
||||
}
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR("At least one digit in fraction part", is.Tell());
|
||||
|
||||
while (s.Peek() >= '0' && s.Peek() <= '9') {
|
||||
if (expFrac > -16) {
|
||||
d = d * 10 + (s.Peek() - '0');
|
||||
--expFrac;
|
||||
}
|
||||
s.Take();
|
||||
}
|
||||
}
|
||||
|
||||
// Parse exp = e [ minus / plus ] 1*DIGIT
|
||||
int exp = 0;
|
||||
if (s.Peek() == 'e' || s.Peek() == 'E') {
|
||||
if (!useDouble) {
|
||||
d = try64bit ? (double)i64 : (double)i;
|
||||
useDouble = true;
|
||||
}
|
||||
s.Take();
|
||||
|
||||
bool expMinus = false;
|
||||
if (s.Peek() == '+')
|
||||
s.Take();
|
||||
else if (s.Peek() == '-') {
|
||||
s.Take();
|
||||
expMinus = true;
|
||||
}
|
||||
|
||||
if (s.Peek() >= '0' && s.Peek() <= '9') {
|
||||
exp = s.Take() - '0';
|
||||
while (s.Peek() >= '0' && s.Peek() <= '9') {
|
||||
exp = exp * 10 + (s.Take() - '0');
|
||||
if (exp > 308)
|
||||
RAPIDJSON_PARSE_ERROR("Number too big to store in double", is.Tell());
|
||||
}
|
||||
}
|
||||
else
|
||||
RAPIDJSON_PARSE_ERROR("At least one digit in exponent", s.Tell());
|
||||
|
||||
if (expMinus)
|
||||
exp = -exp;
|
||||
}
|
||||
|
||||
// Finish parsing, call event according to the type of number.
|
||||
if (useDouble) {
|
||||
d *= internal::Pow10(exp + expFrac);
|
||||
handler.Double(minus ? -d : d);
|
||||
}
|
||||
else {
|
||||
if (try64bit) {
|
||||
if (minus)
|
||||
handler.Int64(-(int64_t)i64);
|
||||
else
|
||||
handler.Uint64(i64);
|
||||
}
|
||||
else {
|
||||
if (minus)
|
||||
handler.Int(-(int)i);
|
||||
else
|
||||
handler.Uint(i);
|
||||
}
|
||||
}
|
||||
|
||||
is = s; // restore is
|
||||
}
|
||||
|
||||
// Parse any JSON value
|
||||
template<unsigned parseFlags, typename InputStream, typename Handler>
|
||||
void ParseValue(InputStream& is, Handler& handler) {
|
||||
switch (is.Peek()) {
|
||||
case 'n': ParseNull <parseFlags>(is, handler); break;
|
||||
case 't': ParseTrue <parseFlags>(is, handler); break;
|
||||
case 'f': ParseFalse <parseFlags>(is, handler); break;
|
||||
case '"': ParseString<parseFlags>(is, handler); break;
|
||||
case '{': ParseObject<parseFlags>(is, handler); break;
|
||||
case '[': ParseArray <parseFlags>(is, handler); break;
|
||||
default : ParseNumber<parseFlags>(is, handler);
|
||||
}
|
||||
}
|
||||
|
||||
static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string.
|
||||
internal::Stack<Allocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing.
|
||||
jmp_buf jmpbuf_; //!< setjmp buffer for fast exit from nested parsing function calls.
|
||||
const char* parseError_;
|
||||
size_t errorOffset_;
|
||||
}; // class GenericReader
|
||||
|
||||
//! Reader with UTF8 encoding and default allocator.
|
||||
typedef GenericReader<UTF8<>, UTF8<> > Reader;
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_READER_H_
|
||||
50
Engine/source/persistence/rapidjson/stringbuffer.h
Normal file
50
Engine/source/persistence/rapidjson/stringbuffer.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef RAPIDJSON_STRINGBUFFER_H_
|
||||
#define RAPIDJSON_STRINGBUFFER_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "internal/stack.h"
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
//! Represents an in-memory output stream.
|
||||
/*!
|
||||
\tparam Encoding Encoding of the stream.
|
||||
\tparam Allocator type for allocating memory buffer.
|
||||
\implements Stream
|
||||
*/
|
||||
template <typename Encoding, typename Allocator = CrtAllocator>
|
||||
struct GenericStringBuffer {
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
|
||||
|
||||
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
|
||||
void Flush() {}
|
||||
|
||||
void Clear() { stack_.Clear(); }
|
||||
|
||||
const Ch* GetString() const {
|
||||
// Push and pop a null terminator. This is safe.
|
||||
*stack_.template Push<Ch>() = '\0';
|
||||
stack_.template Pop<Ch>(1);
|
||||
|
||||
return stack_.template Bottom<Ch>();
|
||||
}
|
||||
|
||||
size_t GetSize() const { return stack_.GetSize(); }
|
||||
|
||||
static const size_t kDefaultCapacity = 256;
|
||||
mutable internal::Stack<Allocator> stack_;
|
||||
};
|
||||
|
||||
typedef GenericStringBuffer<UTF8<> > StringBuffer;
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
|
||||
memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
|
||||
}
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#endif // RAPIDJSON_STRINGBUFFER_H_
|
||||
249
Engine/source/persistence/rapidjson/writer.h
Normal file
249
Engine/source/persistence/rapidjson/writer.h
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
#ifndef RAPIDJSON_WRITER_H_
|
||||
#define RAPIDJSON_WRITER_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "internal/stack.h"
|
||||
#include "internal/strfunc.h"
|
||||
#include <cstdio> // snprintf() or _sprintf_s()
|
||||
#include <new> // placement new
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
namespace rapidjson {
|
||||
|
||||
//! JSON writer
|
||||
/*! Writer implements the concept Handler.
|
||||
It generates JSON text by events to an output os.
|
||||
|
||||
User may programmatically calls the functions of a writer to generate JSON text.
|
||||
|
||||
On the other side, a writer can also be passed to objects that generates events,
|
||||
|
||||
for example Reader::Parse() and Document::Accept().
|
||||
|
||||
\tparam OutputStream Type of output stream.
|
||||
\tparam SourceEncoding Encoding of both source strings.
|
||||
\tparam TargetEncoding Encoding of and output stream.
|
||||
\implements Handler
|
||||
*/
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> >
|
||||
class Writer {
|
||||
public:
|
||||
typedef typename SourceEncoding::Ch Ch;
|
||||
|
||||
Writer(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
||||
os_(os), level_stack_(allocator, levelDepth * sizeof(Level)) {}
|
||||
|
||||
//@name Implementation of Handler
|
||||
//@{
|
||||
Writer& Null() { Prefix(kNullType); WriteNull(); return *this; }
|
||||
Writer& Bool(bool b) { Prefix(b ? kTrueType : kFalseType); WriteBool(b); return *this; }
|
||||
Writer& Int(int i) { Prefix(kNumberType); WriteInt(i); return *this; }
|
||||
Writer& Uint(unsigned u) { Prefix(kNumberType); WriteUint(u); return *this; }
|
||||
Writer& Int64(int64_t i64) { Prefix(kNumberType); WriteInt64(i64); return *this; }
|
||||
Writer& Uint64(uint64_t u64) { Prefix(kNumberType); WriteUint64(u64); return *this; }
|
||||
Writer& Double(double d) { Prefix(kNumberType); WriteDouble(d); return *this; }
|
||||
|
||||
Writer& String(const Ch* str, SizeType length, bool copy = false) {
|
||||
(void)copy;
|
||||
Prefix(kStringType);
|
||||
WriteString(str, length);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer& StartObject() {
|
||||
Prefix(kObjectType);
|
||||
new (level_stack_.template Push<Level>()) Level(false);
|
||||
WriteStartObject();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer& EndObject(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(1);
|
||||
WriteEndObject();
|
||||
if (level_stack_.Empty()) // end of json text
|
||||
os_.Flush();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer& StartArray() {
|
||||
Prefix(kArrayType);
|
||||
new (level_stack_.template Push<Level>()) Level(true);
|
||||
WriteStartArray();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer& EndArray(SizeType elementCount = 0) {
|
||||
(void)elementCount;
|
||||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(1);
|
||||
WriteEndArray();
|
||||
if (level_stack_.Empty()) // end of json text
|
||||
os_.Flush();
|
||||
return *this;
|
||||
}
|
||||
//@}
|
||||
|
||||
//! Simpler but slower overload.
|
||||
Writer& String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||
|
||||
protected:
|
||||
//! Information for each nested level
|
||||
struct Level {
|
||||
Level(bool inArray_) : inArray(inArray_), valueCount(0) {}
|
||||
bool inArray; //!< true if in array, otherwise in object
|
||||
size_t valueCount; //!< number of values in this level
|
||||
};
|
||||
|
||||
static const size_t kDefaultLevelDepth = 32;
|
||||
|
||||
void WriteNull() {
|
||||
os_.Put('n'); os_.Put('u'); os_.Put('l'); os_.Put('l');
|
||||
}
|
||||
|
||||
void WriteBool(bool b) {
|
||||
if (b) {
|
||||
os_.Put('t'); os_.Put('r'); os_.Put('u'); os_.Put('e');
|
||||
}
|
||||
else {
|
||||
os_.Put('f'); os_.Put('a'); os_.Put('l'); os_.Put('s'); os_.Put('e');
|
||||
}
|
||||
}
|
||||
|
||||
void WriteInt(int i) {
|
||||
if (i < 0) {
|
||||
os_.Put('-');
|
||||
i = -i;
|
||||
}
|
||||
WriteUint((unsigned)i);
|
||||
}
|
||||
|
||||
void WriteUint(unsigned u) {
|
||||
char buffer[10];
|
||||
char *p = buffer;
|
||||
do {
|
||||
*p++ = (u % 10) + '0';
|
||||
u /= 10;
|
||||
} while (u > 0);
|
||||
|
||||
do {
|
||||
--p;
|
||||
os_.Put(*p);
|
||||
} while (p != buffer);
|
||||
}
|
||||
|
||||
void WriteInt64(int64_t i64) {
|
||||
if (i64 < 0) {
|
||||
os_.Put('-');
|
||||
i64 = -i64;
|
||||
}
|
||||
WriteUint64((uint64_t)i64);
|
||||
}
|
||||
|
||||
void WriteUint64(uint64_t u64) {
|
||||
char buffer[20];
|
||||
char *p = buffer;
|
||||
do {
|
||||
*p++ = char(u64 % 10) + '0';
|
||||
u64 /= 10;
|
||||
} while (u64 > 0);
|
||||
|
||||
do {
|
||||
--p;
|
||||
os_.Put(*p);
|
||||
} while (p != buffer);
|
||||
}
|
||||
|
||||
//! \todo Optimization with custom double-to-string converter.
|
||||
void WriteDouble(double d) {
|
||||
char buffer[100];
|
||||
#if _MSC_VER
|
||||
int ret = sprintf_s(buffer, sizeof(buffer), "%g", d);
|
||||
#else
|
||||
int ret = snprintf(buffer, sizeof(buffer), "%g", d);
|
||||
#endif
|
||||
RAPIDJSON_ASSERT(ret >= 1);
|
||||
for (int i = 0; i < ret; i++)
|
||||
os_.Put(buffer[i]);
|
||||
}
|
||||
|
||||
void WriteString(const Ch* str, SizeType length) {
|
||||
static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
static const char escape[256] = {
|
||||
#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00
|
||||
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10
|
||||
0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
|
||||
Z16, Z16, // 30~4F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50
|
||||
Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF
|
||||
#undef Z16
|
||||
};
|
||||
|
||||
os_.Put('\"');
|
||||
GenericStringStream<SourceEncoding> is(str);
|
||||
while (is.Tell() < length) {
|
||||
const Ch c = is.Peek();
|
||||
if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) {
|
||||
is.Take();
|
||||
os_.Put('\\');
|
||||
os_.Put(escape[(unsigned char)c]);
|
||||
if (escape[(unsigned char)c] == 'u') {
|
||||
os_.Put('0');
|
||||
os_.Put('0');
|
||||
os_.Put(hexDigits[(unsigned char)c >> 4]);
|
||||
os_.Put(hexDigits[(unsigned char)c & 0xF]);
|
||||
}
|
||||
}
|
||||
else
|
||||
Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, os_);
|
||||
}
|
||||
os_.Put('\"');
|
||||
}
|
||||
|
||||
void WriteStartObject() { os_.Put('{'); }
|
||||
void WriteEndObject() { os_.Put('}'); }
|
||||
void WriteStartArray() { os_.Put('['); }
|
||||
void WriteEndArray() { os_.Put(']'); }
|
||||
|
||||
void Prefix(Type type) {
|
||||
(void)type;
|
||||
if (level_stack_.GetSize() != 0) { // this value is not at root
|
||||
Level* level = level_stack_.template Top<Level>();
|
||||
if (level->valueCount > 0) {
|
||||
if (level->inArray)
|
||||
os_.Put(','); // add comma if it is not the first element in array
|
||||
else // in object
|
||||
os_.Put((level->valueCount % 2 == 0) ? ',' : ':');
|
||||
}
|
||||
if (!level->inArray && level->valueCount % 2 == 0)
|
||||
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
|
||||
level->valueCount++;
|
||||
}
|
||||
else
|
||||
RAPIDJSON_ASSERT(type == kObjectType || type == kArrayType);
|
||||
}
|
||||
|
||||
OutputStream& os_;
|
||||
internal::Stack<Allocator> level_stack_;
|
||||
|
||||
private:
|
||||
// Prohibit assignment for VC C4512 warning
|
||||
Writer& operator=(const Writer& w);
|
||||
};
|
||||
|
||||
} // namespace rapidjson
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
||||
429
Engine/source/persistence/taml/binary/tamlBinaryReader.cpp
Normal file
429
Engine/source/persistence/taml/binary/tamlBinaryReader.cpp
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "persistence/taml/binary/tamlBinaryReader.h"
|
||||
|
||||
#ifndef _ZIPSUBSTREAM_H_
|
||||
#include "core/util/zip/zipSubStream.h"
|
||||
#endif
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlBinaryReader::read( FileStream& stream )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_Read);
|
||||
|
||||
// Read Taml signature.
|
||||
StringTableEntry tamlSignature = stream.readSTString();
|
||||
|
||||
// Is the signature correct?
|
||||
if ( tamlSignature != StringTable->insert( TAML_SIGNATURE ) )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf("Taml: Cannot read binary file as signature is incorrect '%s'.", tamlSignature );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Read version Id.
|
||||
U32 versionId;
|
||||
stream.read( &versionId );
|
||||
|
||||
// Read compressed flag.
|
||||
bool compressed;
|
||||
stream.read( &compressed );
|
||||
|
||||
SimObject* pSimObject = NULL;
|
||||
|
||||
// Is the stream compressed?
|
||||
if ( compressed )
|
||||
{
|
||||
// Yes, so attach zip stream.
|
||||
ZipSubRStream zipStream;
|
||||
zipStream.attachStream( &stream );
|
||||
|
||||
// Parse element.
|
||||
pSimObject = parseElement( zipStream, versionId );
|
||||
|
||||
// Detach zip stream.
|
||||
zipStream.detachStream();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so parse element.
|
||||
pSimObject = parseElement( stream, versionId );
|
||||
}
|
||||
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryReader::resetParse( void )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_ResetParse);
|
||||
|
||||
// Clear object reference map.
|
||||
mObjectReferenceMap.clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlBinaryReader::parseElement( Stream& stream, const U32 versionId )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_ParseElement);
|
||||
|
||||
SimObject* pSimObject = NULL;
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
// Format the type location.
|
||||
char typeLocationBuffer[64];
|
||||
dSprintf( typeLocationBuffer, sizeof(typeLocationBuffer), "Taml [format='binary' offset=%u]", stream.getPosition() );
|
||||
#endif
|
||||
|
||||
// Fetch element name.
|
||||
StringTableEntry typeName = stream.readSTString();
|
||||
|
||||
// Fetch object name.
|
||||
StringTableEntry objectName = stream.readSTString();
|
||||
|
||||
// Read references.
|
||||
U32 tamlRefId;
|
||||
U32 tamlRefToId;
|
||||
stream.read( &tamlRefId );
|
||||
stream.read( &tamlRefToId );
|
||||
|
||||
// Do we have a reference to Id?
|
||||
if ( tamlRefToId != 0 )
|
||||
{
|
||||
// Yes, so fetch reference.
|
||||
typeObjectReferenceHash::Iterator referenceItr = mObjectReferenceMap.find( tamlRefToId );
|
||||
|
||||
// Did we find the reference?
|
||||
if ( referenceItr == mObjectReferenceMap.end() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml: Could not find a reference Id of '%d'", tamlRefToId );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return referenceItr->value;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
// Create type.
|
||||
pSimObject = Taml::createType( typeName, mpTaml, typeLocationBuffer );
|
||||
#else
|
||||
// Create type.
|
||||
pSimObject = Taml::createType( typeName, mpTaml );
|
||||
#endif
|
||||
|
||||
// Finish if we couldn't create the type.
|
||||
if ( pSimObject == NULL )
|
||||
return NULL;
|
||||
|
||||
// Find Taml callbacks.
|
||||
TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject );
|
||||
|
||||
// Are there any Taml callbacks?
|
||||
if ( pCallbacks != NULL )
|
||||
{
|
||||
// Yes, so call it.
|
||||
mpTaml->tamlPreRead( pCallbacks );
|
||||
}
|
||||
|
||||
// Parse attributes.
|
||||
parseAttributes( stream, pSimObject, versionId );
|
||||
|
||||
// Does the object require a name?
|
||||
if ( objectName == StringTable->EmptyString() )
|
||||
{
|
||||
// No, so just register anonymously.
|
||||
pSimObject->registerObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Yes, so register a named object.
|
||||
pSimObject->registerObject( objectName );
|
||||
|
||||
// Was the name assigned?
|
||||
if ( pSimObject->getName() != objectName )
|
||||
{
|
||||
// No, so warn that the name was rejected.
|
||||
#ifdef TORQUE_DEBUG
|
||||
Con::warnf( "Taml::parseElement() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists. '%s'", typeName, objectName, typeLocationBuffer );
|
||||
#else
|
||||
Con::warnf( "Taml::parseElement() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists.", typeName, objectName );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a reference Id?
|
||||
if ( tamlRefId != 0 )
|
||||
{
|
||||
// Yes, so insert reference.
|
||||
mObjectReferenceMap.insertUnique( tamlRefId, pSimObject );
|
||||
}
|
||||
|
||||
// Parse custom elements.
|
||||
TamlCustomNodes customProperties;
|
||||
|
||||
// Parse children.
|
||||
parseChildren( stream, pCallbacks, pSimObject, versionId );
|
||||
|
||||
// Parse custom elements.
|
||||
parseCustomElements( stream, pCallbacks, customProperties, versionId );
|
||||
|
||||
// Are there any Taml callbacks?
|
||||
if ( pCallbacks != NULL )
|
||||
{
|
||||
// Yes, so call it.
|
||||
mpTaml->tamlPostRead( pCallbacks, customProperties );
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryReader::parseAttributes( Stream& stream, SimObject* pSimObject, const U32 versionId )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_ParseAttributes);
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pSimObject != NULL, "Taml: Cannot parse attributes on a NULL object." );
|
||||
|
||||
// Fetch attribute count.
|
||||
U32 attributeCount;
|
||||
stream.read( &attributeCount );
|
||||
|
||||
// Finish if no attributes.
|
||||
if ( attributeCount == 0 )
|
||||
return;
|
||||
|
||||
char valueBuffer[4096];
|
||||
|
||||
// Iterate attributes.
|
||||
for ( U32 index = 0; index < attributeCount; ++index )
|
||||
{
|
||||
// Fetch attribute.
|
||||
StringTableEntry attributeName = stream.readSTString();
|
||||
stream.readLongString( 4096, valueBuffer );
|
||||
|
||||
// We can assume this is a field for now.
|
||||
pSimObject->setPrefixedDataField(attributeName, NULL, valueBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryReader::parseChildren( Stream& stream, TamlCallbacks* pCallbacks, SimObject* pSimObject, const U32 versionId )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_ParseChildren);
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pSimObject != NULL, "Taml: Cannot parse children on a NULL object." );
|
||||
|
||||
// Fetch children count.
|
||||
U32 childrenCount;
|
||||
stream.read( &childrenCount );
|
||||
|
||||
// Finish if no children.
|
||||
if ( childrenCount == 0 )
|
||||
return;
|
||||
|
||||
// Fetch the Taml children.
|
||||
TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject );
|
||||
|
||||
// Is this a sim set?
|
||||
if ( pChildren == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml: Child element found under parent but object cannot have children." );
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch any container child class specifier.
|
||||
AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true );
|
||||
|
||||
// Iterate children.
|
||||
for ( U32 index = 0; index < childrenCount; ++ index )
|
||||
{
|
||||
// Parse child element.
|
||||
SimObject* pChildSimObject = parseElement( stream, versionId );
|
||||
|
||||
// Finish if child failed.
|
||||
if ( pChildSimObject == NULL )
|
||||
return;
|
||||
|
||||
// Do we have a container child class?
|
||||
if ( pContainerChildClass != NULL )
|
||||
{
|
||||
// Yes, so is the child object the correctly derived type?
|
||||
if ( !pChildSimObject->getClassRep()->isClass( pContainerChildClass ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml: Child element '%s' found under parent '%s' but object is restricted to children of type '%s'.",
|
||||
pChildSimObject->getClassName(),
|
||||
pSimObject->getClassName(),
|
||||
pContainerChildClass->getClassName() );
|
||||
|
||||
// NOTE: We can't delete the object as it may be referenced elsewhere!
|
||||
pChildSimObject = NULL;
|
||||
|
||||
// Skip.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add child.
|
||||
pChildren->addTamlChild( pChildSimObject );
|
||||
|
||||
// Find Taml callbacks for child.
|
||||
TamlCallbacks* pChildCallbacks = dynamic_cast<TamlCallbacks*>( pChildSimObject );
|
||||
|
||||
// Do we have callbacks on the child?
|
||||
if ( pChildCallbacks != NULL )
|
||||
{
|
||||
// Yes, so perform callback.
|
||||
mpTaml->tamlAddParent( pChildCallbacks, pSimObject );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryReader::parseCustomElements( Stream& stream, TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes, const U32 versionId )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryReader_ParseCustomElement);
|
||||
|
||||
// Read custom node count.
|
||||
U32 customNodeCount;
|
||||
stream.read( &customNodeCount );
|
||||
|
||||
// Finish if no custom nodes.
|
||||
if ( customNodeCount == 0 )
|
||||
return;
|
||||
|
||||
// Iterate custom nodes.
|
||||
for ( U32 nodeIndex = 0; nodeIndex < customNodeCount; ++nodeIndex )
|
||||
{
|
||||
//Read custom node name.
|
||||
StringTableEntry nodeName = stream.readSTString();
|
||||
|
||||
// Add custom node.
|
||||
TamlCustomNode* pCustomNode = customNodes.addNode( nodeName );
|
||||
|
||||
// Parse the custom node.
|
||||
parseCustomNode( stream, pCustomNode, versionId );
|
||||
}
|
||||
|
||||
// Do we have callbacks?
|
||||
if ( pCallbacks == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml: Encountered custom data but object does not support custom data." );
|
||||
return;
|
||||
}
|
||||
|
||||
// Custom read callback.
|
||||
mpTaml->tamlCustomRead( pCallbacks, customNodes );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryReader::parseCustomNode( Stream& stream, TamlCustomNode* pCustomNode, const U32 versionId )
|
||||
{
|
||||
// Fetch if a proxy object.
|
||||
bool isProxyObject;
|
||||
stream.read( &isProxyObject );
|
||||
|
||||
// Is this a proxy object?
|
||||
if ( isProxyObject )
|
||||
{
|
||||
// Yes, so parse proxy object.
|
||||
SimObject* pProxyObject = parseElement( stream, versionId );
|
||||
|
||||
// Add child node.
|
||||
pCustomNode->addNode( pProxyObject );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// No, so read custom node name.
|
||||
StringTableEntry nodeName = stream.readSTString();
|
||||
|
||||
// Add child node.
|
||||
TamlCustomNode* pChildNode = pCustomNode->addNode( nodeName );
|
||||
|
||||
// Read child node text.
|
||||
char childNodeTextBuffer[MAX_TAML_NODE_FIELDVALUE_LENGTH];
|
||||
stream.readLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, childNodeTextBuffer );
|
||||
pChildNode->setNodeText( childNodeTextBuffer );
|
||||
|
||||
// Read child node count.
|
||||
U32 childNodeCount;
|
||||
stream.read( &childNodeCount );
|
||||
|
||||
// Do we have any children nodes?
|
||||
if ( childNodeCount > 0 )
|
||||
{
|
||||
// Yes, so parse children nodes.
|
||||
for( U32 childIndex = 0; childIndex < childNodeCount; ++childIndex )
|
||||
{
|
||||
// Parse child node.
|
||||
parseCustomNode( stream, pChildNode, versionId );
|
||||
}
|
||||
}
|
||||
|
||||
// Read child field count.
|
||||
U32 childFieldCount;
|
||||
stream.read( &childFieldCount );
|
||||
|
||||
// Do we have any child fields?
|
||||
if ( childFieldCount > 0 )
|
||||
{
|
||||
// Yes, so parse child fields.
|
||||
for( U32 childFieldIndex = 0; childFieldIndex < childFieldCount; ++childFieldIndex )
|
||||
{
|
||||
// Read field name.
|
||||
StringTableEntry fieldName = stream.readSTString();
|
||||
|
||||
// Read field value.
|
||||
char valueBuffer[MAX_TAML_NODE_FIELDVALUE_LENGTH];
|
||||
stream.readLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, valueBuffer );
|
||||
|
||||
// Add field.
|
||||
pChildNode->addField( fieldName, valueBuffer );
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Engine/source/persistence/taml/binary/tamlBinaryReader.h
Normal file
68
Engine/source/persistence/taml/binary/tamlBinaryReader.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_BINARYREADER_H_
|
||||
#define _TAML_BINARYREADER_H_
|
||||
|
||||
#ifndef _TDICTIONARY_H_
|
||||
#include "core/util/tDictionary.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlBinaryReader
|
||||
{
|
||||
public:
|
||||
TamlBinaryReader( Taml* pTaml ) :
|
||||
mpTaml( pTaml )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TamlBinaryReader() {}
|
||||
|
||||
/// Read.
|
||||
SimObject* read( FileStream& stream );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
|
||||
typedef HashTable<SimObjectId, SimObject*> typeObjectReferenceHash;
|
||||
|
||||
typeObjectReferenceHash mObjectReferenceMap;
|
||||
|
||||
private:
|
||||
void resetParse( void );
|
||||
|
||||
SimObject* parseElement( Stream& stream, const U32 versionId );
|
||||
void parseAttributes( Stream& stream, SimObject* pSimObject, const U32 versionId );
|
||||
void parseChildren( Stream& stream, TamlCallbacks* pCallbacks, SimObject* pSimObject, const U32 versionId );
|
||||
void parseCustomElements( Stream& stream, TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes, const U32 versionId );
|
||||
void parseCustomNode( Stream& stream, TamlCustomNode* pCustomNode, const U32 versionId );
|
||||
};
|
||||
|
||||
#endif // _TAML_BINARYREADER_H_
|
||||
297
Engine/source/persistence/taml/binary/tamlBinaryWriter.cpp
Normal file
297
Engine/source/persistence/taml/binary/tamlBinaryWriter.cpp
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "persistence/taml/binary/tamlBinaryWriter.h"
|
||||
|
||||
#ifndef _ZIPSUBSTREAM_H_
|
||||
#include "core/util/zip/zipSubStream.h"
|
||||
#endif
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool TamlBinaryWriter::write( FileStream& stream, const TamlWriteNode* pTamlWriteNode, const bool compressed )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryWriter_Write);
|
||||
|
||||
// Write Taml signature.
|
||||
stream.writeString( StringTable->insert( TAML_SIGNATURE ) );
|
||||
|
||||
// Write version Id.
|
||||
stream.write( mVersionId );
|
||||
|
||||
// Write compressed flag.
|
||||
stream.write( compressed );
|
||||
|
||||
// Are we compressed?
|
||||
if ( compressed )
|
||||
{
|
||||
// yes, so attach zip stream.
|
||||
ZipSubWStream zipStream;
|
||||
zipStream.attachStream( &stream );
|
||||
|
||||
// Write element.
|
||||
writeElement( zipStream, pTamlWriteNode );
|
||||
|
||||
// Detach zip stream.
|
||||
zipStream.detachStream();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so write element.
|
||||
writeElement( stream, pTamlWriteNode );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryWriter::writeElement( Stream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryWriter_WriteElement);
|
||||
|
||||
// Fetch object.
|
||||
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
|
||||
|
||||
// Fetch element name.
|
||||
const char* pElementName = pSimObject->getClassName();
|
||||
|
||||
// Write element name.
|
||||
stream.writeString( pElementName );
|
||||
|
||||
// Fetch object name.
|
||||
const char* pObjectName = pTamlWriteNode->mpObjectName;
|
||||
|
||||
// Write object name.
|
||||
stream.writeString( pObjectName != NULL ? pObjectName : StringTable->EmptyString() );
|
||||
|
||||
// Fetch reference Id.
|
||||
const U32 tamlRefId = pTamlWriteNode->mRefId;
|
||||
|
||||
// Write reference Id.
|
||||
stream.write( tamlRefId );
|
||||
|
||||
// Do we have a reference to node?
|
||||
if ( pTamlWriteNode->mRefToNode != NULL )
|
||||
{
|
||||
// Yes, so fetch reference to Id.
|
||||
const U32 tamlRefToId = pTamlWriteNode->mRefToNode->mRefId;
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( tamlRefToId != 0, "Taml: Invalid reference to Id." );
|
||||
|
||||
// Write reference to Id.
|
||||
stream.write( tamlRefToId );
|
||||
|
||||
// Finished.
|
||||
return;
|
||||
}
|
||||
|
||||
// No, so write no reference to Id.
|
||||
stream.write( 0 );
|
||||
|
||||
// Write attributes.
|
||||
writeAttributes( stream, pTamlWriteNode );
|
||||
|
||||
// Write children.
|
||||
writeChildren( stream, pTamlWriteNode );
|
||||
|
||||
// Write custom elements.
|
||||
writeCustomElements( stream, pTamlWriteNode );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryWriter::writeAttributes( Stream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryWriter_WriteAttributes);
|
||||
|
||||
// Fetch fields.
|
||||
const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
|
||||
|
||||
// Write placeholder attribute count.
|
||||
stream.write( (U32)fields.size() );
|
||||
|
||||
// Finish if no fields.
|
||||
if ( fields.size() == 0 )
|
||||
return;
|
||||
|
||||
// Iterate fields.
|
||||
for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr )
|
||||
{
|
||||
// Fetch field/value pair.
|
||||
TamlWriteNode::FieldValuePair* pFieldValue = (*itr);
|
||||
|
||||
// Write attribute.
|
||||
stream.writeString( pFieldValue->mName );
|
||||
stream.writeLongString( 4096, pFieldValue->mpValue );
|
||||
}
|
||||
}
|
||||
|
||||
void TamlBinaryWriter::writeChildren( Stream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryWriter_WriteChildren);
|
||||
|
||||
// Fetch children.
|
||||
Vector<TamlWriteNode*>* pChildren = pTamlWriteNode->mChildren;
|
||||
|
||||
// Do we have any children?
|
||||
if ( pChildren == NULL )
|
||||
{
|
||||
// No, so write no children.
|
||||
stream.write( (U32)0 );
|
||||
return;
|
||||
}
|
||||
|
||||
// Write children count.
|
||||
stream.write( (U32)pChildren->size() );
|
||||
|
||||
// Iterate children.
|
||||
for( Vector<TamlWriteNode*>::iterator itr = pChildren->begin(); itr != pChildren->end(); ++itr )
|
||||
{
|
||||
// Write child.
|
||||
writeElement( stream, (*itr) );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryWriter::writeCustomElements( Stream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlBinaryWriter_WriteCustomElements);
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodeVector& nodes = customNodes.getNodes();
|
||||
|
||||
// Write custom node count.
|
||||
stream.write( (U32)nodes.size() );
|
||||
|
||||
// Finish if there are no nodes.
|
||||
if ( nodes.size() == 0 )
|
||||
return;
|
||||
|
||||
// Iterate custom nodes.
|
||||
for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
|
||||
{
|
||||
// Fetch the custom node.
|
||||
TamlCustomNode* pCustomNode = *customNodesItr;
|
||||
|
||||
// Write custom node name.
|
||||
stream.writeString( pCustomNode->getNodeName() );
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
// Iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Write the custom node.
|
||||
writeCustomNode( stream, pChildNode );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlBinaryWriter::writeCustomNode( Stream& stream, const TamlCustomNode* pCustomNode )
|
||||
{
|
||||
// Is the node a proxy object?
|
||||
if ( pCustomNode->isProxyObject() )
|
||||
{
|
||||
// Yes, so flag as proxy object.
|
||||
stream.write( true );
|
||||
|
||||
// Write the element.
|
||||
writeElement( stream, pCustomNode->getProxyWriteNode() );
|
||||
return;
|
||||
}
|
||||
|
||||
// No, so flag as custom node.
|
||||
stream.write( false );
|
||||
|
||||
// Write custom node name.
|
||||
stream.writeString( pCustomNode->getNodeName() );
|
||||
|
||||
// Write custom node text.
|
||||
stream.writeLongString(MAX_TAML_NODE_FIELDVALUE_LENGTH, pCustomNode->getNodeTextField().getFieldValue());
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
// Fetch child node count.
|
||||
const U32 childNodeCount = (U32)nodeChildren.size();
|
||||
|
||||
// Write custom node count.
|
||||
stream.write( childNodeCount );
|
||||
|
||||
// Do we have any children nodes.
|
||||
if ( childNodeCount > 0 )
|
||||
{
|
||||
// Yes, so iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Write the custom node.
|
||||
writeCustomNode( stream, pChildNode );
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch fields.
|
||||
const TamlCustomFieldVector& fields = pCustomNode->getFields();
|
||||
|
||||
// Fetch child field count.
|
||||
const U32 childFieldCount = (U32)fields.size();
|
||||
|
||||
// Write custom field count.
|
||||
stream.write( childFieldCount );
|
||||
|
||||
// Do we have any child fields?
|
||||
if ( childFieldCount > 0 )
|
||||
{
|
||||
// Yes, so iterate fields.
|
||||
for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
|
||||
{
|
||||
// Fetch node field.
|
||||
const TamlCustomField* pField = *fieldItr;
|
||||
|
||||
// Write the node field.
|
||||
stream.writeString( pField->getFieldName() );
|
||||
stream.writeLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, pField->getFieldValue() );
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Engine/source/persistence/taml/binary/tamlBinaryWriter.h
Normal file
59
Engine/source/persistence/taml/binary/tamlBinaryWriter.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_BINARYWRITER_H_
|
||||
#define _TAML_BINARYWRITER_H_
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlBinaryWriter
|
||||
{
|
||||
public:
|
||||
TamlBinaryWriter( Taml* pTaml ) :
|
||||
mpTaml( pTaml ),
|
||||
mVersionId(2)
|
||||
{
|
||||
}
|
||||
virtual ~TamlBinaryWriter() {}
|
||||
|
||||
/// Write.
|
||||
bool write( FileStream& stream, const TamlWriteNode* pTamlWriteNode, const bool compressed );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
const U32 mVersionId;
|
||||
|
||||
private:
|
||||
void writeElement( Stream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
void writeAttributes( Stream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
void writeChildren( Stream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
void writeCustomElements( Stream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
void writeCustomNode( Stream& stream, const TamlCustomNode* pCustomNode );
|
||||
};
|
||||
|
||||
#endif // _TAML_BINARYWRITER_H_
|
||||
747
Engine/source/persistence/taml/fsTinyXml.cpp
Normal file
747
Engine/source/persistence/taml/fsTinyXml.cpp
Normal file
|
|
@ -0,0 +1,747 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "fsTinyXml.h"
|
||||
#include "console/console.h"
|
||||
|
||||
bool fsTiXmlDocument::LoadFile( const char * pFilename, TiXmlEncoding encoding )
|
||||
{
|
||||
// Expand the file-path.
|
||||
char filenameBuffer[1024];
|
||||
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
|
||||
|
||||
FileStream stream;
|
||||
|
||||
#ifdef TORQUE_OS_ANDROID
|
||||
if (strlen(pFilename) > strlen(filenameBuffer)) {
|
||||
strcpy(filenameBuffer, pFilename);
|
||||
}
|
||||
#endif
|
||||
|
||||
// File open for read?
|
||||
if ( !stream.open( filenameBuffer, Torque::FS::File::AccessMode::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 fsTiXmlDocument::SaveFile( const char * pFilename ) const
|
||||
{
|
||||
// Expand the file-name into the file-path buffer.
|
||||
char filenameBuffer[1024];
|
||||
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
|
||||
|
||||
FileStream stream;
|
||||
|
||||
// File opened?
|
||||
if ( !stream.open( filenameBuffer, Torque::FS::File::AccessMode::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;
|
||||
}
|
||||
|
||||
bool fsTiXmlDocument::LoadFile( FileStream &stream, TiXmlEncoding encoding )
|
||||
{
|
||||
// Delete the existing data:
|
||||
Clear();
|
||||
//TODO: Can't clear location, investigate if this gives issues.
|
||||
//doc.location.Clear();
|
||||
|
||||
// Get the file size, so we can pre-allocate the string. HUGE speed impact.
|
||||
long length = stream.getStreamSize();
|
||||
|
||||
// Strange case, but good to handle up front.
|
||||
if ( length <= 0 )
|
||||
{
|
||||
SetError( TiXmlDocument::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Subtle bug here. TinyXml did use fgets. But from the XML spec:
|
||||
// 2.11 End-of-Line Handling
|
||||
// <snip>
|
||||
// <quote>
|
||||
// ...the XML processor MUST behave as if it normalized all line breaks in external
|
||||
// parsed entities (including the document entity) on input, before parsing, by translating
|
||||
// both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
|
||||
// a single #xA character.
|
||||
// </quote>
|
||||
//
|
||||
// It is not clear fgets does that, and certainly isn't clear it works cross platform.
|
||||
// Generally, you expect fgets to translate from the convention of the OS to the c/unix
|
||||
// convention, and not work generally.
|
||||
|
||||
/*
|
||||
while( fgets( buf, sizeof(buf), file ) )
|
||||
{
|
||||
data += buf;
|
||||
}
|
||||
*/
|
||||
|
||||
char* buf = new char[ length+1 ];
|
||||
buf[0] = 0;
|
||||
|
||||
if ( !stream.read( length, buf ) ) {
|
||||
delete [] buf;
|
||||
SetError( TiXmlDocument::TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Process the buffer in place to normalize new lines. (See comment above.)
|
||||
// Copies from the 'p' to 'q' pointer, where p can advance faster if
|
||||
// a newline-carriage return is hit.
|
||||
//
|
||||
// Wikipedia:
|
||||
// Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
|
||||
// CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
|
||||
// * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
|
||||
// * 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
|
||||
// * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
|
||||
|
||||
const char* p = buf; // the read head
|
||||
char* q = buf; // the write head
|
||||
const char CR = 0x0d;
|
||||
const char LF = 0x0a;
|
||||
|
||||
buf[length] = 0;
|
||||
while( *p ) {
|
||||
assert( p < (buf+length) );
|
||||
assert( q <= (buf+length) );
|
||||
assert( q <= p );
|
||||
|
||||
if ( *p == CR ) {
|
||||
*q++ = LF;
|
||||
p++;
|
||||
if ( *p == LF ) { // check for CR+LF (and skip LF)
|
||||
p++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*q++ = *p++;
|
||||
}
|
||||
}
|
||||
assert( q <= (buf+length) );
|
||||
*q = 0;
|
||||
|
||||
Parse( buf, 0, encoding );
|
||||
|
||||
delete [] buf;
|
||||
return !Error();
|
||||
}
|
||||
|
||||
bool fsTiXmlDocument::SaveFile( FileStream &stream ) const
|
||||
{
|
||||
if ( useMicrosoftBOM )
|
||||
{
|
||||
const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
|
||||
const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
|
||||
const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
|
||||
|
||||
stream.write( TIXML_UTF_LEAD_0 );
|
||||
stream.write( TIXML_UTF_LEAD_1 );
|
||||
stream.write( TIXML_UTF_LEAD_2 );
|
||||
}
|
||||
Print( stream, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
void fsTiXmlDocument::Print( FileStream& stream, int depth ) const
|
||||
{
|
||||
for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
|
||||
{
|
||||
//AttemptPrintTiNode(const_cast<TiXmlNode*>(node), stream, depth);
|
||||
dynamic_cast<const fsTiXmlNode*>(node)->Print( stream, depth );
|
||||
stream.writeText( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
void fsTiXmlAttribute::Print( FileStream& stream, int depth, TIXML_STRING* str ) const
|
||||
{
|
||||
TIXML_STRING n, v;
|
||||
|
||||
TiXmlString value = TiXmlString(Value());
|
||||
|
||||
EncodeString( NameTStr(), &n );
|
||||
EncodeString( value, &v );
|
||||
|
||||
for ( int i=0; i< depth; i++ ) {
|
||||
stream.writeText( " " );
|
||||
}
|
||||
|
||||
if (value.find ('\"') == TIXML_STRING::npos) {
|
||||
const char* pValue = v.c_str();
|
||||
char buffer[4096];
|
||||
const S32 length = dSprintf(buffer, sizeof(buffer), "%s=\"%s\"", n.c_str(), pValue);
|
||||
stream.write(length, buffer);
|
||||
if ( str ) {
|
||||
(*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\"";
|
||||
}
|
||||
}
|
||||
else {
|
||||
char buffer[4096];
|
||||
const S32 length = dSprintf(buffer, sizeof(buffer), "%s='%s'", n.c_str(), v.c_str());
|
||||
stream.write(length, buffer);
|
||||
if ( str ) {
|
||||
(*str) += n; (*str) += "='"; (*str) += v; (*str) += "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fsTiXmlDeclaration::Print(FileStream& stream, int depth, TiXmlString* str) const
|
||||
{
|
||||
stream.writeStringBuffer( "<?xml " );
|
||||
if ( str ) (*str) += "<?xml ";
|
||||
|
||||
if ( !version.empty() ) {
|
||||
stream.writeFormattedBuffer( "version=\"%s\" ", version.c_str ());
|
||||
if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; }
|
||||
}
|
||||
if ( !encoding.empty() ) {
|
||||
stream.writeFormattedBuffer( "encoding=\"%s\" ", encoding.c_str ());
|
||||
if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; }
|
||||
}
|
||||
if ( !standalone.empty() ) {
|
||||
stream.writeFormattedBuffer( "standalone=\"%s\" ", standalone.c_str ());
|
||||
if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; }
|
||||
}
|
||||
stream.writeStringBuffer( "?>" );
|
||||
if ( str ) (*str) += "?>";
|
||||
}
|
||||
|
||||
void fsTiXmlElement::Print(FileStream& stream, int depth) const
|
||||
{
|
||||
int i;
|
||||
for ( i=0; i<depth; i++ ) {
|
||||
stream.writeStringBuffer( " " );
|
||||
}
|
||||
|
||||
stream.writeFormattedBuffer( "<%s", value.c_str() );
|
||||
|
||||
const TiXmlAttribute* attrib;
|
||||
for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
|
||||
{
|
||||
stream.writeStringBuffer( "\n" );
|
||||
dynamic_cast<const fsTiXmlAttribute*>(attrib)->Print( stream, depth+1 );
|
||||
}
|
||||
|
||||
// There are 3 different formatting approaches:
|
||||
// 1) An element without children is printed as a <foo /> node
|
||||
// 2) An element with only a text child is printed as <foo> text </foo>
|
||||
// 3) An element with children is printed on multiple lines.
|
||||
TiXmlNode* node;
|
||||
if ( !firstChild )
|
||||
{
|
||||
stream.writeStringBuffer( " />" );
|
||||
}
|
||||
else if ( firstChild == lastChild && firstChild->ToText() )
|
||||
{
|
||||
stream.writeStringBuffer( ">" );
|
||||
dynamic_cast<const fsTiXmlNode*>(firstChild)->Print( stream, depth + 1 );
|
||||
stream.writeFormattedBuffer( "</%s>", value.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.writeStringBuffer( ">" );
|
||||
|
||||
for ( node = firstChild; node; node=node->NextSibling() )
|
||||
{
|
||||
if ( !node->ToText() )
|
||||
{
|
||||
stream.writeStringBuffer( "\n" );
|
||||
}
|
||||
dynamic_cast<const fsTiXmlNode*>(node)->Print( stream, depth+1 );
|
||||
}
|
||||
stream.writeStringBuffer( "\n" );
|
||||
for( i=0; i<depth; ++i ) {
|
||||
stream.writeStringBuffer( " " );
|
||||
}
|
||||
stream.writeFormattedBuffer( "</%s>", value.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
void fsTiXmlComment::Print(FileStream& stream, int depth) const
|
||||
{
|
||||
for ( int i=0; i<depth; i++ )
|
||||
{
|
||||
stream.writeStringBuffer( " " );
|
||||
}
|
||||
stream.writeFormattedBuffer( "<!--%s-->", value.c_str() );
|
||||
}
|
||||
|
||||
void fsTiXmlText::Print(FileStream& stream, int depth) const
|
||||
{
|
||||
if ( cdata )
|
||||
{
|
||||
int i;
|
||||
stream.writeStringBuffer( "\n" );
|
||||
for ( i=0; i<depth; i++ ) {
|
||||
stream.writeStringBuffer( " " );
|
||||
}
|
||||
stream.writeFormattedBuffer( "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output
|
||||
}
|
||||
else
|
||||
{
|
||||
TIXML_STRING buffer;
|
||||
EncodeString( value, &buffer );
|
||||
stream.writeFormattedBuffer( "%s", buffer.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
void fsTiXmlUnknown::Print(FileStream& stream, int depth) const
|
||||
{
|
||||
for ( int i=0; i<depth; i++ )
|
||||
stream.writeStringBuffer( " " );
|
||||
|
||||
stream.writeFormattedBuffer( "<%s>", value.c_str() );
|
||||
}
|
||||
|
||||
static TiXmlNode* TiNodeIdentify( TiXmlNode* parent, const char* p, TiXmlEncoding encoding )
|
||||
{
|
||||
TiXmlNode* returnNode = 0;
|
||||
|
||||
p = TiXmlNode::SkipWhiteSpace( p, encoding );
|
||||
if( !p || !*p || *p != '<' )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = TiXmlNode::SkipWhiteSpace( p, encoding );
|
||||
|
||||
if ( !p || !*p )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// What is this thing?
|
||||
// - Elements start with a letter or underscore, but xml is reserved.
|
||||
// - Comments: <!--
|
||||
// - Decleration: <?xml
|
||||
// - Everthing else is unknown to tinyxml.
|
||||
//
|
||||
|
||||
const char* xmlHeader = { "<?xml" };
|
||||
const char* commentHeader = { "<!--" };
|
||||
const char* dtdHeader = { "<!" };
|
||||
const char* cdataHeader = { "<![CDATA[" };
|
||||
|
||||
if ( TiXmlNode::StringEqual( p, xmlHeader, true, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Declaration\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlDeclaration();
|
||||
}
|
||||
else if ( TiXmlNode::StringEqual( p, commentHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Comment\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlComment();
|
||||
}
|
||||
else if ( TiXmlNode::StringEqual( p, cdataHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing CDATA\n" );
|
||||
#endif
|
||||
TiXmlText* text = new fsTiXmlText( "" );
|
||||
text->SetCDATA( true );
|
||||
returnNode = text;
|
||||
}
|
||||
else if ( TiXmlNode::StringEqual( p, dtdHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Unknown(1)\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlUnknown();
|
||||
}
|
||||
else if ( TiXmlNode::IsAlpha( *(p+1), encoding )
|
||||
|| *(p+1) == '_' )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Element\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlElement( "" );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Unknown(2)\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlUnknown();
|
||||
}
|
||||
|
||||
if ( returnNode )
|
||||
{
|
||||
// Set the parent, so it can report errors
|
||||
returnNode->parent = parent;
|
||||
}
|
||||
return returnNode;
|
||||
}
|
||||
|
||||
TiXmlNode* fsTiXmlDocument::Identify( const char* p, TiXmlEncoding encoding )
|
||||
{
|
||||
return TiNodeIdentify(this, p, encoding);
|
||||
}
|
||||
|
||||
TiXmlNode* fsTiXmlElement::Identify( const char* p, TiXmlEncoding encoding )
|
||||
{
|
||||
return TiNodeIdentify(this, p, encoding);
|
||||
}
|
||||
|
||||
const char* fsTiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
|
||||
{
|
||||
p = SkipWhiteSpace( p, encoding );
|
||||
TiXmlDocument* document = GetDocument();
|
||||
|
||||
if ( !p || !*p )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( data )
|
||||
{
|
||||
data->Stamp( p, encoding );
|
||||
location = data->Cursor();
|
||||
}
|
||||
|
||||
if ( *p != '<' )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = SkipWhiteSpace( p+1, encoding );
|
||||
|
||||
// Read the name.
|
||||
const char* pErr = p;
|
||||
|
||||
p = ReadName( p, &value, encoding );
|
||||
if ( !p || !*p )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
|
||||
TIXML_STRING endTag ("</");
|
||||
endTag += value;
|
||||
|
||||
// Check for and read attributes. Also look for an empty
|
||||
// tag or an end tag.
|
||||
while ( p && *p )
|
||||
{
|
||||
pErr = p;
|
||||
p = SkipWhiteSpace( p, encoding );
|
||||
if ( !p || !*p )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
if ( *p == '/' )
|
||||
{
|
||||
++p;
|
||||
// Empty tag.
|
||||
if ( *p != '>' )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
return (p+1);
|
||||
}
|
||||
else if ( *p == '>' )
|
||||
{
|
||||
// Done with attributes (if there were any.)
|
||||
// Read the value -- which can include other
|
||||
// elements -- read the end tag, and return.
|
||||
++p;
|
||||
p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens.
|
||||
if ( !p || !*p ) {
|
||||
// We were looking for the end tag, but found nothing.
|
||||
// Fix for [ 1663758 ] Failure to report error on bad XML
|
||||
if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We should find the end tag now
|
||||
// note that:
|
||||
// </foo > and
|
||||
// </foo>
|
||||
// are both valid end tags.
|
||||
if ( StringEqual( p, endTag.c_str(), false, encoding ) )
|
||||
{
|
||||
p += endTag.length();
|
||||
p = SkipWhiteSpace( p, encoding );
|
||||
if ( p && *p && *p == '>' ) {
|
||||
++p;
|
||||
return p;
|
||||
}
|
||||
if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to read an attribute:
|
||||
TiXmlAttribute* attrib = new fsTiXmlAttribute();
|
||||
if ( !attrib )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
attrib->SetDocument( document );
|
||||
pErr = p;
|
||||
p = attrib->Parse( p, data, encoding );
|
||||
|
||||
if ( !p || !*p )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
|
||||
delete attrib;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle the strange case of double attributes:
|
||||
#ifdef TIXML_USE_STL
|
||||
TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() );
|
||||
#else
|
||||
TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
|
||||
#endif
|
||||
if ( node )
|
||||
{
|
||||
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
|
||||
delete attrib;
|
||||
return 0;
|
||||
}
|
||||
|
||||
attributeSet.Add( attrib );
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
TiXmlNode* fsTiXmlNode::Identify(char const* p, TiXmlEncoding encoding)
|
||||
{
|
||||
TiXmlNode* returnNode = 0;
|
||||
|
||||
p = TiXmlBase::SkipWhiteSpace( p, encoding );
|
||||
if( !p || !*p || *p != '<' )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = TiXmlBase::SkipWhiteSpace( p, encoding );
|
||||
|
||||
if ( !p || !*p )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// What is this thing?
|
||||
// - Elements start with a letter or underscore, but xml is reserved.
|
||||
// - Comments: <!--
|
||||
// - Decleration: <?xml
|
||||
// - Everthing else is unknown to tinyxml.
|
||||
//
|
||||
|
||||
const char* xmlHeader = { "<?xml" };
|
||||
const char* commentHeader = { "<!--" };
|
||||
const char* dtdHeader = { "<!" };
|
||||
const char* cdataHeader = { "<![CDATA[" };
|
||||
|
||||
if ( TiXmlBase::StringEqual( p, xmlHeader, true, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Declaration\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlDeclaration();
|
||||
}
|
||||
else if ( TiXmlBase::StringEqual( p, commentHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Comment\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlComment();
|
||||
}
|
||||
else if ( TiXmlBase::StringEqual( p, cdataHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing CDATA\n" );
|
||||
#endif
|
||||
fsTiXmlText* text = new fsTiXmlText( "" );
|
||||
text->SetCDATA( true );
|
||||
returnNode = text;
|
||||
}
|
||||
else if ( TiXmlBase::StringEqual( p, dtdHeader, false, encoding ) )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Unknown(1)\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlUnknown();
|
||||
}
|
||||
else if ( TiXmlBase::IsAlpha( *(p+1), encoding )
|
||||
|| *(p+1) == '_' )
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Element\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlElement( "" );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
TIXML_LOG( "XML parsing Unknown(2)\n" );
|
||||
#endif
|
||||
returnNode = new fsTiXmlUnknown();
|
||||
}
|
||||
|
||||
if ( returnNode )
|
||||
{
|
||||
// Set the parent, so it can report errors
|
||||
returnNode->parent = this;
|
||||
}
|
||||
return returnNode;
|
||||
}
|
||||
|
||||
const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
|
||||
const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
|
||||
const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
|
||||
|
||||
char const* fsTiXmlDocument::Parse(char const* p, TiXmlParsingData* prevData, TiXmlEncoding encoding)
|
||||
{
|
||||
ClearError();
|
||||
|
||||
// Parse away, at the document level. Since a document
|
||||
// contains nothing but other tags, most of what happens
|
||||
// here is skipping white space.
|
||||
if ( !p || !*p )
|
||||
{
|
||||
SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Note that, for a document, this needs to come
|
||||
// before the while space skip, so that parsing
|
||||
// starts from the pointer we are given.
|
||||
location.Clear();
|
||||
if ( prevData )
|
||||
{
|
||||
location.row = prevData->Cursor().row;
|
||||
location.col = prevData->Cursor().col;
|
||||
}
|
||||
else
|
||||
{
|
||||
location.row = 0;
|
||||
location.col = 0;
|
||||
}
|
||||
TiXmlParsingData data( p, TabSize(), location.row, location.col );
|
||||
location = data.Cursor();
|
||||
|
||||
if ( encoding == TIXML_ENCODING_UNKNOWN )
|
||||
{
|
||||
// Check for the Microsoft UTF-8 lead bytes.
|
||||
const unsigned char* pU = (const unsigned char*)p;
|
||||
if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0
|
||||
&& *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1
|
||||
&& *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 )
|
||||
{
|
||||
encoding = TIXML_ENCODING_UTF8;
|
||||
useMicrosoftBOM = true;
|
||||
}
|
||||
}
|
||||
|
||||
p = TiXmlBase::SkipWhiteSpace( p, encoding );
|
||||
if ( !p )
|
||||
{
|
||||
SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ( p && *p )
|
||||
{
|
||||
TiXmlNode* node = fsTiXmlNode::Identify( p, encoding );
|
||||
if ( node )
|
||||
{
|
||||
p = node->Parse( p, &data, encoding );
|
||||
LinkEndChild( node );
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Did we get encoding info?
|
||||
if ( encoding == TIXML_ENCODING_UNKNOWN
|
||||
&& node->ToDeclaration() )
|
||||
{
|
||||
TiXmlDeclaration* dec = node->ToDeclaration();
|
||||
const char* enc = dec->Encoding();
|
||||
assert( enc );
|
||||
|
||||
if ( *enc == 0 )
|
||||
encoding = TIXML_ENCODING_UTF8;
|
||||
else if ( TiXmlBase::StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) )
|
||||
encoding = TIXML_ENCODING_UTF8;
|
||||
else if ( TiXmlBase::StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) )
|
||||
encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice
|
||||
else
|
||||
encoding = TIXML_ENCODING_LEGACY;
|
||||
}
|
||||
|
||||
p = TiXmlBase::SkipWhiteSpace( p, encoding );
|
||||
}
|
||||
|
||||
// Was this empty?
|
||||
if ( !firstChild ) {
|
||||
SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// All is well.
|
||||
return p;
|
||||
}
|
||||
*/
|
||||
248
Engine/source/persistence/taml/fsTinyXml.h
Normal file
248
Engine/source/persistence/taml/fsTinyXml.h
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FSTINYXML_H_
|
||||
#define _FSTINYXML_H_
|
||||
|
||||
|
||||
#ifndef TINYXML_INCLUDED
|
||||
#include "tinyXML/tinyxml.h"
|
||||
#endif
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#ifndef _FILESTREAM_H_
|
||||
#include "core/stream/fileStream.h"
|
||||
#endif
|
||||
|
||||
class fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
virtual void Print( FileStream& stream, int depth ) const = 0;
|
||||
};
|
||||
|
||||
class fsTiXmlDocument : public TiXmlDocument, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
bool LoadFile( FileStream &stream, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
|
||||
bool SaveFile( FileStream &stream ) const;
|
||||
/// Load a file using the given filename. Returns true if successful.
|
||||
bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
|
||||
/// Save a file using the given filename. Returns true if successful.
|
||||
bool SaveFile( const char * filename ) const;
|
||||
virtual void Print( FileStream& stream, int depth ) const;
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
TiXmlDocument* clone = new fsTiXmlDocument();
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
TiXmlNode* Identify( const char* p, TiXmlEncoding encoding );
|
||||
};
|
||||
|
||||
class fsTiXmlAttribute : public TiXmlAttribute, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
virtual void Print( FileStream& stream, int depth, TIXML_STRING* str ) const;
|
||||
virtual void Print( FileStream& stream, int depth) const
|
||||
{
|
||||
Print(stream, depth, 0);
|
||||
}
|
||||
};
|
||||
|
||||
class fsTiXmlDeclaration : public TiXmlDeclaration, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
fsTiXmlDeclaration(){};
|
||||
fsTiXmlDeclaration( const char* _version,
|
||||
const char* _encoding,
|
||||
const char* _standalone ) : TiXmlDeclaration(_version, _encoding, _standalone) { }
|
||||
|
||||
virtual void Print( FileStream& stream, int depth, TIXML_STRING* str ) const;
|
||||
virtual void Print( FileStream& stream, int depth) const
|
||||
{
|
||||
Print(stream, depth, 0);
|
||||
}
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
fsTiXmlDeclaration* clone = new fsTiXmlDeclaration();
|
||||
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
};
|
||||
|
||||
class fsTiXmlElement : public TiXmlElement, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
fsTiXmlElement(const char* in_value) : TiXmlElement(in_value) { }
|
||||
|
||||
virtual void Print( FileStream& stream, int depth ) const;
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
TiXmlElement* clone = new fsTiXmlElement( Value() );
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
|
||||
void SetAttribute( const char* name, const char * _value )
|
||||
{
|
||||
TiXmlAttribute* attrib = attributeSet.Find( name );
|
||||
if(!attrib)
|
||||
{
|
||||
attrib = new fsTiXmlAttribute();
|
||||
attributeSet.Add( attrib );
|
||||
attrib->SetName( name );
|
||||
}
|
||||
if ( attrib ) {
|
||||
attrib->SetValue( _value );
|
||||
}
|
||||
}
|
||||
void SetAttribute( const char * name, int value )
|
||||
{
|
||||
TiXmlAttribute* attrib = attributeSet.Find( name );
|
||||
if(!attrib)
|
||||
{
|
||||
attrib = new fsTiXmlAttribute();
|
||||
attributeSet.Add( attrib );
|
||||
attrib->SetName( name );
|
||||
}
|
||||
if ( attrib ) {
|
||||
attrib->SetIntValue( value );
|
||||
}
|
||||
}
|
||||
TiXmlNode* Identify( const char* p, TiXmlEncoding encoding );
|
||||
virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
|
||||
};
|
||||
|
||||
class fsTiXmlComment : public TiXmlComment, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
virtual void Print( FileStream& stream, int depth ) const;
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
TiXmlComment* clone = new fsTiXmlComment();
|
||||
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
};
|
||||
|
||||
class fsTiXmlText : public TiXmlText, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
fsTiXmlText (const char * initValue ) : TiXmlText(initValue) { }
|
||||
virtual void Print( FileStream& stream, int depth ) const;
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
TiXmlText* clone = 0;
|
||||
clone = new fsTiXmlText( "" );
|
||||
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
};
|
||||
|
||||
class fsTiXmlUnknown : public TiXmlUnknown, public fsTiXmlNode
|
||||
{
|
||||
public:
|
||||
virtual void Print( FileStream& stream, int depth ) const;
|
||||
|
||||
virtual TiXmlNode* Clone() const
|
||||
{
|
||||
TiXmlUnknown* clone = new fsTiXmlUnknown();
|
||||
|
||||
if ( !clone )
|
||||
return 0;
|
||||
|
||||
CopyTo( clone );
|
||||
return clone;
|
||||
}
|
||||
};
|
||||
|
||||
static bool AttemptPrintTiNode(class fsTiXmlDocument* node, FileStream& stream, int depth)
|
||||
{
|
||||
fsTiXmlDocument* fsDoc = dynamic_cast<fsTiXmlDocument*>(node);
|
||||
if(fsDoc != NULL)
|
||||
{
|
||||
fsDoc->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlUnknown* fsUnk = dynamic_cast<fsTiXmlUnknown*>(node);
|
||||
if(fsUnk != NULL)
|
||||
{
|
||||
fsUnk->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlText* fsTxt = dynamic_cast<fsTiXmlText*>(node);
|
||||
if(fsTxt != NULL)
|
||||
{
|
||||
fsTxt->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlComment* fsCom = dynamic_cast<fsTiXmlComment*>(node);
|
||||
if(fsCom != NULL)
|
||||
{
|
||||
fsCom->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlElement* fsElm = dynamic_cast<fsTiXmlElement*>(node);
|
||||
if(fsElm != NULL)
|
||||
{
|
||||
fsElm->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlDeclaration* fsDec = dynamic_cast<fsTiXmlDeclaration*>(node);
|
||||
if(fsDec != NULL)
|
||||
{
|
||||
fsDec->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
fsTiXmlAttribute* fsAtt = dynamic_cast<fsTiXmlAttribute*>(node);
|
||||
if(fsAtt != NULL)
|
||||
{
|
||||
fsAtt->Print(stream, depth);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif //_FSTINYXML_H_
|
||||
258
Engine/source/persistence/taml/json/tamlJSONParser.cpp
Normal file
258
Engine/source/persistence/taml/json/tamlJSONParser.cpp
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "persistence/taml/json/tamlJSONParser.h"
|
||||
#include "persistence/taml/tamlVisitor.h"
|
||||
#include "console/console.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "core/frameAllocator.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool TamlJSONParser::accept( const char* pFilename, TamlVisitor& visitor )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONParser_Accept);
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pFilename != NULL, "Cannot parse a NULL filename." );
|
||||
|
||||
// Expand the file-path.
|
||||
char filenameBuffer[1024];
|
||||
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
|
||||
|
||||
FileStream stream;
|
||||
|
||||
// File open for read?
|
||||
if ( !stream.open( filenameBuffer, Torque::FS::File::Write ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("TamlJSONParser::parse() - Could not open filename '%s' for parse.", filenameBuffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read JSON file.
|
||||
const U32 streamSize = stream.getStreamSize();
|
||||
FrameTemp<char> jsonText( streamSize + 1 );
|
||||
if ( !stream.read( streamSize, jsonText ) )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlJSONParser::parse() - Could not load Taml JSON file from stream.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create JSON document.
|
||||
rapidjson::Document inputDocument;
|
||||
inputDocument.Parse<0>( jsonText );
|
||||
|
||||
// Close the stream.
|
||||
stream.close();
|
||||
|
||||
// Check the document is valid.
|
||||
if ( inputDocument.GetType() != rapidjson::kObjectType )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlJSONParser::parse() - Load Taml JSON file from stream but was invalid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set parsing filename.
|
||||
setParsingFilename( filenameBuffer );
|
||||
|
||||
// Flag document as not dirty.
|
||||
mDocumentDirty = false;
|
||||
|
||||
// Fetch the root.
|
||||
rapidjson::Value::MemberIterator rootItr = inputDocument.MemberBegin();
|
||||
|
||||
// Parse root value.
|
||||
parseType( rootItr, visitor, true );
|
||||
|
||||
// Reset parsing filename.
|
||||
setParsingFilename( StringTable->EmptyString() );
|
||||
|
||||
// Finish if the document is not dirty.
|
||||
if ( !mDocumentDirty )
|
||||
return true;
|
||||
|
||||
// Open for write?
|
||||
|
||||
if ( !stream.open( filenameBuffer, Torque::FS::File::Write ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("TamlJSONParser::parse() - Could not open filename '%s' for write.", filenameBuffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create output document.
|
||||
rapidjson::Document outputDocument;
|
||||
outputDocument.AddMember( rootItr->name, rootItr->value, outputDocument.GetAllocator() );
|
||||
|
||||
// Write document to stream.
|
||||
rapidjson::PrettyWriter<FileStream> jsonStreamWriter( stream );
|
||||
outputDocument.Accept( jsonStreamWriter );
|
||||
|
||||
// Close the stream.
|
||||
stream.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline bool TamlJSONParser::parseType( rapidjson::Value::MemberIterator& memberItr, TamlVisitor& visitor, const bool isRoot )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONParser_ParseType);
|
||||
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& typeName = memberItr->name;
|
||||
rapidjson::Value& typeValue = memberItr->value;
|
||||
|
||||
// Create a visitor property state.
|
||||
TamlVisitor::PropertyState propertyState;
|
||||
propertyState.setObjectName( typeName.GetString(), isRoot );
|
||||
|
||||
// Parse field members.
|
||||
for( rapidjson::Value::MemberIterator fieldMemberItr = typeValue.MemberBegin(); fieldMemberItr != typeValue.MemberEnd(); ++fieldMemberItr )
|
||||
{
|
||||
// Fetch value.
|
||||
const rapidjson::Value& fieldName = fieldMemberItr->name;
|
||||
rapidjson::Value& fieldValue = fieldMemberItr->value;
|
||||
|
||||
// Skip if not a field.
|
||||
if ( fieldValue.IsObject() )
|
||||
continue;
|
||||
|
||||
char valueBuffer[4096];
|
||||
if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), fieldValue, fieldName.GetString() ) )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlJSONParser::parseTyoe() - Could not interpret value for field '%s'", fieldName.GetString() );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Configure property state.
|
||||
propertyState.setProperty( fieldName.GetString(), valueBuffer );
|
||||
|
||||
// Visit this attribute.
|
||||
const bool visitStatus = visitor.visit( *this, propertyState );
|
||||
|
||||
// Was the property value changed?
|
||||
if ( propertyState.getPropertyValueDirty() )
|
||||
{
|
||||
// Yes, so update the attribute.
|
||||
fieldValue.SetString( propertyState.getPropertyValue() );
|
||||
|
||||
// Flag the document as dirty.
|
||||
mDocumentDirty = true;
|
||||
}
|
||||
|
||||
// Finish if requested.
|
||||
if ( !visitStatus )
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finish if only the root is needed.
|
||||
if ( visitor.wantsRootOnly() )
|
||||
return false;
|
||||
|
||||
// Parse children and custom node members.
|
||||
for( rapidjson::Value::MemberIterator objectMemberItr = typeValue.MemberBegin(); objectMemberItr != typeValue.MemberEnd(); ++objectMemberItr )
|
||||
{
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& objectValue = objectMemberItr->value;
|
||||
|
||||
// Skip if not an object.
|
||||
if ( !objectValue.IsObject() )
|
||||
continue;
|
||||
|
||||
// Parse the type.
|
||||
if ( !parseType( objectMemberItr, visitor, false ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline bool TamlJSONParser::parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONParser_ParseStringValue);
|
||||
|
||||
// Handle field value appropriately.
|
||||
|
||||
if ( value.IsString() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%s", value.GetString() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsNumber() )
|
||||
{
|
||||
if ( value.IsInt() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetInt() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsUint() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetUint() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsInt64() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetInt64() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsUint64() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetUint64() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsDouble() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%f", value.GetDouble() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( value.IsBool() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetBool() );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failed to get value type.
|
||||
Con::warnf( "Taml: Encountered a field '%s' but its value is an unknown type.", pName );
|
||||
return false;
|
||||
}
|
||||
|
||||
57
Engine/source/persistence/taml/json/tamlJSONParser.h
Normal file
57
Engine/source/persistence/taml/json/tamlJSONParser.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_JSONPARSER_H_
|
||||
#define _TAML_JSONPARSER_H_
|
||||
|
||||
#ifndef _TAML_PARSER_H_
|
||||
#include "persistence/taml/tamlParser.h"
|
||||
#endif
|
||||
|
||||
/// RapidJson.
|
||||
#include "persistence/rapidjson/document.h"
|
||||
#include "persistence/rapidjson/prettywriter.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlJSONParser : public TamlParser
|
||||
{
|
||||
public:
|
||||
TamlJSONParser() {}
|
||||
virtual ~TamlJSONParser() {}
|
||||
|
||||
/// Whether the parser can change a property or not.
|
||||
virtual bool canChangeProperty( void ) { return true; }
|
||||
|
||||
/// Accept visitor.
|
||||
virtual bool accept( const char* pFilename, TamlVisitor& visitor );
|
||||
|
||||
private:
|
||||
inline bool parseType( rapidjson::Value::MemberIterator& memberItr, TamlVisitor& visitor, const bool isRoot );
|
||||
inline bool parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName );
|
||||
|
||||
bool mDocumentDirty;
|
||||
};
|
||||
|
||||
#endif // _TAML_JSONPARSER_H_
|
||||
686
Engine/source/persistence/taml/json/tamlJSONReader.cpp
Normal file
686
Engine/source/persistence/taml/json/tamlJSONReader.cpp
Normal file
|
|
@ -0,0 +1,686 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "persistence/taml/json/tamlJSONReader.h"
|
||||
#include "persistence/taml/json/tamlJSONWriter.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "core/strings/stringUnit.h"
|
||||
#include "core/frameAllocator.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlJSONReader::read( FileStream& stream )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_Read);
|
||||
|
||||
// Read JSON file.
|
||||
const U32 streamSize = stream.getStreamSize();
|
||||
FrameTemp<char> jsonText( streamSize + 1 );
|
||||
if ( !stream.read( streamSize, jsonText ) )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlJSONReader::read() - Could not load Taml JSON file from stream.");
|
||||
return NULL;
|
||||
}
|
||||
jsonText[streamSize] = '\0';
|
||||
|
||||
// Create JSON document.
|
||||
rapidjson::Document document;
|
||||
document.Parse<0>( jsonText );
|
||||
|
||||
// Check the document is valid.
|
||||
if ( document.GetType() != rapidjson::kObjectType )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlJSONReader::read() - Load Taml JSON file from stream but was invalid.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Parse root value.
|
||||
SimObject* pSimObject = parseType( document.MemberBegin() );
|
||||
|
||||
// Reset parse.
|
||||
resetParse();
|
||||
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlJSONReader::resetParse( void )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ResetParse);
|
||||
|
||||
// Clear object reference map.
|
||||
mObjectReferenceMap.clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlJSONReader::parseType( const rapidjson::Value::ConstMemberIterator& memberItr )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseType);
|
||||
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& typeName = memberItr->name;
|
||||
const rapidjson::Value& typeValue = memberItr->value;
|
||||
|
||||
// Is value an object?
|
||||
if ( !typeValue.IsObject() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "TamlJSONReader::parseType() - Cannot process type '%s' as it is not an object.", typeName.GetString() );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Fetch engine type name (demangled).
|
||||
StringTableEntry engineTypeName = getDemangledName( typeName.GetString() );
|
||||
|
||||
// Fetch reference to Id.
|
||||
const U32 tamlRefToId = getTamlRefToId( typeValue );
|
||||
|
||||
// Do we have a reference to Id?
|
||||
if ( tamlRefToId != 0 )
|
||||
{
|
||||
// Yes, so fetch reference.
|
||||
typeObjectReferenceHash::Iterator referenceItr = mObjectReferenceMap.find( tamlRefToId );
|
||||
|
||||
// Did we find the reference?
|
||||
if ( referenceItr == mObjectReferenceMap.end() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "TamlJSONReader::parseType() - Could not find a reference Id of '%d'", tamlRefToId );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return referenceItr->value;
|
||||
}
|
||||
|
||||
// No, so fetch reference Id.
|
||||
const U32 tamlRefId = getTamlRefId( typeValue );
|
||||
|
||||
// Create type.
|
||||
SimObject* pSimObject = Taml::createType( engineTypeName, mpTaml );
|
||||
|
||||
// Finish if we couldn't create the type.
|
||||
if ( pSimObject == NULL )
|
||||
return NULL;
|
||||
|
||||
// Find Taml callbacks.
|
||||
TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject );
|
||||
|
||||
TamlCustomNodes customNodes;
|
||||
|
||||
// Are there any Taml callbacks?
|
||||
if ( pCallbacks != NULL )
|
||||
{
|
||||
// Yes, so call it.
|
||||
mpTaml->tamlPreRead( pCallbacks );
|
||||
}
|
||||
|
||||
// Parse field members.
|
||||
for( rapidjson::Value::ConstMemberIterator fieldMemberItr = typeValue.MemberBegin(); fieldMemberItr != typeValue.MemberEnd(); ++fieldMemberItr )
|
||||
{
|
||||
// Fetch value.
|
||||
const rapidjson::Value& fieldValue = fieldMemberItr->value;
|
||||
|
||||
// Skip if not a field.
|
||||
if ( fieldValue.IsObject() )
|
||||
continue;
|
||||
|
||||
// Parse as field.
|
||||
parseField( fieldMemberItr, pSimObject );
|
||||
}
|
||||
|
||||
// Fetch object name.
|
||||
StringTableEntry objectName = StringTable->insert( getTamlObjectName( typeValue ) );
|
||||
|
||||
// Does the object require a name?
|
||||
if ( objectName == StringTable->EmptyString() )
|
||||
{
|
||||
// No, so just register anonymously.
|
||||
pSimObject->registerObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Yes, so register a named object.
|
||||
pSimObject->registerObject( objectName );
|
||||
|
||||
// Was the name assigned?
|
||||
if ( pSimObject->getName() != objectName )
|
||||
{
|
||||
// No, so warn that the name was rejected.
|
||||
Con::warnf( "Taml::parseType() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists.",
|
||||
engineTypeName, objectName );
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a reference Id?
|
||||
if ( tamlRefId != 0 )
|
||||
{
|
||||
// Yes, so insert reference.
|
||||
mObjectReferenceMap.insertUnique( tamlRefId, pSimObject );
|
||||
}
|
||||
|
||||
// Parse children and custom node members.
|
||||
for( rapidjson::Value::ConstMemberIterator objectMemberItr = typeValue.MemberBegin(); objectMemberItr != typeValue.MemberEnd(); ++objectMemberItr )
|
||||
{
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& objectName = objectMemberItr->name;
|
||||
const rapidjson::Value& objectValue = objectMemberItr->value;
|
||||
|
||||
// Skip if not an object.
|
||||
if ( !objectValue.IsObject() )
|
||||
continue;
|
||||
|
||||
// Find the period character in the name.
|
||||
const char* pPeriod = dStrchr( objectName.GetString(), '.' );
|
||||
|
||||
// Did we find the period?
|
||||
if ( pPeriod == NULL )
|
||||
{
|
||||
// No, so parse child object.
|
||||
parseChild( objectMemberItr, pSimObject );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Yes, so parse custom object.
|
||||
parseCustom( objectMemberItr, pSimObject, pPeriod+1, customNodes );
|
||||
}
|
||||
|
||||
// Call custom read.
|
||||
if ( pCallbacks )
|
||||
{
|
||||
mpTaml->tamlCustomRead( pCallbacks, customNodes );
|
||||
mpTaml->tamlPostRead( pCallbacks, customNodes );
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline void TamlJSONReader::parseField( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseField);
|
||||
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& name = memberItr->name;
|
||||
const rapidjson::Value& value = memberItr->value;
|
||||
|
||||
// Insert the field name.
|
||||
StringTableEntry fieldName = StringTable->insert( name.GetString() );
|
||||
|
||||
// Ignore if this is a Taml attribute.
|
||||
if ( fieldName == tamlRefIdName ||
|
||||
fieldName == tamlRefToIdName ||
|
||||
fieldName == tamlNamedObjectName )
|
||||
return;
|
||||
|
||||
// Get field value.
|
||||
char valueBuffer[4096];
|
||||
if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), value, fieldName ) )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "Taml::parseField() Could not interpret value for field '%s'", fieldName );
|
||||
return;
|
||||
}
|
||||
|
||||
// Set field.
|
||||
pSimObject->setPrefixedDataField(fieldName, NULL, valueBuffer);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline void TamlJSONReader::parseChild( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseChild);
|
||||
|
||||
// Fetch name.
|
||||
const rapidjson::Value& name = memberItr->name;
|
||||
|
||||
// Fetch the Taml children.
|
||||
TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject );
|
||||
|
||||
// Is this a Taml child?
|
||||
if ( pChildren == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml::parseChild() - Child member '%s' found under parent '%s' but object cannot have children.",
|
||||
name.GetString(),
|
||||
pSimObject->getClassName() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch any container child class specifier.
|
||||
AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true );
|
||||
|
||||
// Parse child member.
|
||||
SimObject* pChildSimObject = parseType( memberItr );
|
||||
|
||||
// Finish if the child was not created.
|
||||
if ( pChildSimObject == NULL )
|
||||
return;
|
||||
|
||||
// Do we have a container child class?
|
||||
if ( pContainerChildClass != NULL )
|
||||
{
|
||||
// Yes, so is the child object the correctly derived type?
|
||||
if ( !pChildSimObject->getClassRep()->isClass( pContainerChildClass ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml::parseChild() - Child element '%s' found under parent '%s' but object is restricted to children of type '%s'.",
|
||||
pChildSimObject->getClassName(),
|
||||
pSimObject->getClassName(),
|
||||
pContainerChildClass->getClassName() );
|
||||
|
||||
// NOTE: We can't delete the object as it may be referenced elsewhere!
|
||||
pChildSimObject = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add child.
|
||||
pChildren->addTamlChild( pChildSimObject );
|
||||
|
||||
// Find Taml callbacks for child.
|
||||
TamlCallbacks* pChildCallbacks = dynamic_cast<TamlCallbacks*>( pChildSimObject );
|
||||
|
||||
// Do we have callbacks on the child?
|
||||
if ( pChildCallbacks != NULL )
|
||||
{
|
||||
// Yes, so perform callback.
|
||||
mpTaml->tamlAddParent( pChildCallbacks, pSimObject );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline void TamlJSONReader::parseCustom( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject, const char* pCustomNodeName, TamlCustomNodes& customNodes )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseCustom);
|
||||
|
||||
// Fetch value.
|
||||
const rapidjson::Value& value = memberItr->value;
|
||||
|
||||
// Add custom node.
|
||||
TamlCustomNode* pCustomNode = customNodes.addNode( pCustomNodeName );
|
||||
|
||||
// Iterate members.
|
||||
for( rapidjson::Value::ConstMemberIterator customMemberItr = value.MemberBegin(); customMemberItr != value.MemberEnd(); ++customMemberItr )
|
||||
{
|
||||
// Fetch value.
|
||||
const rapidjson::Value& customValue = customMemberItr->value;
|
||||
|
||||
// Is the member an object?
|
||||
if ( !customValue.IsObject() && !customValue.IsArray() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::parseCustom() - Cannot process custom node name '%s' member as child value is not an object or array.", pCustomNodeName );
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse custom node.
|
||||
parseCustomNode( customMemberItr, pCustomNode );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline void TamlJSONReader::parseCustomNode( rapidjson::Value::ConstMemberIterator& memberItr, TamlCustomNode* pCustomNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseCustomNode);
|
||||
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& name = memberItr->name;
|
||||
const rapidjson::Value& value = memberItr->value;
|
||||
|
||||
// Is the value an object?
|
||||
if ( value.IsObject() )
|
||||
{
|
||||
// Yes, so is the node a proxy object?
|
||||
if ( getTamlRefId( value ) != 0 || getTamlRefToId( value ) != 0 )
|
||||
{
|
||||
// Yes, so parse proxy object.
|
||||
SimObject* pProxyObject = parseType( memberItr );
|
||||
|
||||
// Add child node.
|
||||
pCustomNode->addNode( pProxyObject );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char valueBuffer[4096];
|
||||
|
||||
// Fetch the node name.
|
||||
StringTableEntry nodeName = getDemangledName( name.GetString() );
|
||||
|
||||
// Yes, so add child node.
|
||||
TamlCustomNode* pChildNode = pCustomNode->addNode( nodeName );
|
||||
|
||||
// Is the value an array?
|
||||
if ( value.IsArray() )
|
||||
{
|
||||
// Yes, so does it have a single entry?
|
||||
if ( value.Size() == 1 )
|
||||
{
|
||||
// Yes, so parse the node text.
|
||||
if ( parseStringValue( valueBuffer, sizeof(valueBuffer), value.Begin(), nodeName ) )
|
||||
{
|
||||
pChildNode->setNodeText( valueBuffer );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but could not interpret the value.", nodeName );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but more than a single element was found in the array.", nodeName );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate child members.
|
||||
for( rapidjson::Value::ConstMemberIterator childMemberItr = value.MemberBegin(); childMemberItr != value.MemberEnd(); ++childMemberItr )
|
||||
{
|
||||
// Fetch name and value.
|
||||
const rapidjson::Value& childName = childMemberItr->name;
|
||||
const rapidjson::Value& childValue = childMemberItr->value;
|
||||
|
||||
// Fetch the field name.
|
||||
StringTableEntry fieldName = StringTable->insert( childName.GetString() );
|
||||
|
||||
// Is the value an array?
|
||||
if ( childValue.IsArray() )
|
||||
{
|
||||
// Yes, so does it have a single entry?
|
||||
if ( childValue.Size() == 1 )
|
||||
{
|
||||
// Yes, so parse the node text.
|
||||
if ( parseStringValue( valueBuffer, sizeof(valueBuffer), *childValue.Begin(), fieldName ) )
|
||||
{
|
||||
// Yes, so add sub-child node.
|
||||
TamlCustomNode* pSubChildNode = pChildNode->addNode( fieldName );
|
||||
|
||||
// Set sub-child text.
|
||||
pSubChildNode->setNodeText( valueBuffer );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Warn.
|
||||
Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but could not interpret the value.", fieldName );
|
||||
return;
|
||||
}
|
||||
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but more than a single element was found in the array.", fieldName );
|
||||
return;
|
||||
}
|
||||
|
||||
// Is the member an object?
|
||||
if ( childValue.IsObject() )
|
||||
{
|
||||
// Yes, so parse custom node.
|
||||
parseCustomNode( childMemberItr, pChildNode );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore if this is a Taml attribute.
|
||||
if ( fieldName == tamlRefIdName ||
|
||||
fieldName == tamlRefToIdName ||
|
||||
fieldName == tamlNamedObjectName )
|
||||
continue;
|
||||
|
||||
// Parse string value.
|
||||
if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), childValue, childName.GetString() ) )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "Taml::parseCustomNode() - Could not interpret value for field '%s'", fieldName );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add node field.
|
||||
pChildNode->addField( fieldName, valueBuffer );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline StringTableEntry TamlJSONReader::getDemangledName( const char* pMangledName )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_GetDemangledName);
|
||||
|
||||
// Is the type name mangled?
|
||||
if ( StringUnit::getUnitCount( pMangledName, JSON_RFC4627_NAME_MANGLING_CHARACTERS ) > 1 )
|
||||
{
|
||||
// Yes, so fetch type name portion.
|
||||
return StringTable->insert( StringUnit::getUnit( pMangledName, 0, JSON_RFC4627_NAME_MANGLING_CHARACTERS ) );
|
||||
}
|
||||
|
||||
// No, so use all the type name.
|
||||
return StringTable->insert( pMangledName );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline bool TamlJSONReader::parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_ParseStringValue);
|
||||
|
||||
// Handle field value appropriately.
|
||||
|
||||
if ( value.IsString() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%s", value.GetString() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsNumber() )
|
||||
{
|
||||
if ( value.IsInt() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetInt() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsUint() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetUint() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsInt64() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetInt64() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsUint64() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetUint64() );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( value.IsDouble() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%f", value.GetDouble() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( value.IsBool() )
|
||||
{
|
||||
dSprintf( pBuffer, bufferSize, "%d", value.GetBool() );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failed to get value type.
|
||||
Con::warnf( "Taml: Encountered a field '%s' but its value is an unknown type.", pName );
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline U32 TamlJSONReader::getTamlRefId( const rapidjson::Value& value )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_GetTamlRefId);
|
||||
|
||||
// Is the value an object?
|
||||
if ( !value.IsObject() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlRefId() - Cannot get '%s' member as value is not an object.", tamlRefIdName );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Iterate members.
|
||||
for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr )
|
||||
{
|
||||
// Insert member name.
|
||||
StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlRefIdName )
|
||||
continue;
|
||||
|
||||
// Is the value an integer?
|
||||
if ( !memberItr->value.IsInt() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlRefId() - Found '%s' member but it is not an integer.", tamlRefIdName );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return it.
|
||||
return (U32)memberItr->value.GetInt();
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline U32 TamlJSONReader::getTamlRefToId( const rapidjson::Value& value )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_GetTamlRefToId);
|
||||
|
||||
// Is the value an object?
|
||||
if ( !value.IsObject() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlRefToId() - Cannot get '%s' member as value is not an object.", tamlRefToIdName );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Iterate members.
|
||||
for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr )
|
||||
{
|
||||
// Insert member name.
|
||||
StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlRefToIdName )
|
||||
continue;
|
||||
|
||||
// Is the value an integer?
|
||||
if ( !memberItr->value.IsInt() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlRefToId() - Found '%s' member but it is not an integer.", tamlRefToIdName );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return it.
|
||||
return (U32)memberItr->value.GetInt();
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline const char* TamlJSONReader::getTamlObjectName( const rapidjson::Value& value )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONReader_GetTamlObjectName);
|
||||
|
||||
// Is the value an object?
|
||||
if ( !value.IsObject() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlObjectName() - Cannot get '%s' member as value is not an object.", tamlNamedObjectName );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Iterate members.
|
||||
for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr )
|
||||
{
|
||||
// Insert member name.
|
||||
StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlNamedObjectName )
|
||||
continue;
|
||||
|
||||
// Is the value an integer?
|
||||
if ( !memberItr->value.IsString() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::getTamlObjectName() - Found '%s' member but it is not a string.", tamlNamedObjectName );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return it.
|
||||
return memberItr->value.GetString();
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
76
Engine/source/persistence/taml/json/tamlJSONReader.h
Normal file
76
Engine/source/persistence/taml/json/tamlJSONReader.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_JSONREADER_H_
|
||||
#define _TAML_JSONREADER_H_
|
||||
|
||||
#ifndef _TDICTIONARY_H_
|
||||
#include "core/util/tDictionary.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
/// RapidJson.
|
||||
#include "persistence/rapidjson/document.h"
|
||||
#include "persistence/rapidjson/prettywriter.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlJSONReader
|
||||
{
|
||||
public:
|
||||
TamlJSONReader( Taml* pTaml ) :
|
||||
mpTaml( pTaml )
|
||||
{}
|
||||
|
||||
virtual ~TamlJSONReader() {}
|
||||
|
||||
/// Read.
|
||||
SimObject* read( FileStream& stream );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
|
||||
typedef HashTable<SimObjectId, SimObject*> typeObjectReferenceHash;
|
||||
typeObjectReferenceHash mObjectReferenceMap;
|
||||
|
||||
private:
|
||||
void resetParse( void );
|
||||
|
||||
SimObject* parseType( const rapidjson::Value::ConstMemberIterator& memberItr );
|
||||
inline void parseField( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject );
|
||||
inline void parseChild( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject );
|
||||
inline void parseCustom( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject, const char* pCustomNodeName, TamlCustomNodes& customNodes );
|
||||
inline void parseCustomNode( rapidjson::Value::ConstMemberIterator& memberItr, TamlCustomNode* pCustomNode );
|
||||
|
||||
inline StringTableEntry getDemangledName( const char* pMangledName );
|
||||
inline bool parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName );
|
||||
inline U32 getTamlRefId( const rapidjson::Value& value );
|
||||
inline U32 getTamlRefToId( const rapidjson::Value& value );
|
||||
inline const char* getTamlObjectName( const rapidjson::Value& value );
|
||||
};
|
||||
|
||||
#endif // _TAML_JSONREADER_H_
|
||||
367
Engine/source/persistence/taml/json/tamlJSONWriter.cpp
Normal file
367
Engine/source/persistence/taml/json/tamlJSONWriter.cpp
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "persistence/taml/json/tamlJSONWriter.h"
|
||||
|
||||
#include "core/stringTable.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// These are the characters allowed that separate the type-name from the type.
|
||||
// These separators can be used to ensure that each member in an object has
|
||||
// a unique name.
|
||||
//
|
||||
// It is important to understand that TAML does not require entries to be unique
|
||||
// but technically the RFC4627 spec states it as "should" be seems to be taken
|
||||
// as MUST. See here: http://www.ietf.org/rfc/rfc4627.txt
|
||||
//
|
||||
// They can be placed as a suffix to the type-name. The very first occurance
|
||||
// is used to split the type-name from the suffix so you can form whatever
|
||||
// suffix you like to make entries unique.
|
||||
// Examples are "Sprite 0", "Sprite*0", "Sprite[0]", "Sprite,0" etc.
|
||||
//
|
||||
// Type-names can legally consist of a-z, A-Z, 0-9 and "_" (underscore) so these
|
||||
// characters cannot be used for mangling. Feel free to add any characters you
|
||||
// require to this list.
|
||||
StringTableEntry JSON_RFC4627_NAME_MANGLING_CHARACTERS = StringTable->insert(" !$%^&*()-+{}[]@:~#|\\/?<>,.\n\r\t");
|
||||
|
||||
// This is the "dSprintf" format that the JSON writer uses to encode each
|
||||
// member if JSON_RFC4627 mode is on. You are free to change this as long as
|
||||
// you ensure that it starts with the "%s" character (which represents the type name)
|
||||
// and is immediately followed by at least a single mangling character and that the
|
||||
// "%d" is present as that represents the automatically-added member index.
|
||||
StringTableEntry JSON_RFC4627_NAME_MANGLING_FORMAT = StringTable->insert( "%s[%d]" );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool TamlJSONWriter::write( FileStream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_Write);
|
||||
|
||||
// Create document.
|
||||
rapidjson::Document document;
|
||||
document.SetObject();
|
||||
|
||||
// Compile the root type.
|
||||
rapidjson::Value rootValue(rapidjson::kObjectType);
|
||||
compileType( document, &rootValue, NULL, pTamlWriteNode, -1 );
|
||||
|
||||
// Write document to stream.
|
||||
rapidjson::PrettyWriter<FileStream> jsonStreamWriter( stream );
|
||||
document.Accept( jsonStreamWriter );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlJSONWriter::compileType( rapidjson::Document& document, rapidjson::Value* pTypeValue, rapidjson::Value* pParentValue, const TamlWriteNode* pTamlWriteNode, const S32 memberIndex )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_CompileType);
|
||||
|
||||
// Fetch the json document allocator.
|
||||
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
||||
|
||||
// Fetch object.
|
||||
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
|
||||
|
||||
// Fetch JSON strict flag (don't use it if member index is set to not use it).
|
||||
const bool jsonStrict = memberIndex == -1 ? false : mpTaml->getJSONStrict();
|
||||
|
||||
// Fetch element name (mangled or not).
|
||||
StringTableEntry elementName = jsonStrict ? getManagedName( pSimObject->getClassName(), memberIndex ) : pSimObject->getClassName();
|
||||
|
||||
// Is there a parent value?
|
||||
if ( pParentValue == NULL )
|
||||
{
|
||||
// No, so add as document root value member.
|
||||
pTypeValue = &((document.AddMember( elementName, *pTypeValue, allocator ).MemberEnd()-1)->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Yes, so add as a parent value member.
|
||||
pTypeValue = &((pParentValue->AddMember( elementName, *pTypeValue, allocator ).MemberEnd()-1)->value);
|
||||
}
|
||||
|
||||
// Fetch reference Id.
|
||||
const U32 referenceId = pTamlWriteNode->mRefId;
|
||||
|
||||
// Do we have a reference Id?
|
||||
if ( referenceId != 0 )
|
||||
{
|
||||
// Yes, so set reference Id.
|
||||
rapidjson::Value value;
|
||||
value.SetInt( referenceId );
|
||||
pTypeValue->AddMember( tamlRefIdName, value, allocator );
|
||||
}
|
||||
// Do we have a reference to node?
|
||||
else if ( pTamlWriteNode->mRefToNode != NULL )
|
||||
{
|
||||
// Yes, so fetch reference to Id.
|
||||
const U32 referenceToId = pTamlWriteNode->mRefToNode->mRefId;
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( referenceToId != 0, "Taml: Invalid reference to Id." );
|
||||
|
||||
// Set reference to Id.
|
||||
rapidjson::Value value;
|
||||
value.SetInt( referenceToId );
|
||||
pTypeValue->AddMember( tamlRefToIdName, value, allocator );
|
||||
|
||||
// Finish because we're a reference to another object.
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch object name.
|
||||
const char* pObjectName = pTamlWriteNode->mpObjectName;
|
||||
|
||||
// Do we have a name?
|
||||
if ( pObjectName != NULL )
|
||||
{
|
||||
// Yes, so set name.
|
||||
rapidjson::Value value;
|
||||
value.SetString( pObjectName, dStrlen(pObjectName), allocator );
|
||||
pTypeValue->AddMember( tamlNamedObjectName, value, allocator );
|
||||
}
|
||||
|
||||
// Compile field.
|
||||
compileFields( document, pTypeValue, pTamlWriteNode );
|
||||
|
||||
// Fetch children.
|
||||
Vector<TamlWriteNode*>* pChildren = pTamlWriteNode->mChildren;
|
||||
|
||||
// Do we have any children?
|
||||
if ( pChildren )
|
||||
{
|
||||
S32 childMemberIndex = 0;
|
||||
|
||||
// Yes, so iterate children.
|
||||
for( Vector<TamlWriteNode*>::iterator itr = pChildren->begin(); itr != pChildren->end(); ++itr )
|
||||
{
|
||||
// Compile child type.
|
||||
rapidjson::Value childValue(rapidjson::kObjectType);
|
||||
compileType( document, &childValue, pTypeValue, (*itr), childMemberIndex++ );
|
||||
}
|
||||
}
|
||||
|
||||
// Compile custom.
|
||||
compileCustom( document, pTypeValue, pTamlWriteNode );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlJSONWriter::compileFields( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_CompileFields);
|
||||
|
||||
// Fetch fields.
|
||||
const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
|
||||
|
||||
// Ignore if no fields.
|
||||
if ( fields.size() == 0 )
|
||||
return;
|
||||
|
||||
// Fetch the json document allocator.
|
||||
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
||||
|
||||
// Iterate fields.
|
||||
for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr )
|
||||
{
|
||||
// Fetch field/value pair.
|
||||
TamlWriteNode::FieldValuePair* pFieldValue = (*itr);
|
||||
|
||||
// Set field attribute.
|
||||
rapidjson::Value value;
|
||||
value.SetString( pFieldValue->mpValue, dStrlen(pFieldValue->mpValue), allocator );
|
||||
pTypeValue->AddMember( pFieldValue->mName, value, allocator );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlJSONWriter::compileCustom( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_CompileCustom);
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodeVector& nodes = customNodes.getNodes();
|
||||
|
||||
// Finish if no custom nodes to process.
|
||||
if ( nodes.size() == 0 )
|
||||
return;
|
||||
|
||||
// Fetch the json document allocator.
|
||||
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
||||
|
||||
// Fetch object.
|
||||
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
|
||||
|
||||
// Fetch element name.
|
||||
const char* pElementName = pSimObject->getClassName();
|
||||
|
||||
// Iterate custom nodes.
|
||||
for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
|
||||
{
|
||||
// Fetch the custom node.
|
||||
TamlCustomNode* pCustomNode = *customNodesItr;
|
||||
|
||||
// Format extended element name.
|
||||
char extendedElementNameBuffer[256];
|
||||
dSprintf( extendedElementNameBuffer, sizeof(extendedElementNameBuffer), "%s.%s", pElementName, pCustomNode->getNodeName() );
|
||||
StringTableEntry elementNameEntry = StringTable->insert( extendedElementNameBuffer );
|
||||
|
||||
rapidjson::Value elementValue(rapidjson::kObjectType);
|
||||
rapidjson::Value* pElementValue = &((pTypeValue->AddMember( elementNameEntry, elementValue, allocator ).MemberEnd()-1)->value);
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
S32 childMemberIndex = 0;
|
||||
|
||||
// Iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Compile the custom node.
|
||||
compileCustomNode( document, pElementValue, pChildNode, childMemberIndex++ );
|
||||
}
|
||||
|
||||
// Finish if the node is set to ignore if empty and it is empty.
|
||||
if ( pCustomNode->getIgnoreEmpty() && pTypeValue->MemberBegin() == pTypeValue->MemberEnd() )
|
||||
{
|
||||
// Yes, so delete the member.
|
||||
pElementValue->SetNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlJSONWriter::compileCustomNode( rapidjson::Document& document, rapidjson::Value* pParentValue, const TamlCustomNode* pCustomNode, const S32 memberIndex )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_CompileCustomNode);
|
||||
|
||||
// Finish if the node is set to ignore if empty and it is empty.
|
||||
if ( pCustomNode->getIgnoreEmpty() && pCustomNode->isEmpty() )
|
||||
return;
|
||||
|
||||
// Is the node a proxy object?
|
||||
if ( pCustomNode->isProxyObject() )
|
||||
{
|
||||
// Yes, so write the proxy object.
|
||||
rapidjson::Value proxyValue(rapidjson::kObjectType);
|
||||
compileType( document, &proxyValue, pParentValue, pCustomNode->getProxyWriteNode(), memberIndex );
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the json document allocator.
|
||||
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
||||
|
||||
// Fetch fields.
|
||||
const TamlCustomFieldVector& fields = pCustomNode->getFields();
|
||||
|
||||
// Fetch JSON strict flag (don't use it if member index is set to not use it).
|
||||
const bool jsonStrict = memberIndex == -1 ? false : mpTaml->getJSONStrict();
|
||||
|
||||
// Fetch element name (mangled or not).
|
||||
StringTableEntry nodeName = jsonStrict ? getManagedName( pCustomNode->getNodeName(), memberIndex ) : pCustomNode->getNodeName();
|
||||
|
||||
// Is there any node text?
|
||||
if ( !pCustomNode->getNodeTextField().isValueEmpty() )
|
||||
{
|
||||
// Yes, so fetch text.
|
||||
const char* pNodeText = pCustomNode->getNodeTextField().getFieldValue();
|
||||
|
||||
// Create custom value.
|
||||
rapidjson::Value customTextValue(rapidjson::kArrayType);
|
||||
customTextValue.PushBack( pNodeText, allocator );
|
||||
pParentValue->AddMember( nodeName, customTextValue, allocator );
|
||||
return;
|
||||
}
|
||||
|
||||
// Create custom value.
|
||||
rapidjson::Value customValue(rapidjson::kObjectType);
|
||||
rapidjson::Value* pCustomValue = &((pParentValue->AddMember( nodeName, customValue, allocator ).MemberEnd()-1)->value);
|
||||
|
||||
// Iterate fields.
|
||||
for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
|
||||
{
|
||||
// Fetch field.
|
||||
const TamlCustomField* pField = *fieldItr;
|
||||
|
||||
// Add a field.
|
||||
rapidjson::Value fieldValue;
|
||||
fieldValue.SetString( pField->getFieldValue(), dStrlen(pField->getFieldValue()), allocator );
|
||||
pCustomValue->AddMember( pField->getFieldName(), fieldValue, allocator );
|
||||
}
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
S32 childMemberIndex = 0;
|
||||
|
||||
// Iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Compile the child node.
|
||||
compileCustomNode( document, pCustomValue, pChildNode, childMemberIndex++ );
|
||||
}
|
||||
|
||||
// Finish if the node is set to ignore if empty and it is empty (including fields).
|
||||
if ( pCustomNode->getIgnoreEmpty() && fields.size() == 0 && pCustomValue->MemberBegin() == pCustomValue->MemberEnd() )
|
||||
{
|
||||
// Yes, so delete the member.
|
||||
pCustomValue->SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline StringTableEntry TamlJSONWriter::getManagedName( const char* pName, const S32 memberIndex )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlJSONWriter_getManagedName);
|
||||
|
||||
char nameBuffer[1024];
|
||||
|
||||
// Format mangled name.
|
||||
dSprintf( nameBuffer, sizeof(nameBuffer), JSON_RFC4627_NAME_MANGLING_FORMAT, pName, memberIndex );
|
||||
|
||||
return StringTable->insert( nameBuffer );
|
||||
}
|
||||
66
Engine/source/persistence/taml/json/tamlJSONWriter.h
Normal file
66
Engine/source/persistence/taml/json/tamlJSONWriter.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_JSONWRITER_H_
|
||||
#define _TAML_JSONWRITER_H_
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
/// RapidJson.
|
||||
#include "persistence/rapidjson/document.h"
|
||||
#include "persistence/rapidjson/prettywriter.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
extern StringTableEntry JSON_RFC4627_NAME_MANGLING_CHARACTERS;
|
||||
extern StringTableEntry JSON_RFC4627_NAME_MANGLING_FORMAT;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlJSONWriter
|
||||
{
|
||||
public:
|
||||
TamlJSONWriter( Taml* pTaml ) :
|
||||
mpTaml( pTaml )
|
||||
{}
|
||||
virtual ~TamlJSONWriter() {}
|
||||
|
||||
/// Write.
|
||||
bool write( FileStream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
|
||||
private:
|
||||
void compileType( rapidjson::Document& document, rapidjson::Value* pTypeValue, rapidjson::Value* pParentValue, const TamlWriteNode* pTamlWriteNode, const S32 memberIndex );
|
||||
void compileFields( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustom( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustomNode( rapidjson::Document& document, rapidjson::Value* pParentValue, const TamlCustomNode* pCustomNode, const S32 memberIndex );
|
||||
|
||||
inline StringTableEntry getManagedName(const char* pName, const S32 memberIndex );
|
||||
};
|
||||
|
||||
#endif // _TAML_JSONWRITER_H_
|
||||
1544
Engine/source/persistence/taml/taml.cpp
Normal file
1544
Engine/source/persistence/taml/taml.cpp
Normal file
File diff suppressed because it is too large
Load diff
218
Engine/source/persistence/taml/taml.h
Normal file
218
Engine/source/persistence/taml/taml.h
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#define _TAML_H_
|
||||
|
||||
#ifndef _TAML_CALLBACKS_H_
|
||||
#include "persistence/taml/tamlCallbacks.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_CUSTOM_H_
|
||||
#include "persistence/taml/tamlCustom.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_CHILDREN_H_
|
||||
#include "persistence/taml/tamlChildren.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_WRITE_NODE_H_
|
||||
#include "persistence/taml/tamlWriteNode.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_VISITOR_H_
|
||||
#include "persistence/taml/tamlVisitor.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TDICTIONARY_H_
|
||||
#include "core/util/tDictionary.h"
|
||||
#endif
|
||||
|
||||
#ifndef _FILESTREAM_H_
|
||||
#include "core/stream/fileStream.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
extern StringTableEntry tamlRefIdName;
|
||||
extern StringTableEntry tamlRefToIdName;
|
||||
extern StringTableEntry tamlNamedObjectName;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define TAML_SIGNATURE "Taml"
|
||||
#define TAML_SCHEMA_VARIABLE "$pref::T2D::TAMLSchema"
|
||||
#define TAML_JSON_STRICT_VARIBLE "$pref::T2D::JSONStrict"
|
||||
|
||||
class TiXmlElement;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class Taml : public SimObject
|
||||
{
|
||||
public:
|
||||
enum TamlFormatMode
|
||||
{
|
||||
InvalidFormat = 0,
|
||||
XmlFormat,
|
||||
BinaryFormat,
|
||||
JSONFormat,
|
||||
};
|
||||
|
||||
private:
|
||||
typedef SimObject Parent;
|
||||
typedef Vector<TamlWriteNode*> typeNodeVector;
|
||||
typedef HashTable<SimObjectId, TamlWriteNode*> typeCompiledHash;
|
||||
|
||||
typeNodeVector mCompiledNodes;
|
||||
typeCompiledHash mCompiledObjects;
|
||||
U32 mMasterNodeId;
|
||||
TamlFormatMode mFormatMode;
|
||||
StringTableEntry mAutoFormatXmlExtension;
|
||||
StringTableEntry mAutoFormatBinaryExtension;
|
||||
StringTableEntry mAutoFormatJSONExtension;
|
||||
bool mJSONStrict;
|
||||
bool mBinaryCompression;
|
||||
bool mAutoFormat;
|
||||
bool mWriteDefaults;
|
||||
bool mProgenitorUpdate;
|
||||
char mFilePathBuffer[1024];
|
||||
|
||||
private:
|
||||
void resetCompilation( void );
|
||||
|
||||
TamlWriteNode* compileObject( SimObject* pSimObject, const bool forceId = false );
|
||||
void compileStaticFields( TamlWriteNode* pTamlWriteNode );
|
||||
void compileDynamicFields( TamlWriteNode* pTamlWriteNode );
|
||||
void compileChildren( TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustomState( TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustomNodeState( TamlCustomNode* pCustomNode );
|
||||
|
||||
bool write( FileStream& stream, SimObject* pSimObject, const TamlFormatMode formatMode );
|
||||
SimObject* read( FileStream& stream, const TamlFormatMode formatMode );
|
||||
template<typename T> inline T* read( FileStream& stream, const TamlFormatMode formatMode )
|
||||
{
|
||||
SimObject* pSimObject = read( stream, formatMode );
|
||||
if ( pSimObject == NULL )
|
||||
return NULL;
|
||||
T* pObj = dynamic_cast<T*>( pSimObject );
|
||||
if ( pObj != NULL )
|
||||
return pObj;
|
||||
pSimObject->deleteObject();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
Taml();
|
||||
virtual ~Taml() {}
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
/// Format mode.
|
||||
inline void setFormatMode( const TamlFormatMode formatMode ) { mFormatMode = formatMode != Taml::InvalidFormat ? formatMode : Taml::XmlFormat; }
|
||||
inline TamlFormatMode getFormatMode( void ) const { return mFormatMode; }
|
||||
|
||||
/// Auto-Format mode.
|
||||
inline void setAutoFormat( const bool autoFormat ) { mAutoFormat = autoFormat; }
|
||||
inline bool getAutoFormat( void ) const { return mAutoFormat; }
|
||||
|
||||
/// Write defaults.
|
||||
inline void setWriteDefaults( const bool writeDefaults ) { mWriteDefaults = writeDefaults; }
|
||||
inline bool getWriteDefaults( void ) const { return mWriteDefaults; }
|
||||
|
||||
/// Progenitor.
|
||||
inline void setProgenitorUpdate( const bool progenitorUpdate ) { mProgenitorUpdate = progenitorUpdate; }
|
||||
inline bool getProgenitorUpdate( void ) const { return mProgenitorUpdate; }
|
||||
|
||||
/// Auto-format extensions.
|
||||
inline void setAutoFormatXmlExtension( const char* pExtension ) { mAutoFormatXmlExtension = StringTable->insert( pExtension ); }
|
||||
inline StringTableEntry getAutoFormatXmlExtension( void ) const { return mAutoFormatXmlExtension; }
|
||||
inline void setAutoFormatBinaryExtension( const char* pExtension ) { mAutoFormatBinaryExtension = StringTable->insert( pExtension ); }
|
||||
inline StringTableEntry getAutoFormatBinaryExtension( void ) const { return mAutoFormatBinaryExtension; }
|
||||
|
||||
/// Compression.
|
||||
inline void setBinaryCompression( const bool compressed ) { mBinaryCompression = compressed; }
|
||||
inline bool getBinaryCompression( void ) const { return mBinaryCompression; }
|
||||
|
||||
/// JSON Strict RFC4627 mode.
|
||||
inline void setJSONStrict( const bool jsonStrict ) { mJSONStrict = jsonStrict; }
|
||||
inline bool getJSONStrict( void ) const { return mJSONStrict; }
|
||||
|
||||
TamlFormatMode getFileAutoFormatMode( const char* pFilename );
|
||||
|
||||
const char* getFilePathBuffer( void ) const { return mFilePathBuffer; }
|
||||
|
||||
/// Write.
|
||||
bool write( SimObject* pSimObject, const char* pFilename );
|
||||
|
||||
/// Read.
|
||||
template<typename T> inline T* read( const char* pFilename )
|
||||
{
|
||||
SimObject* pSimObject = read( pFilename );
|
||||
if ( pSimObject == NULL )
|
||||
return NULL;
|
||||
T* pObj = dynamic_cast<T*>( pSimObject );
|
||||
if ( pObj != NULL )
|
||||
return pObj;
|
||||
pSimObject->deleteObject();
|
||||
return NULL;
|
||||
}
|
||||
SimObject* read( const char* pFilename );
|
||||
|
||||
/// Parse.
|
||||
bool parse( const char* pFilename, TamlVisitor& visitor );
|
||||
|
||||
/// Create type.
|
||||
static SimObject* createType( StringTableEntry typeName, const Taml* pTaml, const char* pProgenitorSuffix = NULL );
|
||||
|
||||
/// Schema generation.
|
||||
static bool generateTamlSchema();
|
||||
|
||||
/// Write a unrestricted custom Taml schema.
|
||||
static void WriteUnrestrictedCustomTamlSchema( const char* pCustomNodeName, const AbstractClassRep* pClassRep, TiXmlElement* pParentElement );
|
||||
|
||||
/// Get format mode info.
|
||||
static TamlFormatMode getFormatModeEnum( const char* label );
|
||||
static const char* getFormatModeDescription( const TamlFormatMode formatMode );
|
||||
|
||||
/// Taml callbacks.
|
||||
inline void tamlPreWrite( TamlCallbacks* pCallbacks ) { pCallbacks->onTamlPreWrite(); }
|
||||
inline void tamlPostWrite( TamlCallbacks* pCallbacks ) { pCallbacks->onTamlPostWrite(); }
|
||||
inline void tamlPreRead( TamlCallbacks* pCallbacks ) { pCallbacks->onTamlPreRead(); }
|
||||
inline void tamlPostRead( TamlCallbacks* pCallbacks, const TamlCustomNodes& customNodes ) { pCallbacks->onTamlPostRead( customNodes ); }
|
||||
inline void tamlAddParent( TamlCallbacks* pCallbacks, SimObject* pParentObject ) { pCallbacks->onTamlAddParent( pParentObject ); }
|
||||
inline void tamlCustomWrite( TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes ) { pCallbacks->onTamlCustomWrite( customNodes ); }
|
||||
inline void tamlCustomRead( TamlCallbacks* pCallbacks, const TamlCustomNodes& customNodes ) { pCallbacks->onTamlCustomRead( customNodes ); }
|
||||
|
||||
/// Declare Console Object.
|
||||
DECLARE_CONOBJECT( Taml );
|
||||
};
|
||||
|
||||
#endif // _TAML_H_
|
||||
61
Engine/source/persistence/taml/tamlCallbacks.h
Normal file
61
Engine/source/persistence/taml/tamlCallbacks.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_CALLBACKS_H_
|
||||
#define _TAML_CALLBACKS_H_
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCustomNodes;
|
||||
class SimObject;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCallbacks
|
||||
{
|
||||
friend class Taml;
|
||||
|
||||
private:
|
||||
/// Called prior to Taml writing the object.
|
||||
virtual void onTamlPreWrite( void ) = 0;
|
||||
|
||||
/// Called after Taml has finished writing the object.
|
||||
virtual void onTamlPostWrite( void ) = 0;
|
||||
|
||||
/// Called prior to Taml reading the object.
|
||||
virtual void onTamlPreRead( void ) = 0;
|
||||
|
||||
/// Called after Taml has finished reading the object.
|
||||
/// The custom properties is additionally passed here for object who want to process it at the end of reading.
|
||||
virtual void onTamlPostRead( const TamlCustomNodes& customNodes ) = 0;
|
||||
|
||||
/// Called after Taml has finished reading the object and has added the object to any parent.
|
||||
virtual void onTamlAddParent( SimObject* pParentObject ) = 0;
|
||||
|
||||
/// Called during the writing of the object to allow custom properties to be written.
|
||||
virtual void onTamlCustomWrite( TamlCustomNodes& customNodes ) = 0;
|
||||
|
||||
/// Called during the reading of the object to allow custom properties to be read.
|
||||
virtual void onTamlCustomRead( const TamlCustomNodes& customNodes ) = 0;
|
||||
};
|
||||
|
||||
#endif // _TAML_CALLBACKS_H_
|
||||
49
Engine/source/persistence/taml/tamlChildren.h
Normal file
49
Engine/source/persistence/taml/tamlChildren.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_CHILDREN_H_
|
||||
#define _TAML_CHILDREN_H_
|
||||
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
#include "platform/types.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class SimObject;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlChildren
|
||||
{
|
||||
public:
|
||||
/// Called when Taml attempts to compile a list of children.
|
||||
virtual U32 getTamlChildCount( void ) const = 0;
|
||||
|
||||
/// Called when Taml attempts to compile a list of children.
|
||||
virtual SimObject* getTamlChild( const U32 childIndex ) const = 0;
|
||||
|
||||
/// Called when Taml attempts to populate an objects children during a read.
|
||||
virtual void addTamlChild( SimObject* pSimObject ) = 0;
|
||||
};
|
||||
|
||||
#endif // _TAML_CHILDREN_H_
|
||||
72
Engine/source/persistence/taml/tamlCustom.cpp
Normal file
72
Engine/source/persistence/taml/tamlCustom.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "persistence/taml/tamlCustom.h"
|
||||
|
||||
#ifndef _TAML_WRITE_NODE_H_
|
||||
#include "persistence/taml/tamlWriteNode.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
FactoryCache<TamlCustomField> TamlCustomFieldFactory;
|
||||
FactoryCache<TamlCustomNode> TamlCustomNodeFactory;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlCustomField::set( const char* pFieldName, const char* pFieldValue )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pFieldName != NULL, "Field name cannot be NULL." );
|
||||
AssertFatal( pFieldValue != NULL, "Field value cannot be NULL." );
|
||||
|
||||
// Set field name.
|
||||
mFieldName = StringTable->insert( pFieldName );
|
||||
|
||||
#if TORQUE_DEBUG
|
||||
// Is the field value too big?
|
||||
if ( dStrlen(pFieldValue) >= sizeof(mFieldValue) )
|
||||
{
|
||||
// Yes, so warn!
|
||||
Con::warnf( "Taml property field '%s' has a value that exceeds then maximum length: '%s'", pFieldName, pFieldValue );
|
||||
AssertFatal( false, "Field value is too big!" );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Copy field value.
|
||||
dStrcpy( mFieldValue, pFieldValue );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlCustomNode::setWriteNode( TamlWriteNode* pWriteNode )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( mNodeName != StringTable->EmptyString(), "Cannot set write node with an empty node name." );
|
||||
AssertFatal( pWriteNode != NULL, "Write node cannot be NULL." );
|
||||
AssertFatal( pWriteNode->mpSimObject == mpProxyObject, "Write node does not match existing proxy object." );
|
||||
AssertFatal( mpProxyWriteNode == NULL, "Field write node must be NULL." );
|
||||
|
||||
// Set proxy write node.
|
||||
mpProxyWriteNode = pWriteNode;
|
||||
}
|
||||
|
||||
785
Engine/source/persistence/taml/tamlCustom.h
Normal file
785
Engine/source/persistence/taml/tamlCustom.h
Normal file
|
|
@ -0,0 +1,785 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_CUSTOM_H_
|
||||
#define _TAML_CUSTOM_H_
|
||||
|
||||
#ifndef _FACTORY_CACHE_H_
|
||||
#include "core/factoryCache.h"
|
||||
#endif
|
||||
|
||||
#ifndef _STRINGTABLE_H_
|
||||
#include "core/stringTable.h"
|
||||
#endif
|
||||
|
||||
#ifndef _CONSOLE_H_
|
||||
#include "console/console.h"
|
||||
#endif
|
||||
|
||||
#ifndef _CONSOLETYPES_H_
|
||||
#include "console/consoleTypes.h"
|
||||
#endif
|
||||
|
||||
#ifndef B2_MATH_H
|
||||
//TODO: Look at this
|
||||
//#include "box2d/Common/b2Math.h"
|
||||
#endif
|
||||
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
#include "core/util/safeDelete.h"
|
||||
|
||||
#include "math/mMath.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define MAX_TAML_NODE_FIELDVALUE_LENGTH 2048
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlWriteNode;
|
||||
class TamlCustomNode;
|
||||
class TamlCustomField;
|
||||
extern FactoryCache<TamlCustomNode> TamlCustomNodeFactory;
|
||||
extern FactoryCache<TamlCustomField> TamlCustomFieldFactory;
|
||||
typedef Vector<TamlCustomNode*> TamlCustomNodeVector;
|
||||
typedef Vector<TamlCustomField*> TamlCustomFieldVector;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCustomField : public IFactoryObjectReset
|
||||
{
|
||||
public:
|
||||
TamlCustomField()
|
||||
{
|
||||
resetState();
|
||||
}
|
||||
|
||||
virtual ~TamlCustomField()
|
||||
{
|
||||
// Everything should already be cleared in a state reset.
|
||||
// Touching any memory here is dangerous as this type is typically
|
||||
// held in a static factory cache until shutdown at which point
|
||||
// pretty much anything or everything could be invalid!
|
||||
}
|
||||
|
||||
virtual void resetState( void )
|
||||
{
|
||||
mFieldName = StringTable->EmptyString();
|
||||
*mFieldValue = 0;
|
||||
}
|
||||
|
||||
void set( const char* pFieldName, const char* pFieldValue );
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const ColorI& fieldValue )
|
||||
{
|
||||
// Fetch the field value.
|
||||
const char* pFieldValue = Con::getData( TypeColorI, &const_cast<ColorI&>(fieldValue), 0 );
|
||||
|
||||
// Did we get a field value?
|
||||
if ( pFieldValue == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml: Failed to add node field name '%s' with ColorI value.", pFieldName );
|
||||
pFieldValue = StringTable->EmptyString();
|
||||
}
|
||||
|
||||
set( pFieldName, pFieldValue );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const ColorF& fieldValue )
|
||||
{
|
||||
// Fetch the field value.
|
||||
const char* pFieldValue = Con::getData( TypeColorF, &const_cast<ColorF&>(fieldValue), 0 );
|
||||
|
||||
// Did we get a field value?
|
||||
if ( pFieldValue == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml: Failed to add node field name '%s' with ColorF value.", pFieldName );
|
||||
pFieldValue = StringTable->EmptyString();
|
||||
}
|
||||
|
||||
set( pFieldName, pFieldValue );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const Point2I& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d %d", fieldValue.x, fieldValue.y );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const Point2F& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g", fieldValue.x, fieldValue.y );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const Point3I& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d %d %d", fieldValue.x, fieldValue.y, fieldValue.z );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const Point3F& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g", fieldValue.x, fieldValue.y, fieldValue.z );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const RectF& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g %.5g",
|
||||
fieldValue.point.x, fieldValue.point.y, fieldValue.extent.x, fieldValue.extent.y);
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const QuatF& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g %.5g", fieldValue.x, fieldValue.y, fieldValue.z, fieldValue.w );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const AngAxisF& fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[32];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g %.5g", fieldValue.axis.x, fieldValue.axis.y, fieldValue.axis.z, fieldValue.angle );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const U32 fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[16];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const bool fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[16];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const S32 fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[16];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const float fieldValue )
|
||||
{
|
||||
char fieldValueBuffer[16];
|
||||
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g", fieldValue );
|
||||
set( pFieldName, fieldValueBuffer );
|
||||
}
|
||||
|
||||
inline void setFieldValue( const char* pFieldName, const char* fieldValue )
|
||||
{
|
||||
set( pFieldName, fieldValue );
|
||||
}
|
||||
|
||||
inline void getFieldValue( ColorF& fieldValue ) const
|
||||
{
|
||||
fieldValue.set( 1.0f, 1.0f, 1.0f, 1.0f );
|
||||
|
||||
// Set color.
|
||||
const char* argv = (char*)mFieldValue;
|
||||
Con::setData( TypeColorF, &fieldValue, 0, 1, &argv );
|
||||
}
|
||||
|
||||
inline void getFieldValue( ColorI& fieldValue ) const
|
||||
{
|
||||
fieldValue.set( 255, 255, 255, 255 );
|
||||
|
||||
// Set color.
|
||||
const char* argv = (char*)mFieldValue;
|
||||
Con::setData( TypeColorI, &fieldValue, 0, 1, &argv );
|
||||
}
|
||||
|
||||
inline void getFieldValue( Point2I& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%d %d", &fieldValue.x, &fieldValue.y ) != 2 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading point2I but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( Point2F& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%g %g", &fieldValue.x, &fieldValue.y ) != 2 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading point2F but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( Point3I& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%d %d %d", &fieldValue.x, &fieldValue.y, &fieldValue.z ) != 3 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading point3I but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( Point3F& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%g %g %g", &fieldValue.x, &fieldValue.y, &fieldValue.z ) != 3 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading point3F but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( RectF& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%g %g %g %g", &fieldValue.point.x, &fieldValue.point.y, &fieldValue.extent.x, &fieldValue.extent.y ) != 3 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading RectF but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( QuatF& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%g %g %g %g", &fieldValue.x, &fieldValue.y, &fieldValue.z, &fieldValue.w ) != 4 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading QuatF but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( AngAxisF& fieldValue ) const
|
||||
{
|
||||
if ( dSscanf( mFieldValue, "%g %g %g %g", &fieldValue.axis.x, &fieldValue.axis.y, &fieldValue.axis.z, &fieldValue.angle ) != 4 )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf( "TamlCustomField - Reading AngAxisF but it has an incorrect format: '%s'.", mFieldValue );
|
||||
}
|
||||
}
|
||||
|
||||
inline void getFieldValue( bool& fieldValue ) const
|
||||
{
|
||||
fieldValue = dAtob( mFieldValue );
|
||||
}
|
||||
|
||||
inline void getFieldValue( S32& fieldValue ) const
|
||||
{
|
||||
fieldValue = dAtoi( mFieldValue );
|
||||
}
|
||||
|
||||
inline void getFieldValue( U32& fieldValue ) const
|
||||
{
|
||||
fieldValue = (U32)dAtoi( mFieldValue );
|
||||
}
|
||||
|
||||
inline void getFieldValue( F32& fieldValue ) const
|
||||
{
|
||||
fieldValue = dAtof( mFieldValue );
|
||||
}
|
||||
|
||||
inline const char* getFieldValue( void ) const
|
||||
{
|
||||
return mFieldValue;
|
||||
}
|
||||
|
||||
inline StringTableEntry getFieldName( void ) const { return mFieldName; }
|
||||
|
||||
bool fieldNameBeginsWith( const char* pComparison ) const
|
||||
{
|
||||
const U32 comparisonLength = dStrlen( pComparison );
|
||||
const U32 fieldNameLength = dStrlen( mFieldName );
|
||||
|
||||
if ( comparisonLength == 0 || fieldNameLength == 0 || comparisonLength > fieldNameLength )
|
||||
return false;
|
||||
|
||||
StringTableEntry comparison = StringTable->insert( pComparison );
|
||||
|
||||
char fieldNameBuffer[1024];
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( fieldNameLength < sizeof(fieldNameBuffer), "TamlCustomField: Field name is too long." );
|
||||
|
||||
dStrcpy( fieldNameBuffer, mFieldName );
|
||||
fieldNameBuffer[fieldNameLength-1] = 0;
|
||||
StringTableEntry fieldName = StringTable->insert( fieldNameBuffer );
|
||||
|
||||
return ( fieldName == comparison );
|
||||
}
|
||||
|
||||
inline bool isValueEmpty( void ) const { return *mFieldValue == 0; }
|
||||
|
||||
private:
|
||||
StringTableEntry mFieldName;
|
||||
char mFieldValue[MAX_TAML_NODE_FIELDVALUE_LENGTH];
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCustomNode : public IFactoryObjectReset
|
||||
{
|
||||
public:
|
||||
TamlCustomNode()
|
||||
{
|
||||
// Reset proxy object.
|
||||
// NOTE: This MUST be done before the state is reset otherwise we'll be touching uninitialized stuff.
|
||||
mpProxyWriteNode = NULL;
|
||||
mpProxyObject = NULL;
|
||||
|
||||
resetState();
|
||||
}
|
||||
|
||||
virtual ~TamlCustomNode()
|
||||
{
|
||||
// Everything should already be cleared in a state reset.
|
||||
// Touching any memory here is dangerous as this type is typically
|
||||
// held in a static factory cache until shutdown at which point
|
||||
// pretty much anything or everything could be invalid!
|
||||
}
|
||||
|
||||
virtual void resetState( void )
|
||||
{
|
||||
// We don't need to delete the write node as it'll get destroyed when the compilation is reset!
|
||||
mpProxyWriteNode = NULL;
|
||||
mpProxyObject = NULL;
|
||||
|
||||
// Cache the children.
|
||||
while ( mChildren.size() > 0 )
|
||||
{
|
||||
TamlCustomNodeFactory.cacheObject( mChildren.back() );
|
||||
mChildren.pop_back();
|
||||
}
|
||||
|
||||
// Cache the fields.
|
||||
while( mFields.size() > 0 )
|
||||
{
|
||||
TamlCustomFieldFactory.cacheObject( mFields.back() );
|
||||
mFields.pop_back();
|
||||
}
|
||||
|
||||
// Reset the node name.
|
||||
mNodeName = StringTable->EmptyString();
|
||||
|
||||
// Reset node text.
|
||||
mNodeText.resetState();
|
||||
|
||||
// Reset the ignore empty flag.
|
||||
mIgnoreEmpty = false;
|
||||
}
|
||||
|
||||
inline TamlCustomNode* addNode( SimObject* pProxyObject )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pProxyObject != NULL, "Field object cannot be NULL." );
|
||||
AssertFatal( mpProxyWriteNode == NULL, "Field write node must be NULL." );
|
||||
|
||||
// Create a custom node.
|
||||
TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
|
||||
|
||||
// Set node name.
|
||||
pCustomNode->setNodeName( pProxyObject->getClassName() );
|
||||
|
||||
// Set proxy object.
|
||||
pCustomNode->mpProxyObject = pProxyObject;
|
||||
|
||||
// Store node.
|
||||
mChildren.push_back( pCustomNode );
|
||||
|
||||
return pCustomNode;
|
||||
}
|
||||
|
||||
inline TamlCustomNode* addNode( const char* pNodeName, const bool ignoreEmpty = true )
|
||||
{
|
||||
// Create a custom node.
|
||||
TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
|
||||
|
||||
// Fetch node name.
|
||||
pCustomNode->setNodeName( pNodeName );
|
||||
|
||||
// Set ignore-empty flag.
|
||||
pCustomNode->setIgnoreEmpty( ignoreEmpty );
|
||||
|
||||
// Store node.
|
||||
mChildren.push_back( pCustomNode );
|
||||
|
||||
return pCustomNode;
|
||||
}
|
||||
|
||||
inline void removeNode( const U32 index )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( index < (U32)mChildren.size(), "tamlCustomNode::removeNode() - Index is out of bounds." );
|
||||
|
||||
// Cache the custom node.
|
||||
TamlCustomNodeFactory.cacheObject( mChildren[index] );
|
||||
|
||||
// Remove it.
|
||||
mChildren.erase( index );
|
||||
}
|
||||
|
||||
inline const TamlCustomNode* findNode( const char* pNodeName ) const
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pNodeName != NULL, "Cannot find Taml node name that is NULL." );
|
||||
|
||||
// Fetch node name.
|
||||
StringTableEntry nodeName = StringTable->insert( pNodeName );
|
||||
|
||||
// Find node.
|
||||
for( Vector<TamlCustomNode*>::const_iterator nodeItr = mChildren.begin(); nodeItr != mChildren.end(); ++nodeItr )
|
||||
{
|
||||
if ( (*nodeItr)->getNodeName() == nodeName )
|
||||
return (*nodeItr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const ColorI& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const ColorF& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const Point2I& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const Point2F& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const Point3I& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const Point3F& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const RectF& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const QuatF& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const AngAxisF& fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const U32 fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const bool fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const S32 fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const float fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline TamlCustomField* addField( const char* pFieldName, const char* fieldValue )
|
||||
{
|
||||
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
|
||||
pNodeField->setFieldValue( pFieldName, fieldValue );
|
||||
return registerField( pNodeField );
|
||||
}
|
||||
|
||||
inline const TamlCustomField* findField( const char* pFieldName ) const
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pFieldName != NULL, "Cannot find Taml field name that is NULL." );
|
||||
|
||||
// Fetch field name.
|
||||
StringTableEntry fieldName = StringTable->insert( pFieldName );
|
||||
|
||||
// Find node field.
|
||||
for( TamlCustomFieldVector::const_iterator fieldItr = mFields.begin(); fieldItr != mFields.end(); ++fieldItr )
|
||||
{
|
||||
if ( (*fieldItr)->getFieldName() == fieldName )
|
||||
return (*fieldItr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline void setNodeName( const char* pNodeName )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pNodeName != NULL, "Cannot add a NULL node name." );
|
||||
|
||||
mNodeName = StringTable->insert( pNodeName );
|
||||
}
|
||||
|
||||
inline StringTableEntry getNodeName( void ) const { return mNodeName; }
|
||||
|
||||
void setWriteNode( TamlWriteNode* pWriteNode );
|
||||
|
||||
inline void setNodeText( const char* pNodeText )
|
||||
{
|
||||
AssertFatal( dStrlen( pNodeText ) < MAX_TAML_NODE_FIELDVALUE_LENGTH, "Custom node text is too long." );
|
||||
|
||||
mNodeText.set( StringTable->EmptyString(), pNodeText );
|
||||
}
|
||||
inline const TamlCustomField& getNodeTextField( void ) const { return mNodeText; }
|
||||
inline TamlCustomField& getNodeTextField( void ) { return mNodeText; }
|
||||
|
||||
inline const Vector<TamlCustomNode*>& getChildren( void ) const { return mChildren; }
|
||||
inline const TamlCustomFieldVector& getFields( void ) const { return mFields; }
|
||||
|
||||
inline bool isProxyObject( void ) const { return mpProxyObject != NULL; }
|
||||
template<typename T> T* getProxyObject( const bool deleteIfNotType ) const
|
||||
{
|
||||
// Return nothing if no proxy object.
|
||||
if ( mpProxyObject == NULL )
|
||||
return NULL;
|
||||
|
||||
// Cast object to specified type.
|
||||
T* pTypeCast = dynamic_cast<T*>( mpProxyObject );
|
||||
|
||||
// Destroy the object if not the specified type and requested to do so.
|
||||
if ( deleteIfNotType && pTypeCast == NULL )
|
||||
{
|
||||
mpProxyObject->deleteObject();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pTypeCast;
|
||||
}
|
||||
inline const TamlWriteNode* getProxyWriteNode( void ) const { return mpProxyWriteNode; }
|
||||
|
||||
inline bool isEmpty( void ) const { return mNodeText.isValueEmpty() && mFields.size() == 0 && mChildren.size() == 0; }
|
||||
|
||||
inline void setIgnoreEmpty( const bool ignoreEmpty ) { mIgnoreEmpty = ignoreEmpty; }
|
||||
inline bool getIgnoreEmpty( void ) const { return mIgnoreEmpty; }
|
||||
|
||||
private:
|
||||
inline TamlCustomField* registerField( TamlCustomField* pCustomField )
|
||||
{
|
||||
#if TORQUE_DEBUG
|
||||
// Ensure a field name conflict does not exist.
|
||||
for( Vector<TamlCustomField*>::iterator nodeFieldItr = mFields.begin(); nodeFieldItr != mFields.end(); ++nodeFieldItr )
|
||||
{
|
||||
// Skip if field name is not the same.
|
||||
if ( pCustomField->getFieldName() != (*nodeFieldItr)->getFieldName() )
|
||||
continue;
|
||||
|
||||
// Warn!
|
||||
Con::warnf("Conflicting Taml node field name of '%s' in node '%s'.", pCustomField->getFieldName(), mNodeName );
|
||||
|
||||
// Cache node field.
|
||||
TamlCustomFieldFactory.cacheObject( pCustomField );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Ensure the field value is not too long.
|
||||
if ( dStrlen( pCustomField->getFieldValue() ) >= MAX_TAML_NODE_FIELDVALUE_LENGTH )
|
||||
{
|
||||
// Warn.
|
||||
Con::warnf("Taml field name '%s' has a field value that is too long (Max:%d): '%s'.",
|
||||
pCustomField->getFieldName(),
|
||||
MAX_TAML_NODE_FIELDVALUE_LENGTH,
|
||||
pCustomField->getFieldValue() );
|
||||
|
||||
// Cache node field.
|
||||
TamlCustomFieldFactory.cacheObject( pCustomField );
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
// Store node field.
|
||||
mFields.push_back( pCustomField );
|
||||
|
||||
return pCustomField;
|
||||
}
|
||||
|
||||
inline TamlCustomField* createField( void ) const { return TamlCustomFieldFactory.createObject(); }
|
||||
|
||||
private:
|
||||
StringTableEntry mNodeName;
|
||||
TamlCustomField mNodeText;
|
||||
Vector<TamlCustomNode*> mChildren;
|
||||
TamlCustomFieldVector mFields;
|
||||
bool mIgnoreEmpty;
|
||||
|
||||
SimObject* mpProxyObject;
|
||||
TamlWriteNode* mpProxyWriteNode;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCustomNodes : public IFactoryObjectReset
|
||||
{
|
||||
public:
|
||||
TamlCustomNodes()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TamlCustomNodes()
|
||||
{
|
||||
resetState();
|
||||
}
|
||||
|
||||
virtual void resetState( void )
|
||||
{
|
||||
// Cache the nodes.
|
||||
while ( mNodes.size() > 0 )
|
||||
{
|
||||
TamlCustomNodeFactory.cacheObject( mNodes.back() );
|
||||
mNodes.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
inline TamlCustomNode* addNode( const char* pNodeName, const bool ignoreEmpty = true )
|
||||
{
|
||||
// Create a custom node.
|
||||
TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
|
||||
|
||||
// Set node name.
|
||||
pCustomNode->setNodeName( pNodeName );
|
||||
|
||||
// Set ignore-empty flag.
|
||||
pCustomNode->setIgnoreEmpty( ignoreEmpty );
|
||||
|
||||
#if TORQUE_DEBUG
|
||||
// Ensure a node name conflict does not exist.
|
||||
for( TamlCustomNodeVector::iterator nodeItr = mNodes.begin(); nodeItr != mNodes.end(); ++nodeItr )
|
||||
{
|
||||
// Skip if node name is not the same.
|
||||
if ( pCustomNode->getNodeName() != (*nodeItr)->getNodeName() )
|
||||
continue;
|
||||
|
||||
// Warn!
|
||||
Con::warnf("Conflicting Taml custom node name of '%s'.", pNodeName );
|
||||
|
||||
// Cache node.
|
||||
TamlCustomNodeFactory.cacheObject( pCustomNode );
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
// Store node.
|
||||
mNodes.push_back( pCustomNode );
|
||||
|
||||
return pCustomNode;
|
||||
}
|
||||
|
||||
inline void removeNode( const U32 index )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( index < (U32)mNodes.size(), "tamlCustomNode::removeNode() - Index is out of bounds." );
|
||||
|
||||
// Cache the custom node.
|
||||
TamlCustomNodeFactory.cacheObject( mNodes[index] );
|
||||
|
||||
// Remove it.
|
||||
mNodes.erase( index );
|
||||
}
|
||||
|
||||
inline const TamlCustomNode* findNode( const char* pNodeName ) const
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pNodeName != NULL, "Cannot find Taml node name that is NULL." );
|
||||
|
||||
// Fetch node name.
|
||||
StringTableEntry nodeName = StringTable->insert( pNodeName );
|
||||
|
||||
// Find node.
|
||||
for( Vector<TamlCustomNode*>::const_iterator nodeItr = mNodes.begin(); nodeItr != mNodes.end(); ++nodeItr )
|
||||
{
|
||||
if ( (*nodeItr)->getNodeName() == nodeName )
|
||||
return (*nodeItr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline const TamlCustomNodeVector& getNodes( void ) const { return mNodes; }
|
||||
|
||||
private:
|
||||
TamlCustomNodeVector mNodes;
|
||||
};
|
||||
|
||||
#endif // _TAML_CUSTOM_H_
|
||||
58
Engine/source/persistence/taml/tamlParser.h
Normal file
58
Engine/source/persistence/taml/tamlParser.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_PARSER_H_
|
||||
#define _TAML_PARSER_H_
|
||||
|
||||
#include "core/stringTable.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlVisitor;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlParser
|
||||
{
|
||||
public:
|
||||
TamlParser() :
|
||||
mParsingFilename(StringTable->EmptyString())
|
||||
{}
|
||||
virtual ~TamlParser() {}
|
||||
|
||||
/// Whether the parser can change a property or not.
|
||||
virtual bool canChangeProperty( void ) = 0;
|
||||
|
||||
/// Accept visitor.
|
||||
virtual bool accept( const char* pFilename, TamlVisitor& visitor ) = 0;
|
||||
|
||||
/// Filename.
|
||||
inline void setParsingFilename( const char* pFilename ) { mParsingFilename = StringTable->insert(pFilename); }
|
||||
inline StringTableEntry getParsingFilename( void ) const { return mParsingFilename; }
|
||||
|
||||
private:
|
||||
StringTableEntry mParsingFilename;
|
||||
};
|
||||
|
||||
#endif // _TAML_PARSER_H_
|
||||
111
Engine/source/persistence/taml/tamlVisitor.h
Normal file
111
Engine/source/persistence/taml/tamlVisitor.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_VISITOR_H_
|
||||
#define _TAML_VISITOR_H_
|
||||
|
||||
#include "core/stringTable.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlParser;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlVisitor
|
||||
{
|
||||
public:
|
||||
/// Visitor property state.
|
||||
struct PropertyState
|
||||
{
|
||||
private:
|
||||
StringTableEntry mObjectName;
|
||||
StringTableEntry mPropertyName;
|
||||
char mPropertyValue[4096];
|
||||
bool mPropertyValueDirty;
|
||||
bool mIsRootObject;
|
||||
|
||||
public:
|
||||
PropertyState()
|
||||
{
|
||||
mObjectName = StringTable->EmptyString();
|
||||
mPropertyName = StringTable->EmptyString();
|
||||
*mPropertyValue = 0;
|
||||
mPropertyValueDirty = false;
|
||||
mIsRootObject = false;
|
||||
}
|
||||
|
||||
inline void setObjectName( const char* pObjectName, const bool rootObject )
|
||||
{
|
||||
mObjectName = StringTable->insert( pObjectName );
|
||||
mIsRootObject = rootObject;
|
||||
}
|
||||
inline StringTableEntry getObjectName( void ) const { return mObjectName; }
|
||||
inline bool isRootObject( void ) const { return mIsRootObject; }
|
||||
|
||||
inline void setProperty( const char* pPropertyName, const char* pPropertyValue )
|
||||
{
|
||||
// Set property name.
|
||||
mPropertyName = StringTable->insert( pPropertyName );
|
||||
|
||||
// Format property value.
|
||||
dSprintf( mPropertyValue, sizeof(mPropertyValue), "%s", pPropertyValue );
|
||||
|
||||
// Flag as not dirty.
|
||||
mPropertyValueDirty = false;
|
||||
}
|
||||
|
||||
inline void updatePropertyValue( const char* pPropertyValue )
|
||||
{
|
||||
// Update property value.
|
||||
dSprintf( mPropertyValue, sizeof(mPropertyValue), "%s", pPropertyValue );
|
||||
|
||||
// Flag as dirty.
|
||||
mPropertyValueDirty = true;
|
||||
}
|
||||
|
||||
inline StringTableEntry getPropertyName( void ) const { return mPropertyName; }
|
||||
inline const char* getPropertyValue( void ) const { return mPropertyValue; }
|
||||
|
||||
inline void resetPropertyValueDirty( void ) { mPropertyValueDirty = false; }
|
||||
inline bool getPropertyValueDirty( void ) const { return mPropertyValueDirty; }
|
||||
};
|
||||
|
||||
public:
|
||||
TamlVisitor() {}
|
||||
virtual ~TamlVisitor() {}
|
||||
|
||||
/// Whether the visitor wants to perform property changes.
|
||||
/// This allows a check against the parser which may not allow changes.
|
||||
virtual bool wantsPropertyChanges( void ) = 0;
|
||||
|
||||
/// Whether the visitor wants to visit the root node only.
|
||||
virtual bool wantsRootOnly( void ) = 0;
|
||||
|
||||
/// The state of the visited property.
|
||||
virtual bool visit( const TamlParser& parser, PropertyState& propertyState ) = 0;
|
||||
};
|
||||
|
||||
#endif // _TAML_VISITOR_H_
|
||||
66
Engine/source/persistence/taml/tamlWriteNode.cpp
Normal file
66
Engine/source/persistence/taml/tamlWriteNode.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "persistence/taml/tamlWriteNode.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlWriteNode::resetNode( void )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlWriteNode_ResetNode);
|
||||
|
||||
// Clear fields.
|
||||
for( Vector<TamlWriteNode::FieldValuePair*>::iterator itr = mFields.begin(); itr != mFields.end(); ++itr )
|
||||
{
|
||||
delete (*itr)->mpValue;
|
||||
}
|
||||
mFields.clear();
|
||||
|
||||
// Clear children.
|
||||
if ( mChildren != NULL )
|
||||
{
|
||||
for( Vector<TamlWriteNode*>::iterator itr = mChildren->begin(); itr != mChildren->end(); ++itr )
|
||||
{
|
||||
(*itr)->resetNode();
|
||||
}
|
||||
|
||||
mChildren->clear();
|
||||
delete mChildren;
|
||||
mChildren = NULL;
|
||||
}
|
||||
|
||||
mRefId = 0;
|
||||
mRefToNode = NULL;
|
||||
mChildren = NULL;
|
||||
mpObjectName = NULL;
|
||||
mpSimObject = NULL;
|
||||
|
||||
// Reset callbacks.
|
||||
mpTamlCallbacks = NULL;
|
||||
|
||||
// Reset custom nodes.
|
||||
mCustomNodes.resetState();
|
||||
}
|
||||
116
Engine/source/persistence/taml/tamlWriteNode.h
Normal file
116
Engine/source/persistence/taml/tamlWriteNode.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_WRITE_NODE_H_
|
||||
#define _TAML_WRITE_NODE_H_
|
||||
|
||||
#ifndef _TAML_CUSTOM_H_
|
||||
#include "persistence/taml/tamlCustom.h"
|
||||
#endif
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
#ifndef _VECTOR_H_
|
||||
#include "core/util/tVector.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlCallbacks;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class TamlWriteNode
|
||||
{
|
||||
public:
|
||||
class FieldValuePair
|
||||
{
|
||||
public:
|
||||
FieldValuePair( StringTableEntry name, const char* pValue )
|
||||
{
|
||||
// Set the field name.
|
||||
mName = name;
|
||||
|
||||
// Allocate and copy the value.
|
||||
mpValue = new char[ dStrlen(pValue)+1 ];
|
||||
dStrcpy( (char *)mpValue, pValue );
|
||||
}
|
||||
|
||||
|
||||
StringTableEntry mName;
|
||||
const char* mpValue;
|
||||
};
|
||||
|
||||
public:
|
||||
TamlWriteNode()
|
||||
{
|
||||
// NOTE: This MUST be done before the state is reset otherwise we'll be touching uninitialized stuff.
|
||||
mRefToNode = NULL;
|
||||
mpSimObject = NULL;
|
||||
mpTamlCallbacks = NULL;
|
||||
mpObjectName = NULL;
|
||||
mChildren = NULL;
|
||||
|
||||
resetNode();
|
||||
}
|
||||
|
||||
void set( SimObject* pSimObject )
|
||||
{
|
||||
// Sanity!
|
||||
AssertFatal( pSimObject != NULL, "Cannot set a write node with a NULL sim object." );
|
||||
|
||||
// Reset the node.
|
||||
resetNode();
|
||||
|
||||
// Set sim object.
|
||||
mpSimObject = pSimObject;
|
||||
|
||||
// Fetch name.
|
||||
const char* pObjectName = pSimObject->getName();
|
||||
|
||||
// Assign name usage.
|
||||
if ( pObjectName != NULL &&
|
||||
pObjectName != StringTable->EmptyString() &&
|
||||
dStrlen( pObjectName ) > 0 )
|
||||
{
|
||||
mpObjectName = pObjectName;
|
||||
}
|
||||
|
||||
// Find Taml callbacks.
|
||||
mpTamlCallbacks = dynamic_cast<TamlCallbacks*>( mpSimObject );
|
||||
}
|
||||
|
||||
void resetNode( void );
|
||||
|
||||
U32 mRefId;
|
||||
TamlWriteNode* mRefToNode;
|
||||
SimObject* mpSimObject;
|
||||
TamlCallbacks* mpTamlCallbacks;
|
||||
const char* mpObjectName;
|
||||
Vector<TamlWriteNode::FieldValuePair*> mFields;
|
||||
Vector<TamlWriteNode*>* mChildren;
|
||||
TamlCustomNodes mCustomNodes;
|
||||
};
|
||||
|
||||
#endif // _TAML_WRITE_NODE_H_
|
||||
311
Engine/source/persistence/taml/taml_ScriptBinding.h
Normal file
311
Engine/source/persistence/taml/taml_ScriptBinding.h
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _TAML_SCRIPTBINDING_H
|
||||
#define _TAML_SCRIPTBINDING_H
|
||||
|
||||
#include "console/engineAPI.h"
|
||||
|
||||
#include "persistence/taml/taml.h"
|
||||
|
||||
DefineEngineMethod(Taml, setFormat, void, (const char* formatName), , "(format) - Sets the format that Taml should use to read/write.\n"
|
||||
"@param format The format to use: 'xml' or 'binary'.\n"
|
||||
"@return No return value.")
|
||||
{
|
||||
// Fetch format mode.
|
||||
const Taml::TamlFormatMode formatMode = Taml::getFormatModeEnum(formatName);
|
||||
|
||||
// Was the format valid?
|
||||
if ( formatMode == Taml::InvalidFormat )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::setFormat() - Invalid format mode used: '%s'.", formatName );
|
||||
return;
|
||||
}
|
||||
|
||||
// Set format mode.
|
||||
object->setFormatMode( formatMode );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getFormat, const char*, (), , "() - Gets the format that Taml should use to read/write.\n"
|
||||
"@return The format that Taml should use to read/write.")
|
||||
{
|
||||
// Fetch format mode.
|
||||
return Taml::getFormatModeDescription(object->getFormatMode());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setAutoFormat, void, (bool autoFormat), , "(autoFormat) Sets whether the format type is automatically determined by the filename extension or not.\n"
|
||||
"@param autoFormat Whether the format type is automatically determined by the filename extension or not.\n"
|
||||
"@return No return value." )
|
||||
{
|
||||
object->setAutoFormat( autoFormat );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getAutoFormat, bool, (), , "() Gets whether the format type is automatically determined by the filename extension or not.\n"
|
||||
"@return Whether the format type is automatically determined by the filename extension or not." )
|
||||
{
|
||||
return object->getAutoFormat();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setWriteDefaults, void, (bool writeDefaults), , "(writeDefaults) Sets whether to write static fields that are at their default or not.\n"
|
||||
"@param writeDefaults Whether to write static fields that are at their default or not.\n"
|
||||
"@return No return value." )
|
||||
{
|
||||
object->setWriteDefaults( writeDefaults );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getWriteDefaults, bool, (), , "() Gets whether to write static fields that are at their default or not.\n"
|
||||
"@return Whether to write static fields that are at their default or not." )
|
||||
{
|
||||
return object->getWriteDefaults();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setProgenitorUpdate, void, (bool progenitorUpdate), , "(progenitorUpdate) Sets whether to update each type instances file-progenitor or not.\n"
|
||||
"If not updating then the progenitor stay as the script that executed the call to Taml.\n"
|
||||
"@param progenitorUpdate Whether to update each type instances file-progenitor or not.\n"
|
||||
"@return No return value." )
|
||||
{
|
||||
object->setProgenitorUpdate( progenitorUpdate );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getProgenitorUpdate, bool, (), , "() Gets whether to update each type instances file-progenitor or not.\n"
|
||||
"@return Whether to update each type instances file-progenitor or not." )
|
||||
{
|
||||
return object->getProgenitorUpdate();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setAutoFormatXmlExtension, void, (const char* extension), , "(extension) Sets the extension (end of filename) used to detect the XML format.\n"
|
||||
"@param extension The extension (end of filename) used to detect the XML format.\n"
|
||||
"@return No return value." )
|
||||
{
|
||||
object->setAutoFormatXmlExtension( extension );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getAutoFormatXmlExtension, const char*, (), , "() Gets the extension (end of filename) used to detect the XML format.\n"
|
||||
"@return The extension (end of filename) used to detect the XML format." )
|
||||
{
|
||||
return object->getAutoFormatXmlExtension();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setAutoFormatBinaryExtension, void, (const char* extension), , "(extension) Sets the extension (end of filename) used to detect the Binary format.\n"
|
||||
"@param extension The extension (end of filename) used to detect the Binary format.\n"
|
||||
"@return No return value." )
|
||||
{
|
||||
object->setAutoFormatBinaryExtension( extension );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getAutoFormatBinaryExtension, const char*, (), , "() Gets the extension (end of filename) used to detect the Binary format.\n"
|
||||
"@return The extension (end of filename) used to detect the Binary format." )
|
||||
{
|
||||
return object->getAutoFormatBinaryExtension();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, setBinaryCompression, void, (bool compressed), , "(compressed) - Sets whether ZIP compression is used on binary formatting or not.\n"
|
||||
"@param compressed Whether compression is on or off.\n"
|
||||
"@return No return value.")
|
||||
{
|
||||
// Set compression.
|
||||
object->setBinaryCompression( compressed );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, getBinaryCompression, bool, (), , "() - Gets whether ZIP compression is used on binary formatting or not.\n"
|
||||
"@return Whether ZIP compression is used on binary formatting or not.")
|
||||
{
|
||||
// Fetch compression.
|
||||
return object->getBinaryCompression();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*! Sets whether to write JSON that is strictly compatible with RFC4627 or not.
|
||||
@param jsonStrict Whether to write JSON that is strictly compatible with RFC4627 or not.
|
||||
@return No return value.
|
||||
*/
|
||||
DefineEngineMethod(Taml, setJSONStrict, void, (bool strict), , "(jsonStrict) - Sets whether to write JSON that is strictly compatible with RFC4627 or not."
|
||||
"@param jsonStrict Whether to write JSON that is strictly compatible with RFC4627 or not."
|
||||
"@return No return value.")
|
||||
{
|
||||
// Set JSON Strict.
|
||||
object->setJSONStrict( strict );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*! Gets whether to write JSON that is strictly compatible with RFC4627 or not.
|
||||
@return whether to write JSON that is strictly compatible with RFC4627 or not.
|
||||
*/
|
||||
DefineEngineMethod(Taml, getJSONStrict, bool, (), , "() - Gets whether to write JSON that is strictly compatible with RFC4627 or not."
|
||||
"@return whether to write JSON that is strictly compatible with RFC4627 or not.")
|
||||
{
|
||||
// Get RFC strict.
|
||||
return object->getJSONStrict();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, write, bool, (SimObject* obj, const char* filename), , "(object, filename) - Writes an object to a file using Taml.\n"
|
||||
"@param object The object to write.\n"
|
||||
"@param filename The filename to write to.\n"
|
||||
"@return Whether the write was successful or not.")
|
||||
{
|
||||
// Did we find the object?
|
||||
if ( obj == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::write() - Tried to write a NULL object to file '%s'.", filename );
|
||||
return false;
|
||||
}
|
||||
|
||||
return object->write( obj, filename );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineMethod(Taml, read, SimObject*, (const char* filename), , "(filename) - Read an object from a file using Taml.\n"
|
||||
"@param filename The filename to read from.\n"
|
||||
"@return (Object) The object read from the file or an empty string if read failed.")
|
||||
{
|
||||
// Read object.
|
||||
SimObject* pSimObject = object->read( filename );
|
||||
|
||||
// Did we find the object?
|
||||
if ( pSimObject == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml::read() - Could not read object from file '%s'.", filename );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineFunction(TamlWrite, bool, (SimObject* simObject, const char* filename, const char* format, bool compressed),
|
||||
("xml", true),
|
||||
"(object, filename, [format], [compressed]) - Writes an object to a file using Taml.\n"
|
||||
"@param object The object to write.\n"
|
||||
"@param filename The filename to write to.\n"
|
||||
"@param format The file format to use. Optional: Defaults to 'xml'. Can be set to 'binary'.\n"
|
||||
"@param compressed Whether ZIP compression is used on binary formatting or not. Optional: Defaults to 'true'.\n"
|
||||
"@return Whether the write was successful or not.")
|
||||
{
|
||||
|
||||
// Did we find the object?
|
||||
if ( simObject == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
//Con::warnf( "TamlWrite() - Could not find object '%s' to write to file '%s'.", simObject->getIdString(), filename );
|
||||
Con::warnf( "TamlWrite() - Could not find object to write to file '%s'.", filename );
|
||||
return false;
|
||||
}
|
||||
|
||||
Taml taml;
|
||||
|
||||
taml.setFormatMode( Taml::getFormatModeEnum(format) );
|
||||
|
||||
// Yes, so is the format mode binary?
|
||||
if ( taml.getFormatMode() == Taml::BinaryFormat )
|
||||
{
|
||||
// Yes, so set binary compression.
|
||||
taml.setBinaryCompression( compressed );
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "TamlWrite() - Setting binary compression is only valid for XML formatting." );
|
||||
}
|
||||
|
||||
// Turn-off auto-formatting.
|
||||
taml.setAutoFormat( false );
|
||||
|
||||
// Write.
|
||||
return taml.write( simObject, filename );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineFunction(TamlRead, const char*, (const char* filename, const char* format), ("xml"), "(filename, [format]) - Read an object from a file using Taml.\n"
|
||||
"@param filename The filename to read from.\n"
|
||||
"@param format The file format to use. Optional: Defaults to 'xml'. Can be set to 'binary'.\n"
|
||||
"@return (Object) The object read from the file or an empty string if read failed.")
|
||||
{
|
||||
|
||||
// Set the format mode.
|
||||
Taml taml;
|
||||
|
||||
// Yes, so set it.
|
||||
taml.setFormatMode( Taml::getFormatModeEnum(format) );
|
||||
|
||||
// Turn-off auto-formatting.
|
||||
taml.setAutoFormat( false );
|
||||
|
||||
// Read object.
|
||||
SimObject* pSimObject = taml.read( filename );
|
||||
|
||||
// Did we find the object?
|
||||
if ( pSimObject == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "TamlRead() - Could not read object from file '%s'.", filename );
|
||||
return StringTable->EmptyString();
|
||||
}
|
||||
|
||||
return pSimObject->getIdString();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
DefineEngineFunction(GenerateTamlSchema, bool, (), , "() - Generate a TAML schema file of all engine types.\n"
|
||||
"The schema file is specified using the console variable '" TAML_SCHEMA_VARIABLE "'.\n"
|
||||
"@return Whether the schema file was writtent or not." )
|
||||
{
|
||||
// Generate the schema.
|
||||
return Taml::generateTamlSchema();
|
||||
}
|
||||
|
||||
#endif _TAML_SCRIPTBINDING_H
|
||||
193
Engine/source/persistence/taml/xml/tamlXmlParser.cpp
Normal file
193
Engine/source/persistence/taml/xml/tamlXmlParser.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "persistence/taml/xml/tamlXmlParser.h"
|
||||
#include "persistence/taml/tamlVisitor.h"
|
||||
#include "console/console.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
|
||||
#ifndef _FILESTREAM_H_
|
||||
#include "core/stream/fileStream.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool TamlXmlParser::accept( const char* pFilename, TamlVisitor& visitor )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlParser_Accept);
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pFilename != NULL, "Cannot parse a NULL filename." );
|
||||
|
||||
// Expand the file-path.
|
||||
char filenameBuffer[1024];
|
||||
// TODO: Make sure this is a proper substitute for
|
||||
// Con::expandPath (T2D)
|
||||
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
|
||||
/** T2D uses a custom version of TinyXML that supports FileStream.
|
||||
* We don't so we can't do this
|
||||
*
|
||||
FileStream stream;
|
||||
|
||||
#ifdef TORQUE_OS_ANDROID
|
||||
if (strlen(pFilename) > strlen(filenameBuffer)) {
|
||||
strcpy(filenameBuffer, pFilename);
|
||||
}
|
||||
#endif
|
||||
|
||||
// File open for read?
|
||||
if ( !stream.open( filenameBuffer, Torque::FS::File::AccessMode::Read ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for parse.", filenameBuffer );
|
||||
return false;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
TiXmlDocument xmlDocument;
|
||||
|
||||
// Load document from stream.
|
||||
if ( !xmlDocument.LoadFile( filenameBuffer ) )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlXmlParser: Could not load Taml XML file from stream.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Close the stream.
|
||||
// stream.close();
|
||||
|
||||
// Set parsing filename.
|
||||
setParsingFilename( filenameBuffer );
|
||||
|
||||
// Flag document as not dirty.
|
||||
mDocumentDirty = false;
|
||||
|
||||
// Parse root element.
|
||||
parseElement( xmlDocument.RootElement(), visitor );
|
||||
|
||||
// Reset parsing filename.
|
||||
setParsingFilename( StringTable->EmptyString() );
|
||||
|
||||
// Finish if the document is not dirty.
|
||||
if ( !mDocumentDirty )
|
||||
return true;
|
||||
|
||||
// Open for write?
|
||||
/*if ( !stream.open( filenameBuffer, FileStream::StreamWrite ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for write.", filenameBuffer );
|
||||
return false;
|
||||
}*/
|
||||
|
||||
// Yes, so save the document.
|
||||
if ( !xmlDocument.SaveFile( filenameBuffer ) )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("TamlXmlParser: Could not save Taml XML document.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Close the stream.
|
||||
//stream.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline bool TamlXmlParser::parseElement( TiXmlElement* pXmlElement, TamlVisitor& visitor )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlParser_ParseElement);
|
||||
|
||||
// Parse attributes (stop processing if instructed).
|
||||
if ( !parseAttributes( pXmlElement, visitor ) )
|
||||
return false;
|
||||
|
||||
// Finish if only the root is needed.
|
||||
if ( visitor.wantsRootOnly() )
|
||||
return false;
|
||||
|
||||
// Fetch any children.
|
||||
TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
|
||||
|
||||
// Do we have any element children?
|
||||
if ( pChildXmlNode != NULL && pChildXmlNode->Type() == TiXmlNode::TINYXML_ELEMENT )
|
||||
{
|
||||
// Iterate children.
|
||||
for ( TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode ); pChildXmlElement; pChildXmlElement = pChildXmlElement->NextSiblingElement() )
|
||||
{
|
||||
// Parse element (stop processing if instructed).
|
||||
if ( !parseElement( pChildXmlElement, visitor ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline bool TamlXmlParser::parseAttributes( TiXmlElement* pXmlElement, TamlVisitor& visitor )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlParser_ParseAttribute);
|
||||
|
||||
// Calculate if element is at the root or not.
|
||||
const bool isRoot = pXmlElement->GetDocument()->RootElement() == pXmlElement;
|
||||
|
||||
// Create a visitor property state.
|
||||
TamlVisitor::PropertyState propertyState;
|
||||
propertyState.setObjectName( pXmlElement->Value(), isRoot );
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Configure property state.
|
||||
propertyState.setProperty( pAttribute->Name(), pAttribute->Value() );
|
||||
|
||||
// Visit this attribute.
|
||||
const bool visitStatus = visitor.visit( *this, propertyState );
|
||||
|
||||
// Was the property value changed?
|
||||
if ( propertyState.getPropertyValueDirty() )
|
||||
{
|
||||
// Yes, so update the attribute.
|
||||
pAttribute->SetValue( propertyState.getPropertyValue() );
|
||||
|
||||
// Flag the document as dirty.
|
||||
mDocumentDirty = true;
|
||||
}
|
||||
|
||||
// Finish if requested.
|
||||
if ( !visitStatus )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
57
Engine/source/persistence/taml/xml/tamlXmlParser.h
Normal file
57
Engine/source/persistence/taml/xml/tamlXmlParser.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_XMLPARSER_H_
|
||||
#define _TAML_XMLPARSER_H_
|
||||
|
||||
#ifndef _TAML_PARSER_H_
|
||||
#include "persistence/taml/tamlParser.h"
|
||||
#endif
|
||||
|
||||
#ifndef TINYXML_INCLUDED
|
||||
#include "tinyXML/tinyxml.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlXmlParser : public TamlParser
|
||||
{
|
||||
public:
|
||||
TamlXmlParser() {}
|
||||
virtual ~TamlXmlParser() {}
|
||||
|
||||
/// Whether the parser can change a property or not.
|
||||
virtual bool canChangeProperty( void ) { return true; }
|
||||
|
||||
/// Accept visitor.
|
||||
virtual bool accept( const char* pFilename, TamlVisitor& visitor );
|
||||
|
||||
private:
|
||||
inline bool parseElement( TiXmlElement* pXmlElement, TamlVisitor& visitor );
|
||||
inline bool parseAttributes( TiXmlElement* pXmlElement, TamlVisitor& visitor );
|
||||
|
||||
bool mDocumentDirty;
|
||||
};
|
||||
|
||||
#endif // _TAML_XMLPARSER_H_
|
||||
484
Engine/source/persistence/taml/xml/tamlXmlReader.cpp
Normal file
484
Engine/source/persistence/taml/xml/tamlXmlReader.cpp
Normal file
|
|
@ -0,0 +1,484 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "persistence/taml/xml/tamlXmlReader.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
#include "persistence/taml/fsTinyxml.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlXmlReader::read( FileStream& stream )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_Read);
|
||||
|
||||
// Create document.
|
||||
fsTiXmlDocument xmlDocument;
|
||||
|
||||
// Load document from stream.
|
||||
if ( !xmlDocument.LoadFile( stream ) )
|
||||
{
|
||||
// Warn!
|
||||
Con::warnf("Taml: Could not load Taml XML file from stream.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Parse root element.
|
||||
SimObject* pSimObject = parseElement( xmlDocument.RootElement() );
|
||||
|
||||
// Reset parse.
|
||||
resetParse();
|
||||
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlReader::resetParse( void )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_ResetParse);
|
||||
|
||||
// Clear object reference map.
|
||||
mObjectReferenceMap.clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SimObject* TamlXmlReader::parseElement( TiXmlElement* pXmlElement )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_ParseElement);
|
||||
|
||||
SimObject* pSimObject = NULL;
|
||||
|
||||
// Fetch element name.
|
||||
StringTableEntry typeName = StringTable->insert( pXmlElement->Value() );
|
||||
|
||||
// Fetch reference to Id.
|
||||
const U32 tamlRefToId = getTamlRefToId( pXmlElement );
|
||||
|
||||
// Do we have a reference to Id?
|
||||
if ( tamlRefToId != 0 )
|
||||
{
|
||||
// Yes, so fetch reference.
|
||||
typeObjectReferenceHash::Iterator referenceItr = mObjectReferenceMap.find( tamlRefToId );
|
||||
|
||||
// Did we find the reference?
|
||||
if ( referenceItr == mObjectReferenceMap.end() )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf( "Taml: Could not find a reference Id of '%d'", tamlRefToId );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return referenceItr->value;
|
||||
}
|
||||
|
||||
// No, so fetch reference Id.
|
||||
const U32 tamlRefId = getTamlRefId( pXmlElement );
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
// Format the type location.
|
||||
char typeLocationBuffer[64];
|
||||
dSprintf( typeLocationBuffer, sizeof(typeLocationBuffer), "Taml [format='xml' row=%d column=%d]", pXmlElement->Row(), pXmlElement->Column() );
|
||||
|
||||
// Create type.
|
||||
pSimObject = Taml::createType( typeName, mpTaml, typeLocationBuffer );
|
||||
#else
|
||||
// Create type.
|
||||
pSimObject = Taml::createType( typeName, mpTaml );
|
||||
#endif
|
||||
|
||||
|
||||
// Finish if we couldn't create the type.
|
||||
if ( pSimObject == NULL )
|
||||
return NULL;
|
||||
|
||||
// Find Taml callbacks.
|
||||
TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject );
|
||||
|
||||
// Are there any Taml callbacks?
|
||||
if ( pCallbacks != NULL )
|
||||
{
|
||||
// Yes, so call it.
|
||||
mpTaml->tamlPreRead( pCallbacks );
|
||||
}
|
||||
|
||||
// Parse attributes.
|
||||
parseAttributes( pXmlElement, pSimObject );
|
||||
|
||||
// Fetch object name.
|
||||
StringTableEntry objectName = StringTable->insert( getTamlObjectName( pXmlElement ) );
|
||||
|
||||
// Does the object require a name?
|
||||
if ( objectName == StringTable->EmptyString() )
|
||||
{
|
||||
// No, so just register anonymously.
|
||||
pSimObject->registerObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Yes, so register a named object.
|
||||
pSimObject->registerObject( objectName );
|
||||
|
||||
// Was the name assigned?
|
||||
if ( pSimObject->getName() != objectName )
|
||||
{
|
||||
// No, so warn that the name was rejected.
|
||||
#ifdef TORQUE_DEBUG
|
||||
Con::warnf( "Taml::parseElement() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists. '%s'", typeName, objectName, typeLocationBuffer );
|
||||
#else
|
||||
Con::warnf( "Taml::parseElement() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists.", typeName, objectName );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Do we have a reference Id?
|
||||
if ( tamlRefId != 0 )
|
||||
{
|
||||
// Yes, so insert reference.
|
||||
mObjectReferenceMap.insertUnique( tamlRefId, pSimObject );
|
||||
}
|
||||
|
||||
// Fetch any children.
|
||||
TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
|
||||
|
||||
TamlCustomNodes customProperties;
|
||||
|
||||
// Do we have any element children?
|
||||
if ( pChildXmlNode != NULL )
|
||||
{
|
||||
// Fetch the Taml children.
|
||||
TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject );
|
||||
|
||||
// Fetch any container child class specifier.
|
||||
AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true );
|
||||
|
||||
// Iterate siblings.
|
||||
do
|
||||
{
|
||||
// Fetch element.
|
||||
TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode );
|
||||
|
||||
// Move to next sibling.
|
||||
pChildXmlNode = pChildXmlNode->NextSibling();
|
||||
|
||||
// Skip if this is not an element?
|
||||
if ( pChildXmlElement == NULL )
|
||||
continue;
|
||||
|
||||
// Is this a standard child element?
|
||||
if ( dStrchr( pChildXmlElement->Value(), '.' ) == NULL )
|
||||
{
|
||||
// Is this a Taml child?
|
||||
if ( pChildren == NULL )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml: Child element '%s' found under parent '%s' but object cannot have children.",
|
||||
pChildXmlElement->Value(),
|
||||
pXmlElement->Value() );
|
||||
|
||||
// Skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Yes, so parse child element.
|
||||
SimObject* pChildSimObject = parseElement( pChildXmlElement );
|
||||
|
||||
// Skip if the child was not created.
|
||||
if ( pChildSimObject == NULL )
|
||||
continue;
|
||||
|
||||
// Do we have a container child class?
|
||||
if ( pContainerChildClass != NULL )
|
||||
{
|
||||
// Yes, so is the child object the correctly derived type?
|
||||
if ( !pChildSimObject->getClassRep()->isClass( pContainerChildClass ) )
|
||||
{
|
||||
// No, so warn.
|
||||
Con::warnf("Taml: Child element '%s' found under parent '%s' but object is restricted to children of type '%s'.",
|
||||
pChildSimObject->getClassName(),
|
||||
pSimObject->getClassName(),
|
||||
pContainerChildClass->getClassName() );
|
||||
|
||||
// NOTE: We can't delete the object as it may be referenced elsewhere!
|
||||
pChildSimObject = NULL;
|
||||
|
||||
// Skip.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add child.
|
||||
pChildren->addTamlChild( pChildSimObject );
|
||||
|
||||
// Find Taml callbacks for child.
|
||||
TamlCallbacks* pChildCallbacks = dynamic_cast<TamlCallbacks*>( pChildSimObject );
|
||||
|
||||
// Do we have callbacks on the child?
|
||||
if ( pChildCallbacks != NULL )
|
||||
{
|
||||
// Yes, so perform callback.
|
||||
mpTaml->tamlAddParent( pChildCallbacks, pSimObject );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so parse custom element.
|
||||
parseCustomElement( pChildXmlElement, customProperties );
|
||||
}
|
||||
}
|
||||
while( pChildXmlNode != NULL );
|
||||
|
||||
// Call custom read.
|
||||
mpTaml->tamlCustomRead( pCallbacks, customProperties );
|
||||
}
|
||||
|
||||
// Are there any Taml callbacks?
|
||||
if ( pCallbacks != NULL )
|
||||
{
|
||||
// Yes, so call it.
|
||||
mpTaml->tamlPostRead( pCallbacks, customProperties );
|
||||
}
|
||||
|
||||
// Return object.
|
||||
return pSimObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlReader::parseAttributes( TiXmlElement* pXmlElement, SimObject* pSimObject )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_ParseAttributes);
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pSimObject != NULL, "Taml: Cannot parse attributes on a NULL object." );
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Insert attribute name.
|
||||
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
|
||||
|
||||
// Ignore if this is a Taml attribute.
|
||||
if ( attributeName == tamlRefIdName ||
|
||||
attributeName == tamlRefToIdName ||
|
||||
attributeName == tamlNamedObjectName )
|
||||
continue;
|
||||
|
||||
// Set the field.
|
||||
pSimObject->setPrefixedDataField(attributeName, NULL, pAttribute->Value());
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlReader::parseCustomElement( TiXmlElement* pXmlElement, TamlCustomNodes& customNodes )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_ParseCustomElement);
|
||||
|
||||
// Is this a standard child element?
|
||||
const char* pPeriod = dStrchr( pXmlElement->Value(), '.' );
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( pPeriod != NULL, "Parsing extended element but no period character found." );
|
||||
|
||||
// Fetch any custom XML node.
|
||||
TiXmlNode* pCustomXmlNode = pXmlElement->FirstChild();
|
||||
|
||||
// Finish is no XML node exists.
|
||||
if ( pCustomXmlNode == NULL )
|
||||
return;
|
||||
|
||||
// Yes, so add custom node.
|
||||
TamlCustomNode* pCustomNode = customNodes.addNode( pPeriod+1 );
|
||||
|
||||
do
|
||||
{
|
||||
// Fetch element.
|
||||
TiXmlElement* pCustomXmlElement = dynamic_cast<TiXmlElement*>( pCustomXmlNode );
|
||||
|
||||
// Move to next sibling.
|
||||
pCustomXmlNode = pCustomXmlNode->NextSibling();
|
||||
|
||||
// Skip if this is not an element.
|
||||
if ( pCustomXmlElement == NULL )
|
||||
continue;
|
||||
|
||||
// Parse custom node.
|
||||
parseCustomNode( pCustomXmlElement, pCustomNode );
|
||||
}
|
||||
while ( pCustomXmlNode != NULL );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlReader::parseCustomNode( TiXmlElement* pXmlElement, TamlCustomNode* pCustomNode )
|
||||
{
|
||||
// Is the node a proxy object?
|
||||
if ( getTamlRefId( pXmlElement ) != 0 || getTamlRefToId( pXmlElement ) != 0 )
|
||||
{
|
||||
// Yes, so parse proxy object.
|
||||
SimObject* pProxyObject = parseElement( pXmlElement );
|
||||
|
||||
// Add child node.
|
||||
pCustomNode->addNode( pProxyObject );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Yes, so add child node.
|
||||
TamlCustomNode* pChildNode = pCustomNode->addNode( pXmlElement->Value() );
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Insert attribute name.
|
||||
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
|
||||
|
||||
// Skip if a Taml reference attribute.
|
||||
if ( attributeName == tamlRefIdName || attributeName == tamlRefToIdName )
|
||||
continue;
|
||||
|
||||
// Add node field.
|
||||
pChildNode->addField( attributeName, pAttribute->Value() );
|
||||
}
|
||||
|
||||
// Fetch any element text.
|
||||
const char* pElementText = pXmlElement->GetText();
|
||||
|
||||
// Do we have any element text?
|
||||
if ( pElementText != NULL )
|
||||
{
|
||||
// Yes, so store it.
|
||||
pChildNode->setNodeText( pElementText );
|
||||
}
|
||||
|
||||
// Fetch any children.
|
||||
TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
|
||||
|
||||
// Do we have any element children?
|
||||
if ( pChildXmlNode != NULL )
|
||||
{
|
||||
do
|
||||
{
|
||||
// Yes, so fetch child element.
|
||||
TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode );
|
||||
|
||||
// Move to next sibling.
|
||||
pChildXmlNode = pChildXmlNode->NextSibling();
|
||||
|
||||
// Skip if this is not an element.
|
||||
if ( pChildXmlElement == NULL )
|
||||
continue;
|
||||
|
||||
// Parse custom node.
|
||||
parseCustomNode( pChildXmlElement, pChildNode );
|
||||
}
|
||||
while( pChildXmlNode != NULL );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 TamlXmlReader::getTamlRefId( TiXmlElement* pXmlElement )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_GetTamlRefId);
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Insert attribute name.
|
||||
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlRefIdName )
|
||||
continue;
|
||||
|
||||
// Return it.
|
||||
return dAtoi( pAttribute->Value() );
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
U32 TamlXmlReader::getTamlRefToId( TiXmlElement* pXmlElement )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_GetTamlRefToId);
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Insert attribute name.
|
||||
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlRefToIdName )
|
||||
continue;
|
||||
|
||||
// Return it.
|
||||
return dAtoi( pAttribute->Value() );
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const char* TamlXmlReader::getTamlObjectName( TiXmlElement* pXmlElement )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlReader_GetTamlObjectName);
|
||||
|
||||
// Iterate attributes.
|
||||
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
|
||||
{
|
||||
// Insert attribute name.
|
||||
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
|
||||
|
||||
// Skip if not the correct attribute.
|
||||
if ( attributeName != tamlNamedObjectName )
|
||||
continue;
|
||||
|
||||
// Return it.
|
||||
return pAttribute->Value();
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
73
Engine/source/persistence/taml/xml/tamlXmlReader.h
Normal file
73
Engine/source/persistence/taml/xml/tamlXmlReader.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_XMLREADER_H_
|
||||
#define _TAML_XMLREADER_H_
|
||||
|
||||
#ifndef _TDICTIONARY_H_
|
||||
#include "core/util/tDictionary.h"
|
||||
#endif
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
#ifndef TINYXML_INCLUDED
|
||||
#include "tinyXML/tinyxml.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlXmlReader
|
||||
{
|
||||
public:
|
||||
TamlXmlReader( Taml* pTaml ) :
|
||||
mpTaml( pTaml )
|
||||
{}
|
||||
|
||||
virtual ~TamlXmlReader() {}
|
||||
|
||||
/// Read.
|
||||
SimObject* read( FileStream& stream );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
|
||||
typedef HashTable<SimObjectId, SimObject*> typeObjectReferenceHash;
|
||||
typeObjectReferenceHash mObjectReferenceMap;
|
||||
|
||||
private:
|
||||
void resetParse( void );
|
||||
|
||||
SimObject* parseElement( TiXmlElement* pXmlElement );
|
||||
void parseAttributes( TiXmlElement* pXmlElement, SimObject* pSimObject );
|
||||
void parseCustomElement( TiXmlElement* pXmlElement, TamlCustomNodes& pCustomNode );
|
||||
void parseCustomNode( TiXmlElement* pXmlElement, TamlCustomNode* pCustomNode );
|
||||
|
||||
U32 getTamlRefId( TiXmlElement* pXmlElement );
|
||||
U32 getTamlRefToId( TiXmlElement* pXmlElement );
|
||||
const char* getTamlObjectName( TiXmlElement* pXmlElement );
|
||||
};
|
||||
|
||||
#endif // _TAML_XMLREADER_H_
|
||||
301
Engine/source/persistence/taml/xml/tamlXmlWriter.cpp
Normal file
301
Engine/source/persistence/taml/xml/tamlXmlWriter.cpp
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "persistence/taml/xml/tamlXmlWriter.h"
|
||||
|
||||
// Debug Profiling.
|
||||
#include "platform/profiler.h"
|
||||
#include "persistence/taml/fsTinyxml.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool TamlXmlWriter::write( FileStream& stream, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlWriter_Write);
|
||||
|
||||
// Create document.
|
||||
fsTiXmlDocument xmlDocument;
|
||||
|
||||
// Compile the root element.
|
||||
TiXmlElement* pRootElement = compileElement( pTamlWriteNode );
|
||||
|
||||
// Fetch any TAML Schema file reference.
|
||||
const char* pTamlSchemaFile = Con::getVariable( TAML_SCHEMA_VARIABLE );
|
||||
|
||||
// Do we have a schema file reference?
|
||||
if ( pTamlSchemaFile != NULL && *pTamlSchemaFile != 0 )
|
||||
{
|
||||
// Yes, so add namespace attribute to root.
|
||||
pRootElement->SetAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
|
||||
|
||||
// Expand the file-path reference.
|
||||
char schemaFilePathBuffer[1024];
|
||||
Con::expandToolScriptFilename( schemaFilePathBuffer, sizeof(schemaFilePathBuffer), pTamlSchemaFile );
|
||||
|
||||
// Fetch the output path for the Taml file.
|
||||
char outputFileBuffer[1024];
|
||||
dSprintf( outputFileBuffer, sizeof(outputFileBuffer), "%s", mpTaml->getFilePathBuffer() );
|
||||
char* pFileStart = dStrrchr( outputFileBuffer, '/' );
|
||||
if ( pFileStart == NULL )
|
||||
*outputFileBuffer = 0;
|
||||
else
|
||||
*pFileStart = 0;
|
||||
|
||||
// Fetch the schema file-path relative to the output file.
|
||||
StringTableEntry relativeSchemaFilePath = Platform::makeRelativePathName( schemaFilePathBuffer, outputFileBuffer );
|
||||
|
||||
// Add schema location attribute to root.
|
||||
pRootElement->SetAttribute( "xsi:noNamespaceSchemaLocation", relativeSchemaFilePath );
|
||||
}
|
||||
|
||||
// Link the root element.
|
||||
xmlDocument.LinkEndChild( pRootElement );
|
||||
|
||||
// Save document to stream.
|
||||
return xmlDocument.SaveFile( stream );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
TiXmlElement* TamlXmlWriter::compileElement( const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlWriter_CompileElement);
|
||||
|
||||
// Fetch object.
|
||||
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
|
||||
|
||||
// Fetch element name.
|
||||
const char* pElementName = pSimObject->getClassName();
|
||||
|
||||
// Create element.
|
||||
TiXmlElement* pElement = new fsTiXmlElement( pElementName );
|
||||
|
||||
// Fetch reference Id.
|
||||
const U32 referenceId = pTamlWriteNode->mRefId;
|
||||
|
||||
// Do we have a reference Id?
|
||||
if ( referenceId != 0 )
|
||||
{
|
||||
// Yes, so set reference Id attribute.
|
||||
pElement->SetAttribute( tamlRefIdName, referenceId );
|
||||
}
|
||||
|
||||
// Do we have a reference to node?
|
||||
else if ( pTamlWriteNode->mRefToNode != NULL )
|
||||
{
|
||||
// Yes, so fetch reference to Id.
|
||||
const U32 referenceToId = pTamlWriteNode->mRefToNode->mRefId;
|
||||
|
||||
// Sanity!
|
||||
AssertFatal( referenceToId != 0, "Taml: Invalid reference to Id." );
|
||||
|
||||
// Set reference to Id attribute.
|
||||
pElement->SetAttribute( tamlRefToIdName, referenceToId );
|
||||
|
||||
// Finish because we're a reference to another object.
|
||||
return pElement;
|
||||
}
|
||||
|
||||
// Fetch object name.
|
||||
const char* pObjectName = pTamlWriteNode->mpObjectName;
|
||||
|
||||
// Do we have a name?
|
||||
if ( pObjectName != NULL )
|
||||
{
|
||||
// Yes, so set name attribute.
|
||||
pElement->SetAttribute( tamlNamedObjectName, pObjectName );
|
||||
}
|
||||
|
||||
// Compile attributes.
|
||||
compileAttributes( pElement, pTamlWriteNode );
|
||||
|
||||
// Fetch children.
|
||||
Vector<TamlWriteNode*>* pChildren = pTamlWriteNode->mChildren;
|
||||
|
||||
// Do we have any children?
|
||||
if ( pChildren )
|
||||
{
|
||||
// Yes, so iterate children.
|
||||
for( Vector<TamlWriteNode*>::iterator itr = pChildren->begin(); itr != pChildren->end(); ++itr )
|
||||
{
|
||||
// Write child element.
|
||||
pElement->LinkEndChild( compileElement( (*itr) ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Compile custom elements.
|
||||
compileCustomElements( pElement, pTamlWriteNode );
|
||||
|
||||
return pElement;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlWriter::compileAttributes( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlWriter_CompileAttributes);
|
||||
|
||||
// Fetch fields.
|
||||
const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
|
||||
|
||||
// Ignore if no fields.
|
||||
if ( fields.size() == 0 )
|
||||
return;
|
||||
|
||||
// Iterate fields.
|
||||
for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr )
|
||||
{
|
||||
// Fetch field/value pair.
|
||||
TamlWriteNode::FieldValuePair* pFieldValue = (*itr);
|
||||
|
||||
// Set field attribute.
|
||||
pXmlElement->SetAttribute( pFieldValue->mName, pFieldValue->mpValue );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlWriter::compileCustomElements( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode )
|
||||
{
|
||||
// Debug Profiling.
|
||||
PROFILE_SCOPE(TamlXmlWriter_CompileCustomElements);
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
|
||||
|
||||
// Fetch custom nodes.
|
||||
const TamlCustomNodeVector& nodes = customNodes.getNodes();
|
||||
|
||||
// Finish if no custom nodes to process.
|
||||
if ( nodes.size() == 0 )
|
||||
return;
|
||||
|
||||
// Iterate custom nodes.
|
||||
for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
|
||||
{
|
||||
// Fetch the custom node.
|
||||
TamlCustomNode* pCustomNode = *customNodesItr;
|
||||
|
||||
// Format extended element name.
|
||||
char extendedElementNameBuffer[256];
|
||||
dSprintf( extendedElementNameBuffer, sizeof(extendedElementNameBuffer), "%s.%s", pXmlElement->Value(), pCustomNode->getNodeName() );
|
||||
StringTableEntry extendedElementName = StringTable->insert( extendedElementNameBuffer );
|
||||
|
||||
// Create element.
|
||||
TiXmlElement* pExtendedPropertyElement = new fsTiXmlElement( extendedElementName );
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
// Iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Compile the custom node.
|
||||
compileCustomNode( pExtendedPropertyElement, pChildNode );
|
||||
}
|
||||
|
||||
// Finish if the node is set to ignore if empty and it is empty.
|
||||
if ( pCustomNode->getIgnoreEmpty() && pExtendedPropertyElement->NoChildren() )
|
||||
{
|
||||
// Yes, so delete the extended element.
|
||||
delete pExtendedPropertyElement;
|
||||
pExtendedPropertyElement = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so add elementt as child.
|
||||
pXmlElement->LinkEndChild( pExtendedPropertyElement );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void TamlXmlWriter::compileCustomNode( TiXmlElement* pXmlElement, const TamlCustomNode* pCustomNode )
|
||||
{
|
||||
// Finish if the node is set to ignore if empty and it is empty.
|
||||
if ( pCustomNode->getIgnoreEmpty() && pCustomNode->isEmpty() )
|
||||
return;
|
||||
|
||||
// Is the node a proxy object?
|
||||
if ( pCustomNode->isProxyObject() )
|
||||
{
|
||||
// Yes, so write the proxy object.
|
||||
pXmlElement->LinkEndChild( compileElement( pCustomNode->getProxyWriteNode() ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Create element.
|
||||
TiXmlElement* pNodeElement = new fsTiXmlElement( pCustomNode->getNodeName() );
|
||||
|
||||
// Is there any node text?
|
||||
if ( !pCustomNode->getNodeTextField().isValueEmpty() )
|
||||
{
|
||||
// Yes, so add a text node.
|
||||
pNodeElement->LinkEndChild( new TiXmlText( pCustomNode->getNodeTextField().getFieldValue() ) );
|
||||
}
|
||||
|
||||
// Fetch fields.
|
||||
const TamlCustomFieldVector& fields = pCustomNode->getFields();
|
||||
|
||||
// Iterate fields.
|
||||
for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
|
||||
{
|
||||
// Fetch field.
|
||||
const TamlCustomField* pField = *fieldItr;
|
||||
|
||||
// Set field.
|
||||
pNodeElement->SetAttribute( pField->getFieldName(), pField->getFieldValue() );
|
||||
}
|
||||
|
||||
// Fetch node children.
|
||||
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
||||
|
||||
// Iterate children nodes.
|
||||
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
||||
{
|
||||
// Fetch child node.
|
||||
const TamlCustomNode* pChildNode = *childNodeItr;
|
||||
|
||||
// Compile the child node.
|
||||
compileCustomNode( pNodeElement, pChildNode );
|
||||
}
|
||||
|
||||
// Finish if the node is set to ignore if empty and it is empty (including fields).
|
||||
if ( pCustomNode->getIgnoreEmpty() && fields.size() == 0 && pNodeElement->NoChildren() )
|
||||
{
|
||||
// Yes, so delete the extended element.
|
||||
delete pNodeElement;
|
||||
pNodeElement = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add node element as child.
|
||||
pXmlElement->LinkEndChild( pNodeElement );
|
||||
}
|
||||
}
|
||||
59
Engine/source/persistence/taml/xml/tamlXmlWriter.h
Normal file
59
Engine/source/persistence/taml/xml/tamlXmlWriter.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2013 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _TAML_XMLWRITER_H_
|
||||
#define _TAML_XMLWRITER_H_
|
||||
|
||||
#ifndef _TAML_H_
|
||||
#include "persistence/taml/taml.h"
|
||||
#endif
|
||||
|
||||
#ifndef TINYXML_INCLUDED
|
||||
#include "tinyXML/tinyxml.h"
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @ingroup tamlGroup
|
||||
/// @see tamlGroup
|
||||
class TamlXmlWriter
|
||||
{
|
||||
public:
|
||||
TamlXmlWriter( Taml* pTaml ) :
|
||||
mpTaml( pTaml )
|
||||
{}
|
||||
virtual ~TamlXmlWriter() {}
|
||||
|
||||
/// Write.
|
||||
bool write( FileStream& stream, const TamlWriteNode* pTamlWriteNode );
|
||||
|
||||
private:
|
||||
Taml* mpTaml;
|
||||
|
||||
private:
|
||||
TiXmlElement* compileElement( const TamlWriteNode* pTamlWriteNode );
|
||||
void compileAttributes( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustomElements( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode );
|
||||
void compileCustomNode( TiXmlElement* pXmlElement, const TamlCustomNode* pCustomNode );
|
||||
};
|
||||
|
||||
#endif // _TAML_XMLWRITER_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue