2007-05-30 21:56:52 +00:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
2004-10-15 06:06:47 +00:00
*
* This program 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 2
* of the License , or ( at your option ) any later version .
* This program 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 this program ; if not , write to the Free Software
2005-10-18 01:30:26 +00:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
2004-10-15 06:06:47 +00:00
*
2006-02-09 12:19:53 +00:00
* $ URL $
* $ Id $
2004-10-15 06:06:47 +00:00
*
*/
2007-09-19 08:40:12 +00:00
2006-07-15 20:30:36 +00:00
# include "common/config-manager.h"
2006-03-29 15:59:37 +00:00
# include "common/endian.h"
2004-10-15 06:06:47 +00:00
# include "common/file.h"
2006-07-15 20:30:36 +00:00
# include "common/fs.h"
2007-05-23 12:02:31 +00:00
# include "common/func.h"
# include "common/algorithm.h"
2006-07-08 12:23:44 +00:00
# include "gui/message.h"
2005-08-19 22:12:09 +00:00
# include "kyra/resource.h"
2004-10-15 06:06:47 +00:00
namespace Kyra {
2007-05-23 12:02:31 +00:00
2008-02-07 23:13:13 +00:00
Resource : : Resource ( KyraEngine * vm ) : _loaders ( ) , _map ( ) , _vm ( vm ) {
initializeLoaders ( ) ;
2007-10-09 05:40:20 +00:00
}
Resource : : ~ Resource ( ) {
unloadAllPakFiles ( ) ;
2008-02-07 23:38:17 +00:00
for ( LoaderIterator i = _loaders . begin ( ) ; i ! = _loaders . end ( ) ; + + i )
delete ( * i ) ;
_loaders . clear ( ) ;
2007-10-09 05:40:20 +00:00
}
bool Resource : : reset ( ) {
unloadAllPakFiles ( ) ;
FilesystemNode dir ( ConfMan . get ( " path " ) ) ;
2008-01-27 19:47:41 +00:00
2007-10-09 05:40:20 +00:00
if ( ! dir . exists ( ) | | ! dir . isDirectory ( ) )
error ( " invalid game path '%s' " , dir . getPath ( ) . c_str ( ) ) ;
2005-07-30 21:11:48 +00:00
2008-01-10 23:25:19 +00:00
if ( _vm - > game ( ) ! = GI_KYRA3 ) {
2008-01-03 15:20:33 +00:00
if ( ! loadPakFile ( StaticResource : : staticDataFilename ( ) ) | | ! StaticResource : : checkKyraDat ( ) ) {
Common : : String errorMessage = " You're missing the ' " + StaticResource : : staticDataFilename ( ) + " ' file or it got corrupted, (re)get it from the ScummVM website " ;
2008-01-03 14:53:18 +00:00
GUI : : MessageDialog errorMsg ( errorMessage ) ;
2006-07-08 12:23:44 +00:00
errorMsg . runModal ( ) ;
2008-01-03 14:53:18 +00:00
error ( errorMessage . c_str ( ) ) ;
2006-05-28 04:08:25 +00:00
}
2008-01-10 23:25:19 +00:00
}
2006-07-15 20:30:36 +00:00
2008-01-10 23:25:19 +00:00
if ( _vm - > game ( ) = = GI_KYRA1 ) {
2006-07-15 20:32:54 +00:00
// We only need kyra.dat for the demo.
2007-04-01 13:10:50 +00:00
if ( _vm - > gameFlags ( ) . isDemo )
2007-10-09 05:40:20 +00:00
return true ;
2006-07-15 20:32:54 +00:00
2006-07-15 20:30:36 +00:00
// only VRM file we need in the *whole* game for kyra1
2007-04-01 13:10:50 +00:00
if ( _vm - > gameFlags ( ) . isTalkie )
2006-07-16 19:44:39 +00:00
loadPakFile ( " CHAPTER1.VRM " ) ;
2008-01-10 23:25:19 +00:00
} else if ( _vm - > game ( ) = = GI_KYRA2 ) {
// mouse pointer, fonts, etc. required for initializing
if ( _vm - > gameFlags ( ) . isDemo ) {
loadPakFile ( " GENERAL.PAK " ) ;
} else {
loadPakFile ( " INTROGEN.PAK " ) ;
loadPakFile ( " OTHER.PAK " ) ;
}
return true ;
2007-04-01 13:10:50 +00:00
} else if ( _vm - > game ( ) = = GI_KYRA3 ) {
2008-02-07 23:13:13 +00:00
loadPakFile ( " WESTWOOD.001 " ) ;
2006-05-28 04:08:25 +00:00
}
2006-07-15 20:32:54 +00:00
2006-07-15 20:30:36 +00:00
FSList fslist ;
2007-06-05 21:02:35 +00:00
if ( ! dir . getChildren ( fslist , FilesystemNode : : kListFilesOnly ) )
2007-10-09 05:40:20 +00:00
error ( " can't list files inside game path '%s' " , dir . getPath ( ) . c_str ( ) ) ;
2004-11-14 20:11:22 +00:00
2007-04-01 13:10:50 +00:00
if ( _vm - > game ( ) = = GI_KYRA1 & & _vm - > gameFlags ( ) . isTalkie ) {
2006-09-16 14:36:53 +00:00
static const char * list [ ] = {
2006-10-24 00:30:48 +00:00
" ADL.PAK " , " CHAPTER1.VRM " , " COL.PAK " , " FINALE.PAK " , " INTRO1.PAK " , " INTRO2.PAK " ,
" INTRO3.PAK " , " INTRO4.PAK " , " MISC.PAK " , " SND.PAK " , " STARTUP.PAK " , " XMI.PAK " ,
2007-05-23 12:02:31 +00:00
" CAVE.APK " , " DRAGON1.APK " , " DRAGON2.APK " , " LAGOON.APK "
2006-09-16 14:36:53 +00:00
} ;
2007-05-23 12:02:31 +00:00
Common : : for_each ( list , list + ARRAYSIZE ( list ) , Common : : bind1st ( Common : : mem_fun ( & Resource : : loadPakFile ) , this ) ) ;
2008-02-07 23:14:53 +00:00
for ( int i = 0 ; i < ARRAYSIZE ( list ) ; + + i ) {
ResFileMap : : iterator iterator = _map . find ( list [ i ] ) ;
if ( iterator ! = _map . end ( ) )
iterator - > _value . prot = true ;
}
2006-09-16 14:36:53 +00:00
} else {
for ( FSList : : const_iterator file = fslist . begin ( ) ; file ! = fslist . end ( ) ; + + file ) {
2007-06-05 21:02:35 +00:00
Common : : String filename = file - > getName ( ) ;
2006-09-16 14:36:53 +00:00
filename . toUppercase ( ) ;
2007-03-24 00:04:08 +00:00
// No real PAK file!
if ( filename = = " TWMUSIC.PAK " )
continue ;
2008-02-07 23:13:13 +00:00
if ( filename = = ( ( _vm - > gameFlags ( ) . lang = = Common : : EN_ANY ) ? " JMC.PAK " : " EMC.PAK " ) )
continue ;
if ( filename . hasSuffix ( " .PAK " ) | | filename . hasSuffix ( " .APK " ) ) {
2007-06-05 21:02:35 +00:00
if ( ! loadPakFile ( file - > getName ( ) ) )
error ( " couldn't open pakfile '%s' " , file - > getName ( ) . c_str ( ) ) ;
2006-07-15 20:37:32 +00:00
}
2006-07-15 20:30:36 +00:00
}
2004-10-15 06:06:47 +00:00
}
2005-07-30 21:11:48 +00:00
2007-10-09 05:40:20 +00:00
return true ;
2005-10-12 19:15:32 +00:00
}
2007-05-23 12:02:31 +00:00
bool Resource : : loadPakFile ( const Common : : String & filename ) {
2008-02-07 23:13:13 +00:00
ResFileMap : : iterator iter = _map . find ( filename ) ;
if ( iter = = _map . end ( ) )
return false ;
2006-07-08 13:56:56 +00:00
2008-02-09 15:46:06 +00:00
if ( iter - > _value . preload ) {
iter - > _value . mounted = true ;
2008-02-09 15:33:04 +00:00
return true ;
2008-02-09 15:46:06 +00:00
}
2007-09-19 08:40:12 +00:00
2008-02-09 15:33:04 +00:00
const ResArchiveLoader * loader = getLoader ( iter - > _value . type ) ;
if ( ! loader ) {
error ( " no archive loader for file '%s' found which is of type %d " , filename . c_str ( ) , iter - > _value . type ) ;
2006-03-18 14:43:18 +00:00
return false ;
2008-02-09 15:33:04 +00:00
}
2006-07-08 13:56:56 +00:00
2008-02-09 15:46:06 +00:00
if ( ! isAccessable ( filename ) )
2008-02-09 15:33:04 +00:00
return false ;
2008-02-07 23:13:13 +00:00
2008-02-09 15:33:04 +00:00
Common : : SeekableReadStream * stream = getFileStream ( filename ) ;
if ( ! stream ) {
error ( " archive file '%s' not found " , filename . c_str ( ) ) ;
return false ;
}
2008-02-07 23:13:13 +00:00
2008-02-09 15:46:06 +00:00
iter - > _value . mounted = true ;
2008-02-09 15:33:04 +00:00
iter - > _value . preload = true ;
2008-02-09 16:18:44 +00:00
ResArchiveLoader : : FileList files ;
loader - > loadFile ( filename , * stream , files ) ;
2008-02-07 23:13:13 +00:00
delete stream ;
stream = 0 ;
2008-02-09 16:18:44 +00:00
for ( ResArchiveLoader : : FileList : : iterator i = files . begin ( ) ; i ! = files . end ( ) ; + + i ) {
iter = _map . find ( i - > filename ) ;
if ( iter = = _map . end ( ) ) {
2008-02-09 16:27:49 +00:00
// A new file entry, so we just insert it into the file map.
2008-02-09 16:18:44 +00:00
_map [ i - > filename ] = i - > entry ;
} else if ( ! iter - > _value . parent . empty ( ) ) {
if ( ! iter - > _value . parent . equalsIgnoreCase ( filename ) ) {
ResFileMap : : iterator oldParent = _map . find ( iter - > _value . parent ) ;
if ( oldParent ! = _map . end ( ) ) {
2008-02-09 16:27:49 +00:00
// Protected files and their embedded file entries do not get overwritten.
2008-02-09 16:18:44 +00:00
if ( ! oldParent - > _value . prot ) {
2008-02-09 16:27:49 +00:00
// If the old parent is not protected we mark it as not preload anymore,
// since now no longer all of its embedded files are in the filemap.
2008-02-09 16:18:44 +00:00
oldParent - > _value . preload = false ;
_map [ i - > filename ] = i - > entry ;
}
} else {
// Old parent not found? That's strange... But we just overwrite the old
// entry.
_map [ i - > filename ] = i - > entry ;
}
} else {
2008-02-09 16:27:49 +00:00
// The old parent has the same filenames as the new archive, we are sure and overwrite the
// old file entry, could be afterall that the preload flag of the new archive was
// just unflagged.
2008-02-09 16:18:44 +00:00
_map [ i - > filename ] = i - > entry ;
}
}
2008-02-09 16:27:49 +00:00
// 'else' case would mean here overwriting an existing file entry in the map without parent.
2008-02-09 16:18:44 +00:00
// We don't support that though, so one can overwrite files from archives by putting
// them in the gamepath.
}
2007-03-20 21:11:42 +00:00
2008-02-07 23:13:13 +00:00
detectFileTypes ( ) ;
2005-10-12 19:15:32 +00:00
return true ;
}
2007-10-09 05:40:20 +00:00
bool Resource : : loadFileList ( const Common : : String & filedata ) {
Common : : File f ;
2008-01-27 19:47:41 +00:00
2007-10-09 05:40:20 +00:00
if ( ! f . open ( filedata ) )
return false ;
uint32 filenameOffset = 0 ;
while ( ( filenameOffset = f . readUint32LE ( ) ) ! = 0 ) {
uint32 offset = f . pos ( ) ;
f . seek ( filenameOffset , SEEK_SET ) ;
uint8 buffer [ 64 ] ;
f . read ( buffer , sizeof ( buffer ) ) ;
f . seek ( offset + 16 , SEEK_SET ) ;
Common : : String filename = ( char * ) buffer ;
2007-10-09 05:42:52 +00:00
filename . toUppercase ( ) ;
2007-10-09 05:40:20 +00:00
if ( filename . hasSuffix ( " .PAK " ) ) {
2008-03-20 11:55:52 +00:00
if ( ! isAccessable ( filename ) & & _vm - > gameFlags ( ) . isDemo ) {
// the demo version supplied with Kyra3 does not
// contain all pak files listed in filedata.fdt
// so we don't do anything here if they are non
// existant.
2008-03-20 11:58:18 +00:00
} else if ( ! loadPakFile ( filename ) ) {
2007-10-09 05:40:20 +00:00
error ( " couldn't load file '%s' " , filename . c_str ( ) ) ;
return false ;
}
2008-01-27 19:47:41 +00:00
}
2007-10-09 05:40:20 +00:00
}
return true ;
}
2008-01-10 23:25:19 +00:00
bool Resource : : loadFileList ( const char * const * filelist , uint32 numFiles ) {
if ( ! filelist )
return false ;
while ( numFiles - - ) {
if ( ! loadPakFile ( filelist [ numFiles ] ) ) {
error ( " couldn't load file '%s' " , filelist [ numFiles ] ) ;
return false ;
}
}
return true ;
}
2006-06-03 18:30:07 +00:00
void Resource : : unloadPakFile ( const Common : : String & filename ) {
2008-02-07 23:13:13 +00:00
ResFileMap : : iterator iter = _map . find ( filename ) ;
if ( iter ! = _map . end ( ) ) {
if ( ! iter - > _value . prot )
2008-02-09 15:46:06 +00:00
iter - > _value . mounted = false ;
2008-02-07 23:13:13 +00:00
}
2005-10-12 19:15:32 +00:00
}
2007-05-23 12:02:31 +00:00
bool Resource : : isInPakList ( const Common : : String & filename ) const {
2008-02-07 23:13:13 +00:00
return isAccessable ( filename ) ;
2004-11-14 20:11:22 +00:00
}
2005-07-30 21:11:48 +00:00
2007-10-09 05:40:20 +00:00
void Resource : : unloadAllPakFiles ( ) {
2008-02-07 23:13:13 +00:00
// remove all entries
_map . clear ( ) ;
2008-02-24 12:30:12 +00:00
addSearchPath ( ConfMan . get ( " path " ) ) ;
addSearchPath ( ConfMan . get ( " extrapath " ) ) ;
2008-02-07 23:13:13 +00:00
Common : : File temp ;
2008-02-08 18:45:25 +00:00
ResFileMap : : iterator iter = _map . find ( StaticResource : : staticDataFilename ( ) ) ;
2008-02-08 18:43:13 +00:00
if ( iter = = _map . end ( ) ) {
2008-02-08 18:45:25 +00:00
if ( temp . open ( StaticResource : : staticDataFilename ( ) ) ) {
2008-02-08 18:43:13 +00:00
ResFileEntry entry ;
entry . parent = " " ;
entry . size = temp . size ( ) ;
2008-02-09 15:46:06 +00:00
entry . mounted = true ;
2008-02-08 18:43:13 +00:00
entry . preload = false ;
entry . prot = false ;
2008-02-09 15:08:49 +00:00
entry . type = ResFileEntry : : kAutoDetect ;
2008-02-08 18:43:13 +00:00
entry . offset = 0 ;
2008-02-08 18:45:25 +00:00
_map [ StaticResource : : staticDataFilename ( ) ] = entry ;
2008-02-08 18:43:13 +00:00
temp . close ( ) ;
}
}
2008-02-24 12:33:20 +00:00
detectFileTypes ( ) ;
2008-02-24 12:30:12 +00:00
}
bool Resource : : addSearchPath ( const Common : : String & path ) {
if ( path . empty ( ) )
return false ;
2008-02-24 12:33:20 +00:00
FilesystemNode dir ( path ) ;
2008-02-24 12:30:12 +00:00
if ( ! dir . exists ( ) | | ! dir . isDirectory ( ) ) {
warning ( " invalid data path '%s' " , dir . getPath ( ) . c_str ( ) ) ;
return false ;
}
FSList fslist ;
if ( ! dir . getChildren ( fslist , FilesystemNode : : kListFilesOnly ) ) {
warning ( " can't list files inside path '%s' " , dir . getPath ( ) . c_str ( ) ) ;
return false ;
}
Common : : File temp ;
for ( FSList : : const_iterator file = fslist . begin ( ) ; file ! = fslist . end ( ) ; + + file ) {
ResFileEntry entry ;
entry . parent = " " ;
if ( ! temp . open ( file - > getPath ( ) ) )
error ( " couldn't open file '%s' " , file - > getName ( ) . c_str ( ) ) ;
entry . size = temp . size ( ) ;
entry . offset = 0 ;
entry . mounted = false ;
entry . preload = false ;
entry . prot = false ;
entry . type = ResFileEntry : : kAutoDetect ;
_map [ file - > getName ( ) ] = entry ;
temp . close ( ) ;
}
2008-02-08 18:43:13 +00:00
2008-02-07 23:13:13 +00:00
detectFileTypes ( ) ;
2008-02-24 12:30:12 +00:00
return true ;
2007-10-09 05:40:20 +00:00
}
2007-05-23 12:02:31 +00:00
uint8 * Resource : : fileData ( const char * file , uint32 * size ) const {
2008-02-07 23:13:13 +00:00
Common : : SeekableReadStream * stream = getFileStream ( file ) ;
if ( ! stream )
return 0 ;
2005-07-30 21:11:48 +00:00
2008-02-07 23:13:13 +00:00
uint32 bufferSize = stream - > size ( ) ;
uint8 * buffer = new uint8 [ bufferSize ] ;
assert ( buffer ) ;
2006-07-30 07:51:11 +00:00
if ( size )
2008-02-07 23:13:13 +00:00
* size = bufferSize ;
stream - > read ( buffer , bufferSize ) ;
delete stream ;
return buffer ;
}
2005-07-30 21:11:48 +00:00
2008-02-07 23:13:13 +00:00
uint32 Resource : : getFileSize ( const char * file ) const {
if ( ! isAccessable ( file ) )
return 0 ;
2006-09-16 20:51:05 +00:00
2008-02-07 23:13:13 +00:00
ResFileMap : : const_iterator iter = _map . find ( file ) ;
if ( iter ! = _map . end ( ) )
return iter - > _value . size ;
return 0 ;
}
2006-07-30 07:51:11 +00:00
2008-02-07 23:13:13 +00:00
bool Resource : : loadFileToBuf ( const char * file , void * buf , uint32 maxSize ) {
Common : : SeekableReadStream * stream = getFileStream ( file ) ;
if ( ! stream )
return false ;
2007-07-02 23:02:54 +00:00
2008-02-07 23:13:13 +00:00
memset ( buf , 0 , maxSize ) ;
stream - > read ( buf , stream - > size ( ) ) ;
delete stream ;
return true ;
}
2007-07-02 23:02:54 +00:00
2008-02-07 23:13:13 +00:00
Common : : SeekableReadStream * Resource : : getFileStream ( const Common : : String & file ) const {
if ( ! isAccessable ( file ) )
return 0 ;
2007-07-02 23:02:54 +00:00
2008-02-07 23:13:13 +00:00
ResFileMap : : const_iterator iter = _map . find ( file ) ;
if ( iter = = _map . end ( ) )
return 0 ;
2007-07-02 23:02:54 +00:00
2008-02-07 23:13:13 +00:00
if ( ! iter - > _value . parent . empty ( ) ) {
Common : : SeekableReadStream * parent = getFileStream ( iter - > _value . parent ) ;
assert ( parent ) ;
2006-07-30 07:51:11 +00:00
2008-02-07 23:13:13 +00:00
ResFileMap : : const_iterator parentIter = _map . find ( iter - > _value . parent ) ;
const ResArchiveLoader * loader = getLoader ( parentIter - > _value . type ) ;
assert ( loader ) ;
2006-07-30 07:51:11 +00:00
2008-02-09 16:18:44 +00:00
return loader - > loadFileFromArchive ( file , parent , iter - > _value ) ;
2008-02-07 23:13:13 +00:00
} else {
Common : : File * stream = new Common : : File ( ) ;
if ( ! stream - > open ( file . c_str ( ) ) ) {
warning ( " Couldn't open file '%s' " , file . c_str ( ) ) ;
return 0 ;
2004-10-15 06:06:47 +00:00
}
2008-02-07 23:13:13 +00:00
return stream ;
2004-11-14 20:11:22 +00:00
}
2005-07-30 21:11:48 +00:00
2007-03-20 20:46:19 +00:00
return 0 ;
2004-11-14 20:11:22 +00:00
}
2005-07-30 21:11:48 +00:00
2008-02-07 23:13:13 +00:00
bool Resource : : isAccessable ( const Common : : String & file ) const {
ResFileMap : : const_iterator iter = _map . find ( file ) ;
2008-02-09 15:46:06 +00:00
while ( iter ! = _map . end ( ) ) {
if ( ! iter - > _value . parent . empty ( ) ) {
2008-02-07 23:13:13 +00:00
iter = _map . find ( iter - > _value . parent ) ;
2008-02-09 15:46:06 +00:00
if ( iter ! = _map . end ( ) ) {
// parent can never be a non archive file
if ( iter - > _value . type = = ResFileEntry : : kRaw )
return false ;
// not mounted parent means not accessable
else if ( ! iter - > _value . mounted )
return false ;
}
} else {
return true ;
}
2006-02-10 16:39:56 +00:00
}
return false ;
}
2008-02-07 23:13:13 +00:00
void Resource : : detectFileTypes ( ) {
for ( ResFileMap : : iterator i = _map . begin ( ) ; i ! = _map . end ( ) ; + + i ) {
if ( ! isAccessable ( i - > _key ) )
2006-09-16 20:51:05 +00:00
continue ;
2008-02-07 23:13:13 +00:00
if ( i - > _value . type = = ResFileEntry : : kAutoDetect ) {
2008-02-09 15:18:35 +00:00
Common : : SeekableReadStream * stream = 0 ;
2008-02-07 23:13:13 +00:00
for ( LoaderIterator l = _loaders . begin ( ) ; l ! = _loaders . end ( ) ; + + l ) {
2008-02-09 15:18:35 +00:00
if ( ! ( * l ) - > checkFilename ( i - > _key ) )
continue ;
if ( ! stream )
stream = getFileStream ( i - > _key ) ;
2008-02-07 23:13:13 +00:00
if ( ( * l ) - > isLoadable ( i - > _key , * stream ) ) {
i - > _value . type = ( * l ) - > getType ( ) ;
2008-02-09 15:46:06 +00:00
i - > _value . mounted = false ;
2008-02-07 23:13:13 +00:00
i - > _value . preload = false ;
break ;
}
}
2008-02-09 15:18:35 +00:00
delete stream ;
stream = 0 ;
2007-09-19 08:40:12 +00:00
2008-02-09 15:46:06 +00:00
if ( i - > _value . type = = ResFileEntry : : kAutoDetect )
2008-02-07 23:13:13 +00:00
i - > _value . type = ResFileEntry : : kRaw ;
}
2006-07-31 16:37:34 +00:00
}
}
2008-02-07 23:13:13 +00:00
# pragma mark -
# pragma mark - ResFileLodaer
# pragma mark -
2006-07-31 16:37:34 +00:00
2008-02-07 23:13:13 +00:00
class ResLoaderPak : public ResArchiveLoader {
public :
2008-02-09 15:18:35 +00:00
bool checkFilename ( Common : : String filename ) const ;
2008-02-07 23:13:13 +00:00
bool isLoadable ( const Common : : String & filename , Common : : SeekableReadStream & stream ) const ;
2008-02-09 16:18:44 +00:00
bool loadFile ( const Common : : String & filename , Common : : SeekableReadStream & stream , FileList & files ) const ;
Common : : SeekableReadStream * loadFileFromArchive ( const Common : : String & file , Common : : SeekableReadStream * archive , const ResFileEntry entry ) const ;
2006-07-31 16:37:34 +00:00
2008-02-07 23:13:13 +00:00
ResFileEntry : : kType getType ( ) const {
return ResFileEntry : : kPak ;
}
} ;
2006-07-31 16:37:34 +00:00
2008-02-09 15:18:35 +00:00
bool ResLoaderPak : : checkFilename ( Common : : String filename ) const {
filename . toUppercase ( ) ;
return ( filename . hasSuffix ( " .PAK " ) | | filename . hasSuffix ( " .APK " ) | | filename . hasSuffix ( " .VRM " ) | | filename . hasSuffix ( " .TLK " ) | | filename . equalsIgnoreCase ( StaticResource : : staticDataFilename ( ) ) ) ;
}
2008-02-07 23:13:13 +00:00
bool ResLoaderPak : : isLoadable ( const Common : : String & filename , Common : : SeekableReadStream & stream ) const {
2008-02-09 15:08:49 +00:00
uint32 filesize = stream . size ( ) ;
uint32 offset = 0 ;
bool switchEndian = false ;
bool firstFile = true ;
offset = stream . readUint32LE ( ) ;
if ( offset > filesize ) {
switchEndian = true ;
offset = SWAP_BYTES_32 ( offset ) ;
}
while ( ! stream . eos ( ) ) {
// The start offset of a file should never be in the filelist
if ( offset < stream . pos ( ) | | offset > filesize )
return false ;
Common : : String file = " " ;
byte c = 0 ;
while ( ! stream . eos ( ) & & ( c = stream . readByte ( ) ) ! = 0 )
file + = c ;
if ( stream . eos ( ) )
return false ;
// Quit now if we encounter an empty string
if ( file . empty ( ) ) {
if ( firstFile )
return false ;
else
break ;
}
firstFile = false ;
offset = switchEndian ? stream . readUint32BE ( ) : stream . readUint32LE ( ) ;
if ( ! offset | | offset = = filesize )
break ;
}
return true ;
2006-07-31 16:37:34 +00:00
}
2008-02-09 16:18:44 +00:00
bool ResLoaderPak : : loadFile ( const Common : : String & filename , Common : : SeekableReadStream & stream , FileList & files ) const {
2008-02-07 23:13:13 +00:00
uint32 filesize = stream . size ( ) ;
2008-02-09 14:40:52 +00:00
uint32 startoffset = 0 , endoffset = 0 ;
2008-02-07 23:13:13 +00:00
bool switchEndian = false ;
2008-02-09 15:08:49 +00:00
bool firstFile = true ;
2004-10-15 06:06:47 +00:00
2008-02-09 14:40:52 +00:00
startoffset = stream . readUint32LE ( ) ;
2007-03-20 21:11:42 +00:00
if ( startoffset > filesize ) {
2008-02-07 23:13:13 +00:00
switchEndian = true ;
startoffset = SWAP_BYTES_32 ( startoffset ) ;
2006-03-14 19:48:08 +00:00
}
2007-03-20 21:11:42 +00:00
2008-02-09 14:40:52 +00:00
while ( ! stream . eos ( ) ) {
2008-02-09 14:43:43 +00:00
// The start offset of a file should never be in the filelist
2008-02-09 15:08:49 +00:00
if ( startoffset < stream . pos ( ) | | startoffset > filesize ) {
2008-02-09 14:43:43 +00:00
warning ( " PAK file '%s' is corrupted " , filename . c_str ( ) ) ;
return false ;
}
2008-02-09 14:40:52 +00:00
Common : : String file = " " ;
byte c = 0 ;
2007-09-19 08:40:12 +00:00
2008-02-09 14:40:52 +00:00
while ( ! stream . eos ( ) & & ( c = stream . readByte ( ) ) ! = 0 )
file + = c ;
2007-09-19 08:40:12 +00:00
2008-02-09 14:40:52 +00:00
if ( stream . eos ( ) ) {
2008-02-07 23:13:13 +00:00
warning ( " PAK file '%s' is corrupted " , filename . c_str ( ) ) ;
return false ;
2007-03-20 21:11:42 +00:00
}
2007-09-19 08:40:12 +00:00
2006-09-09 12:21:49 +00:00
// Quit now if we encounter an empty string
2008-02-09 15:08:49 +00:00
if ( file . empty ( ) ) {
if ( firstFile ) {
warning ( " PAK file '%s' is corrupted " , filename . c_str ( ) ) ;
return false ;
} else {
break ;
}
}
2006-08-08 21:34:19 +00:00
2008-02-09 15:08:49 +00:00
firstFile = false ;
2008-02-09 14:40:52 +00:00
endoffset = switchEndian ? stream . readUint32BE ( ) : stream . readUint32LE ( ) ;
2005-07-30 21:11:48 +00:00
2008-02-07 23:13:13 +00:00
if ( ! endoffset )
2004-11-14 20:11:22 +00:00
endoffset = filesize ;
2004-10-15 06:06:47 +00:00
2007-03-20 21:11:42 +00:00
if ( startoffset ! = endoffset ) {
2008-02-07 23:13:13 +00:00
ResFileEntry entry ;
entry . size = endoffset - startoffset ;
entry . offset = startoffset ;
entry . parent = filename ;
entry . type = ResFileEntry : : kAutoDetect ;
2008-02-09 15:46:06 +00:00
entry . mounted = false ;
2008-02-07 23:13:13 +00:00
entry . prot = false ;
entry . preload = false ;
2008-02-09 16:18:44 +00:00
files . push_back ( File ( file , entry ) ) ;
2007-03-20 21:11:42 +00:00
}
2005-07-30 21:11:48 +00:00
2004-11-14 20:11:22 +00:00
if ( endoffset = = filesize )
break ;
2004-11-11 13:37:35 +00:00
2004-11-14 20:11:22 +00:00
startoffset = endoffset ;
2004-10-16 22:28:29 +00:00
}
2006-07-08 13:56:56 +00:00
2008-02-07 23:13:13 +00:00
return true ;
2004-11-14 20:11:22 +00:00
}
2004-10-15 06:06:47 +00:00
2008-02-09 16:18:44 +00:00
Common : : SeekableReadStream * ResLoaderPak : : loadFileFromArchive ( const Common : : String & file , Common : : SeekableReadStream * archive , const ResFileEntry entry ) const {
2008-02-07 23:13:13 +00:00
assert ( archive ) ;
2007-05-23 12:02:31 +00:00
2008-02-09 16:18:44 +00:00
archive - > seek ( entry . offset , SEEK_SET ) ;
Common : : SeekableSubReadStream * stream = new Common : : SeekableSubReadStream ( archive , entry . offset , entry . offset + entry . size , true ) ;
2008-02-07 23:13:13 +00:00
assert ( stream ) ;
return stream ;
2004-11-14 20:11:22 +00:00
}
2004-10-15 06:06:47 +00:00
2008-02-07 23:13:13 +00:00
class ResLoaderIns : public ResArchiveLoader {
public :
2008-02-09 15:18:35 +00:00
bool checkFilename ( Common : : String filename ) const ;
2008-02-07 23:13:13 +00:00
bool isLoadable ( const Common : : String & filename , Common : : SeekableReadStream & stream ) const ;
2008-02-09 16:18:44 +00:00
bool loadFile ( const Common : : String & filename , Common : : SeekableReadStream & stream , FileList & files ) const ;
Common : : SeekableReadStream * loadFileFromArchive ( const Common : : String & file , Common : : SeekableReadStream * archive , const ResFileEntry entry ) const ;
2006-02-10 16:39:56 +00:00
2008-02-07 23:13:13 +00:00
ResFileEntry : : kType getType ( ) const {
return ResFileEntry : : kIns ;
}
} ;
2006-02-09 07:37:19 +00:00
2008-02-09 15:18:35 +00:00
bool ResLoaderIns : : checkFilename ( Common : : String filename ) const {
filename . toUppercase ( ) ;
return ( filename . hasSuffix ( " .001 " ) ) ;
}
2008-02-07 23:13:13 +00:00
bool ResLoaderIns : : isLoadable ( const Common : : String & filename , Common : : SeekableReadStream & stream ) const {
stream . seek ( 3 ) ;
uint32 size = stream . readUint32LE ( ) ;
2006-07-08 14:25:23 +00:00
2008-02-07 23:27:08 +00:00
if ( size + 7 > stream . size ( ) )
2006-07-08 14:25:23 +00:00
return false ;
2008-02-07 23:27:08 +00:00
stream . seek ( size + 5 , SEEK_SET ) ;
2008-02-07 23:13:13 +00:00
uint8 buffer [ 2 ] ;
stream . read ( & buffer , 2 ) ;
2006-07-08 14:25:23 +00:00
2008-02-07 23:13:13 +00:00
return ( buffer [ 0 ] = = 0x0D & & buffer [ 1 ] = = 0x0A ) ;
}
2006-07-08 13:56:56 +00:00
2008-02-09 16:18:44 +00:00
bool ResLoaderIns : : loadFile ( const Common : : String & filename , Common : : SeekableReadStream & stream , FileList & files ) const {
2008-02-07 23:13:13 +00:00
Common : : List < Common : : String > filenames ;
2006-07-08 13:56:56 +00:00
// thanks to eriktorbjorn for this code (a bit modified though)
2008-02-07 23:13:13 +00:00
stream . seek ( 3 , SEEK_SET ) ;
2006-07-08 13:56:56 +00:00
// first file is the index table
2008-02-07 23:13:13 +00:00
uint32 size = stream . readUint32LE ( ) ;
2006-07-08 13:56:56 +00:00
Common : : String temp = " " ;
2008-02-07 23:13:13 +00:00
for ( uint32 i = 0 ; i < size ; + + i ) {
byte c = stream . readByte ( ) ;
2006-07-08 13:56:56 +00:00
if ( c = = ' \\ ' ) {
temp = " " ;
} else if ( c = = 0x0D ) {
// line endings are CRLF
2008-02-07 23:13:13 +00:00
c = stream . readByte ( ) ;
2006-07-08 13:56:56 +00:00
assert ( c = = 0x0A ) ;
+ + i ;
2008-02-07 23:13:13 +00:00
filenames . push_back ( temp ) ;
2006-07-08 13:56:56 +00:00
} else {
temp + = ( char ) c ;
}
}
2008-02-07 23:13:13 +00:00
stream . seek ( 3 , SEEK_SET ) ;
2006-07-08 13:56:56 +00:00
2008-02-07 23:13:13 +00:00
for ( Common : : List < Common : : String > : : iterator file = filenames . begin ( ) ; file ! = filenames . end ( ) ; + + file ) {
ResFileEntry entry ;
entry . parent = filename ;
entry . type = ResFileEntry : : kAutoDetect ;
2008-02-09 15:46:06 +00:00
entry . mounted = false ;
2008-02-07 23:13:13 +00:00
entry . preload = false ;
entry . prot = false ;
entry . size = stream . readUint32LE ( ) ;
entry . offset = stream . pos ( ) ;
stream . seek ( entry . size , SEEK_CUR ) ;
2008-02-09 16:18:44 +00:00
files . push_back ( File ( * file , entry ) ) ;
2008-02-07 23:13:13 +00:00
}
2006-07-08 13:56:56 +00:00
2008-02-07 23:13:13 +00:00
return true ;
2006-07-08 13:56:56 +00:00
}
2008-02-09 16:18:44 +00:00
Common : : SeekableReadStream * ResLoaderIns : : loadFileFromArchive ( const Common : : String & file , Common : : SeekableReadStream * archive , const ResFileEntry entry ) const {
2008-02-07 23:13:13 +00:00
assert ( archive ) ;
2007-05-23 12:02:31 +00:00
2008-02-09 16:18:44 +00:00
archive - > seek ( entry . offset , SEEK_SET ) ;
Common : : SeekableSubReadStream * stream = new Common : : SeekableSubReadStream ( archive , entry . offset , entry . offset + entry . size , true ) ;
2008-02-07 23:13:13 +00:00
assert ( stream ) ;
return stream ;
2006-07-08 13:56:56 +00:00
}
2008-02-07 23:13:13 +00:00
void Resource : : initializeLoaders ( ) {
_loaders . push_back ( new ResLoaderPak ( ) ) ;
_loaders . push_back ( new ResLoaderIns ( ) ) ;
2006-07-08 13:56:56 +00:00
}
2008-02-07 23:13:13 +00:00
const ResArchiveLoader * Resource : : getLoader ( ResFileEntry : : kType type ) const {
for ( CLoaderIterator i = _loaders . begin ( ) ; i ! = _loaders . end ( ) ; + + i ) {
if ( ( * i ) - > getType ( ) = = type )
return * i ;
}
return 0 ;
2006-07-08 13:56:56 +00:00
}
2004-10-15 06:06:47 +00:00
} // end of namespace Kyra
2007-04-15 16:41:20 +00:00