DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

File_Resource.cpp (14567B)


      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 /*
     33 ================================================================================================
     34 
     35 idResourceContainer
     36 
     37 ================================================================================================
     38 */
     39 
     40 /*
     41 ========================
     42 idResourceContainer::ReOpen 
     43 ========================
     44 */ 
     45 void idResourceContainer::ReOpen() {
     46 	delete resourceFile;
     47 	resourceFile = fileSystem->OpenFileRead( fileName );
     48 }
     49 
     50 /*
     51 ========================
     52 idResourceContainer::Init 
     53 ========================
     54 */ 
     55 bool idResourceContainer::Init( const char *_fileName, uint8 containerIndex ) {
     56 
     57 	if ( idStr::Icmp( _fileName, "_ordered.resources" ) == 0 ) {
     58 		resourceFile = fileSystem->OpenFileReadMemory( _fileName );
     59 	} else {
     60 		resourceFile = fileSystem->OpenFileRead( _fileName );
     61 	}
     62 
     63 	if ( resourceFile == NULL ) {
     64 		idLib::Warning( "Unable to open resource file %s", _fileName );
     65 		return false;
     66 	}
     67 
     68 	resourceFile->ReadBig( resourceMagic );
     69 	if ( resourceMagic != RESOURCE_FILE_MAGIC ) {
     70 		idLib::FatalError( "resourceFileMagic != RESOURCE_FILE_MAGIC" );
     71 	}
     72 
     73 	fileName = _fileName;
     74 
     75 	resourceFile->ReadBig( tableOffset );
     76 	resourceFile->ReadBig( tableLength );
     77 	// read this into a memory buffer with a single read
     78 	char * const buf = (char *)Mem_Alloc( tableLength, TAG_RESOURCE );
     79 	resourceFile->Seek( tableOffset, FS_SEEK_SET );
     80 	resourceFile->Read( buf, tableLength );
     81 	idFile_Memory memFile( "resourceHeader", (const char *)buf, tableLength );
     82 
     83 	// Parse the resourceFile header, which includes every resource used
     84 	// by the game.
     85 	memFile.ReadBig( numFileResources );
     86 
     87 	cacheTable.SetNum( numFileResources );
     88 
     89 	for ( int i = 0; i < numFileResources; i++ ) {
     90 		idResourceCacheEntry &rt = cacheTable[ i ];
     91 		rt.Read( &memFile );
     92 		rt.filename.BackSlashesToSlashes();
     93 		rt.filename.ToLower();
     94 		rt.containerIndex = containerIndex;
     95 
     96 		const int key = cacheHash.GenerateKey( rt.filename, false );
     97 		bool found = false;
     98 		//for ( int index = cacheHash.GetFirst( key ); index != idHashIndex::NULL_INDEX; index = cacheHash.GetNext( index ) ) {
     99 		//	idResourceCacheEntry & rtc = cacheTable[ index ];
    100 		//	if ( idStr::Icmp( rtc.filename, rt.filename ) == 0 ) {
    101 		//		found = true;
    102 		//		break;
    103 		//	}
    104 		//}
    105 		if ( !found ) {
    106 			//idLib::Printf( "rez file name: %s\n", rt.filename.c_str() );
    107 			cacheHash.Add( key, i );
    108 		}
    109 	}
    110 	Mem_Free( buf );
    111 
    112 	return true;
    113 }
    114 
    115 
    116 /*
    117 ========================
    118 idResourceContainer::WriteManifestFile 
    119 ========================
    120 */ 
    121 void idResourceContainer::WriteManifestFile( const char *name, const idStrList &list ) {
    122 	idStr filename( name );
    123 	filename.SetFileExtension( "manifest" );
    124 	filename.Insert( "maps/", 0 );
    125 	idFile *outFile = fileSystem->OpenFileWrite( filename );
    126 	if ( outFile != NULL ) {
    127 		int num = list.Num();
    128 		outFile->WriteBig( num );
    129 		for ( int i = 0; i < num; i++ ) {
    130 			outFile->WriteString( list[ i ] );
    131 		}
    132 		delete outFile;
    133 	}
    134 }
    135 
    136 /*
    137 ========================
    138 idResourceContainer::ReadManifestFile 
    139 ========================
    140 */ 
    141 int idResourceContainer::ReadManifestFile( const char *name, idStrList &list ) {
    142 	idFile *inFile = fileSystem->OpenFileRead( name );
    143 	if ( inFile != NULL ) {
    144 		list.SetGranularity( 16384 );
    145 		idStr str;
    146 		int num;
    147 		list.Clear();
    148 		inFile->ReadBig( num );
    149 		for ( int i = 0; i < num; i++ ) {
    150 			inFile->ReadString( str );
    151 			list.Append( str );
    152 		}
    153 		delete inFile;
    154 	}
    155 	return list.Num();
    156 }
    157 
    158 
    159 /*
    160 ========================
    161 idResourceContainer::UpdateResourceFile 
    162 ========================
    163 */ 
    164 void idResourceContainer::UpdateResourceFile( const char *_filename, const idStrList &_filesToUpdate ) {
    165 	idFile *outFile = fileSystem->OpenFileWrite( va( "%s.new", _filename ) );
    166 	if ( outFile == NULL ) {
    167 		idLib::Warning( "Unable to open resource file %s or new output file", _filename );
    168 		return;
    169 	}
    170 
    171 	uint32 magic = 0;
    172 	int _tableOffset = 0;
    173 	int _tableLength = 0;
    174 	idList< idResourceCacheEntry > entries;
    175 	idStrList filesToUpdate = _filesToUpdate;
    176 
    177 	idFile *inFile = fileSystem->OpenFileRead( _filename );
    178 	if ( inFile == NULL ) {
    179 		magic = RESOURCE_FILE_MAGIC;
    180 
    181 		outFile->WriteBig( magic );
    182 		outFile->WriteBig( _tableOffset );
    183 		outFile->WriteBig( _tableLength );
    184 
    185 	} else {
    186 		inFile->ReadBig( magic );
    187 		if ( magic != RESOURCE_FILE_MAGIC ) {
    188 			delete inFile;
    189 			return;
    190 		}
    191 
    192 		inFile->ReadBig( _tableOffset );
    193 		inFile->ReadBig( _tableLength );
    194 		// read this into a memory buffer with a single read
    195 		char * const buf = (char *)Mem_Alloc( _tableLength, TAG_RESOURCE );
    196 		inFile->Seek( _tableOffset, FS_SEEK_SET );
    197 		inFile->Read( buf, _tableLength );
    198 		idFile_Memory memFile( "resourceHeader", (const char *)buf, _tableLength );
    199 
    200 		int _numFileResources = 0;
    201 		memFile.ReadBig( _numFileResources );
    202 
    203 		outFile->WriteBig( magic );
    204 		outFile->WriteBig( _tableOffset );
    205 		outFile->WriteBig( _tableLength );
    206 
    207 		entries.SetNum( _numFileResources );
    208 
    209 		for ( int i = 0; i < _numFileResources; i++ ) {
    210 			entries[ i ].Read( &memFile );
    211 
    212 
    213 			idLib::Printf( "examining %s\n", entries[ i ].filename.c_str() );
    214 			byte * fileData = NULL;
    215 
    216 			for ( int j = filesToUpdate.Num() - 1; j >= 0; j-- ) {
    217 				if ( filesToUpdate[ j ].Icmp( entries[ i ].filename ) == 0 ) {
    218 					idFile *newFile = fileSystem->OpenFileReadMemory( filesToUpdate[ j ] );
    219 					if ( newFile != NULL ) {
    220 						idLib::Printf( "Updating %s\n", filesToUpdate[ j ].c_str() );
    221 						entries[ i ].length = newFile->Length();
    222 						fileData = (byte *)Mem_Alloc( entries[ i ].length, TAG_TEMP );
    223 						newFile->Read( fileData, newFile->Length() );
    224 						delete newFile;
    225 					}
    226 					filesToUpdate.RemoveIndex( j );
    227 				}
    228 			}
    229 
    230 			if ( fileData == NULL ) {
    231 				inFile->Seek( entries[ i ].offset, FS_SEEK_SET );
    232 				fileData = (byte *)Mem_Alloc( entries[ i ].length, TAG_TEMP );
    233 				inFile->Read( fileData, entries[ i ].length );
    234 			}
    235 
    236 			entries[ i ].offset = outFile->Tell();
    237 			outFile->Write( ( void* )fileData, entries[ i ].length );
    238 
    239 			Mem_Free( fileData );
    240 		}
    241 
    242 		Mem_Free( buf );
    243 	}
    244 
    245 	while ( filesToUpdate.Num() > 0 ) {
    246 		idFile *newFile = fileSystem->OpenFileReadMemory( filesToUpdate[ 0 ] );
    247 		if ( newFile != NULL ) {
    248 			idLib::Printf( "Appending %s\n", filesToUpdate[ 0 ].c_str() );
    249 			idResourceCacheEntry rt;
    250 			rt.filename = filesToUpdate[ 0 ];
    251 			rt.length = newFile->Length();
    252 			byte * fileData = (byte *)Mem_Alloc( rt.length, TAG_TEMP );
    253 			newFile->Read( fileData, rt.length );
    254 			int idx = entries.Append( rt );
    255 			if ( idx >= 0 ) {
    256 				entries[ idx ].offset = outFile->Tell();
    257 				outFile->Write( ( void* )fileData, entries[ idx ].length );
    258 			}
    259 			delete newFile;
    260 			Mem_Free( fileData );
    261 		}
    262 		filesToUpdate.RemoveIndex( 0 );
    263 	}
    264 
    265 	_tableOffset = outFile->Tell();
    266 	outFile->WriteBig( entries.Num() );
    267 
    268 	// write the individual resource entries
    269 	for ( int i = 0; i < entries.Num(); i++ ) {
    270 		entries[ i ].Write( outFile );
    271 	}
    272 
    273 	// go back and write the header offsets again, now that we have file offsets and lengths
    274 	_tableLength = outFile->Tell() - _tableOffset;
    275 	outFile->Seek( 0, FS_SEEK_SET );
    276 	outFile->WriteBig( magic );
    277 	outFile->WriteBig( _tableOffset );
    278 	outFile->WriteBig( _tableLength );
    279 
    280 	delete outFile;
    281 	delete inFile;
    282 }
    283 
    284 
    285 /*
    286 ========================
    287 idResourceContainer::ExtractResourceFile 
    288 ========================
    289 */ 
    290 void idResourceContainer::SetContainerIndex( const int & _idx ) {
    291 	for ( int i = 0; i < cacheTable.Num(); i++ ) {
    292 		cacheTable[ i ].containerIndex = _idx;
    293 	}
    294 }
    295 
    296 /*
    297 ========================
    298 idResourceContainer::ExtractResourceFile 
    299 ========================
    300 */ 
    301 void idResourceContainer::ExtractResourceFile ( const char * _fileName, const char * _outPath, bool _copyWavs ) {
    302 	idFile *inFile = fileSystem->OpenFileRead( _fileName );
    303 
    304 	if ( inFile == NULL ) {
    305 		idLib::Warning( "Unable to open resource file %s", _fileName );
    306 		return;
    307 	}
    308 
    309 	uint32 magic;
    310 	inFile->ReadBig( magic );
    311 	if ( magic != RESOURCE_FILE_MAGIC ) {
    312 		delete inFile;
    313 		return;
    314 	}
    315 
    316 	int _tableOffset;
    317 	int _tableLength;
    318 	inFile->ReadBig( _tableOffset );
    319 	inFile->ReadBig( _tableLength );
    320 	// read this into a memory buffer with a single read
    321 	char * const buf = (char *)Mem_Alloc( _tableLength, TAG_RESOURCE );
    322 	inFile->Seek( _tableOffset, FS_SEEK_SET );
    323 	inFile->Read( buf, _tableLength );
    324 	idFile_Memory memFile( "resourceHeader", (const char *)buf, _tableLength );
    325 
    326 	int _numFileResources;
    327 	memFile.ReadBig( _numFileResources );
    328 
    329 	for ( int i = 0; i < _numFileResources; i++ ) {
    330 		idResourceCacheEntry rt;
    331 		rt.Read( &memFile );
    332 		rt.filename.BackSlashesToSlashes();
    333 		rt.filename.ToLower();
    334 		byte *fbuf = NULL;
    335 		if ( _copyWavs && ( rt.filename.Find( ".idwav" ) >= 0 ||  rt.filename.Find( ".idxma" ) >= 0 ||  rt.filename.Find( ".idmsf" ) >= 0 ) ) {
    336 			rt.filename.SetFileExtension( "wav" );
    337 			rt.filename.Replace( "generated/", "" );
    338 			int len = fileSystem->GetFileLength( rt.filename );
    339 			fbuf =  (byte *)Mem_Alloc( len, TAG_RESOURCE );
    340 			fileSystem->ReadFile( rt.filename, (void**)&fbuf, NULL );
    341 		} else {
    342 			inFile->Seek( rt.offset, FS_SEEK_SET );
    343 			fbuf =  (byte *)Mem_Alloc( rt.length, TAG_RESOURCE );
    344 			inFile->Read( fbuf, rt.length );
    345 		}
    346 		idStr outName = _outPath;
    347 		outName.AppendPath( rt.filename );
    348 		idFile *outFile = fileSystem->OpenExplicitFileWrite( outName );
    349 		if ( outFile != NULL ) {
    350 			outFile->Write( ( byte* )fbuf, rt.length );
    351 			delete outFile;
    352 		}
    353 		Mem_Free( fbuf );
    354 	}
    355 	delete inFile;
    356 	Mem_Free( buf );
    357 }
    358 
    359 
    360 
    361 /*
    362 ========================
    363 idResourceContainer::Open 
    364 ========================
    365 */ 
    366 void idResourceContainer::WriteResourceFile( const char *manifestName, const idStrList &manifest, const bool &_writeManifest ) {
    367 
    368 	if ( manifest.Num() == 0 ) {
    369 		return;
    370 	}
    371 
    372 	idLib::Printf( "Writing resource file %s\n", manifestName );
    373 
    374 	// build multiple output files at 1GB each
    375 	idList < idStrList > outPutFiles;
    376 
    377 	idFileManifest outManifest;
    378 	int64 size = 0;
    379 	idStrList flist;
    380 	flist.SetGranularity( 16384 );
    381 	for ( int i = 0; i < manifest.Num(); i++ ) {
    382 		flist.Append( manifest[ i ] );
    383 		size += fileSystem->GetFileLength( manifest[ i ] );
    384 		if ( size > 1024 * 1024 * 1024 ) {
    385 			outPutFiles.Append( flist );
    386 			size = 0;
    387 			flist.Clear();
    388 		}
    389 		outManifest.AddFile( manifest[ i ] );
    390 	}
    391 
    392 	outPutFiles.Append( flist );
    393 
    394 	if ( _writeManifest ) {
    395 		idStr temp = manifestName;
    396 		temp.Replace( "maps/", "manifests/" );
    397 		temp.StripFileExtension();
    398 		temp.SetFileExtension( "manifest" );
    399 		outManifest.WriteManifestFile( temp );
    400 	}
    401 
    402 	for ( int idx = 0; idx < outPutFiles.Num(); idx++ ) {
    403 
    404 		idStrList &fileList = outPutFiles[ idx ];
    405 		if ( fileList.Num() == 0 ) {
    406 			continue;
    407 		}
    408 
    409 		idStr fileName = manifestName;
    410 		if ( idx > 0 ) {
    411 			fileName = va( "%s_%02d", manifestName, idx );
    412 		} 
    413 		fileName.SetFileExtension( "resources" );
    414 
    415 		idFile *resFile = fileSystem->OpenFileWrite( fileName );
    416 
    417 		if ( resFile == NULL ) {
    418 			idLib::Warning( "Cannot open %s for writing.\n", fileName.c_str() );
    419 			return;
    420 		}
    421 
    422 		idLib::Printf( "Writing resource file %s\n", fileName.c_str() );
    423 
    424 		int	tableOffset = 0;
    425 		int	tableLength = 0;
    426 		int	tableNewLength = 0;
    427 		uint32	resourceFileMagic = RESOURCE_FILE_MAGIC;
    428 
    429 		resFile->WriteBig( resourceFileMagic );
    430 		resFile->WriteBig( tableOffset );
    431 		resFile->WriteBig( tableLength );
    432 
    433 		idList< idResourceCacheEntry > entries;
    434 
    435 		entries.Resize( fileList.Num() );
    436 
    437 		for ( int i = 0; i < fileList.Num(); i++ ) {
    438 			idResourceCacheEntry ent;
    439 
    440 			ent.filename = fileList[ i ];
    441 			ent.length = 0;
    442 			ent.offset = 0;
    443 
    444 			idFile *file = fileSystem->OpenFileReadMemory( ent.filename, false );
    445 			idFile_Memory *fm = dynamic_cast< idFile_Memory* >( file );
    446 			if ( fm == NULL ) {
    447 				continue;
    448 			}
    449 			// if the entry is uncompressed, align the file pointer to a 16 byte boundary
    450 			// so it will be usable if memory mapped
    451 			ent.length = fm->Length();
    452 
    453 			// always get the offset, even if the file will have zero length
    454 			ent.offset = resFile->Tell();
    455 
    456 			entries.Append( ent );
    457 
    458 			if ( ent.length == 0 ) {
    459 				ent.filename = "";
    460 				delete fm;
    461 				continue;
    462 			}
    463 
    464 			resFile->Write( fm->GetDataPtr(), ent.length );
    465 
    466 			delete fm;
    467 
    468 			// pacifier every ten megs
    469 			if ( ( ent.offset + ent.length ) / 10000000 != ent.offset / 10000000 ) {
    470 				idLib::Printf( "." );
    471 			}
    472 		}
    473 
    474 		idLib::Printf( "\n" );
    475 
    476 		// write the table out now that we have all the files
    477 		tableOffset = resFile->Tell();
    478 
    479 		// count how many we are going to write for this platform
    480 		int	numFileResources = entries.Num();
    481 
    482 		resFile->WriteBig( numFileResources );
    483 
    484 		// write the individual resource entries
    485 		for ( int i = 0; i < entries.Num(); i++ ) {
    486 			entries[ i ].Write( resFile );
    487 			if ( i + 1 == numFileResources ) {
    488 				// we just wrote out the last new entry
    489 				tableNewLength = resFile->Tell() - tableOffset;
    490 			}
    491 		}
    492 
    493 		// go back and write the header offsets again, now that we have file offsets and lengths
    494 		tableLength = resFile->Tell() - tableOffset;
    495 		resFile->Seek( 0, FS_SEEK_SET );
    496 		resFile->WriteBig( resourceFileMagic );
    497 		resFile->WriteBig( tableOffset );
    498 		resFile->WriteBig( tableLength );
    499 		delete resFile;
    500 	}
    501 }