//========= Copyright (c), Valve Corporation, All rights reserved. ============// // // Purpose: Contains a branch-neutral binary packer for KeyValues trees. // // $NoKeywords: $ // //=============================================================================// #include #include "kvpacker.h" #include "tier0/dbg.h" #include "utlbuffer.h" // memdbgon must be the last include file in a .cpp file!!! #include #define KEYVALUES_TOKEN_SIZE 1024 // writes KeyValue as binary data to buffer bool KVPacker::WriteAsBinary( KeyValues *pNode, CUtlBuffer &buffer ) { if ( buffer.IsText() ) // must be a binary buffer return false; if ( !buffer.IsValid() ) // must be valid, no overflows etc return false; // Write subkeys: // loop through all our peers for ( KeyValues *dat = pNode; dat != NULL; dat = dat->GetNextKey() ) { // write type switch ( dat->GetDataType() ) { case KeyValues::TYPE_NONE: { buffer.PutUnsignedChar( PACKTYPE_NONE ); break; } case KeyValues::TYPE_STRING: { buffer.PutUnsignedChar( PACKTYPE_STRING ); break; } case KeyValues::TYPE_WSTRING: { buffer.PutUnsignedChar( PACKTYPE_WSTRING ); break; } case KeyValues::TYPE_INT: { buffer.PutUnsignedChar( PACKTYPE_INT ); break; } case KeyValues::TYPE_UINT64: { buffer.PutUnsignedChar( PACKTYPE_UINT64 ); break; } case KeyValues::TYPE_FLOAT: { buffer.PutUnsignedChar( PACKTYPE_FLOAT ); break; } case KeyValues::TYPE_COLOR: { buffer.PutUnsignedChar( PACKTYPE_COLOR ); break; } case KeyValues::TYPE_PTR: { buffer.PutUnsignedChar( PACKTYPE_PTR ); break; } default: break; } // write name buffer.PutString( dat->GetName() ); // write value switch ( dat->GetDataType() ) { case KeyValues::TYPE_NONE: { if( !WriteAsBinary( dat->GetFirstSubKey(), buffer ) ) return false; break; } case KeyValues::TYPE_STRING: { if (dat->GetString() && *(dat->GetString())) { buffer.PutString( dat->GetString() ); } else { buffer.PutString( "" ); } break; } case KeyValues::TYPE_WSTRING: { int nLength = dat->GetWString() ? Q_wcslen( dat->GetWString() ) : 0; buffer.PutShort( nLength ); for( int k = 0; k < nLength; ++ k ) { buffer.PutShort( ( unsigned short ) dat->GetWString()[k] ); } break; } case KeyValues::TYPE_INT: { buffer.PutInt( dat->GetInt() ); break; } case KeyValues::TYPE_UINT64: { buffer.PutInt64( dat->GetUint64() ); break; } case KeyValues::TYPE_FLOAT: { buffer.PutFloat( dat->GetFloat() ); break; } case KeyValues::TYPE_COLOR: { Color color = dat->GetColor(); buffer.PutUnsignedChar( color[0] ); buffer.PutUnsignedChar( color[1] ); buffer.PutUnsignedChar( color[2] ); buffer.PutUnsignedChar( color[3] ); break; } case KeyValues::TYPE_PTR: { buffer.PutPtr( dat->GetPtr() ); break; } default: break; } } // write tail, marks end of peers buffer.PutUnsignedChar( PACKTYPE_NULLMARKER ); return buffer.IsValid(); } // read KeyValues from binary buffer, returns true if parsing was successful bool KVPacker::ReadAsBinary( KeyValues *pNode, CUtlBuffer &buffer ) { if ( buffer.IsText() ) // must be a binary buffer return false; if ( !buffer.IsValid() ) // must be valid, no overflows etc return false; pNode->Clear(); char token[KEYVALUES_TOKEN_SIZE]; KeyValues *dat = pNode; EPackType ePackType = (EPackType)buffer.GetUnsignedChar(); // loop through all our peers while ( true ) { if ( ePackType == PACKTYPE_NULLMARKER ) break; // no more peers buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 ); token[KEYVALUES_TOKEN_SIZE-1] = 0; dat->SetName( token ); switch ( ePackType ) { case PACKTYPE_NONE: { KeyValues *pNewNode = new KeyValues(""); dat->AddSubKey( pNewNode ); if( !ReadAsBinary( pNewNode, buffer ) ) return false; break; } case PACKTYPE_STRING: { buffer.GetString( token, KEYVALUES_TOKEN_SIZE-1 ); token[KEYVALUES_TOKEN_SIZE-1] = 0; dat->SetStringValue( token ); break; } case PACKTYPE_WSTRING: { int nLength = buffer.GetShort(); if ( nLength >= 0 && nLength*sizeof( uint16 ) <= (uint)buffer.GetBytesRemaining() ) { if ( nLength > 0 ) { wchar_t *pTemp = (wchar_t *)malloc( sizeof( wchar_t ) * (1 + nLength) ); for ( int k = 0; k < nLength; ++k ) { pTemp[k] = buffer.GetShort(); // ugly, but preserving existing behavior } pTemp[nLength] = 0; dat->SetWString( NULL, pTemp ); free( pTemp ); } else dat->SetWString( NULL, L"" ); } break; } case PACKTYPE_INT: { dat->SetInt( NULL, buffer.GetInt() ); break; } case PACKTYPE_UINT64: { dat->SetUint64( NULL, (uint64)buffer.GetInt64() ); break; } case PACKTYPE_FLOAT: { dat->SetFloat( NULL, buffer.GetFloat() ); break; } case PACKTYPE_COLOR: { Color color( buffer.GetUnsignedChar(), buffer.GetUnsignedChar(), buffer.GetUnsignedChar(), buffer.GetUnsignedChar() ); dat->SetColor( NULL, color ); break; } case PACKTYPE_PTR: { dat->SetPtr( NULL, buffer.GetPtr() ); break; } default: break; } if ( !buffer.IsValid() ) // error occured return false; ePackType = (EPackType)buffer.GetUnsignedChar(); if ( ePackType == PACKTYPE_NULLMARKER ) break; // new peer follows KeyValues *pNewPeer = new KeyValues(""); dat->SetNextKey( pNewPeer ); dat = pNewPeer; } return buffer.IsValid(); }