You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
374 lines
9.7 KiB
374 lines
9.7 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
//=======================================================================================// |
|
|
|
#include "replay/replay.h" |
|
#include "replay/iclientreplaycontext.h" |
|
#include "replay/ireplaymanager.h" |
|
#include "replay/replayutils.h" |
|
#include "replay/screenshot.h" |
|
#include "replay/shared_defs.h" |
|
#include "replay/ireplayscreenshotmanager.h" |
|
#include "replay/ireplayperformancemanager.h" |
|
#include "replay/performance.h" |
|
#include "KeyValues.h" |
|
#include "filesystem.h" |
|
#include "vgui/ILocalize.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//---------------------------------------------------------------------------------------- |
|
|
|
extern IClientReplayContext *g_pClientReplayContext; |
|
extern vgui::ILocalize *g_pVGuiLocalize; |
|
|
|
//---------------------------------------------------------------------------------------- |
|
|
|
CReplay::CReplay() |
|
: m_pDownloadEventHandler( NULL ), |
|
m_pUserData( NULL ), |
|
m_bComplete( false ), |
|
m_bRequestedByUser( false ), |
|
m_bSaved( false ), |
|
m_bRendered( false ), |
|
m_bDirty( false ), |
|
m_bSavedDuringThisSession( true ), |
|
m_flLength( 0 ), |
|
m_nPlayerSlot( -1 ), |
|
m_nSpawnTick( -1 ), |
|
m_nDeathTick( -1 ), |
|
m_iMaxSessionBlockRequired( 0 ), |
|
m_nStatus( REPLAYSTATUS_INVALID ), |
|
m_hSession( REPLAY_HANDLE_INVALID ), |
|
m_pFileURL( NULL ), |
|
m_nPostDeathRecordTime( 0 ), |
|
m_flStartTime( 0.0f ), |
|
m_flNextUpdateTime( 0.0f ) |
|
{ |
|
m_wszTitle[0] = L'\0'; |
|
m_szMapName[0] = 0; |
|
} |
|
|
|
bool CReplay::IsDownloaded() const |
|
{ |
|
return m_nStatus == REPLAYSTATUS_READYTOCONVERT; |
|
} |
|
|
|
const CReplayPerformance *CReplay::GetPerformance( int i ) const |
|
{ |
|
return const_cast< CReplay * >( this )->GetPerformance( i ); |
|
} |
|
|
|
CReplayPerformance *CReplay::GetPerformance( int i ) |
|
{ |
|
if ( i < 0 || i >= m_vecPerformances.Count() ) |
|
return NULL; |
|
|
|
return m_vecPerformances[ i ]; |
|
} |
|
|
|
bool CReplay::FindPerformance( CReplayPerformance *pPerformance, int &iResult ) |
|
{ |
|
const int it = m_vecPerformances.Find( pPerformance ); |
|
if ( it == m_vecPerformances.InvalidIndex() ) |
|
{ |
|
iResult = -1; |
|
return false; |
|
} |
|
|
|
iResult = it; |
|
return true; |
|
} |
|
|
|
CReplayPerformance *CReplay::GetPerformanceWithTitle( const wchar_t *pTitle ) |
|
{ |
|
FOR_EACH_VEC( m_vecPerformances, i ) |
|
{ |
|
CReplayPerformance *pCurPerformance = m_vecPerformances[ i ]; |
|
if ( !V_wcscmp( pTitle, pCurPerformance->m_wszTitle ) ) |
|
{ |
|
return pCurPerformance; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
CReplayPerformance *CReplay::AddNewPerformance( bool bGenTitle/*=true*/, bool bGenFilename/*=true*/ ) |
|
{ |
|
// Create a performance |
|
IReplayPerformanceManager *pPerformanceManager = g_pClientReplayContext->GetPerformanceManager(); |
|
CReplayPerformance *pPerformance = pPerformanceManager->CreatePerformance( this ); |
|
|
|
if ( bGenTitle ) |
|
{ |
|
// Give the performance a name |
|
pPerformance->AutoNameIfHasNoTitle( m_szMapName ); |
|
} |
|
|
|
if ( bGenFilename ) |
|
{ |
|
// Generate a filename for the new performance |
|
pPerformance->SetFilename( pPerformanceManager->GeneratePerformanceFilename( this ) ); |
|
} |
|
|
|
// Cache |
|
m_vecPerformances.AddToTail( pPerformance ); |
|
|
|
return pPerformance; |
|
} |
|
|
|
void CReplay::AddPerformance( KeyValues *pIn ) |
|
{ |
|
// Create a performance |
|
IReplayPerformanceManager *pPerformanceManager = g_pClientReplayContext->GetPerformanceManager(); |
|
CReplayPerformance *pPerformance = pPerformanceManager->CreatePerformance( this ); |
|
|
|
// Read |
|
pPerformance->Read( pIn ); |
|
|
|
// Cache |
|
m_vecPerformances.AddToTail( pPerformance ); |
|
} |
|
|
|
void CReplay::AddPerformance( CReplayPerformance *pPerformance ) |
|
{ |
|
Assert( pPerformance ); |
|
m_vecPerformances.AddToTail( pPerformance ); |
|
} |
|
|
|
const CReplayTime &CReplay::GetItemDate() const |
|
{ |
|
return m_RecordTime; |
|
} |
|
|
|
bool CReplay::IsItemRendered() const |
|
{ |
|
return m_bRendered; |
|
} |
|
|
|
CReplay *CReplay::GetItemReplay() |
|
{ |
|
return this; |
|
} |
|
|
|
ReplayHandle_t CReplay::GetItemReplayHandle() const |
|
{ |
|
return GetHandle(); |
|
} |
|
|
|
QueryableReplayItemHandle_t CReplay::GetItemHandle() const |
|
{ |
|
return GetHandle(); |
|
} |
|
|
|
const wchar_t *CReplay::GetItemTitle() const |
|
{ |
|
return m_wszTitle; |
|
} |
|
|
|
void CReplay::SetItemTitle( const wchar_t *pTitle ) |
|
{ |
|
V_wcsncpy( m_wszTitle, pTitle, sizeof( m_wszTitle ) ); |
|
} |
|
|
|
float CReplay::GetItemLength() const |
|
{ |
|
return m_flLength; |
|
} |
|
|
|
void *CReplay::GetUserData() |
|
{ |
|
return m_pUserData; |
|
} |
|
|
|
void CReplay::SetUserData( void* pUserData ) |
|
{ |
|
m_pUserData = pUserData; |
|
} |
|
|
|
bool CReplay::IsItemAMovie() const |
|
{ |
|
return false; |
|
} |
|
|
|
void CReplay::AddScreenshot( int nWidth, int nHeight, const char *pBaseFilename ) |
|
{ |
|
m_vecScreenshots.AddToTail( new CReplayScreenshot( nWidth, nHeight, pBaseFilename ) ); |
|
} |
|
|
|
void CReplay::AutoNameTitleIfEmpty() |
|
{ |
|
// Autoname it |
|
if ( !m_wszTitle[0] ) |
|
{ |
|
Replay_GetAutoName( m_wszTitle, sizeof( m_wszTitle ), m_szMapName ); |
|
} |
|
} |
|
|
|
const char *CReplay::GetSubKeyTitle() const |
|
{ |
|
return Replay_va( "replay_%i", GetHandle() ); |
|
} |
|
|
|
const char *CReplay::GetPath() const |
|
{ |
|
return Replay_va( "%s%s%c", g_pClientReplayContext->GetBaseDir(), SUBDIR_REPLAYS, CORRECT_PATH_SEPARATOR ); |
|
} |
|
|
|
void CReplay::OnDelete() |
|
{ |
|
BaseClass::OnDelete(); |
|
|
|
// Delete reconstructed replay if one exists |
|
if ( HasReconstructedReplay() ) |
|
{ |
|
g_pFullFileSystem->RemoveFile( m_strReconstructedFilename.Get() ); |
|
} |
|
|
|
// Delete screenshots |
|
g_pClientReplayContext->GetScreenshotManager()->DeleteScreenshotsForReplay( this ); |
|
|
|
// TODO: Delete performance(s) |
|
} |
|
|
|
bool CReplay::Read( KeyValues *pIn ) |
|
{ |
|
if ( !BaseClass::Read( pIn ) ) |
|
return false; |
|
|
|
m_hSession = (ReplayHandle_t)pIn->GetInt( "session", REPLAY_HANDLE_INVALID ); |
|
V_strcpy_safe( m_szMapName, pIn->GetString( "map", "" ) ); |
|
m_nSpawnTick = pIn->GetInt( "spawn_tick", -1 ); |
|
m_nDeathTick = pIn->GetInt( "death_tick", -1 ); |
|
m_nStatus = static_cast< CReplay::ReplayStatus_t >( pIn->GetInt( "status", (int)CReplay::REPLAYSTATUS_INVALID ) ); |
|
m_bComplete = pIn->GetInt( "complete" ) != 0; |
|
m_flLength = pIn->GetFloat( "length" ); |
|
m_nPostDeathRecordTime = pIn->GetInt( "postdeathrecordtime" ); |
|
m_bRendered = pIn->GetInt( "rendered" ) != 0; |
|
m_nPlayerSlot = pIn->GetInt( "player_slot", -1 ); |
|
m_iMaxSessionBlockRequired = pIn->GetInt( "max_block", 0 ); Assert( m_iMaxSessionBlockRequired >= 0 ); |
|
m_flStartTime = pIn->GetFloat( "start_time", -1.0f ); Assert( m_flStartTime >= 0.0f ); |
|
V_wcsncpy( m_wszTitle, pIn->GetWString( "title" ), sizeof( m_wszTitle ) ); |
|
|
|
// Read reconstructed filename and infer path |
|
const char *pReplaysDir = g_pClientReplayContext->GetReplayManager()->GetReplaysDir(); |
|
const char *pReconFilename = pIn->GetString( "recon_filename" ); |
|
if ( pReconFilename[0] != 0 ) |
|
{ |
|
m_strReconstructedFilename = Replay_va( "%s%s", pReplaysDir, pReconFilename ); |
|
} |
|
|
|
// Read screenshots |
|
KeyValues *pScreenshots = pIn->FindKey( "screenshots" ); |
|
if ( pScreenshots ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pScreenshots, pScreenshot ) |
|
{ |
|
int nWidth = pScreenshot->GetInt( "width" ); |
|
int nHeight = pScreenshot->GetInt( "height" ); |
|
const char *pBaseFilename = pScreenshot->GetString( "base_filename" ); |
|
AddScreenshot( nWidth, nHeight, pBaseFilename ); |
|
} |
|
} |
|
|
|
// Read performances |
|
KeyValues *pPerformances = pIn->FindKey( "edits" ); |
|
if ( pPerformances ) |
|
{ |
|
FOR_EACH_TRUE_SUBKEY( pPerformances, pPerformance ) |
|
{ |
|
AddPerformance( pPerformance ); |
|
} |
|
} |
|
|
|
// Record time |
|
KeyValues *pRecordTimeSubKey = pIn->FindKey( "record_time" ); |
|
if ( pRecordTimeSubKey ) |
|
{ |
|
m_RecordTime.Read( pRecordTimeSubKey ); |
|
} |
|
|
|
// Mark replay as saved, since it was just loaded from disk |
|
m_bSaved = true; |
|
|
|
return true; |
|
} |
|
|
|
void CReplay::Write( KeyValues *pOut ) |
|
{ |
|
BaseClass::Write( pOut ); |
|
|
|
pOut->SetString( "map", m_szMapName ); |
|
pOut->SetInt( "session", m_hSession ); |
|
pOut->SetInt( "spawn_tick", m_nSpawnTick ); |
|
pOut->SetInt( "death_tick", m_nDeathTick ); |
|
pOut->SetInt( "status", static_cast< int >( m_nStatus ) ); |
|
pOut->SetInt( "complete", static_cast< int >( m_bComplete ) ); |
|
pOut->SetFloat( "length", m_flLength ); |
|
pOut->SetInt( "postdeathrecordtime", m_nPostDeathRecordTime ); |
|
pOut->SetInt( "rendered", m_bRendered ); |
|
pOut->SetInt( "player_slot", m_nPlayerSlot ); |
|
pOut->SetInt( "max_block", m_iMaxSessionBlockRequired ); |
|
pOut->SetFloat( "start_time", m_flStartTime ); |
|
pOut->SetWString( "title", m_wszTitle ); |
|
|
|
// Store only filename for reconstructed .dem |
|
if ( !m_strReconstructedFilename.IsEmpty() ) |
|
{ |
|
const char *pReconFilename = V_UnqualifiedFileName( m_strReconstructedFilename.Get() ); |
|
if ( pReconFilename[0] ) |
|
{ |
|
pOut->SetString( "recon_filename", pReconFilename ); |
|
} |
|
} |
|
|
|
// Write screenshots |
|
KeyValues *pScreenshots = new KeyValues( "screenshots" ); |
|
pOut->AddSubKey( pScreenshots ); |
|
for ( int i = 0; i < m_vecScreenshots.Count(); ++i ) |
|
{ |
|
KeyValues *pScreenshotOut = new KeyValues( "screenshot" ); |
|
CReplayScreenshot *pScreenshot = m_vecScreenshots[ i ]; |
|
pScreenshotOut->SetInt( "width", pScreenshot->m_nWidth ); |
|
pScreenshotOut->SetInt( "height", pScreenshot->m_nHeight ); |
|
pScreenshotOut->SetString( "base_filename", pScreenshot->m_szBaseFilename ); |
|
pScreenshots->AddSubKey( pScreenshotOut ); |
|
} |
|
|
|
// Write performances |
|
KeyValues *pPerformances = new KeyValues( "edits" ); |
|
pOut->AddSubKey( pPerformances ); |
|
for ( int i = 0; i < m_vecPerformances.Count(); ++i ) |
|
{ |
|
KeyValues *pPerfOut = new KeyValues( "edit" ); |
|
CReplayPerformance *pPerformance = m_vecPerformances[ i ]; |
|
pPerformance->Write( pPerfOut ); |
|
pPerformances->AddSubKey( pPerfOut ); |
|
} |
|
|
|
KeyValues *pRecordTime = new KeyValues( "record_time" ); |
|
pOut->AddSubKey( pRecordTime ); |
|
m_RecordTime.Write( pRecordTime ); |
|
|
|
// Mark as saved |
|
m_bSaved = true; |
|
} |
|
|
|
bool CReplay::HasReconstructedReplay() const |
|
{ |
|
return !m_strReconstructedFilename.IsEmpty() && |
|
g_pFullFileSystem->FileExists( m_strReconstructedFilename.Get() ); |
|
} |
|
|
|
bool CReplay::IsSignificantBlock( int iBlockReconstruction ) const |
|
{ |
|
return iBlockReconstruction <= m_iMaxSessionBlockRequired; |
|
} |
|
|
|
void CReplay::OnComplete() |
|
{ |
|
AutoNameTitleIfEmpty(); |
|
} |
|
|
|
//----------------------------------------------------------------------------------------
|
|
|