// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. #include "PxSimpleTypes.h" #include "PxAssert.h" #include "PsArray.h" #include "PxVec3.h" #include "PxQuat.h" #include "PxBounds3.h" #include "PsFastXml.h" #include "PsIOStream.h" #include "nvparameterized/NvSerializer.h" #include "XmlSerializer.h" #include "nvparameterized/NvParameterized.h" #include "nvparameterized/NvParameterizedTraits.h" #include "NvParameters.h" #include "NvTraitsInternal.h" #include "XmlDeserializer.h" #define PRINT_ELEMENT_HINTS 0 #define PRINT_ELEMENTS_WITHIN_EMPTY_ARRAYS 0 #define UNOPTIMIZED_XML 0 namespace NvParameterized { static const char indentStr[] = " "; struct traversalState { traversalState() { indent[0] = 0; indentLen = 0; level = 0; } void incLevel() { physx::shdfnd::strlcat(indent, (uint32_t)strlen(indent) + (uint32_t)strlen(indentStr) + 1, indentStr); level++; } void decLevel() { level--; indentLen = (sizeof(indentStr) - 1) * level; if(indentLen < sizeof(indent)) indent[indentLen] = 0; } char indent[4096]; uint32_t indentLen; int32_t level; }; Serializer::ErrorType XmlSerializer::peekNumObjects(char *data, uint32_t len, uint32_t &numObjects) { //FIXME: this code is not robust data[len-1] = 0; const char *root = ::strstr(data, " => read className mClassNames[mNumObjs] = mTraits->strdup( attr.get("className") ); ++mNumObjs; return mNumObjs < mNumClassNames; } void *allocate(uint32_t size) { return ::malloc(size); } void deallocate(void *ptr) { ::free(ptr); }; }; ClassNameReader myReader(classNames, numClassNames, mTraits); physx::shdfnd::FastXml *xmlParser = physx::shdfnd::createFastXml(&myReader); InputDataFromPxFileBuf inputData(stream); xmlParser->processXml(inputData); numClassNames = myReader.numObjs(); return Serializer::ERROR_NONE; } Serializer::ErrorType XmlSerializer::peekNumObjectsInplace(const void * data, uint32_t dataLen, uint32_t & numObjects) { if ( !dataLen || ! data ) return ERROR_STREAM_ERROR; char hdr[100]; uint32_t len = physx::PxMin(dataLen, sizeof(hdr) - 1); physx::shdfnd::strlcpy(hdr, len+1, (const char *)data); return peekNumObjects(hdr, len, numObjects); } Serializer::ErrorType XmlSerializer::peekNumObjects(physx::PxFileBuf &stream, uint32_t &numObjects) { //FIXME: this code is not robust char hdr[100]; uint32_t len = stream.peek(hdr, sizeof(hdr)); return peekNumObjects(hdr, len, numObjects); } #ifndef WITHOUT_APEX_SERIALIZATION static void storeVersionAndChecksum(physx::PsIOStream &stream, const Interface *obj) { uint16_t major = obj->getMajorVersion(), minor = obj->getMinorVersion(); stream << " version=\"" << major << '.' << minor << '"'; uint32_t bits; const uint32_t *checksum = obj->checksum(bits); uint32_t u32s = bits / 32; PX_ASSERT( 0 == bits % 32 ); stream << " checksum=\""; for(uint32_t i = 0; i < u32s; ++i) { char hex[20]; physx::shdfnd::snprintf(hex, sizeof(hex), "0x%x", checksum[i]); stream << hex; if( u32s - 1 != i ) stream << ' '; } stream << '"'; } static bool IsSimpleType(const Definition *d) { //We do not consider strings simple because it causes errors with NULL and "" if (d->type() == TYPE_ARRAY || d->type() == TYPE_STRUCT || d->type() == TYPE_REF || d->type() == TYPE_STRING || d->type() == TYPE_ENUM) { return false; } else { PX_ASSERT( d->numChildren() == 0 ); return true; } } static bool IsSimpleStruct(const Definition *pd) { bool ret = true; int32_t count = pd->numChildren(); for (int32_t i=0; i < count; i++) { const Definition *d = pd->child(i); if ( !IsSimpleType(d) ) { ret = false; break; } } return ret; } static bool DoesNeedQuote(const char *c) { bool ret = false; while ( *c ) { if ( *c == 32 || *c == ',' || *c == '<' || *c == '>' || *c == 9 ) { ret = true; break; } c++; } return ret; } Serializer::ErrorType XmlSerializer::traverseParamDefTree( const Interface &obj, physx::PsIOStream &stream, traversalState &state, Handle &handle, bool printValues) { bool isRoot = !handle.numIndexes() && 0 == state.level; if( !handle.numIndexes() ) { NV_PARAM_ERR_CHECK_RETURN( obj.getParameterHandle("", handle), Serializer::ERROR_UNKNOWN ); if( isRoot ) { NV_ERR_CHECK_RETURN( emitElement(obj, stream, "value", handle, false, true, true) ); stream << "\n"; state.incLevel(); } } const Definition *paramDef = handle.parameterDefinition(); if( !paramDef->hint("DONOTSERIALIZE") ) { # if PRINT_ELEMENT_HINTS bool includedRef = false; NV_ERR_CHECK_RETURN( emitElementNxHints(stream, handle, state, includedRef) ); # else bool includedRef = paramDef->isIncludedRef(); # endif switch( paramDef->type() ) { case TYPE_STRUCT: { stream << state.indent; NV_ERR_CHECK_RETURN( emitElement(obj, stream, "struct", handle, false, true) ); stream << "\n"; state.incLevel(); for(int32_t i = 0; i < paramDef->numChildren(); ++i) { handle.set(i); NV_ERR_CHECK_RETURN( traverseParamDefTree(obj, stream, state, handle, printValues) ); handle.popIndex(); } state.decLevel(); stream << state.indent << "\n"; break; } case TYPE_ARRAY: { stream << state.indent; NV_ERR_CHECK_RETURN( emitElement(obj, stream, "array", handle, false, true) ); int32_t arraySize; NV_PARAM_ERR_CHECK_RETURN( handle.getArraySize(arraySize), Serializer::ERROR_INVALID_ARRAY ); if( arraySize) stream << "\n"; state.incLevel(); if ( arraySize > 0 ) { #if UNOPTIMIZED_XML for(int32_t i = 0; i < arraySize; ++i) { handle.set(i); NV_ERR_CHECK_RETURN( traverseParamDefTree(obj, stream, state, handle, printValues) ); handle.popIndex(); } #else handle.set(0); const Definition *pd = handle.parameterDefinition(); handle.popIndex(); switch ( pd->type() ) { case TYPE_STRUCT: { if ( IsSimpleStruct(pd) ) { for(int32_t i = 0; i < arraySize; ++i) { if ( (i&3) == 0 ) { if ( i ) stream << "\n"; stream << state.indent; } handle.set(i); for( int32_t j=0; jnumChildren(); j++ ) { if (pd->child(j)->hint("DONOTSERIALIZE")) continue; handle.set(j); char buf[512]; const char *str = 0; NV_PARAM_ERR_CHECK_RETURN( handle.valueToStr(buf, sizeof(buf), str), Serializer::ERROR_VAL2STRING_FAILED ); stream << str; if ( (j+1) < pd->numChildren() ) { stream << " "; } handle.popIndex(); } if ( (i+1) < arraySize ) { stream << ","; } handle.popIndex(); } //i stream << "\n"; } else { for(int32_t i = 0; i < arraySize; ++i) { handle.set(i); NV_ERR_CHECK_RETURN( traverseParamDefTree(obj, stream, state, handle, printValues) ); handle.popIndex(); } } } break; case TYPE_REF: for(int32_t i = 0; i < arraySize; ++i) { handle.set(i); NV_ERR_CHECK_RETURN( traverseParamDefTree(obj, stream, state, handle, printValues) ); handle.popIndex(); } break; case TYPE_BOOL: { bool v = false; stream << state.indent; for (int32_t i=0; i\n"; break; } case TYPE_REF: { stream << state.indent; NV_ERR_CHECK_RETURN( emitElement(obj, stream, "value", handle, includedRef, printValues) ); if( printValues && includedRef ) { stream << state.indent << "\n"; Interface *refObj = 0; NV_PARAM_ERR_CHECK_RETURN( handle.getParamRef(refObj), Serializer::ERROR_UNKNOWN ); if( refObj ) { Handle refHandle(refObj); state.incLevel(); NV_ERR_CHECK_RETURN( traverseParamDefTree(*refObj, stream, state, refHandle) ); state.decLevel(); stream << state.indent; } } stream << "\n"; break; } case TYPE_POINTER: //Don't do anything with pointer break; NV_PARAMETRIZED_LINAL_DATATYPE_LABELS NV_PARAMETRIZED_ARITHMETIC_DATATYPE_LABELS NV_PARAMETRIZED_STRING_DATATYPE_LABELS NV_PARAMETRIZED_ENUM_DATATYPE_LABELS NV_PARAMETRIZED_UNDEFINED_AND_LAST_DATATYPE_LABELS default: { stream << state.indent; NV_ERR_CHECK_RETURN( emitElement(obj, stream, "value", handle, includedRef, printValues) ); char buf[512]; const char *str = 0; if( printValues ) NV_PARAM_ERR_CHECK_RETURN( handle.valueToStr(buf, sizeof(buf), str), Serializer::ERROR_VAL2STRING_FAILED ); if( str ) stream << str; stream << "\n"; break; } //default } //switch } //DONOTSERIALIZE if( isRoot ) { state.decLevel(); stream << "\n"; } return Serializer::ERROR_NONE; } Serializer::ErrorType XmlSerializer::emitElementNxHints( physx::PsIOStream &stream, Handle &handle, traversalState &state, bool &includedRef) { const Definition *paramDef = handle.parameterDefinition(); for(int32_t j = 0; j < paramDef->numHints(); ++j) { if( 0 == j ) stream << "\n"; const Hint *hint = paramDef->hint(j); stream << state.indent << "\n"; } includedRef = paramDef->isIncludedRef(); return Serializer::ERROR_NONE; } Serializer::ErrorType XmlSerializer::emitElement( const Interface &obj, physx::PsIOStream &stream, const char *elementName, Handle &handle, bool includedRef, bool printValues, bool isRoot) { const Definition *paramDef = handle.parameterDefinition(); DataType parentType = TYPE_UNDEFINED; if( paramDef->parent() ) parentType = paramDef->parent()->type(); stream << '<' << elementName; if( isRoot ) { stream << " name=\"\"" << " type=\"Ref\"" << " className=\"" << obj.className() << "\""; const char *objectName = obj.name(); if( objectName ) stream << " objectName=\"" << objectName << "\""; if( isRoot ) //We only emit version info for root storeVersionAndChecksum(stream, &obj); } else { if( parentType != TYPE_ARRAY ) { const char *name = paramDef->name(); stream << " name=\"" << (name ? name : "") << "\""; } } switch( paramDef->type() ) { case TYPE_STRUCT: break; case TYPE_ARRAY: { int32_t arraySize; NV_PARAM_ERR_CHECK_RETURN( handle.getArraySize(arraySize), Serializer::ERROR_INVALID_ARRAY ); stream << " size=\"" << arraySize << '"'; handle.set(0); const Definition *pd = handle.parameterDefinition(); handle.popIndex(); stream << " type=\"" << typeToStr(pd->type()) << '"'; // ** handle use case for simple structs written out flat.. #if !UNOPTIMIZED_XML if ( pd->type() == TYPE_STRUCT && IsSimpleStruct(pd) ) { stream << " structElements=\""; const int32_t count = pd->numChildren(); // find how many of them need serialization int32_t serializeCount = 0; for (int32_t i=0; ichild(i); if (d->hint("DONOTSERIALIZE") == NULL) { serializeCount++; } } for (int32_t i=0; ichild(i); if (d->hint("DONOTSERIALIZE")) continue; stream << d->name(); stream << "("; stream << typeToStr(d->type()); stream << ")"; if ( (i+1) < serializeCount ) { stream<<","; } } stream << "\""; } #endif // break; } case TYPE_REF: { stream << " type=\"" << typeToStr(paramDef->type()) << '"'; Interface *paramPtr = 0; if( printValues ) NV_PARAM_ERR_CHECK_RETURN( handle.getParamRef(paramPtr), Serializer::ERROR_UNKNOWN ); stream << " included=\"" << ( includedRef ? "1" : "0" ) << "\""; if( !printValues || !paramPtr ) { stream << " classNames=\""; for(int32_t i = 0; i < paramDef->numRefVariants(); ++i) { const char *ref = paramDef->refVariantVal(i); if ( DoesNeedQuote(ref) ) stream << "%20" << ref << "%20" << " "; else stream << ref << " "; } stream << '"'; break; } stream << " className=\"" << paramPtr->className() << '"'; const char *objectName = paramPtr->name(); if( objectName ) stream << " objectName=\"" << objectName << "\""; if( includedRef ) storeVersionAndChecksum(stream, paramPtr); break; } case TYPE_STRING: case TYPE_ENUM: { const char *val; NV_PARAM_ERR_CHECK_RETURN( handle.getParamString(val), Serializer::ERROR_UNKNOWN ); //Make a note if value is NULL if( !val ) stream << " null=\"1\""; } //Fall-through to default NV_PARAMETRIZED_LINAL_DATATYPE_LABELS NV_PARAMETRIZED_ARITHMETIC_DATATYPE_LABELS NV_PARAMETRIZED_SERVICE_DATATYPE_LABELS NV_PARAMETRIZED_UNDEFINED_AND_LAST_DATATYPE_LABELS default: stream << " type=\"" << typeToStr(paramDef->type()) << "\""; break; } //switch stream << '>'; return Serializer::ERROR_NONE; } Serializer::ErrorType XmlSerializer::internalSerialize(physx::PxFileBuf &fbuf, const Interface **objs, uint32_t n, bool doMetadata) { PX_UNUSED(doMetadata); physx::PsIOStream stream(fbuf, fbuf.getFileLength()); stream.setBinary(false); uint32_t minor = version() & 0xffffUL, major = version() >> 16; stream << "\n" << "\n"; for(uint32_t i = 0; i < n; ++i) { const Interface &obj = *objs[i]; Handle handle(obj); traversalState state; NV_ERR_CHECK_RETURN( traverseParamDefTree(obj, stream, state, handle) ); } stream << "\n"; return Serializer::ERROR_NONE; } #endif Serializer::ErrorType XmlSerializer::internalDeserialize( physx::PxFileBuf &stream, Serializer::DeserializedData &res, bool & /*doesNeedUpdate*/) { XmlDeserializer *d = XmlDeserializer::Create(mTraits, XmlSerializer::version()); physx::shdfnd::FastXml *xmlParser = physx::shdfnd::createFastXml(d); InputDataFromPxFileBuf inputData(stream); if( xmlParser && !xmlParser->processXml(inputData) ) { Serializer::ErrorType err = d->getLastError(); if( Serializer::ERROR_NONE == err ) //Proper error code not set? { DEBUG_ALWAYS_ASSERT(); //XmlDeserializer should set explicit error codes err = Serializer::ERROR_UNKNOWN; } xmlParser->release(); d->releaseAll(); d->destroy(); return err; } if ( xmlParser ) xmlParser->release(); res.init(mTraits, d->getObjs(), d->getNobjs()); d->destroy(); return Serializer::ERROR_NONE; } bool isXmlFormat(physx::PxFileBuf &stream) { // if it is at least 32 bytes long and the first 32 byte are all ASCII, then consider it potentially valid XML if( stream.getFileLength() < 32 ) return false; char hdr[32]; stream.peek(hdr, sizeof(hdr)); for(size_t i = 0; i < sizeof(hdr); ++i) { char c = hdr[i]; if( !(c == '\r' || c == '\t' || c == '\n' || ( c >= 32 && c < 127)) ) return false; } const char *magic1 = ""; const char *magic2 = ""; return 0 == ::strncmp(hdr, magic1, strlen(magic1)) || 0 == ::strncmp(hdr, magic2, strlen(magic2)); } } // namespace NvParameterized