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 }