//====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include "platform.h" #include "utlvector.h" #include "utlbuffer.h" #include "chunkfile.h" #include "utlencode.h" #include "fgdlib/wckeyvalues.h" #include "vmfmeshdatasupport.h" CVmfMeshDataSupport_SaveLoadHandler::CVmfMeshDataSupport_SaveLoadHandler() { } CVmfMeshDataSupport_SaveLoadHandler::~CVmfMeshDataSupport_SaveLoadHandler() { NULL; } ChunkFileResult_t CVmfMeshDataSupport_SaveLoadHandler::WriteDataChunk( CChunkFile *pFile, char const *szHash ) { ChunkFileResult_t eResult; eResult = pFile->BeginChunk( GetCustomSectionName() ); if ( eResult != ChunkFile_Ok ) return eResult; // Write out our data version char szModelDataVer[ 16 ] = {0}; sprintf( szModelDataVer, "%d", GetCustomSectionVer() ); eResult = pFile->WriteKeyValue( "version", szModelDataVer ); if ( eResult != ChunkFile_Ok ) return eResult; // Write our hash eResult = pFile->WriteKeyValue( "hash", szHash ); if ( eResult != ChunkFile_Ok ) return eResult; // Write out additional data eResult = OnFileDataWriting( pFile, szHash ); if ( eResult != ChunkFile_Ok ) return eResult; return pFile->EndChunk(); } ChunkFileResult_t CVmfMeshDataSupport_SaveLoadHandler::OnFileDataWriting( CChunkFile *pFile, char const *szHash ) { return ChunkFile_Ok; } ChunkFileResult_t CVmfMeshDataSupport_SaveLoadHandler::WriteBufferData( CChunkFile *pFile, CUtlBuffer &bufData, char const *szPrefix ) { int numEncBytes = KvEncoder::GuessEncodedLength( bufData.TellPut() ); CUtlBuffer bufEncoded; bufEncoded.EnsureCapacity( numEncBytes ); if ( !KvEncoder::Encode( bufData, bufEncoded ) ) return ChunkFile_Fail; numEncBytes = bufEncoded.TellPut(); // Now we have the encoded data, split it into blocks int numBytesPerLine = KEYVALUE_MAX_VALUE_LENGTH - 2; int numLines = (numEncBytes + numBytesPerLine - 1 ) / numBytesPerLine; int numLastLineBytes = numEncBytes % numBytesPerLine; if ( !numLastLineBytes ) numLastLineBytes = numBytesPerLine; // Key buffer char chKeyBuf[ KEYVALUE_MAX_KEY_LENGTH ] = {0}; char chKeyValue[ KEYVALUE_MAX_VALUE_LENGTH ] = {0}; // Write the data ChunkFileResult_t eResult; sprintf( chKeyBuf, "%s_ebytes", szPrefix ); eResult = pFile->WriteKeyValueInt( chKeyBuf, numEncBytes ); if ( eResult != ChunkFile_Ok ) return eResult; sprintf( chKeyBuf, "%s_rbytes", szPrefix ); eResult = pFile->WriteKeyValueInt( chKeyBuf, bufData.TellPut() ); if ( eResult != ChunkFile_Ok ) return eResult; sprintf( chKeyBuf, "%s_lines", szPrefix ); eResult = pFile->WriteKeyValueInt( chKeyBuf, numLines ); if ( eResult != ChunkFile_Ok ) return eResult; for ( int ln = 0; ln < numLines; ++ ln ) { int lnLen = ( ln < (numLines - 1) ) ? numBytesPerLine : numLastLineBytes; sprintf( chKeyBuf, "%s_ln_%d", szPrefix, ln ); sprintf( chKeyValue, "%.*s", lnLen, ( char * ) bufEncoded.Base() + ln * numBytesPerLine ); eResult = pFile->WriteKeyValue( chKeyBuf, chKeyValue ); if ( eResult != ChunkFile_Ok ) return eResult; } return ChunkFile_Ok; } ChunkFileResult_t CVmfMeshDataSupport_SaveLoadHandler::LoadKeyValueBegin( CChunkFile *pFile ) { m_eLoadState = LOAD_VERSION; m_iLoadVer = 0; m_hLoadHeader.sHash[0] = 0; LoadInitHeader(); return ChunkFile_Ok; } ChunkFileResult_t CVmfMeshDataSupport_SaveLoadHandler::LoadKeyValue( const char *szKey, const char *szValue ) { switch ( m_eLoadState ) { case LOAD_VERSION: return LoadKeyValue_Hdr( szKey, szValue ); default: switch ( m_iLoadVer ) { case 1: return LoadKeyValue_Ver1( szKey, szValue ); default: return ChunkFile_Fail; } } } ChunkFileResult_t CVmfMeshDataSupport_SaveLoadHandler::LoadKeyValue_Hdr( const char *szKey, const char *szValue ) { switch ( m_eLoadState ) { case LOAD_VERSION: if ( stricmp( szKey, "version" ) ) return ChunkFile_Fail; m_iLoadVer = atoi( szValue ); switch ( m_iLoadVer ) { case 1: m_eLoadState = LOAD_HASH; return ChunkFile_Ok; default: return ChunkFile_Fail; } default: return ChunkFile_Fail; } } ChunkFileResult_t CVmfMeshDataSupport_SaveLoadHandler::LoadKeyValue_Ver1( const char *szKey, const char *szValue ) { const char *szKeyName = szKey; const int nPrefixLen = 3; switch ( m_eLoadState ) { case LOAD_HASH: if ( stricmp( szKey, "hash" ) ) return ChunkFile_Fail; strncpy( m_hLoadHeader.sHash, szValue, sizeof( m_hLoadHeader.sHash ) ); m_eLoadState = LOAD_PREFIX; break; case LOAD_PREFIX: sprintf( m_hLoadHeader.sPrefix, "%.*s", nPrefixLen, szKey ); if ( strlen( szKey ) < 4 ) return ChunkFile_Fail; if ( szKey[3] != '_' ) return ChunkFile_Fail; m_eLoadState = LOAD_HEADER; // fall-through case LOAD_HEADER: if ( strnicmp( m_hLoadHeader.sPrefix, szKey, nPrefixLen ) ) return ChunkFile_Fail; if ( szKey[3] != '_' ) return ChunkFile_Fail; szKeyName = szKey + 4; if ( !stricmp( szKeyName, "ebytes" ) ) m_hLoadHeader.numEncBytes = atoi( szValue ); else if ( !stricmp( szKeyName, "rbytes" ) ) m_hLoadHeader.numBytes = atoi( szValue ); else if ( !stricmp( szKeyName, "lines" ) ) m_hLoadHeader.numLines = atoi( szValue ); else return ChunkFile_Fail; if ( !LoadHaveHeader() ) break; m_eLoadState = LOAD_DATA; return LoadHaveLines( 0 ); case LOAD_DATA: if ( strnicmp( m_hLoadHeader.sPrefix, szKey, nPrefixLen ) ) return ChunkFile_Fail; if ( szKey[3] != '_' ) return ChunkFile_Fail; szKeyName = szKey + 4; if ( strnicmp( szKeyName, "ln", 2 ) || szKeyName[2] != '_' ) return ChunkFile_Fail; szKeyName += 3; { int iLineNum = atoi( szKeyName ); if ( iLineNum != m_hLoadHeader.numHaveLines ) return ChunkFile_Fail; m_bufLoadData.Put( szValue, strlen( szValue ) ); return LoadHaveLines( iLineNum + 1 ); } default: return ChunkFile_Fail; } return ChunkFile_Ok; } ChunkFileResult_t CVmfMeshDataSupport_SaveLoadHandler::LoadKeyValueEnd( CChunkFile *pFile, ChunkFileResult_t eLoadResult ) { if ( eLoadResult != ChunkFile_Ok ) return eLoadResult; if ( m_eLoadState == LOAD_VERSION ) return ChunkFile_Ok; switch ( m_iLoadVer ) { case 1: switch ( m_eLoadState ) { case LOAD_HASH: case LOAD_PREFIX: return ChunkFile_Ok; default: return ChunkFile_Fail; } default: return ChunkFile_Fail; } } void CVmfMeshDataSupport_SaveLoadHandler::LoadInitHeader() { m_hLoadHeader.sPrefix[0] = 0; m_hLoadHeader.numLines = -1; m_hLoadHeader.numBytes = -1; m_hLoadHeader.numEncBytes = -1; m_hLoadHeader.numHaveLines = 0; } bool CVmfMeshDataSupport_SaveLoadHandler::LoadHaveHeader() { return m_hLoadHeader.numLines >= 0 && m_hLoadHeader.numBytes >= 0 && m_hLoadHeader.numEncBytes >= 0; } ChunkFileResult_t CVmfMeshDataSupport_SaveLoadHandler::LoadHaveLines( int numHaveLines ) { if ( !numHaveLines ) { m_bufLoadData.EnsureCapacity( m_hLoadHeader.numEncBytes ); m_bufLoadData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); } m_hLoadHeader.numHaveLines = numHaveLines; if ( m_hLoadHeader.numHaveLines < m_hLoadHeader.numLines ) return ChunkFile_Ok; if ( m_hLoadHeader.numHaveLines > m_hLoadHeader.numLines ) return ChunkFile_Fail; ChunkFileResult_t eRes = LoadSaveFullData(); if ( eRes != ChunkFile_Ok ) return eRes; LoadInitHeader(); m_eLoadState = LOAD_PREFIX; return ChunkFile_Ok; } ChunkFileResult_t CVmfMeshDataSupport_SaveLoadHandler::LoadSaveFullData() { // The filename CUtlBuffer bufBytes; bufBytes.EnsureCapacity( m_hLoadHeader.numBytes + 0x10 ); if ( !KvEncoder::Decode( m_bufLoadData, bufBytes ) ) return ChunkFile_Fail; return OnFileDataLoaded( bufBytes ); }