DemoFile.cpp (7829B)
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 idCVar idDemoFile::com_logDemos( "com_logDemos", "0", CVAR_SYSTEM | CVAR_BOOL, "Write demo.log with debug information in it" ); 33 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" ); 34 idCVar idDemoFile::com_preloadDemos( "com_preloadDemos", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_ARCHIVE, "Load the whole demo in to RAM before running it" ); 35 36 #define DEMO_MAGIC GAME_NAME " RDEMO" 37 38 /* 39 ================ 40 idDemoFile::idDemoFile 41 ================ 42 */ 43 idDemoFile::idDemoFile() { 44 f = NULL; 45 fLog = NULL; 46 log = false; 47 fileImage = NULL; 48 compressor = NULL; 49 writing = false; 50 } 51 52 /* 53 ================ 54 idDemoFile::~idDemoFile 55 ================ 56 */ 57 idDemoFile::~idDemoFile() { 58 Close(); 59 } 60 61 /* 62 ================ 63 idDemoFile::AllocCompressor 64 ================ 65 */ 66 idCompressor *idDemoFile::AllocCompressor( int type ) { 67 switch ( type ) { 68 case 0: return idCompressor::AllocNoCompression(); 69 default: 70 case 1: return idCompressor::AllocLZW(); 71 case 2: return idCompressor::AllocLZSS(); 72 case 3: return idCompressor::AllocHuffman(); 73 } 74 } 75 76 /* 77 ================ 78 idDemoFile::OpenForReading 79 ================ 80 */ 81 bool idDemoFile::OpenForReading( const char *fileName ) { 82 static const int magicLen = sizeof(DEMO_MAGIC) / sizeof(DEMO_MAGIC[0]); 83 char magicBuffer[magicLen]; 84 int compression; 85 int fileLength; 86 87 Close(); 88 89 f = fileSystem->OpenFileRead( fileName ); 90 if ( !f ) { 91 return false; 92 } 93 94 fileLength = f->Length(); 95 96 if ( com_preloadDemos.GetBool() ) { 97 fileImage = (byte *)Mem_Alloc( fileLength, TAG_CRAP ); 98 f->Read( fileImage, fileLength ); 99 fileSystem->CloseFile( f ); 100 f = new (TAG_SYSTEM) idFile_Memory( va( "preloaded(%s)", fileName ), (const char *)fileImage, fileLength ); 101 } 102 103 if ( com_logDemos.GetBool() ) { 104 fLog = fileSystem->OpenFileWrite( "demoread.log" ); 105 } 106 107 writing = false; 108 109 f->Read(magicBuffer, magicLen); 110 if ( memcmp(magicBuffer, DEMO_MAGIC, magicLen) == 0 ) { 111 f->ReadInt( compression ); 112 } else { 113 // Ideally we would error out if the magic string isn't there, 114 // but for backwards compatibility we are going to assume it's just an uncompressed demo file 115 compression = 0; 116 f->Rewind(); 117 } 118 119 compressor = AllocCompressor( compression ); 120 compressor->Init( f, false, 8 ); 121 122 return true; 123 } 124 125 /* 126 ================ 127 idDemoFile::SetLog 128 ================ 129 */ 130 void idDemoFile::SetLog(bool b, const char *p) { 131 log = b; 132 if (p) { 133 logStr = p; 134 } 135 } 136 137 /* 138 ================ 139 idDemoFile::Log 140 ================ 141 */ 142 void idDemoFile::Log(const char *p) { 143 if ( fLog && p && *p ) { 144 fLog->Write( p, strlen(p) ); 145 } 146 } 147 148 /* 149 ================ 150 idDemoFile::OpenForWriting 151 ================ 152 */ 153 bool idDemoFile::OpenForWriting( const char *fileName ) { 154 Close(); 155 156 f = fileSystem->OpenFileWrite( fileName ); 157 if ( f == NULL ) { 158 return false; 159 } 160 161 if ( com_logDemos.GetBool() ) { 162 fLog = fileSystem->OpenFileWrite( "demowrite.log" ); 163 } 164 165 writing = true; 166 167 f->Write(DEMO_MAGIC, sizeof(DEMO_MAGIC)); 168 f->WriteInt( com_compressDemos.GetInteger() ); 169 f->Flush(); 170 171 compressor = AllocCompressor( com_compressDemos.GetInteger() ); 172 compressor->Init( f, true, 8 ); 173 174 return true; 175 } 176 177 /* 178 ================ 179 idDemoFile::Close 180 ================ 181 */ 182 void idDemoFile::Close() { 183 if ( writing && compressor ) { 184 compressor->FinishCompress(); 185 } 186 187 if ( f ) { 188 fileSystem->CloseFile( f ); 189 f = NULL; 190 } 191 if ( fLog ) { 192 fileSystem->CloseFile( fLog ); 193 fLog = NULL; 194 } 195 if ( fileImage ) { 196 Mem_Free( fileImage ); 197 fileImage = NULL; 198 } 199 if ( compressor ) { 200 delete compressor; 201 compressor = NULL; 202 } 203 204 demoStrings.DeleteContents( true ); 205 } 206 207 /* 208 ================ 209 idDemoFile::ReadHashString 210 ================ 211 */ 212 const char *idDemoFile::ReadHashString() { 213 int index; 214 215 if ( log && fLog ) { 216 const char *text = va( "%s > Reading hash string\n", logStr.c_str() ); 217 fLog->Write( text, strlen( text ) ); 218 } 219 220 ReadInt( index ); 221 222 if ( index == -1 ) { 223 // read a new string for the table 224 idStr *str = new (TAG_SYSTEM) idStr; 225 226 idStr data; 227 ReadString( data ); 228 *str = data; 229 230 demoStrings.Append( str ); 231 232 return *str; 233 } 234 235 if ( index < -1 || index >= demoStrings.Num() ) { 236 Close(); 237 common->Error( "demo hash index out of range" ); 238 } 239 240 return demoStrings[index]->c_str(); 241 } 242 243 /* 244 ================ 245 idDemoFile::WriteHashString 246 ================ 247 */ 248 void idDemoFile::WriteHashString( const char *str ) { 249 if ( log && fLog ) { 250 const char *text = va( "%s > Writing hash string\n", logStr.c_str() ); 251 fLog->Write( text, strlen( text ) ); 252 } 253 // see if it is already in the has table 254 for ( int i = 0 ; i < demoStrings.Num() ; i++ ) { 255 if ( !strcmp( demoStrings[i]->c_str(), str ) ) { 256 WriteInt( i ); 257 return; 258 } 259 } 260 261 // add it to our table and the demo table 262 idStr *copy = new (TAG_SYSTEM) idStr( str ); 263 //common->Printf( "hash:%i = %s\n", demoStrings.Num(), str ); 264 demoStrings.Append( copy ); 265 int cmd = -1; 266 WriteInt( cmd ); 267 WriteString( str ); 268 } 269 270 /* 271 ================ 272 idDemoFile::ReadDict 273 ================ 274 */ 275 void idDemoFile::ReadDict( idDict &dict ) { 276 int i, c; 277 idStr key, val; 278 279 dict.Clear(); 280 ReadInt( c ); 281 for ( i = 0; i < c; i++ ) { 282 key = ReadHashString(); 283 val = ReadHashString(); 284 dict.Set( key, val ); 285 } 286 } 287 288 /* 289 ================ 290 idDemoFile::WriteDict 291 ================ 292 */ 293 void idDemoFile::WriteDict( const idDict &dict ) { 294 int i, c; 295 296 c = dict.GetNumKeyVals(); 297 WriteInt( c ); 298 for ( i = 0; i < c; i++ ) { 299 WriteHashString( dict.GetKeyVal( i )->GetKey() ); 300 WriteHashString( dict.GetKeyVal( i )->GetValue() ); 301 } 302 } 303 304 /* 305 ================ 306 idDemoFile::Read 307 ================ 308 */ 309 int idDemoFile::Read( void *buffer, int len ) { 310 int read = compressor->Read( buffer, len ); 311 if ( read == 0 && len >= 4 ) { 312 *(demoSystem_t *)buffer = DS_FINISHED; 313 } 314 return read; 315 } 316 317 /* 318 ================ 319 idDemoFile::Write 320 ================ 321 */ 322 int idDemoFile::Write( const void *buffer, int len ) { 323 return compressor->Write( buffer, len ); 324 } 325 326 327 328