Team Fortress 2 Source Code as on 22/4/2020
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.

563 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include <tier0/dbg.h>
  7. #include <tier1/strtools.h>
  8. #include <utlbuffer.h>
  9. #include "demofile.h"
  10. #include "filesystem_engine.h"
  11. #include "demo.h"
  12. #include "proto_version.h"
  13. #include "convar.h" // For dbg_demofile
  14. // NOTE: This has to be the last file included!
  15. #include "tier0/memdbgon.h"
  16. void Host_EndGame (bool bShowMainMenu, const char *message, ...);
  17. // Debug helpers - this class prints in a nested format
  18. ConVar dbg_demofile( "dbg_demofile", "0", FCVAR_DEVELOPMENTONLY | FCVAR_HIDDEN );
  19. //#define DEMOFILE_DBG_PRINT
  20. #if defined( DEMOFILE_DBG_PRINT )
  21. class CDbgPrint
  22. {
  23. public:
  24. static int s_nIndent;
  25. CDbgPrint( const char *pMsg )
  26. {
  27. ++s_nIndent;
  28. if ( dbg_demofile.GetInt() )
  29. {
  30. for (int i = 0; i < 3*s_nIndent; ++i)
  31. DevMsg(" ");
  32. DevMsg( pMsg );
  33. }
  34. }
  35. ~CDbgPrint() { --s_nIndent; }
  36. };
  37. int CDbgPrint::s_nIndent = 0;
  38. #define DemoFileDbg(_txt) CDbgPrint printer( _txt )
  39. #else
  40. #define DemoFileDbg(_txt) (void)0
  41. #endif
  42. //////////////////////////////////////////////////////////////////////
  43. // Construction/Destruction
  44. //////////////////////////////////////////////////////////////////////
  45. CDemoFile::CDemoFile() :
  46. m_pBuffer( NULL ),
  47. m_bAllowHeaderWrite( true ),
  48. m_bIsStreamBuffer( false )
  49. {
  50. }
  51. CDemoFile::~CDemoFile()
  52. {
  53. if ( IsOpen() )
  54. {
  55. Close();
  56. }
  57. }
  58. void CDemoFile::WriteSequenceInfo(int nSeqNrIn, int nSeqNrOut)
  59. {
  60. DemoFileDbg( "WriteSequenceInfo()\n" );
  61. Assert( m_pBuffer && m_pBuffer->IsValid() );
  62. m_pBuffer->PutInt( nSeqNrIn );
  63. m_pBuffer->PutInt( nSeqNrOut );
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose:
  67. //-----------------------------------------------------------------------------
  68. void CDemoFile::ReadSequenceInfo(int &nSeqNrIn, int &nSeqNrOut)
  69. {
  70. Assert( m_pBuffer && m_pBuffer->IsValid() );
  71. nSeqNrIn = m_pBuffer->GetInt( );
  72. nSeqNrOut = m_pBuffer->GetInt( );
  73. }
  74. inline void ByteSwap_democmdinfo_t( democmdinfo_t &swap )
  75. {
  76. swap.flags = LittleDWord( swap.flags );
  77. LittleFloat( &swap.viewOrigin.x, &swap.viewOrigin.x );
  78. LittleFloat( &swap.viewOrigin.y, &swap.viewOrigin.y );
  79. LittleFloat( &swap.viewOrigin.z, &swap.viewOrigin.z );
  80. LittleFloat( &swap.viewAngles.x, &swap.viewAngles.x );
  81. LittleFloat( &swap.viewAngles.y, &swap.viewAngles.y );
  82. LittleFloat( &swap.viewAngles.z, &swap.viewAngles.z );
  83. LittleFloat( &swap.localViewAngles.x, &swap.localViewAngles.x );
  84. LittleFloat( &swap.localViewAngles.y, &swap.localViewAngles.y );
  85. LittleFloat( &swap.localViewAngles.z, &swap.localViewAngles.z );
  86. LittleFloat( &swap.viewOrigin2.x, &swap.viewOrigin2.x );
  87. LittleFloat( &swap.viewOrigin2.y, &swap.viewOrigin2.y );
  88. LittleFloat( &swap.viewOrigin2.z, &swap.viewOrigin2.z );
  89. LittleFloat( &swap.viewAngles2.x, &swap.viewAngles2.x );
  90. LittleFloat( &swap.viewAngles2.y, &swap.viewAngles2.y );
  91. LittleFloat( &swap.viewAngles2.z, &swap.viewAngles2.z );
  92. LittleFloat( &swap.localViewAngles2.x, &swap.localViewAngles2.x );
  93. LittleFloat( &swap.localViewAngles2.y, &swap.localViewAngles2.y );
  94. LittleFloat( &swap.localViewAngles2.z, &swap.localViewAngles2.z );
  95. }
  96. void CDemoFile::WriteCmdInfo( democmdinfo_t& info )
  97. {
  98. DemoFileDbg( "WriteCmdInfo()\n" );
  99. democmdinfo_t littleEndianInfo = info;
  100. ByteSwap_democmdinfo_t( littleEndianInfo );
  101. Assert( m_pBuffer && m_pBuffer->IsValid() );
  102. m_pBuffer->Put( &littleEndianInfo, sizeof(democmdinfo_t) );
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Purpose:
  106. //-----------------------------------------------------------------------------
  107. void CDemoFile::ReadCmdInfo( democmdinfo_t& info )
  108. {
  109. Assert( m_pBuffer && m_pBuffer->IsValid() );
  110. m_pBuffer->Get( &info, sizeof(democmdinfo_t) );
  111. ByteSwap_democmdinfo_t( info );
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Purpose:
  115. // Input : cmd -
  116. // *fp -
  117. //-----------------------------------------------------------------------------
  118. void CDemoFile::WriteCmdHeader( unsigned char cmd, int tick )
  119. {
  120. if ( dbg_demofile.GetInt() ) DevMsg( "----------------------------------------\n" );
  121. Assert( cmd >= dem_signon && cmd <= dem_lastcmd );
  122. Assert( m_pBuffer && m_pBuffer->IsValid() );
  123. m_pBuffer->PutUnsignedChar( cmd );
  124. m_pBuffer->PutInt( tick );
  125. static const char *cmdname[] =
  126. {
  127. "dem_unknown",
  128. "dem_signon",
  129. "dem_packet",
  130. "dem_synctick",
  131. "dem_consolecmd",
  132. "dem_usercmd",
  133. "dem_datatables",
  134. "dem_stop",
  135. "dem_stringtables"
  136. };
  137. DemoFileDbg( "WriteCmdHeader()..." );
  138. if ( dbg_demofile.GetInt() ) DevMsg( "tick %i, cmd %s \n", tick, cmdname[cmd] );
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose:
  142. // Input : cmd -
  143. // dt -
  144. // frame -
  145. //-----------------------------------------------------------------------------
  146. void CDemoFile::ReadCmdHeader( unsigned char& cmd, int& tick )
  147. {
  148. Assert( m_pBuffer && m_pBuffer->IsValid() );
  149. cmd = m_pBuffer->GetUnsignedChar( );
  150. if ( !m_pBuffer || !m_pBuffer->IsValid() )
  151. {
  152. ConDMsg("Missing end tag in demo file.\n");
  153. cmd = dem_stop;
  154. return;
  155. }
  156. if ( cmd <= 0 || cmd > dem_lastcmd )
  157. {
  158. ConDMsg("Unexepcted command token [%d] in .demo file\n", cmd );
  159. cmd = dem_stop;
  160. return;
  161. }
  162. tick = m_pBuffer->GetInt( );
  163. }
  164. void CDemoFile::WriteConsoleCommand( const char *cmdstring, int tick )
  165. {
  166. DemoFileDbg( "WriteConsoleCommand()\n" );
  167. if ( !cmdstring || !cmdstring[0] )
  168. return;
  169. if ( !m_pBuffer || !m_pBuffer->IsValid() )
  170. return;
  171. int len = Q_strlen( cmdstring ) + 1;
  172. if ( len >= 1024 )
  173. {
  174. DevMsg("CDemoFile::WriteConsoleCommand: command too long (>1024).\n");
  175. return;
  176. }
  177. WriteCmdHeader( dem_consolecmd, tick );
  178. WriteRawData( cmdstring, len );
  179. }
  180. const char *CDemoFile::ReadConsoleCommand()
  181. {
  182. static char cmdstring[1024];
  183. ReadRawData( cmdstring, sizeof(cmdstring) );
  184. return cmdstring;
  185. }
  186. unsigned int CDemoFile::GetCurPos( bool bRead )
  187. {
  188. if ( !m_pBuffer || !m_pBuffer->IsValid() )
  189. return 0;
  190. if ( bRead )
  191. return m_pBuffer->TellGet();
  192. return m_pBuffer->TellPut();
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose:
  196. // Input : buf -
  197. //-----------------------------------------------------------------------------
  198. void CDemoFile::WriteNetworkDataTables( bf_write *buf, int tick )
  199. {
  200. DemoFileDbg( "WriteNetworkDataTables()\n" );
  201. MEM_ALLOC_CREDIT();
  202. if ( !m_pBuffer || !m_pBuffer->IsValid() )
  203. {
  204. DevMsg("CDemoFile::WriteNetworkDataTables: Haven't opened file yet!\n" );
  205. return;
  206. }
  207. WriteCmdHeader( dem_datatables, tick );
  208. WriteRawData( (char*)buf->GetBasePointer(), buf->GetNumBytesWritten() );
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Purpose:
  212. // Input : expected_length -
  213. // &demofile -
  214. //-----------------------------------------------------------------------------
  215. int CDemoFile::ReadNetworkDataTables( bf_read *buf )
  216. {
  217. if ( buf )
  218. return ReadRawData( (char*)buf->GetBasePointer(), buf->GetNumBytesLeft() );
  219. return ReadRawData( NULL, 0 ); // skip data
  220. }
  221. void CDemoFile::WriteStringTables( bf_write *buf, int tick )
  222. {
  223. DemoFileDbg( "WriteStringTables()\n" );
  224. MEM_ALLOC_CREDIT();
  225. if ( !m_pBuffer || !m_pBuffer->IsValid() )
  226. {
  227. DevMsg("CDemoFile::WriteStringTables: Haven't opened file yet!\n" );
  228. return;
  229. }
  230. WriteCmdHeader( dem_stringtables, tick );
  231. WriteRawData( (char*)buf->GetBasePointer(), buf->GetNumBytesWritten() );
  232. }
  233. int CDemoFile::ReadStringTables( bf_read *buf )
  234. {
  235. if ( buf )
  236. return ReadRawData( (char*)buf->GetBasePointer(), buf->GetNumBytesLeft() );
  237. return ReadRawData( NULL, 0 ); // skip data
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose:
  241. // Input : cmdnumber -
  242. //-----------------------------------------------------------------------------
  243. void CDemoFile::WriteUserCmd( int cmdnumber, const char *buffer, unsigned char bytes, int tick )
  244. {
  245. DemoFileDbg( "WriteUserCmd()\n" );
  246. if ( !m_pBuffer || !m_pBuffer->IsValid() )
  247. return;
  248. WriteCmdHeader( dem_usercmd, tick );
  249. m_pBuffer->PutInt( cmdnumber );
  250. WriteRawData( buffer, bytes );
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Purpose:
  254. // Input : discard -
  255. //-----------------------------------------------------------------------------
  256. int CDemoFile::ReadUserCmd( char *buffer, int &size )
  257. {
  258. int outgoing_sequence;
  259. Assert( m_pBuffer && m_pBuffer->IsValid() );
  260. outgoing_sequence = m_pBuffer->GetInt();
  261. size = ReadRawData( buffer, size );
  262. return outgoing_sequence;
  263. }
  264. //
  265. // Purpose: Rewind from the current spot by the time stamp, byte code and frame counter offsets
  266. //-----------------------------------------------------------------------------
  267. void CDemoFile::SeekTo( int position, bool bRead )
  268. {
  269. Assert( m_pBuffer && m_pBuffer->IsValid() );
  270. if ( bRead )
  271. {
  272. m_pBuffer->SeekGet( CUtlBuffer::SEEK_HEAD, position );
  273. }
  274. else
  275. {
  276. m_pBuffer->SeekPut( CUtlBuffer::SEEK_HEAD, position );
  277. }
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Purpose:
  281. // Output : Returns true on success, false on failure.
  282. //-----------------------------------------------------------------------------
  283. int CDemoFile::ReadRawData( char *buffer, int length )
  284. {
  285. int size;
  286. Assert( m_pBuffer && m_pBuffer->IsValid() );
  287. if ( !m_pBuffer || !m_pBuffer->IsValid() )
  288. {
  289. Host_EndGame(true, "Error reading demo message data.\n");
  290. return -1;
  291. }
  292. size = m_pBuffer->GetInt();
  293. if ( !buffer )
  294. {
  295. // just skip it
  296. m_pBuffer->SeekGet( CUtlBuffer::SEEK_CURRENT, size );
  297. return size;
  298. }
  299. if ( length < size )
  300. {
  301. // given buffer is too small
  302. DevMsg("CDemoFile::ReadRawData: buffer overflow (%i).\n", size );
  303. m_pBuffer->SeekGet( CUtlBuffer::SEEK_CURRENT, -(int)sizeof( int ) ); // rewind our get pointer
  304. return -1;
  305. }
  306. // read data into buffer
  307. m_pBuffer->Get( buffer, size );
  308. return size;
  309. }
  310. void CDemoFile::WriteRawData( const char *buffer, int length )
  311. {
  312. DemoFileDbg( "WriteRawData()\n" );
  313. MEM_ALLOC_CREDIT();
  314. Assert( m_pBuffer && m_pBuffer->IsValid() );
  315. m_pBuffer->PutInt( length );
  316. m_pBuffer->Put( buffer, length );
  317. }
  318. void CDemoFile::WriteDemoHeader()
  319. {
  320. if ( !m_bAllowHeaderWrite )
  321. return;
  322. DemoFileDbg( "WriteDemoHeader()\n" );
  323. Assert( m_DemoHeader.networkprotocol == PROTOCOL_VERSION );
  324. if ( dbg_demofile.GetInt() )
  325. {
  326. DevMsg( "\n" );
  327. DevMsg( " demofilestamp: %s\n", m_DemoHeader.demofilestamp );
  328. DevMsg( " demoprotocol (should be %i): %i\n", DEMO_PROTOCOL, m_DemoHeader.demoprotocol );
  329. DevMsg( " networkprotocol (should be %i): %i\n", PROTOCOL_VERSION, m_DemoHeader.networkprotocol );
  330. DevMsg( " servername: %s\n", m_DemoHeader.servername );
  331. DevMsg( " clientname: %s\n", m_DemoHeader.clientname );
  332. DevMsg( " mapname: %s\n", m_DemoHeader.mapname );
  333. DevMsg( " gamedirectory: %s\n", m_DemoHeader.gamedirectory );
  334. DevMsg( " playback_time: %f\n", m_DemoHeader.playback_time );
  335. DevMsg( " playback_ticks: %i\n", m_DemoHeader.playback_ticks );
  336. DevMsg( " playback_frames: %i\n", m_DemoHeader.playback_frames );
  337. DevMsg( " signonlength: %i\n", m_DemoHeader.signonlength );
  338. DevMsg( "\n" );
  339. }
  340. // Swaps endianness, goes to file start and writes header
  341. demoheader_t littleEndianHeader = *((demoheader_t*)&m_DemoHeader);
  342. ByteSwap_demoheader_t( littleEndianHeader );
  343. // Goto file start
  344. m_pBuffer->SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
  345. // Write
  346. m_pBuffer->Put( &m_DemoHeader, sizeof( m_DemoHeader ) );
  347. }
  348. demoheader_t *CDemoFile::ReadDemoHeader()
  349. {
  350. bool bOk;
  351. Q_memset( &m_DemoHeader, 0, sizeof(m_DemoHeader) );
  352. if ( !m_pBuffer || !m_pBuffer->IsValid() )
  353. return NULL;
  354. m_pBuffer->SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  355. m_pBuffer->Get( &m_DemoHeader, sizeof(demoheader_t) );
  356. bOk = m_pBuffer->IsValid();
  357. ByteSwap_demoheader_t( m_DemoHeader );
  358. if ( !bOk )
  359. return NULL; // reading failed
  360. if ( Q_strcmp( m_DemoHeader.demofilestamp, DEMO_HEADER_ID ) )
  361. {
  362. ConMsg( "%s has invalid demo header ID.\n", m_szFileName );
  363. return NULL;
  364. }
  365. if ( m_DemoHeader.networkprotocol != PROTOCOL_VERSION
  366. #if defined( DEMO_BACKWARDCOMPATABILITY )
  367. && m_DemoHeader.networkprotocol < PROTOCOL_VERSION_12
  368. #endif
  369. )
  370. {
  371. ConMsg ("ERROR: demo network protocol %i outdated, engine version is %i \n",
  372. m_DemoHeader.networkprotocol, PROTOCOL_VERSION );
  373. return NULL;
  374. }
  375. if ( ( m_DemoHeader.demoprotocol > DEMO_PROTOCOL) ||
  376. ( m_DemoHeader.demoprotocol < 2 ) )
  377. {
  378. ConMsg ("ERROR: demo file protocol %i outdated, engine vnoteersion is %i \n",
  379. m_DemoHeader.demoprotocol, DEMO_PROTOCOL );
  380. return NULL;
  381. }
  382. return &m_DemoHeader;
  383. }
  384. void CDemoFile::WriteFileBytes( FileHandle_t fh, int length )
  385. {
  386. DemoFileDbg( "WriteFileBytes()\n" );
  387. int copysize = length;
  388. char copybuf[COM_COPY_CHUNK_SIZE];
  389. while ( copysize > COM_COPY_CHUNK_SIZE )
  390. {
  391. g_pFileSystem->Read ( copybuf, COM_COPY_CHUNK_SIZE, fh );
  392. m_pBuffer->Put( copybuf, COM_COPY_CHUNK_SIZE );
  393. copysize -= COM_COPY_CHUNK_SIZE;
  394. }
  395. g_pFileSystem->Read ( copybuf, copysize, fh );
  396. m_pBuffer->Put( copybuf, copysize );
  397. g_pFileSystem->Flush ( fh );
  398. }
  399. bool CDemoFile::Open(const char *name, bool bReadOnly, bool bMemoryBuffer, int nBufferSize/*=0*/, bool bAllowHeaderWrite/*=true*/)
  400. {
  401. if ( m_pBuffer && m_pBuffer->IsValid() )
  402. {
  403. ConMsg ("CDemoFile::Open: file already open.\n");
  404. return false;
  405. }
  406. m_szFileName[0] = 0; // clear name
  407. Q_memset( &m_DemoHeader, 0, sizeof(m_DemoHeader) ); // and demo header
  408. // This is used by replay, which manually writes a header.
  409. m_bAllowHeaderWrite = bAllowHeaderWrite;
  410. if ( bMemoryBuffer )
  411. {
  412. Assert( !bReadOnly ); // Only read from files
  413. Assert( nBufferSize > 0 );
  414. m_pBuffer = new CUtlBuffer( nBufferSize, nBufferSize, 0 );
  415. m_bIsStreamBuffer = false;
  416. }
  417. else
  418. {
  419. m_pBuffer = new CUtlStreamBuffer( name, NULL, bReadOnly ? CUtlBuffer::READ_ONLY : 0, false );
  420. m_bIsStreamBuffer = true;
  421. }
  422. // Demo files are always little endian
  423. m_pBuffer->SetBigEndian( false );
  424. if ( !m_pBuffer || !m_pBuffer->IsValid() )
  425. {
  426. ConMsg ("CDemoFile::Open: couldn't open file %s for %s.\n",
  427. name, bReadOnly?"reading":"writing" );
  428. Close();
  429. return false;
  430. }
  431. if ( name )
  432. {
  433. Q_strncpy( m_szFileName, name, sizeof(m_szFileName) );
  434. }
  435. return true;
  436. }
  437. bool CDemoFile::IsOpen()
  438. {
  439. return m_pBuffer && m_pBuffer->IsValid();
  440. }
  441. void CDemoFile::Close()
  442. {
  443. // CUtlBuffer base class does NOT have a virtual destructor!
  444. if ( m_bIsStreamBuffer )
  445. {
  446. // Destructor will call Close() as needed
  447. delete static_cast<CUtlStreamBuffer*>(m_pBuffer);
  448. }
  449. else
  450. {
  451. delete m_pBuffer;
  452. }
  453. m_pBuffer = NULL;
  454. }
  455. int CDemoFile::GetSize()
  456. {
  457. return m_pBuffer->TellMaxPut();
  458. }
  459. // Returns the PROTOCOL_VERSION used when .dem was recorded
  460. int CDemoFile::GetProtocolVersion()
  461. {
  462. return m_DemoHeader.networkprotocol;
  463. }