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.

527 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A simple application demonstrating the HL2 demo file format ( subject to change!!! )
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <windows.h>
  8. #include "tier0/dbg.h"
  9. #include "filesystem.h"
  10. #include "FileSystem_Tools.h"
  11. #include "cmdlib.h"
  12. #include "tooldemofile.h"
  13. static bool uselogfile = false;
  14. static bool spewed = false;
  15. #define LOGFILE_NAME "log.txt"
  16. #define COM_COPY_CHUNK_SIZE 8192
  17. //-----------------------------------------------------------------------------
  18. // Purpose: Prints to stdout and to the developer console and optionally to a log file
  19. // Input : depth -
  20. // *fmt -
  21. // ... -
  22. //-----------------------------------------------------------------------------
  23. void vprint( int depth, const char *fmt, ... )
  24. {
  25. char string[ 8192 ];
  26. va_list va;
  27. va_start( va, fmt );
  28. vsprintf( string, fmt, va );
  29. va_end( va );
  30. FILE *fp = NULL;
  31. if ( uselogfile )
  32. {
  33. fp = fopen( LOGFILE_NAME, "ab" );
  34. }
  35. while ( depth-- > 0 )
  36. {
  37. printf( " " );
  38. OutputDebugString( " " );
  39. if ( fp )
  40. {
  41. fprintf( fp, " " );
  42. }
  43. }
  44. ::printf( "%s", string );
  45. OutputDebugString( string );
  46. if ( fp )
  47. {
  48. char *p = string;
  49. while ( *p )
  50. {
  51. if ( *p == '\n' )
  52. {
  53. fputc( '\r', fp );
  54. }
  55. fputc( *p, fp );
  56. p++;
  57. }
  58. fclose( fp );
  59. }
  60. }
  61. //-----------------------------------------------------------------------------
  62. // Purpose: Warning/Msg call back through this API
  63. // Input : type -
  64. // *pMsg -
  65. // Output : SpewRetval_t
  66. //-----------------------------------------------------------------------------
  67. SpewRetval_t SpewFunc( SpewType_t type, char const *pMsg )
  68. {
  69. spewed = true;
  70. switch ( type )
  71. {
  72. default:
  73. case SPEW_MESSAGE:
  74. case SPEW_ASSERT:
  75. case SPEW_LOG:
  76. vprint( 0, "%s", pMsg );
  77. break;
  78. case SPEW_WARNING:
  79. if ( verbose )
  80. {
  81. vprint( 0, "%s", pMsg );
  82. }
  83. break;
  84. case SPEW_ERROR:
  85. vprint( 0, "%s\n", pMsg );
  86. break;
  87. }
  88. return SPEW_CONTINUE;
  89. }
  90. //-----------------------------------------------------------------------------
  91. // Purpose: Shows usage information
  92. //-----------------------------------------------------------------------------
  93. void printusage( void )
  94. {
  95. vprint( 0, "usage: demoinfo <.dem file>\n\
  96. \t-v = verbose output\n\
  97. \t-l = log to file log.txt\n\
  98. \ne.g.: demoinfo -v u:/hl2/hl2/foo.dem\n" );
  99. // Exit app
  100. exit( 1 );
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose: Removes previous log file
  104. //-----------------------------------------------------------------------------
  105. void CheckLogFile( void )
  106. {
  107. if ( uselogfile )
  108. {
  109. _unlink( LOGFILE_NAME );
  110. vprint( 0, " Outputting to log.txt\n" );
  111. }
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Purpose: Prints banner
  115. //-----------------------------------------------------------------------------
  116. void PrintHeader()
  117. {
  118. vprint( 0, "Valve Software - demoinfo.exe (%s)\n", __DATE__ );
  119. vprint( 0, "--- Demo File Info Sample ---\n" );
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Parses all "smoothing" info from .dem file
  123. // Input : &demoFile -
  124. // smooth -
  125. //-----------------------------------------------------------------------------
  126. void ParseSmoothingInfo( CToolDemoFile &demoFile, CUtlVector< demosmoothing_t >& smooth )
  127. {
  128. democmdinfo_t info;
  129. int dummy;
  130. bool demofinished = false;
  131. while ( !demofinished )
  132. {
  133. int tick = 0;
  134. byte cmd;
  135. bool swallowmessages = true;
  136. do
  137. {
  138. demoFile.ReadCmdHeader( cmd, tick );
  139. // COMMAND HANDLERS
  140. switch ( cmd )
  141. {
  142. case dem_synctick:
  143. break;
  144. case dem_stop:
  145. {
  146. swallowmessages = false;
  147. demofinished = true;
  148. }
  149. break;
  150. case dem_consolecmd:
  151. {
  152. demoFile.ReadConsoleCommand();
  153. }
  154. break;
  155. case dem_datatables:
  156. {
  157. demoFile.ReadNetworkDataTables( NULL );
  158. }
  159. break;
  160. case dem_usercmd:
  161. {
  162. demoFile.ReadUserCmd( NULL, dummy );
  163. }
  164. break;
  165. default:
  166. {
  167. swallowmessages = false;
  168. }
  169. break;
  170. }
  171. }
  172. while ( swallowmessages );
  173. if ( demofinished )
  174. {
  175. // StopPlayback();
  176. return;
  177. }
  178. int curpos = demoFile.GetCurPos();
  179. demoFile.ReadCmdInfo( info );
  180. demoFile.ReadSequenceInfo( dummy, dummy );
  181. demoFile.ReadRawData( NULL, 0 );
  182. demosmoothing_t smoothing_entry;
  183. smoothing_entry.file_offset = curpos;
  184. smoothing_entry.frametick = tick;
  185. smoothing_entry.info = info;
  186. smoothing_entry.samplepoint = false;
  187. smoothing_entry.vecmoved = info.GetViewOrigin();
  188. smoothing_entry.angmoved = info.GetViewAngles();
  189. smoothing_entry.targetpoint = false;
  190. smoothing_entry.vectarget = info.GetViewOrigin();
  191. // Add to end of list
  192. smooth.AddToTail( smoothing_entry );
  193. }
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose: Resets all smoothing data back to original values
  197. // Input : smoothing -
  198. //-----------------------------------------------------------------------------
  199. void ClearSmoothingInfo( CSmoothingContext& smoothing )
  200. {
  201. int c = smoothing.smooth.Count();
  202. int i;
  203. for ( i = 0; i < c; i++ )
  204. {
  205. demosmoothing_t *p = &smoothing.smooth[ i ];
  206. p->info.Reset();
  207. p->vecmoved = p->info.GetViewOrigin();
  208. p->angmoved = p->info.GetViewAngles();
  209. p->samplepoint = false;
  210. p->vectarget = p->info.GetViewOrigin();
  211. p->targetpoint = false;
  212. }
  213. }
  214. //-----------------------------------------------------------------------------
  215. // Purpose: Helper for copying sub-chunk of file
  216. // Input : dst -
  217. // src -
  218. // nSize -
  219. //-----------------------------------------------------------------------------
  220. void COM_CopyFileChunk( FileHandle_t dst, FileHandle_t src, int nSize )
  221. {
  222. int copysize = nSize;
  223. char copybuf[COM_COPY_CHUNK_SIZE];
  224. while (copysize > COM_COPY_CHUNK_SIZE)
  225. {
  226. g_pFileSystem->Read ( copybuf, COM_COPY_CHUNK_SIZE, src );
  227. g_pFileSystem->Write( copybuf, COM_COPY_CHUNK_SIZE, dst );
  228. copysize -= COM_COPY_CHUNK_SIZE;
  229. }
  230. g_pFileSystem->Read ( copybuf, copysize, src );
  231. g_pFileSystem->Write( copybuf, copysize, dst );
  232. g_pFileSystem->Flush ( src );
  233. g_pFileSystem->Flush ( dst );
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Purpose: Writes out a new .dem file based on the existing dem file with new camera positions saved into the dem file
  237. // Note: The new file is named filename_smooth.dem
  238. // Input : *filename -
  239. // smoothing -
  240. //-----------------------------------------------------------------------------
  241. void SaveSmoothingInfo( char const *filename, CSmoothingContext& smoothing )
  242. {
  243. // Nothing to do
  244. int c = smoothing.smooth.Count();
  245. if ( !c )
  246. return;
  247. IBaseFileSystem *fs = g_pFileSystem;
  248. FileHandle_t infile, outfile;
  249. infile = fs->Open( filename, "rb", "GAME" );
  250. if ( infile == FILESYSTEM_INVALID_HANDLE )
  251. return;
  252. int filesize = fs->Size( infile );
  253. char outfilename[ 512 ];
  254. Q_StripExtension( filename, outfilename, sizeof( outfilename ) );
  255. Q_strncat( outfilename, "_smooth", sizeof(outfilename), COPY_ALL_CHARACTERS );
  256. Q_DefaultExtension( outfilename, ".dem", sizeof( outfilename ) );
  257. outfile = fs->Open( outfilename, "wb", "GAME" );
  258. if ( outfile == FILESYSTEM_INVALID_HANDLE )
  259. {
  260. fs->Close( infile );
  261. return;
  262. }
  263. int i;
  264. // The basic algorithm is to seek to each sample and "overwrite" it during copy with the new data...
  265. int lastwritepos = 0;
  266. for ( i = 0; i < c; i++ )
  267. {
  268. demosmoothing_t *p = &smoothing.smooth[ i ];
  269. int copyamount = p->file_offset - lastwritepos;
  270. COM_CopyFileChunk( outfile, infile, copyamount );
  271. fs->Seek( infile, p->file_offset, FILESYSTEM_SEEK_HEAD );
  272. // wacky hacky overwriting
  273. fs->Write( &p->info, sizeof( democmdinfo_t ), outfile );
  274. lastwritepos = fs->Tell( outfile );
  275. fs->Seek( infile, p->file_offset + sizeof( democmdinfo_t ), FILESYSTEM_SEEK_HEAD );
  276. }
  277. // Copy the final bit of data, if any...
  278. int final = filesize - lastwritepos;
  279. COM_CopyFileChunk( outfile, infile, final );
  280. fs->Close( outfile );
  281. fs->Close( infile );
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose: Helper for spewing verbose sample information
  285. // Input : flags -
  286. // Output : char const
  287. //-----------------------------------------------------------------------------
  288. char const *DescribeFlags( int flags )
  289. {
  290. static char outbuf[ 256 ];
  291. outbuf[ 0 ] = 0;
  292. if ( flags & FDEMO_USE_ORIGIN2 )
  293. {
  294. Q_strncat( outbuf, "USE_ORIGIN2, ", sizeof( outbuf ), COPY_ALL_CHARACTERS );
  295. }
  296. if ( flags & FDEMO_USE_ANGLES2 )
  297. {
  298. Q_strncat( outbuf, "USE_ANGLES2, ", sizeof( outbuf ), COPY_ALL_CHARACTERS );
  299. }
  300. if ( flags & FDEMO_NOINTERP )
  301. {
  302. Q_strncat( outbuf, "NOINTERP, ", sizeof( outbuf ), COPY_ALL_CHARACTERS );
  303. }
  304. int len = Q_strlen( outbuf );
  305. if ( len > 2 )
  306. {
  307. outbuf[ len - 2 ] = 0;
  308. }
  309. else
  310. {
  311. Q_strncpy( outbuf, "N/A", sizeof( outbuf ) );
  312. }
  313. return outbuf;
  314. }
  315. //-----------------------------------------------------------------------------
  316. // Purpose: Loads up all camera samples from a .dem file into the passed in context.
  317. // Input : *filename -
  318. // smoothing -
  319. //-----------------------------------------------------------------------------
  320. void LoadSmoothingInfo( const char *filename, CSmoothingContext& smoothing )
  321. {
  322. char name[ MAX_OSPATH ];
  323. Q_strncpy (name, filename, sizeof(name) );
  324. Q_DefaultExtension( name, ".dem", sizeof( name ) );
  325. CToolDemoFile demoFile;
  326. if ( !demoFile.Open( filename, true ) )
  327. {
  328. Warning( "ERROR: couldn't open %s.\n", name );
  329. return;
  330. }
  331. demoheader_t * header = demoFile.ReadDemoHeader();
  332. if ( !header )
  333. {
  334. demoFile.Close();
  335. return;
  336. }
  337. Msg( "\n\n" );
  338. Msg( "--------------------------------------------------------------\n" );
  339. Msg( "demofilestamp: '%s'\n", header->demofilestamp );
  340. Msg( "demoprotocol: %i\n", header->demoprotocol );
  341. Msg( "networkprotocol: %i\n", header->networkprotocol );
  342. Msg( "servername: '%s'\n", header->servername );
  343. Msg( "clientname: '%s'\n", header->clientname );
  344. Msg( "mapname: '%s'\n", header->mapname );
  345. Msg( "gamedirectory: '%s'\n", header->gamedirectory );
  346. Msg( "playback_time: %f seconds\n", header->playback_time );
  347. Msg( "playback_ticks: %i ticks\n", header->playback_ticks );
  348. Msg( "playback_frames: %i frames\n", header->playback_frames );
  349. Msg( "signonlength: %s\n", Q_pretifymem( header->signonlength ) );
  350. smoothing.active = true;
  351. Q_strncpy( smoothing.filename, name, sizeof(smoothing.filename) );
  352. smoothing.smooth.RemoveAll();
  353. ClearSmoothingInfo( smoothing );
  354. ParseSmoothingInfo( demoFile, smoothing.smooth );
  355. Msg( "--------------------------------------------------------------\n" );
  356. Msg( "smoothing data: %i samples\n", smoothing.smooth.Count() );
  357. if ( verbose )
  358. {
  359. int c = smoothing.smooth.Count();
  360. for ( int i = 0; i < c; ++i )
  361. {
  362. demosmoothing_t& sample = smoothing.smooth[ i ];
  363. Msg( "Sample %i:\n", i );
  364. Msg( " file pos: %i\n", sample.file_offset );
  365. Msg( " tick: %i\n", sample.frametick );
  366. Msg( " flags: %s\n", DescribeFlags( sample.info.flags ) );
  367. Msg( " Original Data:\n" );
  368. Msg( " origin: %.4f %.4f %.4f\n", sample.info.viewOrigin.x, sample.info.viewOrigin.y, sample.info.viewOrigin.z );
  369. Msg( " viewangles: %.4f %.4f %.4f\n", sample.info.viewAngles.x, sample.info.viewAngles.y, sample.info.viewAngles.z );
  370. Msg( " localviewangles: %.4f %.4f %.4f\n", sample.info.localViewAngles.x, sample.info.localViewAngles.y, sample.info.localViewAngles.z );
  371. Msg( " Resampled Data:\n" );
  372. Msg( " origin: %.4f %.4f %.4f\n", sample.info.viewOrigin2.x, sample.info.viewOrigin2.y, sample.info.viewOrigin2.z );
  373. Msg( " viewangles: %.4f %.4f %.4f\n", sample.info.viewAngles2.x, sample.info.viewAngles2.y, sample.info.viewAngles2.z );
  374. Msg( " localviewangles: %.4f %.4f %.4f\n", sample.info.localViewAngles2.x, sample.info.localViewAngles2.y, sample.info.localViewAngles2.z );
  375. Msg( "\n" );
  376. }
  377. }
  378. demoFile.Close();
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Purpose:
  382. // Input : argc -
  383. // argv[] -
  384. // Output : int
  385. //-----------------------------------------------------------------------------
  386. int main( int argc, char* argv[] )
  387. {
  388. SpewOutputFunc( SpewFunc );
  389. SpewActivate( "demoinfo", 2 );
  390. int i = 1;
  391. for ( i ; i<argc ; i++)
  392. {
  393. if ( argv[ i ][ 0 ] == '-' )
  394. {
  395. switch( argv[ i ][ 1 ] )
  396. {
  397. case 'l':
  398. uselogfile = true;
  399. break;
  400. case 'v':
  401. verbose = true;
  402. break;
  403. case 'g':
  404. ++i;
  405. break;
  406. default:
  407. printusage();
  408. break;
  409. }
  410. }
  411. }
  412. if ( argc < 2 || ( i != argc ) )
  413. {
  414. PrintHeader();
  415. printusage();
  416. }
  417. CheckLogFile();
  418. PrintHeader();
  419. vprint( 0, " Info for %s..\n", argv[ i - 1 ] );
  420. char workingdir[ 256 ];
  421. workingdir[0] = 0;
  422. Q_getwd( workingdir, sizeof( workingdir ) );
  423. if ( !FileSystem_Init( NULL, 0, FS_INIT_FULL ) )
  424. return 1;
  425. // Add this so relative filenames work.
  426. g_pFullFileSystem->AddSearchPath( workingdir, "game", PATH_ADD_TO_HEAD );
  427. // Load the demo
  428. CSmoothingContext context;
  429. LoadSmoothingInfo( argv[ i - 1 ], context );
  430. // Note to tool makers:
  431. // Do your work here!!!
  432. //Performsmoothing( context );
  433. // Save out updated .dem file
  434. // UNCOMMENT THIS TO ENABLE OUTPUTTING NEW .DEM FILES!!!
  435. // SaveSmoothingInfo( argv[ i - 1 ], context );
  436. FileSystem_Term();
  437. return 0;
  438. }