//========= Copyright Valve Corporation, All rights reserved. ============// // //=======================================================================================// #include "replaysystem.h" #include "sv_sessionrecorder.h" #include "utlbuffer.h" #include "sessioninfoheader.h" #include "sv_fileservercleanup.h" #include "sv_publishtest.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //---------------------------------------------------------------------------------------- #define ENSURE_DEDICATED() if ( !g_pEngine->IsDedicated() ) return; //---------------------------------------------------------------------------------------- CON_COMMAND( replay_record, "Starts Replay demo recording." ) { ENSURE_DEDICATED(); SV_GetSessionRecorder()->StartRecording(); } //---------------------------------------------------------------------------------------- CON_COMMAND( replay_stoprecord, "Stop Replay demo recording." ) { ENSURE_DEDICATED(); g_pReplay->SV_EndRecordingSession(); } //---------------------------------------------------------------------------------------- CON_COMMAND( replay_docleanup, "Deletes stale session data from the fileserver. \"replay_docleanup force\" will remove all replay session data." ) { ENSURE_DEDICATED(); bool bForceCleanAll = false; if ( args.ArgC() == 2 ) { if ( !V_stricmp( args[1], "force" ) ) { bForceCleanAll = true; } else { ConMsg( "\n ** ERROR: '%s' is not a valid paramter - use 'force' to force clean all replay session data.\n\n", args[1] ); return; } } if ( !SV_DoFileserverCleanup( bForceCleanAll, g_pBlockSpewer ) ) { Msg( "No demos were deleted.\n" ); } } //---------------------------------------------------------------------------------------- CON_COMMAND_F( replay_dopublishtest, "Do a replay publish test using the current setup.", FCVAR_DONTRECORD ) { ENSURE_DEDICATED(); g_pBlockSpewer->PrintBlockStart(); SV_DoTestPublish(); g_pBlockSpewer->PrintBlockEnd(); } //---------------------------------------------------------------------------------------- void PrintSessionInfo( const char *pFilename ) { CUtlBuffer buf; if ( !g_pFullFileSystem->ReadFile( pFilename, NULL, buf ) ) { Msg( "Failed to read file, \"%s\"\n", pFilename ); return; } int nFileSize = buf.TellPut(); SessionInfoHeader_t header; if ( !ReadSessionInfoHeader( buf.Base(), nFileSize, header ) ) { Msg( "Failed to read header information.\n" ); return; } char szDigestStr[33]; V_binarytohex( header.m_aHash, sizeof( header.m_aHash ), szDigestStr, sizeof( szDigestStr ) ); Msg( "\n\theader:\n" ); Msg( "\n" ); Msg( "\t%27s: %u\n", "version", header.m_uVersion ); Msg( "\t%27s: %s\n", "session name", header.m_szSessionName ); Msg( "\t%27s: %s\n", "currently recording?", header.m_bRecording ? "yes" : "no" ); Msg( "\t%27s: %i\n", "# blocks", header.m_nNumBlocks ); Msg( "\t%27s: %s\n", "compressor", GetCompressorNameSafe( header.m_nCompressorType ) ); Msg( "\t%27s: %s\n", "md5 digest", szDigestStr ); Msg( "\t%27s: %u bytes\n", "payload size (compressed)", header.m_uPayloadSize ); Msg( "\t%27s: %u bytes\n", "payload size (uncompressed)", header.m_uPayloadSizeUC ); Msg( "\n" ); const uint8 *pPayload = (uint8 *)buf.Base() + sizeof( SessionInfoHeader_t ); uint32 uUncompressedPayloadSize = header.m_uPayloadSizeUC; if ( !g_pEngine->MD5_HashBuffer( header.m_aHash, (const uint8 *)pPayload, header.m_uPayloadSize, NULL ) ) { Msg( "Data validation failed.\n" ); return; } const uint8 *pUncompressedPayload; bool bFreeUncompressedPayload = true; if ( header.m_nCompressorType == COMPRESSORTYPE_INVALID ) { // The payload is already uncompressed - don't free, since this buffer was allocated by the CUtlBuffer "buf" pUncompressedPayload = pPayload; bFreeUncompressedPayload = false; } else { if ( uUncompressedPayloadSize != header.m_uPayloadSizeUC ) { Msg( "Decompressed to a different size (%u) than specified by header (%u)\n", uUncompressedPayloadSize, header.m_uPayloadSizeUC ); return; } ICompressor *pCompressor = CreateCompressor( header.m_nCompressorType ); if ( !pCompressor ) { Msg( "Failed to create compressor.\n" ); return; } pUncompressedPayload = new uint8[ uUncompressedPayloadSize ]; if ( !pUncompressedPayload ) { Msg( "Failed to allocate uncompressed payload.\n" ); delete [] pCompressor; return; } pCompressor->Decompress( (char *)pUncompressedPayload, &uUncompressedPayloadSize, (const char *)pPayload, header.m_uPayloadSize ); delete pCompressor; } if ( uUncompressedPayloadSize <= MIN_SESSION_INFO_PAYLOAD_SIZE ) { Msg( "Uncompressed payload not large enough to read a single block.\n" ); } else { RecordingSessionBlockSpec_t DummyBlock; CUtlBuffer bufPayload( pUncompressedPayload, uUncompressedPayloadSize, CUtlBuffer::READ_ONLY ); Msg( "\n\tblocks:\n\n" ); Msg( "\t index status MD5 compressor size (uncompressed) size (compressed)\n" ); bool bBlockReadFailed = false; for ( int i = 0; i < header.m_nNumBlocks; ++i ) { // Attempt to read the current block from the buffer bufPayload.Get( &DummyBlock, sizeof( DummyBlock ) ); if ( !bufPayload.IsValid() ) { bBlockReadFailed = true; break; } V_binarytohex( DummyBlock.m_aHash, sizeof( DummyBlock.m_aHash ), szDigestStr, sizeof( szDigestStr ) ); Msg( "\t %5i", DummyBlock.m_iReconstruction ); Msg( "%20s", CBaseRecordingSessionBlock::GetRemoteStatusStringSafe( (CBaseRecordingSessionBlock::RemoteStatus_t)DummyBlock.m_uRemoteStatus ) ); Msg( "%35s", szDigestStr ); Msg( "%8s", GetCompressorNameSafe( (CompressorType_t)DummyBlock.m_nCompressorType ) ); Msg( "%20u", DummyBlock.m_uFileSize ); Msg( "%20u", DummyBlock.m_uUncompressedSize ); Msg( "\n" ); } } Msg( "\n" ); if ( bFreeUncompressedPayload ) { delete [] pUncompressedPayload; } } CON_COMMAND_F( replay_printsessioninfo, "Print session info", FCVAR_DONTRECORD ) { ENSURE_DEDICATED(); if ( args.ArgC() != 2 ) { Msg( "Usage: replay_printsessioninfo \n" ); return; } PrintSessionInfo( args[1] ); } //----------------------------------------------------------------------------------------