//========= Copyright Valve Corporation, All rights reserved. ============// // //=======================================================================================// #include "baserecordingsessionblock.h" #include "replay/replayutils.h" #include "replay/ireplaycontext.h" #include "replay/irecordingsessionmanager.h" #include "replay/shared_defs.h" #include "KeyValues.h" #include "qlimits.h" #include "utlbuffer.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //---------------------------------------------------------------------------------------- CBaseRecordingSessionBlock::CBaseRecordingSessionBlock( IReplayContext *pContext ) : m_pContext( pContext ), m_nRemoteStatus( STATUS_INVALID ), m_nHttpError( ERROR_NONE ), m_hSession( REPLAY_HANDLE_INVALID ), m_bHashValid( false ), m_iReconstruction( -1 ), m_uFileSize( 0 ), m_uUncompressedSize( 0 ), m_nCompressorType( COMPRESSORTYPE_INVALID ) { m_szFullFilename[ 0 ] = '\0'; V_memset( m_aHash, 0, sizeof( m_aHash ) ); } const char *CBaseRecordingSessionBlock::GetSubKeyTitle() const { CBaseRecordingSession *pOwnerSession = m_pContext->GetRecordingSessionManager()->FindSession( m_hSession ); if ( !pOwnerSession ) { AssertMsg( 0, "Owner session not found" ); return ""; } return Replay_va( "%s_part_%i", pOwnerSession->m_strName.Get(), m_iReconstruction ); } const char *CBaseRecordingSessionBlock::GetPath() const { return Replay_va( "%s%s%c", m_pContext->GetBaseDir(), SUBDIR_BLOCKS, CORRECT_PATH_SEPARATOR ); } bool CBaseRecordingSessionBlock::Read( KeyValues *pIn ) { if ( !BaseClass::Read( pIn ) ) return false; m_nRemoteStatus = (RemoteStatus_t)pIn->GetInt( "remote_status", (int)STATUS_INVALID ); Assert( m_nRemoteStatus != STATUS_INVALID ); m_nHttpError = (Error_t)pIn->GetInt( "error", (int)ERROR_NONE ); m_iReconstruction = pIn->GetInt( "recon_index", -1 ); Assert( m_iReconstruction >= 0 ); m_hSession = (ReplayHandle_t)pIn->GetInt( "session", REPLAY_HANDLE_INVALID ); Assert( m_hSession != REPLAY_HANDLE_INVALID ); m_uFileSize = pIn->GetInt( "size", 0 ); Assert( m_uFileSize > 0 ); m_uUncompressedSize = pIn->GetInt( "usize", 0 ); m_nCompressorType = (CompressorType_t)pIn->GetInt( "compressor", 0 ); ReadHash( pIn, "hash" ); return true; } void CBaseRecordingSessionBlock::Write( KeyValues *pOut ) { BaseClass::Write( pOut ); pOut->SetInt( "remote_status", (int)m_nRemoteStatus ); pOut->SetInt( "error", (int)m_nHttpError ); pOut->SetInt( "recon_index", m_iReconstruction ); pOut->SetInt( "session", (int)m_hSession ); pOut->SetInt( "size", m_uFileSize ); pOut->SetInt( "usize", m_uUncompressedSize ); pOut->SetInt( "compressor", (int)m_nCompressorType ); WriteHash( pOut, "hash" ); // NOTE: Filename written in subclasses, since it's handled differently for client vs. server } void CBaseRecordingSessionBlock::OnDelete() { BaseClass::OnDelete(); // NOTE: The actual .block files get deleted in subclasses, since each handle the case differently. } bool CBaseRecordingSessionBlock::ReadHash( KeyValues *pIn, const char *pHashName ) { const char *pHashStr = pIn->GetString( pHashName ); bool bResult = false; if ( V_strlen( pHashStr ) > 0 ) { int iHash = 0; char *p = strtok( const_cast< char * >( pHashStr ), " " ); while ( p ) { // Should have no more than 3 characters if ( V_strlen( p ) > 3 ) { break; } m_aHash[ iHash++ ] = (uint8)atoi( p ); p = strtok( NULL, " " ); bResult = true; } } // Keep track of whether we have a valid hash or not m_bHashValid = bResult; AssertMsg( bResult, "Invalid hash string" ); return bResult; } void CBaseRecordingSessionBlock::WriteHash( KeyValues *pOut, const char *pHashName ) const { CFmtStr fmtHash( "%03i %03i %03i %03i %03i %03i %03i %03i %03i %03i %03i %03i %03i %03i %03i %03i", m_aHash[0], m_aHash[1], m_aHash[2], m_aHash[3], m_aHash[4], m_aHash[5], m_aHash[6], m_aHash[7], m_aHash[8], m_aHash[9], m_aHash[10], m_aHash[11], m_aHash[12], m_aHash[13], m_aHash[14], m_aHash[15] ); pOut->SetString( pHashName, fmtHash.Access() ); } bool CBaseRecordingSessionBlock::HasValidHash() const { return m_bHashValid; } void CBaseRecordingSessionBlock::WriteSessionInfoDataToBuffer( CUtlBuffer &buf ) const { RecordingSessionBlockSpec_t blob; blob.m_iReconstruction = (int32)m_iReconstruction; blob.m_uRemoteStatus = (uint8)m_nRemoteStatus; blob.m_uFileSize = m_uFileSize; blob.m_nCompressorType = (int8)m_nCompressorType; // Can be COMPRESSORTYPE_INVALID if not compressed blob.m_uUncompressedSize = m_uUncompressedSize; // Can be 0 if not compressed V_memcpy( blob.m_aHash, m_aHash, sizeof( m_aHash ) ); // Write the blob at the appropriate position in the buffer Assert( m_iReconstruction >= 0 ); buf.SeekPut( CUtlBuffer::SEEK_HEAD, m_iReconstruction * sizeof( blob ) ); buf.Put( &blob, sizeof( RecordingSessionBlockSpec_t ) ); } //---------------------------------------------------------------------------------------- /*static*/ const char *CBaseRecordingSessionBlock::GetRemoteStatusStringSafe( RemoteStatus_t nStatus ) { switch ( nStatus ) { case STATUS_INVALID: return "invalid"; case STATUS_ERROR: return "error"; case STATUS_WRITING: return "writing"; case STATUS_READYFORDOWNLOAD: return "ready for download"; default: return "unknown"; } } //----------------------------------------------------------------------------------------