File_Resource.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. 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.
  17. 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.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. /*
  23. ================================================================================================
  24. idResourceContainer
  25. ================================================================================================
  26. */
  27. /*
  28. ========================
  29. idResourceContainer::ReOpen
  30. ========================
  31. */
  32. void idResourceContainer::ReOpen() {
  33. delete resourceFile;
  34. resourceFile = fileSystem->OpenFileRead( fileName );
  35. }
  36. /*
  37. ========================
  38. idResourceContainer::Init
  39. ========================
  40. */
  41. bool idResourceContainer::Init( const char *_fileName, uint8 containerIndex ) {
  42. if ( idStr::Icmp( _fileName, "_ordered.resources" ) == 0 ) {
  43. resourceFile = fileSystem->OpenFileReadMemory( _fileName );
  44. } else {
  45. resourceFile = fileSystem->OpenFileRead( _fileName );
  46. }
  47. if ( resourceFile == NULL ) {
  48. idLib::Warning( "Unable to open resource file %s", _fileName );
  49. return false;
  50. }
  51. resourceFile->ReadBig( resourceMagic );
  52. if ( resourceMagic != RESOURCE_FILE_MAGIC ) {
  53. idLib::FatalError( "resourceFileMagic != RESOURCE_FILE_MAGIC" );
  54. }
  55. fileName = _fileName;
  56. resourceFile->ReadBig( tableOffset );
  57. resourceFile->ReadBig( tableLength );
  58. // read this into a memory buffer with a single read
  59. char * const buf = (char *)Mem_Alloc( tableLength, TAG_RESOURCE );
  60. resourceFile->Seek( tableOffset, FS_SEEK_SET );
  61. resourceFile->Read( buf, tableLength );
  62. idFile_Memory memFile( "resourceHeader", (const char *)buf, tableLength );
  63. // Parse the resourceFile header, which includes every resource used
  64. // by the game.
  65. memFile.ReadBig( numFileResources );
  66. cacheTable.SetNum( numFileResources );
  67. for ( int i = 0; i < numFileResources; i++ ) {
  68. idResourceCacheEntry &rt = cacheTable[ i ];
  69. rt.Read( &memFile );
  70. rt.filename.BackSlashesToSlashes();
  71. rt.filename.ToLower();
  72. rt.containerIndex = containerIndex;
  73. const int key = cacheHash.GenerateKey( rt.filename, false );
  74. bool found = false;
  75. //for ( int index = cacheHash.GetFirst( key ); index != idHashIndex::NULL_INDEX; index = cacheHash.GetNext( index ) ) {
  76. // idResourceCacheEntry & rtc = cacheTable[ index ];
  77. // if ( idStr::Icmp( rtc.filename, rt.filename ) == 0 ) {
  78. // found = true;
  79. // break;
  80. // }
  81. //}
  82. if ( !found ) {
  83. //idLib::Printf( "rez file name: %s\n", rt.filename.c_str() );
  84. cacheHash.Add( key, i );
  85. }
  86. }
  87. Mem_Free( buf );
  88. return true;
  89. }
  90. /*
  91. ========================
  92. idResourceContainer::WriteManifestFile
  93. ========================
  94. */
  95. void idResourceContainer::WriteManifestFile( const char *name, const idStrList &list ) {
  96. idStr filename( name );
  97. filename.SetFileExtension( "manifest" );
  98. filename.Insert( "maps/", 0 );
  99. idFile *outFile = fileSystem->OpenFileWrite( filename );
  100. if ( outFile != NULL ) {
  101. int num = list.Num();
  102. outFile->WriteBig( num );
  103. for ( int i = 0; i < num; i++ ) {
  104. outFile->WriteString( list[ i ] );
  105. }
  106. delete outFile;
  107. }
  108. }
  109. /*
  110. ========================
  111. idResourceContainer::ReadManifestFile
  112. ========================
  113. */
  114. int idResourceContainer::ReadManifestFile( const char *name, idStrList &list ) {
  115. idFile *inFile = fileSystem->OpenFileRead( name );
  116. if ( inFile != NULL ) {
  117. list.SetGranularity( 16384 );
  118. idStr str;
  119. int num;
  120. list.Clear();
  121. inFile->ReadBig( num );
  122. for ( int i = 0; i < num; i++ ) {
  123. inFile->ReadString( str );
  124. list.Append( str );
  125. }
  126. delete inFile;
  127. }
  128. return list.Num();
  129. }
  130. /*
  131. ========================
  132. idResourceContainer::UpdateResourceFile
  133. ========================
  134. */
  135. void idResourceContainer::UpdateResourceFile( const char *_filename, const idStrList &_filesToUpdate ) {
  136. idFile *outFile = fileSystem->OpenFileWrite( va( "%s.new", _filename ) );
  137. if ( outFile == NULL ) {
  138. idLib::Warning( "Unable to open resource file %s or new output file", _filename );
  139. return;
  140. }
  141. uint32 magic = 0;
  142. int _tableOffset = 0;
  143. int _tableLength = 0;
  144. idList< idResourceCacheEntry > entries;
  145. idStrList filesToUpdate = _filesToUpdate;
  146. idFile *inFile = fileSystem->OpenFileRead( _filename );
  147. if ( inFile == NULL ) {
  148. magic = RESOURCE_FILE_MAGIC;
  149. outFile->WriteBig( magic );
  150. outFile->WriteBig( _tableOffset );
  151. outFile->WriteBig( _tableLength );
  152. } else {
  153. inFile->ReadBig( magic );
  154. if ( magic != RESOURCE_FILE_MAGIC ) {
  155. delete inFile;
  156. return;
  157. }
  158. inFile->ReadBig( _tableOffset );
  159. inFile->ReadBig( _tableLength );
  160. // read this into a memory buffer with a single read
  161. char * const buf = (char *)Mem_Alloc( _tableLength, TAG_RESOURCE );
  162. inFile->Seek( _tableOffset, FS_SEEK_SET );
  163. inFile->Read( buf, _tableLength );
  164. idFile_Memory memFile( "resourceHeader", (const char *)buf, _tableLength );
  165. int _numFileResources = 0;
  166. memFile.ReadBig( _numFileResources );
  167. outFile->WriteBig( magic );
  168. outFile->WriteBig( _tableOffset );
  169. outFile->WriteBig( _tableLength );
  170. entries.SetNum( _numFileResources );
  171. for ( int i = 0; i < _numFileResources; i++ ) {
  172. entries[ i ].Read( &memFile );
  173. idLib::Printf( "examining %s\n", entries[ i ].filename.c_str() );
  174. byte * fileData = NULL;
  175. for ( int j = filesToUpdate.Num() - 1; j >= 0; j-- ) {
  176. if ( filesToUpdate[ j ].Icmp( entries[ i ].filename ) == 0 ) {
  177. idFile *newFile = fileSystem->OpenFileReadMemory( filesToUpdate[ j ] );
  178. if ( newFile != NULL ) {
  179. idLib::Printf( "Updating %s\n", filesToUpdate[ j ].c_str() );
  180. entries[ i ].length = newFile->Length();
  181. fileData = (byte *)Mem_Alloc( entries[ i ].length, TAG_TEMP );
  182. newFile->Read( fileData, newFile->Length() );
  183. delete newFile;
  184. }
  185. filesToUpdate.RemoveIndex( j );
  186. }
  187. }
  188. if ( fileData == NULL ) {
  189. inFile->Seek( entries[ i ].offset, FS_SEEK_SET );
  190. fileData = (byte *)Mem_Alloc( entries[ i ].length, TAG_TEMP );
  191. inFile->Read( fileData, entries[ i ].length );
  192. }
  193. entries[ i ].offset = outFile->Tell();
  194. outFile->Write( ( void* )fileData, entries[ i ].length );
  195. Mem_Free( fileData );
  196. }
  197. Mem_Free( buf );
  198. }
  199. while ( filesToUpdate.Num() > 0 ) {
  200. idFile *newFile = fileSystem->OpenFileReadMemory( filesToUpdate[ 0 ] );
  201. if ( newFile != NULL ) {
  202. idLib::Printf( "Appending %s\n", filesToUpdate[ 0 ].c_str() );
  203. idResourceCacheEntry rt;
  204. rt.filename = filesToUpdate[ 0 ];
  205. rt.length = newFile->Length();
  206. byte * fileData = (byte *)Mem_Alloc( rt.length, TAG_TEMP );
  207. newFile->Read( fileData, rt.length );
  208. int idx = entries.Append( rt );
  209. if ( idx >= 0 ) {
  210. entries[ idx ].offset = outFile->Tell();
  211. outFile->Write( ( void* )fileData, entries[ idx ].length );
  212. }
  213. delete newFile;
  214. Mem_Free( fileData );
  215. }
  216. filesToUpdate.RemoveIndex( 0 );
  217. }
  218. _tableOffset = outFile->Tell();
  219. outFile->WriteBig( entries.Num() );
  220. // write the individual resource entries
  221. for ( int i = 0; i < entries.Num(); i++ ) {
  222. entries[ i ].Write( outFile );
  223. }
  224. // go back and write the header offsets again, now that we have file offsets and lengths
  225. _tableLength = outFile->Tell() - _tableOffset;
  226. outFile->Seek( 0, FS_SEEK_SET );
  227. outFile->WriteBig( magic );
  228. outFile->WriteBig( _tableOffset );
  229. outFile->WriteBig( _tableLength );
  230. delete outFile;
  231. delete inFile;
  232. }
  233. /*
  234. ========================
  235. idResourceContainer::ExtractResourceFile
  236. ========================
  237. */
  238. void idResourceContainer::SetContainerIndex( const int & _idx ) {
  239. for ( int i = 0; i < cacheTable.Num(); i++ ) {
  240. cacheTable[ i ].containerIndex = _idx;
  241. }
  242. }
  243. /*
  244. ========================
  245. idResourceContainer::ExtractResourceFile
  246. ========================
  247. */
  248. void idResourceContainer::ExtractResourceFile ( const char * _fileName, const char * _outPath, bool _copyWavs ) {
  249. idFile *inFile = fileSystem->OpenFileRead( _fileName );
  250. if ( inFile == NULL ) {
  251. idLib::Warning( "Unable to open resource file %s", _fileName );
  252. return;
  253. }
  254. uint32 magic;
  255. inFile->ReadBig( magic );
  256. if ( magic != RESOURCE_FILE_MAGIC ) {
  257. delete inFile;
  258. return;
  259. }
  260. int _tableOffset;
  261. int _tableLength;
  262. inFile->ReadBig( _tableOffset );
  263. inFile->ReadBig( _tableLength );
  264. // read this into a memory buffer with a single read
  265. char * const buf = (char *)Mem_Alloc( _tableLength, TAG_RESOURCE );
  266. inFile->Seek( _tableOffset, FS_SEEK_SET );
  267. inFile->Read( buf, _tableLength );
  268. idFile_Memory memFile( "resourceHeader", (const char *)buf, _tableLength );
  269. int _numFileResources;
  270. memFile.ReadBig( _numFileResources );
  271. for ( int i = 0; i < _numFileResources; i++ ) {
  272. idResourceCacheEntry rt;
  273. rt.Read( &memFile );
  274. rt.filename.BackSlashesToSlashes();
  275. rt.filename.ToLower();
  276. byte *fbuf = NULL;
  277. if ( _copyWavs && ( rt.filename.Find( ".idwav" ) >= 0 || rt.filename.Find( ".idxma" ) >= 0 || rt.filename.Find( ".idmsf" ) >= 0 ) ) {
  278. rt.filename.SetFileExtension( "wav" );
  279. rt.filename.Replace( "generated/", "" );
  280. int len = fileSystem->GetFileLength( rt.filename );
  281. fbuf = (byte *)Mem_Alloc( len, TAG_RESOURCE );
  282. fileSystem->ReadFile( rt.filename, (void**)&fbuf, NULL );
  283. } else {
  284. inFile->Seek( rt.offset, FS_SEEK_SET );
  285. fbuf = (byte *)Mem_Alloc( rt.length, TAG_RESOURCE );
  286. inFile->Read( fbuf, rt.length );
  287. }
  288. idStr outName = _outPath;
  289. outName.AppendPath( rt.filename );
  290. idFile *outFile = fileSystem->OpenExplicitFileWrite( outName );
  291. if ( outFile != NULL ) {
  292. outFile->Write( ( byte* )fbuf, rt.length );
  293. delete outFile;
  294. }
  295. Mem_Free( fbuf );
  296. }
  297. delete inFile;
  298. Mem_Free( buf );
  299. }
  300. /*
  301. ========================
  302. idResourceContainer::Open
  303. ========================
  304. */
  305. void idResourceContainer::WriteResourceFile( const char *manifestName, const idStrList &manifest, const bool &_writeManifest ) {
  306. if ( manifest.Num() == 0 ) {
  307. return;
  308. }
  309. idLib::Printf( "Writing resource file %s\n", manifestName );
  310. // build multiple output files at 1GB each
  311. idList < idStrList > outPutFiles;
  312. idFileManifest outManifest;
  313. int64 size = 0;
  314. idStrList flist;
  315. flist.SetGranularity( 16384 );
  316. for ( int i = 0; i < manifest.Num(); i++ ) {
  317. flist.Append( manifest[ i ] );
  318. size += fileSystem->GetFileLength( manifest[ i ] );
  319. if ( size > 1024 * 1024 * 1024 ) {
  320. outPutFiles.Append( flist );
  321. size = 0;
  322. flist.Clear();
  323. }
  324. outManifest.AddFile( manifest[ i ] );
  325. }
  326. outPutFiles.Append( flist );
  327. if ( _writeManifest ) {
  328. idStr temp = manifestName;
  329. temp.Replace( "maps/", "manifests/" );
  330. temp.StripFileExtension();
  331. temp.SetFileExtension( "manifest" );
  332. outManifest.WriteManifestFile( temp );
  333. }
  334. for ( int idx = 0; idx < outPutFiles.Num(); idx++ ) {
  335. idStrList &fileList = outPutFiles[ idx ];
  336. if ( fileList.Num() == 0 ) {
  337. continue;
  338. }
  339. idStr fileName = manifestName;
  340. if ( idx > 0 ) {
  341. fileName = va( "%s_%02d", manifestName, idx );
  342. }
  343. fileName.SetFileExtension( "resources" );
  344. idFile *resFile = fileSystem->OpenFileWrite( fileName );
  345. if ( resFile == NULL ) {
  346. idLib::Warning( "Cannot open %s for writing.\n", fileName.c_str() );
  347. return;
  348. }
  349. idLib::Printf( "Writing resource file %s\n", fileName.c_str() );
  350. int tableOffset = 0;
  351. int tableLength = 0;
  352. int tableNewLength = 0;
  353. uint32 resourceFileMagic = RESOURCE_FILE_MAGIC;
  354. resFile->WriteBig( resourceFileMagic );
  355. resFile->WriteBig( tableOffset );
  356. resFile->WriteBig( tableLength );
  357. idList< idResourceCacheEntry > entries;
  358. entries.Resize( fileList.Num() );
  359. for ( int i = 0; i < fileList.Num(); i++ ) {
  360. idResourceCacheEntry ent;
  361. ent.filename = fileList[ i ];
  362. ent.length = 0;
  363. ent.offset = 0;
  364. idFile *file = fileSystem->OpenFileReadMemory( ent.filename, false );
  365. idFile_Memory *fm = dynamic_cast< idFile_Memory* >( file );
  366. if ( fm == NULL ) {
  367. continue;
  368. }
  369. // if the entry is uncompressed, align the file pointer to a 16 byte boundary
  370. // so it will be usable if memory mapped
  371. ent.length = fm->Length();
  372. // always get the offset, even if the file will have zero length
  373. ent.offset = resFile->Tell();
  374. entries.Append( ent );
  375. if ( ent.length == 0 ) {
  376. ent.filename = "";
  377. delete fm;
  378. continue;
  379. }
  380. resFile->Write( fm->GetDataPtr(), ent.length );
  381. delete fm;
  382. // pacifier every ten megs
  383. if ( ( ent.offset + ent.length ) / 10000000 != ent.offset / 10000000 ) {
  384. idLib::Printf( "." );
  385. }
  386. }
  387. idLib::Printf( "\n" );
  388. // write the table out now that we have all the files
  389. tableOffset = resFile->Tell();
  390. // count how many we are going to write for this platform
  391. int numFileResources = entries.Num();
  392. resFile->WriteBig( numFileResources );
  393. // write the individual resource entries
  394. for ( int i = 0; i < entries.Num(); i++ ) {
  395. entries[ i ].Write( resFile );
  396. if ( i + 1 == numFileResources ) {
  397. // we just wrote out the last new entry
  398. tableNewLength = resFile->Tell() - tableOffset;
  399. }
  400. }
  401. // go back and write the header offsets again, now that we have file offsets and lengths
  402. tableLength = resFile->Tell() - tableOffset;
  403. resFile->Seek( 0, FS_SEEK_SET );
  404. resFile->WriteBig( resourceFileMagic );
  405. resFile->WriteBig( tableOffset );
  406. resFile->WriteBig( tableLength );
  407. delete resFile;
  408. }
  409. }