DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Common_demos.cpp (13375B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #include "../idlib/precompiled.h"
     30 #pragma hdrstop
     31 
     32 #include "Common_local.h"
     33 
     34 /*
     35 ================
     36 FindUnusedFileName
     37 ================
     38 */
     39 static idStr FindUnusedFileName( const char *format ) {
     40 	idStr filename;
     41 
     42 	for ( int i = 0 ; i < 999 ; i++ ) {
     43 		filename.Format( format, i );
     44 		int len = fileSystem->ReadFile( filename, NULL, NULL );
     45 		if ( len <= 0 ) {
     46 			return filename;	// file doesn't exist
     47 		}
     48 	}
     49 
     50 	return filename;
     51 }
     52 
     53 /*
     54 ================
     55 idCommonLocal::StartRecordingRenderDemo
     56 ================
     57 */
     58 void idCommonLocal::StartRecordingRenderDemo( const char *demoName ) {
     59 	if ( writeDemo ) {
     60 		// allow it to act like a toggle
     61 		StopRecordingRenderDemo();
     62 		return;
     63 	}
     64 
     65 	if ( !demoName[0] ) {
     66 		common->Printf( "idCommonLocal::StartRecordingRenderDemo: no name specified\n" );
     67 		return;
     68 	}
     69 
     70 	console->Close();
     71 
     72 	writeDemo = new (TAG_SYSTEM) idDemoFile;
     73 	if ( !writeDemo->OpenForWriting( demoName ) ) {
     74 		common->Printf( "error opening %s\n", demoName );
     75 		delete writeDemo;
     76 		writeDemo = NULL;
     77 		return;
     78 	}
     79 
     80 	common->Printf( "recording to %s\n", writeDemo->GetName() );
     81 
     82 	writeDemo->WriteInt( DS_VERSION );
     83 	writeDemo->WriteInt( RENDERDEMO_VERSION );
     84 
     85 	// if we are in a map already, dump the current state
     86 	soundWorld->StartWritingDemo( writeDemo );
     87 	renderWorld->StartWritingDemo( writeDemo );
     88 }
     89 
     90 /*
     91 ================
     92 idCommonLocal::StopRecordingRenderDemo
     93 ================
     94 */
     95 void idCommonLocal::StopRecordingRenderDemo() {
     96 	if ( !writeDemo ) {
     97 		common->Printf( "idCommonLocal::StopRecordingRenderDemo: not recording\n" );
     98 		return;
     99 	}
    100 	soundWorld->StopWritingDemo();
    101 	renderWorld->StopWritingDemo();
    102 
    103 	writeDemo->Close();
    104 	common->Printf( "stopped recording %s.\n", writeDemo->GetName() );
    105 	delete writeDemo;
    106 	writeDemo = NULL;
    107 }
    108 
    109 /*
    110 ================
    111 idCommonLocal::StopPlayingRenderDemo
    112 
    113 Reports timeDemo numbers and finishes any avi recording
    114 ================
    115 */
    116 void idCommonLocal::StopPlayingRenderDemo() {
    117 	if ( !readDemo ) {
    118 		timeDemo = TD_NO;
    119 		return;
    120 	}
    121 
    122 	// Record the stop time before doing anything that could be time consuming 
    123 	int timeDemoStopTime = Sys_Milliseconds();
    124 
    125 	EndAVICapture();
    126 
    127 	readDemo->Close();
    128 
    129 	soundWorld->StopAllSounds();
    130 	soundSystem->SetPlayingSoundWorld( menuSoundWorld );
    131 
    132 	common->Printf( "stopped playing %s.\n", readDemo->GetName() );
    133 	delete readDemo;
    134 	readDemo = NULL;
    135 
    136 	if ( timeDemo ) {
    137 		// report the stats
    138 		float	demoSeconds = ( timeDemoStopTime - timeDemoStartTime ) * 0.001f;
    139 		float	demoFPS = numDemoFrames / demoSeconds;
    140 		idStr	message = va( "%i frames rendered in %3.1f seconds = %3.1f fps\n", numDemoFrames, demoSeconds, demoFPS );
    141 
    142 		common->Printf( message );
    143 		if ( timeDemo == TD_YES_THEN_QUIT ) {
    144 			cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
    145 		}
    146 		timeDemo = TD_NO;
    147 	}
    148 }
    149 
    150 /*
    151 ================
    152 idCommonLocal::DemoShot
    153 
    154 A demoShot is a single frame demo
    155 ================
    156 */
    157 void idCommonLocal::DemoShot( const char *demoName ) {
    158 	StartRecordingRenderDemo( demoName );
    159 
    160 	// force draw one frame
    161 	const bool captureToImage = false;
    162 	UpdateScreen( captureToImage );
    163 
    164 	StopRecordingRenderDemo();
    165 }
    166 
    167 /*
    168 ================
    169 idCommonLocal::StartPlayingRenderDemo
    170 ================
    171 */
    172 void idCommonLocal::StartPlayingRenderDemo( idStr demoName ) {
    173 	if ( !demoName[0] ) {
    174 		common->Printf( "idCommonLocal::StartPlayingRenderDemo: no name specified\n" );
    175 		return;
    176 	}
    177 
    178 	// make sure localSound / GUI intro music shuts up
    179 	soundWorld->StopAllSounds();
    180 	soundWorld->PlayShaderDirectly( "", 0 );	
    181 	menuSoundWorld->StopAllSounds();
    182 	menuSoundWorld->PlayShaderDirectly( "", 0 );
    183 
    184 	// exit any current game
    185 	Stop();
    186 
    187 	// automatically put the console away
    188 	console->Close();
    189 
    190 	readDemo = new (TAG_SYSTEM) idDemoFile;
    191 	demoName.DefaultFileExtension( ".demo" );
    192 	if ( !readDemo->OpenForReading( demoName ) ) {
    193 		common->Printf( "couldn't open %s\n", demoName.c_str() );
    194 		delete readDemo;
    195 		readDemo = NULL;
    196 		Stop();
    197 		StartMenu();
    198 		return;
    199 	}
    200 
    201 	const bool captureToImage = false;
    202 	UpdateScreen( captureToImage );
    203 
    204 	AdvanceRenderDemo( true );
    205 
    206 	numDemoFrames = 1;
    207 
    208 	timeDemoStartTime = Sys_Milliseconds();
    209 }
    210 
    211 /*
    212 ================
    213 idCommonLocal::TimeRenderDemo
    214 ================
    215 */
    216 void idCommonLocal::TimeRenderDemo( const char *demoName, bool twice, bool quit ) {
    217 	idStr demo = demoName;
    218 
    219 	StartPlayingRenderDemo( demo );
    220 	
    221 	if ( twice && readDemo ) {
    222 		while ( readDemo ) {
    223 			const bool captureToImage = false;
    224 			UpdateScreen( captureToImage );
    225 			AdvanceRenderDemo( true );
    226 		}
    227 
    228 		StartPlayingRenderDemo( demo );
    229 	}
    230 	
    231 
    232 	if ( !readDemo ) {
    233 		return;
    234 	}
    235 
    236 	if ( quit ) {
    237 		// this allows hardware vendors to automate some testing
    238 		timeDemo = TD_YES_THEN_QUIT;
    239 	} else {
    240 		timeDemo = TD_YES;
    241 	}
    242 }
    243 
    244 
    245 /*
    246 ================
    247 idCommonLocal::BeginAVICapture
    248 ================
    249 */
    250 void idCommonLocal::BeginAVICapture( const char *demoName ) {
    251 	idStr name = demoName;
    252 	name.ExtractFileBase( aviDemoShortName );
    253 	aviCaptureMode = true;
    254 	aviDemoFrameCount = 0;
    255 	soundWorld->AVIOpen( va( "demos/%s/", aviDemoShortName.c_str() ), aviDemoShortName.c_str() );
    256 }
    257 
    258 /*
    259 ================
    260 idCommonLocal::EndAVICapture
    261 ================
    262 */
    263 void idCommonLocal::EndAVICapture() {
    264 	if ( !aviCaptureMode ) {
    265 		return;
    266 	}
    267 
    268 	soundWorld->AVIClose();
    269 
    270 	// write a .roqParam file so the demo can be converted to a roq file
    271 	idFile *f = fileSystem->OpenFileWrite( va( "demos/%s/%s.roqParam", 
    272 		aviDemoShortName.c_str(), aviDemoShortName.c_str() ) );
    273 	f->Printf( "INPUT_DIR demos/%s\n", aviDemoShortName.c_str() );
    274 	f->Printf( "FILENAME demos/%s/%s.RoQ\n", aviDemoShortName.c_str(), aviDemoShortName.c_str() );
    275 	f->Printf( "\nINPUT\n" );
    276 	f->Printf( "%s_*.tga [00000-%05i]\n", aviDemoShortName.c_str(), (int)( aviDemoFrameCount-1 ) );
    277 	f->Printf( "END_INPUT\n" );
    278 	delete f;
    279 
    280 	common->Printf( "captured %i frames for %s.\n", ( int )aviDemoFrameCount, aviDemoShortName.c_str() );
    281 
    282 	aviCaptureMode = false;
    283 }
    284 
    285 
    286 /*
    287 ================
    288 idCommonLocal::AVIRenderDemo
    289 ================
    290 */
    291 void idCommonLocal::AVIRenderDemo( const char *_demoName ) {
    292 	idStr	demoName = _demoName;	// copy off from va() buffer
    293 
    294 	StartPlayingRenderDemo( demoName );
    295 	if ( !readDemo ) {
    296 		return;
    297 	}
    298 
    299 	BeginAVICapture( demoName.c_str() ) ;
    300 
    301 	// I don't understand why I need to do this twice, something
    302 	// strange with the nvidia swapbuffers?
    303 	const bool captureToImage = false;
    304 	UpdateScreen( captureToImage );
    305 }
    306 
    307 /*
    308 ================
    309 idCommonLocal::AVIGame
    310 
    311 Start AVI recording the current game session
    312 ================
    313 */
    314 void idCommonLocal::AVIGame( const char *demoName ) {
    315 	if ( aviCaptureMode ) {
    316 		EndAVICapture();
    317 		return;
    318 	}
    319 
    320 	if ( !mapSpawned ) {
    321 		common->Printf( "No map spawned.\n" );
    322 	}
    323 
    324 	if ( !demoName || !demoName[0] ) {
    325 		idStr filename = FindUnusedFileName( "demos/game%03i.game" );
    326 		demoName = filename.c_str();
    327 
    328 		// write a one byte stub .game file just so the FindUnusedFileName works,
    329 		fileSystem->WriteFile( demoName, demoName, 1 );
    330 	}
    331 
    332 	BeginAVICapture( demoName ) ;
    333 }
    334 
    335 /*
    336 ================
    337 idCommonLocal::CompressDemoFile
    338 ================
    339 */
    340 void idCommonLocal::CompressDemoFile( const char *scheme, const char *demoName ) {
    341 	idStr	fullDemoName = "demos/";
    342 	fullDemoName += demoName;
    343 	fullDemoName.DefaultFileExtension( ".demo" );
    344 	idStr compressedName = fullDemoName;
    345 	compressedName.StripFileExtension();
    346 	compressedName.Append( "_compressed.demo" );
    347 
    348 	int savedCompression = cvarSystem->GetCVarInteger("com_compressDemos");
    349 	bool savedPreload = cvarSystem->GetCVarBool("com_preloadDemos");
    350 	cvarSystem->SetCVarBool( "com_preloadDemos", false );
    351 	cvarSystem->SetCVarInteger("com_compressDemos", atoi(scheme) );
    352 
    353 	idDemoFile demoread, demowrite;
    354 	if ( !demoread.OpenForReading( fullDemoName ) ) {
    355 		common->Printf( "Could not open %s for reading\n", fullDemoName.c_str() );
    356 		return;
    357 	}
    358 	if ( !demowrite.OpenForWriting( compressedName ) ) {
    359 		common->Printf( "Could not open %s for writing\n", compressedName.c_str() );
    360 		demoread.Close();
    361 		cvarSystem->SetCVarBool( "com_preloadDemos", savedPreload );
    362 		cvarSystem->SetCVarInteger("com_compressDemos", savedCompression);
    363 		return;
    364 	}
    365 	common->SetRefreshOnPrint( true );
    366 	common->Printf( "Compressing %s to %s...\n", fullDemoName.c_str(), compressedName.c_str() );
    367 
    368 	static const int bufferSize = 65535;
    369 	char buffer[bufferSize];
    370 	int bytesRead;
    371 	while ( 0 != (bytesRead = demoread.Read( buffer, bufferSize ) ) ) {
    372 		demowrite.Write( buffer, bytesRead );
    373 		common->Printf( "." );
    374 	}
    375 
    376 	demoread.Close();
    377 	demowrite.Close();
    378 
    379 	cvarSystem->SetCVarBool( "com_preloadDemos", savedPreload );
    380 	cvarSystem->SetCVarInteger("com_compressDemos", savedCompression);
    381 
    382 	common->Printf( "Done\n" );
    383 	common->SetRefreshOnPrint( false );
    384 
    385 }
    386 
    387 /*
    388 ===============
    389 idCommonLocal::AdvanceRenderDemo
    390 ===============
    391 */
    392 void idCommonLocal::AdvanceRenderDemo( bool singleFrameOnly ) {
    393 	int	ds = DS_FINISHED;
    394 	readDemo->ReadInt( ds );
    395 
    396 	switch ( ds ) {
    397 	case DS_FINISHED:
    398 		if ( numDemoFrames != 1 ) {
    399 			// if the demo has a single frame (a demoShot), continuously replay
    400 			// the renderView that has already been read
    401 			Stop();
    402 			StartMenu();
    403 		}
    404 		return;
    405 	case DS_RENDER:
    406 		if ( renderWorld->ProcessDemoCommand( readDemo, &currentDemoRenderView, &demoTimeOffset ) ) {
    407 			// a view is ready to render
    408 			numDemoFrames++;
    409 		}
    410 		break;
    411 	case DS_SOUND:
    412 		soundWorld->ProcessDemoCommand( readDemo );
    413 		break;
    414 	default:
    415 		common->Error( "Bad render demo token" );
    416 	}
    417 }
    418 
    419 /*
    420 ================
    421 Common_DemoShot_f
    422 ================
    423 */
    424 CONSOLE_COMMAND( demoShot, "writes a screenshot as a demo", NULL ) {
    425 	if ( args.Argc() != 2 ) {
    426 		idStr filename = FindUnusedFileName( "demos/shot%03i.demo" );
    427 		commonLocal.DemoShot( filename );
    428 	} else {
    429 		commonLocal.DemoShot( va( "demos/shot_%s.demo", args.Argv(1) ) );
    430 	}
    431 }
    432 
    433 /*
    434 ================
    435 Common_RecordDemo_f
    436 ================
    437 */
    438 CONSOLE_COMMAND( recordDemo, "records a demo", NULL ) {
    439 	if ( args.Argc() != 2 ) {
    440 		idStr filename = FindUnusedFileName( "demos/demo%03i.demo" );
    441 		commonLocal.StartRecordingRenderDemo( filename );
    442 	} else {
    443 		commonLocal.StartRecordingRenderDemo( va( "demos/%s.demo", args.Argv(1) ) );
    444 	}
    445 }
    446 
    447 /*
    448 ================
    449 Common_CompressDemo_f
    450 ================
    451 */
    452 CONSOLE_COMMAND( compressDemo, "compresses a demo file", idCmdSystem::ArgCompletion_DemoName ) {
    453 	if ( args.Argc() == 2 ) {
    454 		commonLocal.CompressDemoFile( "2", args.Argv(1) );
    455 	} else if ( args.Argc() == 3 ) {
    456 		commonLocal.CompressDemoFile( args.Argv(2), args.Argv(1) );
    457 	} else {
    458 		common->Printf("use: CompressDemo <file> [scheme]\nscheme is the same as com_compressDemo, defaults to 2" );
    459 	}
    460 }
    461 
    462 /*
    463 ================
    464 Common_StopRecordingDemo_f
    465 ================
    466 */
    467 CONSOLE_COMMAND( stopRecording, "stops demo recording", NULL ) {
    468 	commonLocal.StopRecordingRenderDemo();
    469 }
    470 
    471 /*
    472 ================
    473 Common_PlayDemo_f
    474 ================
    475 */
    476 CONSOLE_COMMAND( playDemo, "plays back a demo", idCmdSystem::ArgCompletion_DemoName ) {
    477 	if ( args.Argc() >= 2 ) {
    478 		commonLocal.StartPlayingRenderDemo( va( "demos/%s", args.Argv(1) ) );
    479 	}
    480 }
    481 
    482 /*
    483 ================
    484 Common_TimeDemo_f
    485 ================
    486 */
    487 CONSOLE_COMMAND( timeDemo, "times a demo", idCmdSystem::ArgCompletion_DemoName ) {
    488 	if ( args.Argc() >= 2 ) {
    489 		commonLocal.TimeRenderDemo( va( "demos/%s", args.Argv(1) ), ( args.Argc() > 2 ), false );
    490 	}
    491 }
    492 
    493 /*
    494 ================
    495 Common_TimeDemoQuit_f
    496 ================
    497 */
    498 CONSOLE_COMMAND( timeDemoQuit, "times a demo and quits", idCmdSystem::ArgCompletion_DemoName ) {
    499 	commonLocal.TimeRenderDemo( va( "demos/%s", args.Argv(1) ), true );
    500 }
    501 
    502 /*
    503 ================
    504 Common_AVIDemo_f
    505 ================
    506 */
    507 CONSOLE_COMMAND( aviDemo, "writes AVIs for a demo", idCmdSystem::ArgCompletion_DemoName ) {
    508 	commonLocal.AVIRenderDemo( va( "demos/%s", args.Argv(1) ) );
    509 }
    510 
    511 /*
    512 ================
    513 Common_AVIGame_f
    514 ================
    515 */
    516 CONSOLE_COMMAND( aviGame, "writes AVIs for the current game", NULL ) {
    517 	commonLocal.AVIGame( args.Argv(1) );
    518 }