/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . 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. 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. =========================================================================== */ #include "../idlib/precompiled.h" #pragma hdrstop idCVar idDemoFile::com_logDemos( "com_logDemos", "0", CVAR_SYSTEM | CVAR_BOOL, "Write demo.log with debug information in it" ); idCVar idDemoFile::com_compressDemos( "com_compressDemos", "1", CVAR_SYSTEM | CVAR_INTEGER | CVAR_ARCHIVE, "Compression scheme for demo files\n0: None (Fast, large files)\n1: LZW (Fast to compress, Fast to decompress, medium/small files)\n2: LZSS (Slow to compress, Fast to decompress, small files)\n3: Huffman (Fast to compress, Slow to decompress, medium files)\nSee also: The 'CompressDemo' command" ); idCVar idDemoFile::com_preloadDemos( "com_preloadDemos", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_ARCHIVE, "Load the whole demo in to RAM before running it" ); #define DEMO_MAGIC GAME_NAME " RDEMO" /* ================ idDemoFile::idDemoFile ================ */ idDemoFile::idDemoFile() { f = NULL; fLog = NULL; log = false; fileImage = NULL; compressor = NULL; writing = false; } /* ================ idDemoFile::~idDemoFile ================ */ idDemoFile::~idDemoFile() { Close(); } /* ================ idDemoFile::AllocCompressor ================ */ idCompressor* idDemoFile::AllocCompressor( int type ) { switch( type ) { case 0: return idCompressor::AllocNoCompression(); default: case 1: return idCompressor::AllocLZW(); case 2: return idCompressor::AllocLZSS(); case 3: return idCompressor::AllocHuffman(); } } /* ================ idDemoFile::OpenForReading ================ */ bool idDemoFile::OpenForReading( const char* fileName ) { static const int magicLen = sizeof( DEMO_MAGIC ) / sizeof( DEMO_MAGIC[0] ); char magicBuffer[magicLen]; int compression; int fileLength; Close(); f = fileSystem->OpenFileRead( fileName ); if( !f ) { return false; } fileLength = f->Length(); if( com_preloadDemos.GetBool() ) { fileImage = ( byte* )Mem_Alloc( fileLength, TAG_CRAP ); f->Read( fileImage, fileLength ); fileSystem->CloseFile( f ); f = new( TAG_SYSTEM ) idFile_Memory( va( "preloaded(%s)", fileName ), ( const char* )fileImage, fileLength ); } if( com_logDemos.GetBool() ) { fLog = fileSystem->OpenFileWrite( "demoread.log" ); } writing = false; f->Read( magicBuffer, magicLen ); if( memcmp( magicBuffer, DEMO_MAGIC, magicLen ) == 0 ) { f->ReadInt( compression ); } else { // Ideally we would error out if the magic string isn't there, // but for backwards compatibility we are going to assume it's just an uncompressed demo file compression = 0; f->Rewind(); } compressor = AllocCompressor( compression ); compressor->Init( f, false, 8 ); return true; } /* ================ idDemoFile::SetLog ================ */ void idDemoFile::SetLog( bool b, const char* p ) { log = b; if( p ) { logStr = p; } } /* ================ idDemoFile::Log ================ */ void idDemoFile::Log( const char* p ) { if( fLog && p && *p ) { fLog->Write( p, strlen( p ) ); } } /* ================ idDemoFile::OpenForWriting ================ */ bool idDemoFile::OpenForWriting( const char* fileName ) { Close(); f = fileSystem->OpenFileWrite( fileName ); if( f == NULL ) { return false; } if( com_logDemos.GetBool() ) { fLog = fileSystem->OpenFileWrite( "demowrite.log" ); } writing = true; f->Write( DEMO_MAGIC, sizeof( DEMO_MAGIC ) ); f->WriteInt( com_compressDemos.GetInteger() ); f->Flush(); compressor = AllocCompressor( com_compressDemos.GetInteger() ); compressor->Init( f, true, 8 ); return true; } /* ================ idDemoFile::Close ================ */ void idDemoFile::Close() { if( writing && compressor ) { compressor->FinishCompress(); } if( f ) { fileSystem->CloseFile( f ); f = NULL; } if( fLog ) { fileSystem->CloseFile( fLog ); fLog = NULL; } if( fileImage ) { Mem_Free( fileImage ); fileImage = NULL; } if( compressor ) { delete compressor; compressor = NULL; } demoStrings.DeleteContents( true ); } /* ================ idDemoFile::ReadHashString ================ */ const char* idDemoFile::ReadHashString() { int index; if( log && fLog ) { const char* text = va( "%s > Reading hash string\n", logStr.c_str() ); fLog->Write( text, strlen( text ) ); } ReadInt( index ); if( index == -1 ) { // read a new string for the table idStr* str = new( TAG_SYSTEM ) idStr; idStr data; ReadString( data ); *str = data; demoStrings.Append( str ); return *str; } if( index < -1 || index >= demoStrings.Num() ) { Close(); common->Error( "demo hash index out of range" ); } return demoStrings[index]->c_str(); } /* ================ idDemoFile::WriteHashString ================ */ void idDemoFile::WriteHashString( const char* str ) { if( log && fLog ) { const char* text = va( "%s > Writing hash string\n", logStr.c_str() ); fLog->Write( text, strlen( text ) ); } // see if it is already in the has table for( int i = 0 ; i < demoStrings.Num() ; i++ ) { if( !strcmp( demoStrings[i]->c_str(), str ) ) { WriteInt( i ); return; } } // add it to our table and the demo table idStr* copy = new( TAG_SYSTEM ) idStr( str ); //common->Printf( "hash:%i = %s\n", demoStrings.Num(), str ); demoStrings.Append( copy ); int cmd = -1; WriteInt( cmd ); WriteString( str ); } /* ================ idDemoFile::ReadDict ================ */ void idDemoFile::ReadDict( idDict& dict ) { int i, c; idStr key, val; dict.Clear(); ReadInt( c ); for( i = 0; i < c; i++ ) { key = ReadHashString(); val = ReadHashString(); dict.Set( key, val ); } } /* ================ idDemoFile::WriteDict ================ */ void idDemoFile::WriteDict( const idDict& dict ) { int i, c; c = dict.GetNumKeyVals(); WriteInt( c ); for( i = 0; i < c; i++ ) { WriteHashString( dict.GetKeyVal( i )->GetKey() ); WriteHashString( dict.GetKeyVal( i )->GetValue() ); } } /* ================ idDemoFile::Read ================ */ int idDemoFile::Read( void* buffer, int len ) { int read = compressor->Read( buffer, len ); if( read == 0 && len >= 4 ) { *( demoSystem_t* )buffer = DS_FINISHED; } return read; } /* ================ idDemoFile::Write ================ */ int idDemoFile::Write( const void* buffer, int len ) { return compressor->Write( buffer, len ); }