mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-02 17:02:17 +00:00
501 lines
14 KiB
C++
501 lines
14 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
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
|
|
|
|
/*
|
|
================================================================================================
|
|
|
|
idResourceContainer
|
|
|
|
================================================================================================
|
|
*/
|
|
|
|
/*
|
|
========================
|
|
idResourceContainer::ReOpen
|
|
========================
|
|
*/
|
|
void idResourceContainer::ReOpen() {
|
|
delete resourceFile;
|
|
resourceFile = fileSystem->OpenFileRead( fileName );
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idResourceContainer::Init
|
|
========================
|
|
*/
|
|
bool idResourceContainer::Init( const char *_fileName, uint8 containerIndex ) {
|
|
|
|
if ( idStr::Icmp( _fileName, "_ordered.resources" ) == 0 ) {
|
|
resourceFile = fileSystem->OpenFileReadMemory( _fileName );
|
|
} else {
|
|
resourceFile = fileSystem->OpenFileRead( _fileName );
|
|
}
|
|
|
|
if ( resourceFile == NULL ) {
|
|
idLib::Warning( "Unable to open resource file %s", _fileName );
|
|
return false;
|
|
}
|
|
|
|
resourceFile->ReadBig( resourceMagic );
|
|
if ( resourceMagic != RESOURCE_FILE_MAGIC ) {
|
|
idLib::FatalError( "resourceFileMagic != RESOURCE_FILE_MAGIC" );
|
|
}
|
|
|
|
fileName = _fileName;
|
|
|
|
resourceFile->ReadBig( tableOffset );
|
|
resourceFile->ReadBig( tableLength );
|
|
// read this into a memory buffer with a single read
|
|
char * const buf = (char *)Mem_Alloc( tableLength, TAG_RESOURCE );
|
|
resourceFile->Seek( tableOffset, FS_SEEK_SET );
|
|
resourceFile->Read( buf, tableLength );
|
|
idFile_Memory memFile( "resourceHeader", (const char *)buf, tableLength );
|
|
|
|
// Parse the resourceFile header, which includes every resource used
|
|
// by the game.
|
|
memFile.ReadBig( numFileResources );
|
|
|
|
cacheTable.SetNum( numFileResources );
|
|
|
|
for ( int i = 0; i < numFileResources; i++ ) {
|
|
idResourceCacheEntry &rt = cacheTable[ i ];
|
|
rt.Read( &memFile );
|
|
rt.filename.BackSlashesToSlashes();
|
|
rt.filename.ToLower();
|
|
rt.containerIndex = containerIndex;
|
|
|
|
const int key = cacheHash.GenerateKey( rt.filename, false );
|
|
bool found = false;
|
|
//for ( int index = cacheHash.GetFirst( key ); index != idHashIndex::NULL_INDEX; index = cacheHash.GetNext( index ) ) {
|
|
// idResourceCacheEntry & rtc = cacheTable[ index ];
|
|
// if ( idStr::Icmp( rtc.filename, rt.filename ) == 0 ) {
|
|
// found = true;
|
|
// break;
|
|
// }
|
|
//}
|
|
if ( !found ) {
|
|
//idLib::Printf( "rez file name: %s\n", rt.filename.c_str() );
|
|
cacheHash.Add( key, i );
|
|
}
|
|
}
|
|
Mem_Free( buf );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
========================
|
|
idResourceContainer::WriteManifestFile
|
|
========================
|
|
*/
|
|
void idResourceContainer::WriteManifestFile( const char *name, const idStrList &list ) {
|
|
idStr filename( name );
|
|
filename.SetFileExtension( "manifest" );
|
|
filename.Insert( "maps/", 0 );
|
|
idFile *outFile = fileSystem->OpenFileWrite( filename );
|
|
if ( outFile != NULL ) {
|
|
int num = list.Num();
|
|
outFile->WriteBig( num );
|
|
for ( int i = 0; i < num; i++ ) {
|
|
outFile->WriteString( list[ i ] );
|
|
}
|
|
delete outFile;
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idResourceContainer::ReadManifestFile
|
|
========================
|
|
*/
|
|
int idResourceContainer::ReadManifestFile( const char *name, idStrList &list ) {
|
|
idFile *inFile = fileSystem->OpenFileRead( name );
|
|
if ( inFile != NULL ) {
|
|
list.SetGranularity( 16384 );
|
|
idStr str;
|
|
int num;
|
|
list.Clear();
|
|
inFile->ReadBig( num );
|
|
for ( int i = 0; i < num; i++ ) {
|
|
inFile->ReadString( str );
|
|
list.Append( str );
|
|
}
|
|
delete inFile;
|
|
}
|
|
return list.Num();
|
|
}
|
|
|
|
|
|
/*
|
|
========================
|
|
idResourceContainer::UpdateResourceFile
|
|
========================
|
|
*/
|
|
void idResourceContainer::UpdateResourceFile( const char *_filename, const idStrList &_filesToUpdate ) {
|
|
idFile *outFile = fileSystem->OpenFileWrite( va( "%s.new", _filename ) );
|
|
if ( outFile == NULL ) {
|
|
idLib::Warning( "Unable to open resource file %s or new output file", _filename );
|
|
return;
|
|
}
|
|
|
|
uint32 magic = 0;
|
|
int _tableOffset = 0;
|
|
int _tableLength = 0;
|
|
idList< idResourceCacheEntry > entries;
|
|
idStrList filesToUpdate = _filesToUpdate;
|
|
|
|
idFile *inFile = fileSystem->OpenFileRead( _filename );
|
|
if ( inFile == NULL ) {
|
|
magic = RESOURCE_FILE_MAGIC;
|
|
|
|
outFile->WriteBig( magic );
|
|
outFile->WriteBig( _tableOffset );
|
|
outFile->WriteBig( _tableLength );
|
|
|
|
} else {
|
|
inFile->ReadBig( magic );
|
|
if ( magic != RESOURCE_FILE_MAGIC ) {
|
|
delete inFile;
|
|
return;
|
|
}
|
|
|
|
inFile->ReadBig( _tableOffset );
|
|
inFile->ReadBig( _tableLength );
|
|
// read this into a memory buffer with a single read
|
|
char * const buf = (char *)Mem_Alloc( _tableLength, TAG_RESOURCE );
|
|
inFile->Seek( _tableOffset, FS_SEEK_SET );
|
|
inFile->Read( buf, _tableLength );
|
|
idFile_Memory memFile( "resourceHeader", (const char *)buf, _tableLength );
|
|
|
|
int _numFileResources = 0;
|
|
memFile.ReadBig( _numFileResources );
|
|
|
|
outFile->WriteBig( magic );
|
|
outFile->WriteBig( _tableOffset );
|
|
outFile->WriteBig( _tableLength );
|
|
|
|
entries.SetNum( _numFileResources );
|
|
|
|
for ( int i = 0; i < _numFileResources; i++ ) {
|
|
entries[ i ].Read( &memFile );
|
|
|
|
|
|
idLib::Printf( "examining %s\n", entries[ i ].filename.c_str() );
|
|
byte * fileData = NULL;
|
|
|
|
for ( int j = filesToUpdate.Num() - 1; j >= 0; j-- ) {
|
|
if ( filesToUpdate[ j ].Icmp( entries[ i ].filename ) == 0 ) {
|
|
idFile *newFile = fileSystem->OpenFileReadMemory( filesToUpdate[ j ] );
|
|
if ( newFile != NULL ) {
|
|
idLib::Printf( "Updating %s\n", filesToUpdate[ j ].c_str() );
|
|
entries[ i ].length = newFile->Length();
|
|
fileData = (byte *)Mem_Alloc( entries[ i ].length, TAG_TEMP );
|
|
newFile->Read( fileData, newFile->Length() );
|
|
delete newFile;
|
|
}
|
|
filesToUpdate.RemoveIndex( j );
|
|
}
|
|
}
|
|
|
|
if ( fileData == NULL ) {
|
|
inFile->Seek( entries[ i ].offset, FS_SEEK_SET );
|
|
fileData = (byte *)Mem_Alloc( entries[ i ].length, TAG_TEMP );
|
|
inFile->Read( fileData, entries[ i ].length );
|
|
}
|
|
|
|
entries[ i ].offset = outFile->Tell();
|
|
outFile->Write( ( void* )fileData, entries[ i ].length );
|
|
|
|
Mem_Free( fileData );
|
|
}
|
|
|
|
Mem_Free( buf );
|
|
}
|
|
|
|
while ( filesToUpdate.Num() > 0 ) {
|
|
idFile *newFile = fileSystem->OpenFileReadMemory( filesToUpdate[ 0 ] );
|
|
if ( newFile != NULL ) {
|
|
idLib::Printf( "Appending %s\n", filesToUpdate[ 0 ].c_str() );
|
|
idResourceCacheEntry rt;
|
|
rt.filename = filesToUpdate[ 0 ];
|
|
rt.length = newFile->Length();
|
|
byte * fileData = (byte *)Mem_Alloc( rt.length, TAG_TEMP );
|
|
newFile->Read( fileData, rt.length );
|
|
int idx = entries.Append( rt );
|
|
if ( idx >= 0 ) {
|
|
entries[ idx ].offset = outFile->Tell();
|
|
outFile->Write( ( void* )fileData, entries[ idx ].length );
|
|
}
|
|
delete newFile;
|
|
Mem_Free( fileData );
|
|
}
|
|
filesToUpdate.RemoveIndex( 0 );
|
|
}
|
|
|
|
_tableOffset = outFile->Tell();
|
|
outFile->WriteBig( entries.Num() );
|
|
|
|
// write the individual resource entries
|
|
for ( int i = 0; i < entries.Num(); i++ ) {
|
|
entries[ i ].Write( outFile );
|
|
}
|
|
|
|
// go back and write the header offsets again, now that we have file offsets and lengths
|
|
_tableLength = outFile->Tell() - _tableOffset;
|
|
outFile->Seek( 0, FS_SEEK_SET );
|
|
outFile->WriteBig( magic );
|
|
outFile->WriteBig( _tableOffset );
|
|
outFile->WriteBig( _tableLength );
|
|
|
|
delete outFile;
|
|
delete inFile;
|
|
}
|
|
|
|
|
|
/*
|
|
========================
|
|
idResourceContainer::ExtractResourceFile
|
|
========================
|
|
*/
|
|
void idResourceContainer::SetContainerIndex( const int & _idx ) {
|
|
for ( int i = 0; i < cacheTable.Num(); i++ ) {
|
|
cacheTable[ i ].containerIndex = _idx;
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idResourceContainer::ExtractResourceFile
|
|
========================
|
|
*/
|
|
void idResourceContainer::ExtractResourceFile ( const char * _fileName, const char * _outPath, bool _copyWavs ) {
|
|
idFile *inFile = fileSystem->OpenFileRead( _fileName );
|
|
|
|
if ( inFile == NULL ) {
|
|
idLib::Warning( "Unable to open resource file %s", _fileName );
|
|
return;
|
|
}
|
|
|
|
uint32 magic;
|
|
inFile->ReadBig( magic );
|
|
if ( magic != RESOURCE_FILE_MAGIC ) {
|
|
delete inFile;
|
|
return;
|
|
}
|
|
|
|
int _tableOffset;
|
|
int _tableLength;
|
|
inFile->ReadBig( _tableOffset );
|
|
inFile->ReadBig( _tableLength );
|
|
// read this into a memory buffer with a single read
|
|
char * const buf = (char *)Mem_Alloc( _tableLength, TAG_RESOURCE );
|
|
inFile->Seek( _tableOffset, FS_SEEK_SET );
|
|
inFile->Read( buf, _tableLength );
|
|
idFile_Memory memFile( "resourceHeader", (const char *)buf, _tableLength );
|
|
|
|
int _numFileResources;
|
|
memFile.ReadBig( _numFileResources );
|
|
|
|
for ( int i = 0; i < _numFileResources; i++ ) {
|
|
idResourceCacheEntry rt;
|
|
rt.Read( &memFile );
|
|
rt.filename.BackSlashesToSlashes();
|
|
rt.filename.ToLower();
|
|
byte *fbuf = NULL;
|
|
if ( _copyWavs && ( rt.filename.Find( ".idwav" ) >= 0 || rt.filename.Find( ".idxma" ) >= 0 || rt.filename.Find( ".idmsf" ) >= 0 ) ) {
|
|
rt.filename.SetFileExtension( "wav" );
|
|
rt.filename.Replace( "generated/", "" );
|
|
int len = fileSystem->GetFileLength( rt.filename );
|
|
fbuf = (byte *)Mem_Alloc( len, TAG_RESOURCE );
|
|
fileSystem->ReadFile( rt.filename, (void**)&fbuf, NULL );
|
|
} else {
|
|
inFile->Seek( rt.offset, FS_SEEK_SET );
|
|
fbuf = (byte *)Mem_Alloc( rt.length, TAG_RESOURCE );
|
|
inFile->Read( fbuf, rt.length );
|
|
}
|
|
idStr outName = _outPath;
|
|
outName.AppendPath( rt.filename );
|
|
idFile *outFile = fileSystem->OpenExplicitFileWrite( outName );
|
|
if ( outFile != NULL ) {
|
|
outFile->Write( ( byte* )fbuf, rt.length );
|
|
delete outFile;
|
|
}
|
|
Mem_Free( fbuf );
|
|
}
|
|
delete inFile;
|
|
Mem_Free( buf );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
========================
|
|
idResourceContainer::Open
|
|
========================
|
|
*/
|
|
void idResourceContainer::WriteResourceFile( const char *manifestName, const idStrList &manifest, const bool &_writeManifest ) {
|
|
|
|
if ( manifest.Num() == 0 ) {
|
|
return;
|
|
}
|
|
|
|
idLib::Printf( "Writing resource file %s\n", manifestName );
|
|
|
|
// build multiple output files at 1GB each
|
|
idList < idStrList > outPutFiles;
|
|
|
|
idFileManifest outManifest;
|
|
int64 size = 0;
|
|
idStrList flist;
|
|
flist.SetGranularity( 16384 );
|
|
for ( int i = 0; i < manifest.Num(); i++ ) {
|
|
flist.Append( manifest[ i ] );
|
|
size += fileSystem->GetFileLength( manifest[ i ] );
|
|
if ( size > 1024 * 1024 * 1024 ) {
|
|
outPutFiles.Append( flist );
|
|
size = 0;
|
|
flist.Clear();
|
|
}
|
|
outManifest.AddFile( manifest[ i ] );
|
|
}
|
|
|
|
outPutFiles.Append( flist );
|
|
|
|
if ( _writeManifest ) {
|
|
idStr temp = manifestName;
|
|
temp.Replace( "maps/", "manifests/" );
|
|
temp.StripFileExtension();
|
|
temp.SetFileExtension( "manifest" );
|
|
outManifest.WriteManifestFile( temp );
|
|
}
|
|
|
|
for ( int idx = 0; idx < outPutFiles.Num(); idx++ ) {
|
|
|
|
idStrList &fileList = outPutFiles[ idx ];
|
|
if ( fileList.Num() == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
idStr fileName = manifestName;
|
|
if ( idx > 0 ) {
|
|
fileName = va( "%s_%02d", manifestName, idx );
|
|
}
|
|
fileName.SetFileExtension( "resources" );
|
|
|
|
idFile *resFile = fileSystem->OpenFileWrite( fileName );
|
|
|
|
if ( resFile == NULL ) {
|
|
idLib::Warning( "Cannot open %s for writing.\n", fileName.c_str() );
|
|
return;
|
|
}
|
|
|
|
idLib::Printf( "Writing resource file %s\n", fileName.c_str() );
|
|
|
|
int tableOffset = 0;
|
|
int tableLength = 0;
|
|
int tableNewLength = 0;
|
|
uint32 resourceFileMagic = RESOURCE_FILE_MAGIC;
|
|
|
|
resFile->WriteBig( resourceFileMagic );
|
|
resFile->WriteBig( tableOffset );
|
|
resFile->WriteBig( tableLength );
|
|
|
|
idList< idResourceCacheEntry > entries;
|
|
|
|
entries.Resize( fileList.Num() );
|
|
|
|
for ( int i = 0; i < fileList.Num(); i++ ) {
|
|
idResourceCacheEntry ent;
|
|
|
|
ent.filename = fileList[ i ];
|
|
ent.length = 0;
|
|
ent.offset = 0;
|
|
|
|
idFile *file = fileSystem->OpenFileReadMemory( ent.filename, false );
|
|
idFile_Memory *fm = dynamic_cast< idFile_Memory* >( file );
|
|
if ( fm == NULL ) {
|
|
continue;
|
|
}
|
|
// if the entry is uncompressed, align the file pointer to a 16 byte boundary
|
|
// so it will be usable if memory mapped
|
|
ent.length = fm->Length();
|
|
|
|
// always get the offset, even if the file will have zero length
|
|
ent.offset = resFile->Tell();
|
|
|
|
entries.Append( ent );
|
|
|
|
if ( ent.length == 0 ) {
|
|
ent.filename = "";
|
|
delete fm;
|
|
continue;
|
|
}
|
|
|
|
resFile->Write( fm->GetDataPtr(), ent.length );
|
|
|
|
delete fm;
|
|
|
|
// pacifier every ten megs
|
|
if ( ( ent.offset + ent.length ) / 10000000 != ent.offset / 10000000 ) {
|
|
idLib::Printf( "." );
|
|
}
|
|
}
|
|
|
|
idLib::Printf( "\n" );
|
|
|
|
// write the table out now that we have all the files
|
|
tableOffset = resFile->Tell();
|
|
|
|
// count how many we are going to write for this platform
|
|
int numFileResources = entries.Num();
|
|
|
|
resFile->WriteBig( numFileResources );
|
|
|
|
// write the individual resource entries
|
|
for ( int i = 0; i < entries.Num(); i++ ) {
|
|
entries[ i ].Write( resFile );
|
|
if ( i + 1 == numFileResources ) {
|
|
// we just wrote out the last new entry
|
|
tableNewLength = resFile->Tell() - tableOffset;
|
|
}
|
|
}
|
|
|
|
// go back and write the header offsets again, now that we have file offsets and lengths
|
|
tableLength = resFile->Tell() - tableOffset;
|
|
resFile->Seek( 0, FS_SEEK_SET );
|
|
resFile->WriteBig( resourceFileMagic );
|
|
resFile->WriteBig( tableOffset );
|
|
resFile->WriteBig( tableLength );
|
|
delete resFile;
|
|
}
|
|
}
|