2009-02-17 15:09:09 +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 .
*
* 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 .
2014-02-18 02:34:24 +01:00
*
2009-02-17 15:09:09 +00:00
* 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 .
2014-02-18 02:34:24 +01:00
*
2009-02-17 15:09:09 +00:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
*/
2009-02-15 06:10:59 +00:00
2009-02-21 12:33:24 +00:00
// Resource library
2009-02-15 06:10:59 +00:00
2010-02-17 23:38:43 +00:00
# include "common/file.h"
2010-06-15 12:20:03 +00:00
# include "common/fs.h"
2010-06-15 12:09:30 +00:00
# include "common/macresman.h"
2011-04-24 11:34:27 +03:00
# include "common/textconsole.h"
2017-05-07 14:09:58 -05:00
# include "common/translation.h"
2016-03-18 22:55:56 -05:00
# ifdef ENABLE_SCI32
# include "common/memstream.h"
# endif
2009-02-17 21:18:11 +00:00
2017-02-08 11:52:36 -06:00
# include "sci/parser/vocabulary.h"
2009-05-15 14:07:45 +00:00
# include "sci/resource.h"
2010-06-15 12:08:40 +00:00
# include "sci/resource_intern.h"
2010-05-18 04:17:58 +00:00
# include "sci/util.h"
2009-02-15 06:10:59 +00:00
2009-02-21 10:23:36 +00:00
namespace Sci {
2010-02-17 23:37:32 +00:00
enum {
SCI0_RESMAP_ENTRIES_SIZE = 6 ,
SCI1_RESMAP_ENTRIES_SIZE = 6 ,
2011-02-15 21:47:29 -05:00
KQ5FMT_RESMAP_ENTRIES_SIZE = 7 ,
2010-02-17 23:37:32 +00:00
SCI11_RESMAP_ENTRIES_SIZE = 5
} ;
/** resource type for SCI1 resource.map file */
struct resource_index_t {
uint16 wOffset ;
uint16 wSize ;
} ;
//////////////////////////////////////////////////////////////////////
static SciVersion s_sciVersion = SCI_VERSION_NONE ; // FIXME: Move this inside a suitable class, e.g. SciEngine
2009-09-23 12:12:37 +00:00
SciVersion getSciVersion ( ) {
2010-01-03 22:50:39 +00:00
assert ( s_sciVersion ! = SCI_VERSION_NONE ) ;
2009-09-23 12:12:37 +00:00
return s_sciVersion ;
}
2014-10-18 15:33:07 +03:00
SciVersion getSciVersionForDetection ( ) {
assert ( ! g_sci ) ;
return s_sciVersion ;
}
2010-02-13 11:58:15 +00:00
const char * getSciVersionDesc ( SciVersion version ) {
switch ( version ) {
case SCI_VERSION_NONE :
return " Invalid SCI version " ;
case SCI_VERSION_0_EARLY :
return " Early SCI0 " ;
case SCI_VERSION_0_LATE :
return " Late SCI0 " ;
case SCI_VERSION_01 :
return " SCI01 " ;
2011-02-27 16:48:53 +02:00
case SCI_VERSION_1_EGA_ONLY :
2010-02-13 11:58:15 +00:00
return " SCI1 EGA " ;
case SCI_VERSION_1_EARLY :
return " Early SCI1 " ;
case SCI_VERSION_1_MIDDLE :
return " Middle SCI1 " ;
case SCI_VERSION_1_LATE :
return " Late SCI1 " ;
case SCI_VERSION_1_1 :
return " SCI1.1 " ;
case SCI_VERSION_2 :
return " SCI2 " ;
2015-12-29 01:44:11 +01:00
case SCI_VERSION_2_1_EARLY :
return " Early SCI2.1 " ;
case SCI_VERSION_2_1_MIDDLE :
return " Middle SCI2.1 " ;
case SCI_VERSION_2_1_LATE :
return " Late SCI2.1 " ;
2010-02-13 11:58:15 +00:00
case SCI_VERSION_3 :
return " SCI3 " ;
default :
return " Unknown " ;
}
}
2010-02-17 23:37:32 +00:00
//////////////////////////////////////////////////////////////////////
2010-09-28 20:30:50 +00:00
//#define SCI_VERBOSE_RESMAN 1
2009-02-15 06:10:59 +00:00
2012-05-15 03:05:08 +03:00
static const char * const s_errorDescriptions [ ] = {
2009-02-15 06:10:59 +00:00
" No error " ,
" I/O error " ,
" Resource is empty (size 0) " ,
" resource.map entry is invalid " ,
" resource.map file not found " ,
" No resource files found " ,
" Unknown compression method " ,
" Decompression failed: Sanity check failed " ,
2012-05-15 03:05:08 +03:00
" Decompression failed: Resource too big "
2009-02-15 22:28:50 +00:00
} ;
2009-02-15 06:10:59 +00:00
2011-09-08 00:35:12 +02:00
static const char * const s_resourceTypeNames [ ] = {
2009-02-28 23:46:50 +00:00
" view " , " pic " , " script " , " text " , " sound " ,
" memory " , " vocab " , " font " , " cursor " ,
" patch " , " bitmap " , " palette " , " cdaudio " ,
2009-05-19 02:10:58 +00:00
" audio " , " sync " , " message " , " map " , " heap " ,
2010-07-18 00:16:19 +00:00
" audio36 " , " sync36 " , " xlate " , " robot " , " vmd " ,
2010-11-13 00:20:23 +00:00
" chunk " , " animation " , " etc " , " duck " , " clut " ,
2013-12-10 01:40:46 +02:00
" tga " , " zzz " , " macibin " , " macibis " , " macpict " ,
" rave "
2009-02-28 23:46:50 +00:00
} ;
2009-02-15 06:10:59 +00:00
2010-11-11 19:38:32 +00:00
// Resource type suffixes. Note that the
2013-12-10 01:40:46 +02:00
// suffix of SCI3 scripts has been changed from
2010-11-11 19:38:32 +00:00
// scr to csc
2011-09-08 00:35:12 +02:00
static const char * const s_resourceTypeSuffixes [ ] = {
2009-02-28 23:46:50 +00:00
" v56 " , " p56 " , " scr " , " tex " , " snd " ,
2010-07-18 00:16:19 +00:00
" " , " voc " , " fon " , " cur " , " pat " ,
2009-02-28 23:46:50 +00:00
" bit " , " pal " , " cda " , " aud " , " syn " ,
2010-07-18 00:16:19 +00:00
" msg " , " map " , " hep " , " " , " " ,
" trn " , " rbt " , " vmd " , " chk " , " " ,
2010-11-13 00:20:23 +00:00
" etc " , " duk " , " clu " , " tga " , " zzz " ,
2013-12-10 01:40:46 +02:00
" " , " " , " " , " "
2011-06-20 00:59:48 +02:00
} ;
2009-02-15 06:10:59 +00:00
2009-02-28 23:46:50 +00:00
const char * getResourceTypeName ( ResourceType restype ) {
2017-09-06 16:19:01 -05:00
if ( restype < ARRAYSIZE ( s_resourceTypeNames ) )
2010-07-18 00:16:19 +00:00
return s_resourceTypeNames [ restype ] ;
2009-06-07 19:15:55 +00:00
else
return " invalid " ;
2009-02-28 23:46:50 +00:00
}
2017-09-06 16:19:18 -05:00
const char * getResourceTypeExtension ( ResourceType restype ) {
if ( restype < ARRAYSIZE ( s_resourceTypeSuffixes ) )
return s_resourceTypeSuffixes [ restype ] ;
else
return " " ;
}
2010-07-18 01:32:48 +00:00
static const ResourceType s_resTypeMapSci0 [ ] = {
kResourceTypeView , kResourceTypePic , kResourceTypeScript , kResourceTypeText , // 0x00-0x03
kResourceTypeSound , kResourceTypeMemory , kResourceTypeVocab , kResourceTypeFont , // 0x04-0x07
kResourceTypeCursor , kResourceTypePatch , kResourceTypeBitmap , kResourceTypePalette , // 0x08-0x0B
kResourceTypeCdAudio , kResourceTypeAudio , kResourceTypeSync , kResourceTypeMessage , // 0x0C-0x0F
kResourceTypeMap , kResourceTypeHeap , kResourceTypeAudio36 , kResourceTypeSync36 , // 0x10-0x13
2013-12-10 01:40:46 +02:00
kResourceTypeTranslation , kResourceTypeRave // 0x14
2010-07-18 00:16:19 +00:00
} ;
2010-07-18 01:32:48 +00:00
// TODO: 12 should be "Wave", but SCI seems to just store it in Audio resources
static const ResourceType s_resTypeMapSci21 [ ] = {
2010-11-13 00:20:23 +00:00
kResourceTypeView , kResourceTypePic , kResourceTypeScript , kResourceTypeAnimation , // 0x00-0x03
kResourceTypeSound , kResourceTypeEtc , kResourceTypeVocab , kResourceTypeFont , // 0x04-0x07
2010-07-18 01:32:48 +00:00
kResourceTypeCursor , kResourceTypePatch , kResourceTypeBitmap , kResourceTypePalette , // 0x08-0x0B
2016-08-20 20:56:19 -05:00
kResourceTypeAudio , kResourceTypeAudio , kResourceTypeSync , kResourceTypeMessage , // 0x0C-0x0F
2010-07-18 01:32:48 +00:00
kResourceTypeMap , kResourceTypeHeap , kResourceTypeChunk , kResourceTypeAudio36 , // 0x10-0x13
2010-11-13 00:20:23 +00:00
kResourceTypeSync36 , kResourceTypeTranslation , kResourceTypeRobot , kResourceTypeVMD , // 0x14-0x17
kResourceTypeDuck , kResourceTypeClut , kResourceTypeTGA , kResourceTypeZZZ // 0x18-0x1B
2010-07-18 00:16:19 +00:00
} ;
ResourceType ResourceManager : : convertResType ( byte type ) {
type & = 0x7f ;
2013-04-20 16:17:49 +02:00
bool forceSci0 = false ;
// LSL6 hires doesn't have the chunk resource type, to match
// the resource types of the lowres version, thus we use the
// older resource types here.
// PQ4 CD and QFG4 CD are SCI2.1, but use the resource types of the
// corresponding SCI2 floppy disk versions.
if ( g_sci & & ( g_sci - > getGameId ( ) = = GID_LSL6HIRES | |
g_sci - > getGameId ( ) = = GID_QFG4 | | g_sci - > getGameId ( ) = = GID_PQ4 ) )
forceSci0 = true ;
if ( _mapVersion < kResVersionSci2 | | forceSci0 ) {
2010-07-18 00:16:19 +00:00
// SCI0 - SCI2
2010-07-18 01:32:48 +00:00
if ( type < ARRAYSIZE ( s_resTypeMapSci0 ) )
return s_resTypeMapSci0 [ type ] ;
2010-07-18 00:16:19 +00:00
} else {
2013-04-20 16:17:49 +02:00
if ( type < ARRAYSIZE ( s_resTypeMapSci21 ) )
return s_resTypeMapSci21 [ type ] ;
2010-07-18 00:16:19 +00:00
}
return kResourceTypeInvalid ;
}
2009-03-03 23:07:06 +00:00
//-- Resource main functions --
2016-12-31 20:39:57 -06:00
Resource : : Resource ( ResourceManager * resMan , ResourceId id ) : SciSpan < const byte > ( nullptr , 0 , id . toString ( ) ) , _resMan ( resMan ) , _id ( id ) {
2010-02-17 23:37:59 +00:00
_fileOffset = 0 ;
_status = kResStatusNoMalloc ;
_lockers = 0 ;
2016-12-31 20:39:57 -06:00
_source = nullptr ;
_header = nullptr ;
2010-02-17 23:37:59 +00:00
_headerSize = 0 ;
2009-02-15 06:10:59 +00:00
}
2009-03-03 23:07:06 +00:00
Resource : : ~ Resource ( ) {
2016-12-31 20:39:57 -06:00
delete [ ] _data ;
2010-12-14 23:30:22 +00:00
delete [ ] _header ;
2010-06-15 12:09:30 +00:00
if ( _source & & _source - > getSourceType ( ) = = kSourcePatch )
2010-02-17 23:37:59 +00:00
delete _source ;
2009-02-15 06:10:59 +00:00
}
2009-03-03 23:07:06 +00:00
void Resource : : unalloc ( ) {
2016-12-31 20:39:57 -06:00
delete [ ] _data ;
_data = nullptr ;
2010-02-17 23:37:59 +00:00
_status = kResStatusNoMalloc ;
}
void Resource : : writeToStream ( Common : : WriteStream * stream ) const {
2013-12-10 01:40:46 +02:00
stream - > writeByte ( getType ( ) | 0x80 ) ; // 0x80 is required by old Sierra SCI, otherwise it wont accept the patch file
2010-02-17 23:37:59 +00:00
stream - > writeByte ( _headerSize ) ;
if ( _headerSize > 0 )
stream - > write ( _header , _headerSize ) ;
2016-12-31 20:39:57 -06:00
stream - > write ( _data , _size ) ;
2009-02-15 06:10:59 +00:00
}
2016-03-18 22:55:56 -05:00
# ifdef ENABLE_SCI32
Common : : SeekableReadStream * Resource : : makeStream ( ) const {
2016-12-31 20:39:57 -06:00
return new Common : : MemoryReadStream ( _data , _size , DisposeAfterUse : : NO ) ;
2016-03-18 22:55:56 -05:00
}
# endif
2010-06-15 12:17:47 +00:00
uint32 Resource : : getAudioCompressionType ( ) const {
return _source - > getAudioCompressionType ( ) ;
}
uint32 AudioVolumeResourceSource : : getAudioCompressionType ( ) const {
return _audioCompressionType ;
2010-04-29 15:54:59 +00:00
}
2010-06-15 12:16:17 +00:00
ResourceSource : : ResourceSource ( ResSourceType type , const Common : : String & name , int volNum , const Common : : FSNode * resFile )
: _sourceType ( type ) , _name ( name ) , _volumeNumber ( volNum ) , _resourceFile ( resFile ) {
2010-06-15 12:15:05 +00:00
_scanned = false ;
2010-06-15 12:09:30 +00:00
}
ResourceSource : : ~ ResourceSource ( ) {
2010-06-15 12:13:29 +00:00
}
2010-06-15 12:14:39 +00:00
MacResourceForkResourceSource : : MacResourceForkResourceSource ( const Common : : String & name , int volNum )
: ResourceSource ( kSourceMacResourceFork , name , volNum ) {
2010-06-15 12:13:29 +00:00
_macResMan = new Common : : MacResManager ( ) ;
2010-06-15 12:13:52 +00:00
assert ( _macResMan ) ;
2010-06-15 12:13:29 +00:00
}
MacResourceForkResourceSource : : ~ MacResourceForkResourceSource ( ) {
2010-06-15 12:09:30 +00:00
delete _macResMan ;
}
2009-09-02 12:02:37 +00:00
//-- resMan helper functions --
2009-03-03 23:07:06 +00:00
2009-02-21 12:33:24 +00:00
// Resource source list management
2009-02-15 06:10:59 +00:00
2010-06-15 12:10:18 +00:00
ResourceSource * ResourceManager : : addExternalMap ( const Common : : String & filename , int volume_nr ) {
2010-06-15 12:14:39 +00:00
ResourceSource * newsrc = new ExtMapResourceSource ( filename , volume_nr ) ;
2009-08-18 14:10:31 +00:00
_sources . push_back ( newsrc ) ;
return newsrc ;
}
2010-06-01 22:06:52 +00:00
ResourceSource * ResourceManager : : addExternalMap ( const Common : : FSNode * mapFile , int volume_nr ) {
2010-06-15 12:16:17 +00:00
ResourceSource * newsrc = new ExtMapResourceSource ( mapFile - > getName ( ) , volume_nr , mapFile ) ;
2009-02-15 06:10:59 +00:00
2009-06-08 12:20:36 +00:00
_sources . push_back ( newsrc ) ;
2009-02-15 06:10:59 +00:00
return newsrc ;
}
2010-06-15 12:14:39 +00:00
ResourceSource * ResourceManager : : addSource ( ResourceSource * newsrc ) {
2010-06-15 12:11:04 +00:00
assert ( newsrc ) ;
2009-02-15 06:10:59 +00:00
2009-08-18 14:10:31 +00:00
_sources . push_back ( newsrc ) ;
return newsrc ;
}
2010-06-15 12:10:18 +00:00
ResourceSource * ResourceManager : : addPatchDir ( const Common : : String & dirname ) {
2010-06-15 12:10:41 +00:00
ResourceSource * newsrc = new DirectoryResourceSource ( dirname ) ;
2009-02-21 12:33:24 +00:00
2009-06-08 12:20:36 +00:00
_sources . push_back ( newsrc ) ;
2009-02-15 12:29:09 +00:00
return 0 ;
2009-02-15 06:10:59 +00:00
}
2010-06-15 12:11:56 +00:00
ResourceSource * ResourceManager : : findVolume ( ResourceSource * map , int volume_nr ) {
2009-06-08 12:20:36 +00:00
for ( Common : : List < ResourceSource * > : : iterator it = _sources . begin ( ) ; it ! = _sources . end ( ) ; + + it ) {
2010-06-15 12:11:56 +00:00
ResourceSource * src = ( * it ) - > findVolume ( map , volume_nr ) ;
if ( src )
2009-06-08 12:20:36 +00:00
return src ;
2009-02-15 06:10:59 +00:00
}
return NULL ;
}
2009-02-21 12:33:24 +00:00
// Resource manager constructors and operations
2009-02-15 06:10:59 +00:00
2010-06-15 12:18:57 +00:00
bool Resource : : loadPatch ( Common : : SeekableReadStream * file ) {
2016-11-02 11:24:46 -05:00
// We assume that the resource type matches `type`
2010-05-01 14:51:57 +00:00
// We also assume that the current file position is right at the actual data (behind resourceid/headersize byte)
2009-06-06 00:07:18 +00:00
2016-12-31 20:39:57 -06:00
byte * ptr = new byte [ size ( ) ] ;
_data = ptr ;
2009-06-06 00:07:18 +00:00
2016-11-02 11:24:46 -05:00
if ( _headerSize > 0 )
_header = new byte [ _headerSize ] ;
2009-06-06 00:07:18 +00:00
2016-12-31 20:39:57 -06:00
if ( data ( ) = = nullptr | | ( _headerSize > 0 & & _header = = nullptr ) ) {
2017-03-30 14:02:27 -05:00
error ( " Can't allocate %u bytes needed for loading %s " , size ( ) + _headerSize , _id . toString ( ) . c_str ( ) ) ;
2009-06-06 00:07:18 +00:00
}
2016-12-31 20:39:57 -06:00
uint32 bytesRead ;
2016-11-02 11:24:46 -05:00
if ( _headerSize > 0 ) {
2016-12-31 20:39:57 -06:00
bytesRead = file - > read ( _header , _headerSize ) ;
if ( bytesRead ! = _headerSize )
error ( " Read %d bytes from %s but expected %d " , bytesRead , _id . toString ( ) . c_str ( ) , _headerSize ) ;
2009-06-06 00:07:18 +00:00
}
2016-12-31 20:39:57 -06:00
bytesRead = file - > read ( ptr , size ( ) ) ;
if ( bytesRead ! = size ( ) )
2017-03-30 14:02:27 -05:00
error ( " Read %d bytes from %s but expected %u " , bytesRead , _id . toString ( ) . c_str ( ) , size ( ) ) ;
2009-06-06 00:07:18 +00:00
2016-11-02 11:24:46 -05:00
_status = kResStatusAllocated ;
2009-06-06 00:07:18 +00:00
return true ;
}
2010-06-15 12:18:57 +00:00
bool Resource : : loadFromPatchFile ( ) {
2009-03-03 23:07:06 +00:00
Common : : File file ;
2010-06-15 12:18:57 +00:00
const Common : : String & filename = _source - > getLocationName ( ) ;
2011-02-10 16:48:29 +00:00
if ( ! file . open ( filename ) ) {
2010-06-15 12:09:51 +00:00
warning ( " Failed to open patch file %s " , filename . c_str ( ) ) ;
2010-06-15 12:18:57 +00:00
unalloc ( ) ;
2009-03-03 23:07:06 +00:00
return false ;
}
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
file . seek ( 0 , SEEK_SET ) ;
2010-06-15 12:18:57 +00:00
return loadPatch ( & file ) ;
2009-06-06 00:07:18 +00:00
}
2010-06-06 13:04:24 +00:00
Common : : SeekableReadStream * ResourceManager : : getVolumeFile ( ResourceSource * source ) {
2009-03-11 20:15:42 +00:00
Common : : List < Common : : File * > : : iterator it = _volumeFiles . begin ( ) ;
Common : : File * file ;
2017-05-07 23:55:36 -05:00
# ifdef ENABLE_SCI32
ChunkResourceSource * chunkSource = dynamic_cast < ChunkResourceSource * > ( source ) ;
if ( chunkSource ! = nullptr ) {
Resource * res = findResource ( ResourceId ( kResourceTypeChunk , chunkSource - > getNumber ( ) ) , false ) ;
return res ? res - > makeStream ( ) : nullptr ;
}
# endif
2010-06-15 12:13:07 +00:00
if ( source - > _resourceFile )
return source - > _resourceFile - > createReadStream ( ) ;
2010-06-06 13:04:24 +00:00
2010-06-15 12:09:51 +00:00
const char * filename = source - > getLocationName ( ) . c_str ( ) ;
2010-06-06 13:04:24 +00:00
2009-03-11 20:15:42 +00:00
// check if file is already opened
while ( it ! = _volumeFiles . end ( ) ) {
file = * it ;
if ( scumm_stricmp ( file - > getName ( ) , filename ) = = 0 ) {
// move file to top
if ( it ! = _volumeFiles . begin ( ) ) {
_volumeFiles . erase ( it ) ;
_volumeFiles . push_front ( file ) ;
}
return file ;
}
2010-01-13 23:58:25 +00:00
+ + it ;
2009-03-11 20:15:42 +00:00
}
// adding a new file
file = new Common : : File ;
if ( file - > open ( filename ) ) {
if ( _volumeFiles . size ( ) = = MAX_OPENED_VOLUMES ) {
it = - - _volumeFiles . end ( ) ;
delete * it ;
_volumeFiles . erase ( it ) ;
}
_volumeFiles . push_front ( file ) ;
return file ;
}
// failed
delete file ;
return NULL ;
}
2017-05-13 22:07:53 -05:00
void ResourceManager : : disposeVolumeFileStream ( Common : : SeekableReadStream * fileStream , Sci : : ResourceSource * source ) {
# ifdef ENABLE_SCI32
ChunkResourceSource * chunkSource = dynamic_cast < ChunkResourceSource * > ( source ) ;
if ( chunkSource ! = nullptr ) {
delete fileStream ;
return ;
}
# endif
if ( source - > _resourceFile ) {
delete fileStream ;
return ;
}
// Other volume file streams are cached in _volumeFiles and should only be
// deleted from _volumeFiles
}
2010-05-10 18:29:13 +00:00
void ResourceManager : : loadResource ( Resource * res ) {
2010-06-16 00:24:16 +00:00
res - > _source - > loadResource ( this , res ) ;
2010-06-15 12:13:29 +00:00
}
2010-06-16 00:24:16 +00:00
void PatchResourceSource : : loadResource ( ResourceManager * resMan , Resource * res ) {
2010-06-15 12:18:57 +00:00
bool result = res - > loadFromPatchFile ( ) ;
2010-06-15 12:13:52 +00:00
if ( ! result ) {
// TODO: We used to fallback to the "default" code here if loadFromPatchFile
// failed, but I am not sure whether that is really appropriate.
// In fact it looks like a bug to me, so I commented this out for now.
2010-06-15 12:17:05 +00:00
//ResourceSource::loadResource(res);
2010-06-15 12:13:52 +00:00
}
}
2009-06-08 12:20:36 +00:00
2010-08-14 04:21:09 +00:00
static Common : : Array < uint32 > resTypeToMacTags ( ResourceType type ) ;
2010-06-16 00:24:16 +00:00
void MacResourceForkResourceSource : : loadResource ( ResourceManager * resMan , Resource * res ) {
2011-02-02 00:23:46 +00:00
ResourceType type = res - > getType ( ) ;
2010-08-14 04:21:09 +00:00
Common : : SeekableReadStream * stream = 0 ;
2011-02-02 00:23:46 +00:00
if ( type = = kResourceTypeAudio36 | | type = = kResourceTypeSync36 ) {
// Handle audio36/sync36, convert back to audio/sync
2013-12-10 01:40:46 +02:00
stream = _macResMan - > getResource ( res - > _id . toPatchNameBase36 ( ) ) ;
2011-02-02 00:23:46 +00:00
} else {
// Plain resource handling
Common : : Array < uint32 > tagArray = resTypeToMacTags ( type ) ;
for ( uint32 i = 0 ; i < tagArray . size ( ) & & ! stream ; i + + )
stream = _macResMan - > getResource ( tagArray [ i ] , res - > getNumber ( ) ) ;
}
2010-05-10 18:29:13 +00:00
2011-02-20 01:19:20 -05:00
if ( stream )
decompressResource ( stream , res ) ;
2011-01-31 22:45:51 +00:00
}
bool MacResourceForkResourceSource : : isCompressableResource ( ResourceType type ) const {
// Any types that were not originally an SCI format are not compressed, it seems.
2011-02-02 00:23:46 +00:00
// (Audio/36 being Mac snd resources here)
2011-01-31 22:45:51 +00:00
return type ! = kResourceTypeMacPict & & type ! = kResourceTypeAudio & &
2011-02-02 00:23:46 +00:00
type ! = kResourceTypeMacIconBarPictN & & type ! = kResourceTypeMacIconBarPictS & &
2011-02-02 02:20:25 +00:00
type ! = kResourceTypeAudio36 & & type ! = kResourceTypeSync & &
2011-02-02 15:21:24 +00:00
type ! = kResourceTypeSync36 & & type ! = kResourceTypeCursor ;
2011-01-31 22:45:51 +00:00
}
# define OUTPUT_LITERAL() \
2016-12-31 20:39:57 -06:00
assert ( ptr + literalLength < = bufferEnd ) ; \
2011-01-31 22:45:51 +00:00
while ( literalLength - - ) \
* ptr + + = stream - > readByte ( ) ;
# define OUTPUT_COPY() \
2016-12-31 20:39:57 -06:00
assert ( ptr + copyLength < = bufferEnd ) ; \
2011-01-31 22:45:51 +00:00
while ( copyLength - - ) { \
byte value = ptr [ - offset ] ; \
* ptr + + = value ; \
}
2011-06-20 00:59:48 +02:00
void MacResourceForkResourceSource : : decompressResource ( Common : : SeekableReadStream * stream , Resource * resource ) const {
2011-01-31 22:45:51 +00:00
// KQ6 Mac is the only game not compressed. It's not worth writing a
// heuristic just for that game. Also, skip over any resource that cannot
// be compressed.
2011-06-20 00:59:48 +02:00
bool canBeCompressed = ! ( g_sci & & g_sci - > getGameId ( ) = = GID_KQ6 ) & & isCompressableResource ( resource - > _id . getType ( ) ) ;
2011-01-31 22:45:51 +00:00
uint32 uncompressedSize = 0 ;
2011-02-13 18:00:39 -05:00
// GK2 Mac is crazy. In its Patches resource fork, picture 2315 is not
// compressed and it is hardcoded in the executable to say that it's
// not compressed. Why didn't they just add four zeroes to the end of
// the resource? (Checked with PPC disasm)
if ( g_sci & & g_sci - > getGameId ( ) = = GID_GK2 & & resource - > _id . getType ( ) = = kResourceTypePic & & resource - > _id . getNumber ( ) = = 2315 )
canBeCompressed = false ;
2011-01-31 22:45:51 +00:00
// Get the uncompressed size from the end of the resource
if ( canBeCompressed & & stream - > size ( ) > 4 ) {
2016-12-31 20:39:57 -06:00
stream - > seek ( - 4 , SEEK_END ) ;
2011-01-31 22:45:51 +00:00
uncompressedSize = stream - > readUint32BE ( ) ;
stream - > seek ( 0 ) ;
}
if ( uncompressedSize = = 0 ) {
// Not compressed
2016-12-31 20:39:57 -06:00
resource - > _size = stream - > size ( ) ;
2011-01-31 22:45:51 +00:00
// Cut out the 'non-compressed marker' (four zeroes) at the end
if ( canBeCompressed )
2016-12-31 20:39:57 -06:00
resource - > _size - = 4 ;
2011-01-31 22:45:51 +00:00
2016-12-31 20:39:57 -06:00
byte * ptr = new byte [ resource - > size ( ) ] ;
resource - > _data = ptr ;
stream - > read ( ptr , resource - > size ( ) ) ;
2011-01-31 22:45:51 +00:00
} else {
// Decompress
2016-12-31 20:39:57 -06:00
resource - > _size = uncompressedSize ;
byte * ptr = new byte [ uncompressedSize ] ;
resource - > _data = ptr ;
2011-01-31 22:45:51 +00:00
2016-12-31 20:39:57 -06:00
const byte * const bufferEnd = resource - > data ( ) + uncompressedSize ;
2011-01-31 22:45:51 +00:00
while ( stream - > pos ( ) < stream - > size ( ) ) {
byte code = stream - > readByte ( ) ;
int literalLength = 0 , offset = 0 , copyLength = 0 ;
byte extraByte1 = 0 , extraByte2 = 0 ;
if ( code = = 0xFF ) {
// End of stream marker
break ;
}
switch ( code & 0xC0 ) {
case 0x80 :
// Copy chunk expanded
extraByte1 = stream - > readByte ( ) ;
extraByte2 = stream - > readByte ( ) ;
2011-06-20 00:59:48 +02:00
2011-01-31 22:45:51 +00:00
literalLength = extraByte2 & 3 ;
OUTPUT_LITERAL ( )
offset = ( ( code & 0x3f ) | ( ( extraByte1 & 0xe0 ) < < 1 ) | ( ( extraByte2 & 0xfc ) < < 7 ) ) + 1 ;
copyLength = ( extraByte1 & 0x1f ) + 3 ;
OUTPUT_COPY ( )
break ;
case 0xC0 :
// Literal chunk
if ( code > = 0xD0 ) {
// These codes cannot be used
if ( code = = 0xD0 | | code > 0xD3 )
error ( " Bad Mac compression code %02x " , code ) ;
literalLength = code & 3 ;
} else
literalLength = ( code & 0xf ) * 4 + 4 ;
OUTPUT_LITERAL ( )
break ;
default :
// Copy chunk
extraByte1 = stream - > readByte ( ) ;
literalLength = ( extraByte1 > > 3 ) & 0x3 ;
OUTPUT_LITERAL ( )
offset = ( code + ( ( extraByte1 & 0xE0 ) < < 2 ) ) + 1 ;
copyLength = ( extraByte1 & 0x7 ) + 3 ;
OUTPUT_COPY ( )
break ;
}
}
2010-05-10 18:29:13 +00:00
}
2011-01-31 22:45:51 +00:00
resource - > _status = kResStatusAllocated ;
delete stream ;
2010-06-15 12:13:52 +00:00
}
2010-05-10 18:29:13 +00:00
2011-01-31 22:45:51 +00:00
# undef OUTPUT_LITERAL
# undef OUTPUT_COPY
2010-06-16 00:24:16 +00:00
Common : : SeekableReadStream * ResourceSource : : getVolumeFile ( ResourceManager * resMan , Resource * res ) {
2010-06-15 12:13:29 +00:00
Common : : SeekableReadStream * fileStream = resMan - > getVolumeFile ( this ) ;
2010-06-02 13:17:36 +00:00
if ( ! fileStream ) {
2010-06-15 12:13:29 +00:00
warning ( " Failed to open %s " , getLocationName ( ) . c_str ( ) ) ;
2017-05-07 14:09:58 -05:00
resMan - > _hasBadResources = true ;
2010-06-15 12:17:25 +00:00
if ( res )
res - > unalloc ( ) ;
2009-02-15 06:10:59 +00:00
}
2010-01-25 01:39:44 +00:00
2010-06-15 12:14:15 +00:00
return fileStream ;
}
2010-06-16 00:24:16 +00:00
void ResourceSource : : loadResource ( ResourceManager * resMan , Resource * res ) {
Common : : SeekableReadStream * fileStream = getVolumeFile ( resMan , res ) ;
2010-06-15 12:14:15 +00:00
if ( ! fileStream )
2010-04-29 15:54:59 +00:00
return ;
2010-06-15 12:14:15 +00:00
fileStream - > seek ( res - > _fileOffset , SEEK_SET ) ;
2010-06-15 12:19:14 +00:00
2010-06-16 00:24:16 +00:00
int error = res - > decompress ( resMan - > getVolVersion ( ) , fileStream ) ;
2010-06-15 12:14:15 +00:00
if ( error ) {
2010-09-10 14:07:32 +00:00
warning ( " Error %d occurred while reading %s from resource file %s: %s " ,
error , res - > _id . toString ( ) . c_str ( ) , res - > getResourceLocation ( ) . c_str ( ) ,
2012-05-15 03:05:08 +03:00
s_errorDescriptions [ error ] ) ;
2010-06-15 12:14:15 +00:00
res - > unalloc ( ) ;
2009-06-08 12:20:36 +00:00
}
2010-06-15 16:50:10 +00:00
2017-05-13 22:07:53 -05:00
resMan - > disposeVolumeFileStream ( fileStream , this ) ;
2009-02-15 06:10:59 +00:00
}
2009-06-07 19:15:55 +00:00
Resource * ResourceManager : : testResource ( ResourceId id ) {
2010-02-17 23:37:59 +00:00
return _resMap . getVal ( id , NULL ) ;
2009-02-15 06:10:59 +00:00
}
2009-02-28 20:45:36 +00:00
int ResourceManager : : addAppropriateSources ( ) {
2016-08-19 10:05:05 -05:00
# ifdef ENABLE_SCI32
_multiDiscAudio = false ;
# endif
2010-06-01 22:06:52 +00:00
if ( Common : : File : : exists ( " resource.map " ) ) {
2010-01-27 04:26:28 +00:00
// SCI0-SCI2 file naming scheme
2010-06-01 22:06:52 +00:00
ResourceSource * map = addExternalMap ( " resource.map " ) ;
2009-12-27 12:17:43 +00:00
2016-03-01 19:16:03 +01:00
Common : : ArchiveMemberList files ;
2010-06-01 22:06:52 +00:00
SearchMan . listMatchingMembers ( files , " resource.0?? " ) ;
2009-02-15 06:10:59 +00:00
2010-01-27 04:26:28 +00:00
for ( Common : : ArchiveMemberList : : const_iterator x = files . begin ( ) ; x ! = files . end ( ) ; + + x ) {
const Common : : String name = ( * x ) - > getName ( ) ;
const char * dot = strrchr ( name . c_str ( ) , ' . ' ) ;
int number = atoi ( dot + 1 ) ;
2009-12-27 12:17:43 +00:00
2010-06-15 12:14:39 +00:00
addSource ( new VolumeResourceSource ( name , map , number ) ) ;
2010-01-27 04:26:28 +00:00
}
2009-08-21 18:12:13 +00:00
# ifdef ENABLE_SCI32
2010-01-29 16:25:18 +00:00
// GK1CD hires content
2010-06-01 22:06:52 +00:00
if ( Common : : File : : exists ( " alt.map " ) & & Common : : File : : exists ( " resource.alt " ) )
2010-06-15 12:14:39 +00:00
addSource ( new VolumeResourceSource ( " resource.alt " , addExternalMap ( " alt.map " , 10 ) , 10 ) ) ;
2010-05-10 18:29:13 +00:00
# endif
2012-03-08 08:02:58 -05:00
} else if ( Common : : MacResManager : : exists ( " Data1 " ) ) {
2010-05-10 18:29:13 +00:00
// Mac SCI1.1+ file naming scheme
2016-03-01 19:17:59 +01:00
Common : : StringArray files ;
2016-03-01 19:16:03 +01:00
Common : : MacResManager : : listFiles ( files , " Data? " ) ;
2010-05-10 18:29:13 +00:00
2016-03-01 19:17:59 +01:00
for ( Common : : StringArray : : const_iterator x = files . begin ( ) ; x ! = files . end ( ) ; + + x ) {
2016-03-01 19:16:03 +01:00
addSource ( new MacResourceForkResourceSource ( * x , atoi ( x - > c_str ( ) + 4 ) ) ) ;
2010-01-28 22:26:13 +00:00
}
2010-05-18 04:17:58 +00:00
2011-02-05 08:27:33 +00:00
# ifdef ENABLE_SCI32
2010-05-18 04:17:58 +00:00
// There can also be a "Patches" resource fork with patches
2016-03-01 13:37:04 +01:00
if ( Common : : MacResManager : : exists ( " Patches " ) )
2010-06-15 12:14:39 +00:00
addSource ( new MacResourceForkResourceSource ( " Patches " , 100 ) ) ;
2010-01-27 04:26:28 +00:00
} else {
// SCI2.1-SCI3 file naming scheme
2016-03-01 19:16:03 +01:00
Common : : ArchiveMemberList mapFiles , files ;
2010-06-01 22:06:52 +00:00
SearchMan . listMatchingMembers ( mapFiles , " resmap.0?? " ) ;
SearchMan . listMatchingMembers ( files , " ressci.0?? " ) ;
2009-02-20 16:03:50 +00:00
2017-04-23 22:03:10 -05:00
if ( mapFiles . empty ( ) | | files . empty ( ) )
2010-01-27 04:26:28 +00:00
return 0 ;
2017-07-22 23:33:56 -05:00
if ( Common : : File : : exists ( " ressci.001 " ) ) {
2016-08-19 10:05:05 -05:00
_multiDiscAudio = true ;
}
2010-01-27 21:43:23 +00:00
for ( Common : : ArchiveMemberList : : const_iterator mapIterator = mapFiles . begin ( ) ; mapIterator ! = mapFiles . end ( ) ; + + mapIterator ) {
2010-01-27 04:26:28 +00:00
Common : : String mapName = ( * mapIterator ) - > getName ( ) ;
2010-01-27 21:43:23 +00:00
int mapNumber = atoi ( strrchr ( mapName . c_str ( ) , ' . ' ) + 1 ) ;
2017-04-23 22:03:10 -05:00
bool foundVolume = false ;
2009-02-15 06:10:59 +00:00
2010-01-27 21:43:23 +00:00
for ( Common : : ArchiveMemberList : : const_iterator fileIterator = files . begin ( ) ; fileIterator ! = files . end ( ) ; + + fileIterator ) {
Common : : String resName = ( * fileIterator ) - > getName ( ) ;
int resNumber = atoi ( strrchr ( resName . c_str ( ) , ' . ' ) + 1 ) ;
2010-01-27 04:26:28 +00:00
2010-01-27 21:43:23 +00:00
if ( mapNumber = = resNumber ) {
2017-04-23 22:03:10 -05:00
foundVolume = true ;
2010-06-15 12:14:39 +00:00
addSource ( new VolumeResourceSource ( resName , addExternalMap ( mapName , mapNumber ) , mapNumber ) ) ;
2010-01-27 21:43:23 +00:00
break ;
}
}
2017-04-23 22:03:10 -05:00
2017-05-07 14:09:58 -05:00
if ( ! foundVolume & &
// GK2 on Steam comes with an extra bogus resource map file;
2017-06-08 00:28:32 -05:00
// ignore it instead of treating it as a bad resource
2017-05-07 14:09:58 -05:00
( g_sci - > getGameId ( ) ! = GID_GK2 | | mapFiles . size ( ) ! = 2 | | mapNumber ! = 1 ) ) {
2017-04-23 22:03:10 -05:00
warning ( " Could not find corresponding volume for %s " , mapName . c_str ( ) ) ;
2017-05-07 14:09:58 -05:00
_hasBadResources = true ;
2017-04-23 22:03:10 -05:00
}
2010-01-27 04:26:28 +00:00
}
2010-01-28 11:19:27 +00:00
// SCI2.1 resource patches
2010-06-01 22:06:52 +00:00
if ( Common : : File : : exists ( " resmap.pat " ) & & Common : : File : : exists ( " ressci.pat " ) ) {
2010-01-28 22:26:13 +00:00
// We add this resource with a map which surely won't exist
2017-07-30 22:59:41 -05:00
addSource ( new VolumeResourceSource ( " ressci.pat " , addExternalMap ( " resmap.pat " , kResPatVolumeNumber ) , kResPatVolumeNumber ) ) ;
2010-01-28 11:19:27 +00:00
}
2009-02-15 06:10:59 +00:00
}
2010-01-27 04:26:28 +00:00
# else
} else
return 0 ;
# endif
2009-06-06 00:07:18 +00:00
addPatchDir ( " . " ) ;
2010-07-17 20:29:33 +00:00
2010-06-01 22:06:52 +00:00
if ( Common : : File : : exists ( " message.map " ) )
2010-06-15 12:14:39 +00:00
addSource ( new VolumeResourceSource ( " resource.msg " , addExternalMap ( " message.map " ) , 0 ) ) ;
2010-01-27 04:26:28 +00:00
2010-07-17 20:29:33 +00:00
if ( Common : : File : : exists ( " altres.map " ) )
addSource ( new VolumeResourceSource ( " altres.000 " , addExternalMap ( " altres.map " ) , 0 ) ) ;
2009-06-06 00:07:18 +00:00
return 1 ;
}
2014-10-18 15:02:32 +03:00
int ResourceManager : : addAppropriateSourcesForDetection ( const Common : : FSList & fslist ) {
2009-08-18 14:10:31 +00:00
ResourceSource * map = 0 ;
2010-11-10 03:34:08 +00:00
Common : : Array < ResourceSource * > sci21Maps ;
2010-06-02 00:20:52 +00:00
# ifdef ENABLE_SCI32
2010-06-01 22:06:52 +00:00
ResourceSource * sci21PatchMap = 0 ;
const Common : : FSNode * sci21PatchRes = 0 ;
2016-08-22 18:50:36 +03:00
_multiDiscAudio = false ;
2010-06-02 00:20:52 +00:00
# endif
2009-08-18 14:10:31 +00:00
// First, find resource.map
for ( Common : : FSList : : const_iterator file = fslist . begin ( ) ; file ! = fslist . end ( ) ; + + file ) {
if ( file - > isDirectory ( ) )
continue ;
Common : : String filename = file - > getName ( ) ;
filename . toLowercase ( ) ;
2010-06-01 22:06:52 +00:00
if ( filename . contains ( " resource.map " ) )
2009-08-18 14:10:31 +00:00
map = addExternalMap ( file ) ;
2010-06-01 22:06:52 +00:00
if ( filename . contains ( " resmap.0 " ) ) {
const char * dot = strrchr ( file - > getName ( ) . c_str ( ) , ' . ' ) ;
2010-11-10 03:34:08 +00:00
uint number = atoi ( dot + 1 ) ;
// We need to store each of these maps for use later on
if ( number > = sci21Maps . size ( ) )
sci21Maps . resize ( number + 1 ) ;
sci21Maps [ number ] = addExternalMap ( file , number ) ;
2009-08-18 14:10:31 +00:00
}
2010-06-01 22:06:52 +00:00
# ifdef ENABLE_SCI32
// SCI2.1 resource patches
if ( filename . contains ( " resmap.pat " ) )
2017-07-30 22:59:41 -05:00
sci21PatchMap = addExternalMap ( file , kResPatVolumeNumber ) ;
2010-06-01 22:06:52 +00:00
if ( filename . contains ( " ressci.pat " ) )
sci21PatchRes = file ;
# endif
2009-08-18 14:10:31 +00:00
}
2010-11-10 03:34:08 +00:00
if ( ! map & & sci21Maps . empty ( ) )
2009-08-18 14:10:31 +00:00
return 0 ;
2010-06-01 22:06:52 +00:00
# ifdef ENABLE_SCI32
if ( sci21PatchMap & & sci21PatchRes )
2017-07-30 22:59:41 -05:00
addSource ( new VolumeResourceSource ( sci21PatchRes - > getName ( ) , sci21PatchMap , kResPatVolumeNumber , sci21PatchRes ) ) ;
2010-06-01 22:06:52 +00:00
# endif
2009-08-18 14:10:31 +00:00
// Now find all the resource.0?? files
for ( Common : : FSList : : const_iterator file = fslist . begin ( ) ; file ! = fslist . end ( ) ; + + file ) {
if ( file - > isDirectory ( ) )
continue ;
Common : : String filename = file - > getName ( ) ;
filename . toLowercase ( ) ;
2010-11-10 03:34:08 +00:00
if ( filename . contains ( " resource.0 " ) ) {
2009-08-18 14:10:31 +00:00
const char * dot = strrchr ( filename . c_str ( ) , ' . ' ) ;
int number = atoi ( dot + 1 ) ;
2010-06-15 12:16:17 +00:00
addSource ( new VolumeResourceSource ( file - > getName ( ) , map , number , file ) ) ;
2010-11-10 03:34:08 +00:00
} else if ( filename . contains ( " ressci.0 " ) ) {
const char * dot = strrchr ( filename . c_str ( ) , ' . ' ) ;
int number = atoi ( dot + 1 ) ;
// Match this volume to its own map
addSource ( new VolumeResourceSource ( file - > getName ( ) , sci21Maps [ number ] , number , file ) ) ;
2009-08-18 14:10:31 +00:00
}
}
// This function is only called by the advanced detector, and we don't really need
// to add a patch directory or message.map here
return 1 ;
}
2011-02-10 12:57:43 +00:00
void ResourceManager : : addScriptChunkSources ( ) {
2010-11-09 16:56:31 +00:00
# ifdef ENABLE_SCI32
if ( _mapVersion > = kResVersionSci2 ) {
// If we have no scripts, but chunk 0 is present, open up the chunk
// to try to get to any scripts in there. The Lighthouse SCI2.1 demo
// does exactly this.
2012-01-15 18:25:00 +01:00
Common : : List < ResourceId > resources = listResources ( kResourceTypeScript ) ;
2010-11-09 16:56:31 +00:00
2012-01-15 18:25:00 +01:00
if ( resources . empty ( ) & & testResource ( ResourceId ( kResourceTypeChunk , 0 ) ) )
2010-11-09 16:56:31 +00:00
addResourcesFromChunk ( 0 ) ;
}
# endif
2009-02-15 06:10:59 +00:00
}
2017-05-07 14:09:58 -05:00
extern void showScummVMDialog ( const Common : : String & message ) ;
2009-06-08 12:20:36 +00:00
void ResourceManager : : scanNewSources ( ) {
2017-05-07 14:09:58 -05:00
_hasBadResources = false ;
2009-06-08 12:20:36 +00:00
for ( Common : : List < ResourceSource * > : : iterator it = _sources . begin ( ) ; it ! = _sources . end ( ) ; + + it ) {
ResourceSource * source = * it ;
2010-06-15 12:15:05 +00:00
if ( ! source - > _scanned ) {
source - > _scanned = true ;
2010-06-15 16:50:10 +00:00
source - > scanSource ( this ) ;
2009-02-15 06:10:59 +00:00
}
}
2017-05-07 14:09:58 -05:00
// The warning dialog is shown here instead of someplace more obvious like
// SciEngine::run because resource sources can be dynamically added
// (e.g. KQ5 via kDoAudio, MGDX via kSetLanguage), and users really should
// be warned of bad resources in this situation (KQ Collection 1997 has a
// bad copy of KQ5 on CD 1; the working copy is on CD 2)
2017-05-13 22:45:20 -05:00
if ( ! _detectionMode & & _hasBadResources ) {
2017-05-07 14:09:58 -05:00
showScummVMDialog ( _ ( " Missing or corrupt game resources have been detected. "
" Some game features may not work properly. Please check "
" the console for more information, and verify that your "
" game files are valid. " ) ) ;
}
2009-02-15 06:10:59 +00:00
}
2010-06-15 16:50:10 +00:00
void DirectoryResourceSource : : scanSource ( ResourceManager * resMan ) {
2010-06-15 12:19:39 +00:00
resMan - > readResourcePatches ( ) ;
2010-06-15 12:12:21 +00:00
// We can't use getSciVersion() at this point, thus using _volVersion
2010-06-15 12:19:39 +00:00
if ( resMan - > _volVersion > = kResVersionSci11 ) // SCI1.1+
resMan - > readResourcePatchesBase36 ( ) ;
2010-06-15 12:12:21 +00:00
resMan - > readWaveAudioPatches ( ) ;
}
2010-06-15 16:50:10 +00:00
void ExtMapResourceSource : : scanSource ( ResourceManager * resMan ) {
2017-05-07 14:09:58 -05:00
if ( resMan - > _mapVersion < kResVersionSci1Late ) {
if ( resMan - > readResourceMapSCI0 ( this ) ! = SCI_ERROR_NONE ) {
resMan - > _hasBadResources = true ;
}
} else {
if ( resMan - > readResourceMapSCI1 ( this ) ! = SCI_ERROR_NONE ) {
resMan - > _hasBadResources = true ;
}
}
2010-06-15 12:12:21 +00:00
}
2010-06-15 16:50:10 +00:00
void ExtAudioMapResourceSource : : scanSource ( ResourceManager * resMan ) {
2017-05-07 14:09:58 -05:00
if ( resMan - > readAudioMapSCI1 ( this ) ! = SCI_ERROR_NONE ) {
resMan - > _hasBadResources = true ;
}
2010-06-15 12:12:21 +00:00
}
2010-06-15 16:50:10 +00:00
void IntMapResourceSource : : scanSource ( ResourceManager * resMan ) {
2017-05-07 14:09:58 -05:00
if ( resMan - > readAudioMapSCI11 ( this ) ! = SCI_ERROR_NONE ) {
resMan - > _hasBadResources = true ;
}
2010-06-15 12:12:21 +00:00
}
2010-08-03 14:58:57 +00:00
# ifdef ENABLE_SCI32
// Chunk resources are resources that hold other resources. They are normally called
// when using the kLoadChunk SCI2.1 kernel function. However, for example, the Lighthouse
// SCI2.1 demo has a chunk but no scripts outside of the chunk.
// A chunk resource is pretty straightforward in terms of layout
// It begins with 11-byte entries in the header:
// =========
// b resType
// w nEntry
// dw offset
// dw length
ChunkResourceSource : : ChunkResourceSource ( const Common : : String & name , uint16 number )
: ResourceSource ( kSourceChunk , name ) {
2017-05-07 23:55:36 -05:00
_number = number ;
2010-08-03 14:58:57 +00:00
}
void ChunkResourceSource : : scanSource ( ResourceManager * resMan ) {
Resource * chunk = resMan - > findResource ( ResourceId ( kResourceTypeChunk , _number ) , false ) ;
if ( ! chunk )
error ( " Trying to load non-existent chunk " ) ;
2016-12-31 20:39:57 -06:00
const byte * ptr = chunk - > data ( ) ;
2010-08-03 14:58:57 +00:00
uint32 firstOffset = 0 ;
2011-06-20 00:59:48 +02:00
2010-08-03 14:58:57 +00:00
for ( ; ; ) {
ResourceType type = resMan - > convertResType ( * ptr ) ;
uint16 number = READ_LE_UINT16 ( ptr + 1 ) ;
ResourceId id ( type , number ) ;
ResourceEntry entry ;
entry . offset = READ_LE_UINT32 ( ptr + 3 ) ;
entry . length = READ_LE_UINT32 ( ptr + 7 ) ;
_resMap [ id ] = entry ;
ptr + = 11 ;
debugC ( kDebugLevelResMan , 2 , " Found %s in chunk %d " , id . toString ( ) . c_str ( ) , _number ) ;
2017-05-07 14:09:58 -05:00
resMan - > updateResource ( id , this , entry . length , chunk - > _source - > getLocationName ( ) ) ;
2010-08-03 14:58:57 +00:00
// There's no end marker to the data table, but the first resource
// begins directly after the entry table. So, when we hit the first
// resource, we're at the end of the entry table.
2011-06-20 00:59:48 +02:00
2010-08-03 14:58:57 +00:00
if ( ! firstOffset )
firstOffset = entry . offset ;
2016-12-31 20:39:57 -06:00
if ( ( size_t ) ( ptr - chunk - > data ( ) ) > = firstOffset )
2010-08-03 14:58:57 +00:00
break ;
}
}
void ChunkResourceSource : : loadResource ( ResourceManager * resMan , Resource * res ) {
Resource * chunk = resMan - > findResource ( ResourceId ( kResourceTypeChunk , _number ) , false ) ;
if ( ! _resMap . contains ( res - > _id ) )
2017-05-07 14:09:58 -05:00
error ( " Trying to load non-existent resource %s from chunk %d " , res - > _id . toString ( ) . c_str ( ) , _number ) ;
2010-08-03 14:58:57 +00:00
ResourceEntry entry = _resMap [ res - > _id ] ;
2017-05-07 14:09:58 -05:00
if ( entry . offset + entry . length > chunk - > size ( ) ) {
error ( " Resource %s is too large to exist within chunk %d (%u + %u > %u) " , res - > _id . toString ( ) . c_str ( ) , _number , entry . offset , entry . length , chunk - > size ( ) ) ;
}
2016-12-31 20:39:57 -06:00
byte * ptr = new byte [ entry . length ] ;
res - > _data = ptr ;
res - > _size = entry . length ;
2010-08-03 14:58:57 +00:00
res - > _header = 0 ;
res - > _headerSize = 0 ;
res - > _status = kResStatusAllocated ;
// Copy the resource data over
2016-12-31 20:39:57 -06:00
memcpy ( ptr , chunk - > data ( ) + entry . offset , entry . length ) ;
2010-08-03 14:58:57 +00:00
}
void ResourceManager : : addResourcesFromChunk ( uint16 id ) {
2010-11-01 16:02:28 +00:00
addSource ( new ChunkResourceSource ( Common : : String : : format ( " Chunk %d " , id ) , id ) ) ;
2010-08-03 14:58:57 +00:00
scanNewSources ( ) ;
}
2016-08-19 08:59:55 -05:00
void ResourceManager : : findDisc ( const int16 discNo ) {
// Since all resources are expected to be copied from the original discs
// into a single game directory, this call just records the number of the CD
// that the game has requested
_currentDiscNo = discNo ;
}
2010-08-03 14:58:57 +00:00
# endif
2010-06-15 12:12:21 +00:00
2009-06-08 12:20:36 +00:00
void ResourceManager : : freeResourceSources ( ) {
2010-05-18 04:17:58 +00:00
for ( Common : : List < ResourceSource * > : : iterator it = _sources . begin ( ) ; it ! = _sources . end ( ) ; + + it )
2009-06-08 12:20:36 +00:00
delete * it ;
_sources . clear ( ) ;
2009-02-15 06:10:59 +00:00
}
2017-05-13 22:45:20 -05:00
ResourceManager : : ResourceManager ( const bool detectionMode ) :
_detectionMode ( detectionMode ) { }
2009-08-18 14:10:31 +00:00
2014-10-18 15:33:07 +03:00
void ResourceManager : : init ( ) {
2016-02-17 18:47:28 -06:00
_maxMemoryLRU = 256 * 1024 ; // 256KiB
2009-02-28 20:45:36 +00:00
_memoryLocked = 0 ;
_memoryLRU = 0 ;
2009-03-05 23:27:02 +00:00
_LRU . clear ( ) ;
2009-03-03 23:07:06 +00:00
_resMap . clear ( ) ;
2009-06-09 19:18:48 +00:00
_audioMapSCI1 = NULL ;
2016-08-19 08:59:55 -05:00
# ifdef ENABLE_SCI32
_currentDiscNo = 1 ;
# endif
2009-08-15 00:28:59 +00:00
// FIXME: put this in an Init() function, so that we can error out if detection fails completely
2009-07-11 16:07:14 +00:00
2009-08-15 00:28:59 +00:00
_mapVersion = detectMapVersion ( ) ;
_volVersion = detectVolVersion ( ) ;
2010-11-09 19:37:42 +00:00
2009-08-15 00:28:59 +00:00
if ( ( _volVersion = = kResVersionUnknown ) & & ( _mapVersion ! = kResVersionUnknown ) ) {
warning ( " Volume version not detected, but map version has been detected. Setting volume version to map version " ) ;
_volVersion = _mapVersion ;
}
if ( ( _mapVersion = = kResVersionUnknown ) & & ( _volVersion ! = kResVersionUnknown ) ) {
warning ( " Map version not detected, but volume version has been detected. Setting map version to volume version " ) ;
_mapVersion = _volVersion ;
2009-03-07 01:08:34 +00:00
}
2009-08-15 00:28:59 +00:00
2009-10-10 00:07:19 +00:00
debugC ( 1 , kDebugLevelResMan , " resMan: Detected resource map version %d: %s " , _mapVersion , versionDescription ( _mapVersion ) ) ;
debugC ( 1 , kDebugLevelResMan , " resMan: Detected volume version %d: %s " , _volVersion , versionDescription ( _volVersion ) ) ;
2009-03-07 01:08:34 +00:00
2010-01-17 22:32:53 +00:00
if ( ( _mapVersion = = kResVersionUnknown ) & & ( _volVersion = = kResVersionUnknown ) ) {
2014-10-18 15:02:32 +03:00
warning ( " Volume and map version not detected, assuming that this is not a SCI game " ) ;
2010-01-17 22:32:53 +00:00
_viewType = kViewUnknown ;
return ;
}
2009-06-08 12:20:36 +00:00
scanNewSources ( ) ;
2010-11-09 14:25:39 +00:00
2014-10-18 15:33:07 +03:00
if ( ! addAudioSources ( ) ) {
// FIXME: This error message is not always correct.
// OTOH, it is nice to be able to detect missing files/sources
// So we should definitely fix addAudioSources so this error
// only pops up when necessary. Disabling for now.
//error("Somehow I can't seem to find the sound files I need (RESOURCE.AUD/RESOURCE.SFX), aborting");
2010-11-09 17:07:34 +00:00
}
2009-06-06 00:07:18 +00:00
2014-10-18 15:33:07 +03:00
addScriptChunkSources ( ) ;
scanNewSources ( ) ;
2009-09-23 10:55:35 +00:00
detectSciVersion ( ) ;
2009-07-11 19:00:56 +00:00
2010-02-13 11:58:15 +00:00
debugC ( 1 , kDebugLevelResMan , " resMan: Detected %s " , getSciVersionDesc ( getSciVersion ( ) ) ) ;
2009-08-16 19:18:19 +00:00
2016-02-17 18:47:28 -06:00
// Resources in SCI32 games are significantly larger than SCI16
// games and can cause immediate exhaustion of the LRU resource
// cache, leading to constant decompression of picture resources
// and making the renderer very slow.
if ( getSciVersion ( ) > = SCI_VERSION_2 ) {
2016-09-26 15:58:39 -05:00
_maxMemoryLRU = 4096 * 1024 ; // 4MiB
2016-02-17 18:47:28 -06:00
}
2009-08-16 19:18:19 +00:00
switch ( _viewType ) {
case kViewEga :
2009-10-10 00:07:19 +00:00
debugC ( 1 , kDebugLevelResMan , " resMan: Detected EGA graphic resources " ) ;
2009-03-11 01:40:08 +00:00
break ;
2011-03-07 13:23:52 +02:00
case kViewAmiga :
debugC ( 1 , kDebugLevelResMan , " resMan: Detected Amiga ECS graphic resources " ) ;
break ;
case kViewAmiga64 :
debugC ( 1 , kDebugLevelResMan , " resMan: Detected Amiga AGA graphic resources " ) ;
break ;
2009-08-16 19:18:19 +00:00
case kViewVga :
2009-10-10 00:07:19 +00:00
debugC ( 1 , kDebugLevelResMan , " resMan: Detected VGA graphic resources " ) ;
2009-03-11 01:40:08 +00:00
break ;
2009-08-16 19:18:19 +00:00
case kViewVga11 :
2009-10-10 00:07:19 +00:00
debugC ( 1 , kDebugLevelResMan , " resMan: Detected SCI1.1 VGA graphic resources " ) ;
2009-08-19 21:08:17 +00:00
break ;
default :
2016-02-20 16:55:01 +02:00
// Throw a warning, but do not error out here, because this is called from the
// fallback detector, and the user could be pointing to a folder with a non-SCI
// game, but with SCI-like file names (e.g. Pinball Creep)
warning ( " resMan: Couldn't determine view type " ) ;
break ;
2009-03-07 00:59:38 +00:00
}
2009-02-15 06:10:59 +00:00
}
2009-02-20 16:03:50 +00:00
ResourceManager : : ~ ResourceManager ( ) {
2009-03-03 23:07:06 +00:00
// freeing resources
2009-06-07 02:18:38 +00:00
ResourceMap : : iterator itr = _resMap . begin ( ) ;
2009-03-03 23:07:06 +00:00
while ( itr ! = _resMap . end ( ) ) {
delete itr - > _value ;
2010-01-13 23:58:25 +00:00
+ + itr ;
2009-03-03 23:07:06 +00:00
}
2009-06-08 12:20:36 +00:00
freeResourceSources ( ) ;
2009-03-11 20:15:42 +00:00
Common : : List < Common : : File * > : : iterator it = _volumeFiles . begin ( ) ;
while ( it ! = _volumeFiles . end ( ) ) {
delete * it ;
2010-01-13 23:58:25 +00:00
+ + it ;
2009-03-11 20:15:42 +00:00
}
2009-02-15 06:10:59 +00:00
}
2009-02-28 21:59:49 +00:00
void ResourceManager : : removeFromLRU ( Resource * res ) {
2010-02-17 23:37:59 +00:00
if ( res - > _status ! = kResStatusEnqueued ) {
2009-09-02 12:02:37 +00:00
warning ( " resMan: trying to remove resource that isn't enqueued " ) ;
2009-02-15 06:10:59 +00:00
return ;
}
2009-03-05 23:27:02 +00:00
_LRU . remove ( res ) ;
2016-12-31 20:39:57 -06:00
_memoryLRU - = res - > size ( ) ;
2010-02-17 23:37:59 +00:00
res - > _status = kResStatusAllocated ;
2009-02-15 06:10:59 +00:00
}
2009-02-28 21:59:49 +00:00
void ResourceManager : : addToLRU ( Resource * res ) {
2010-02-17 23:37:59 +00:00
if ( res - > _status ! = kResStatusAllocated ) {
warning ( " resMan: trying to enqueue resource with state %d " , res - > _status ) ;
2009-02-15 06:10:59 +00:00
return ;
}
2009-03-05 23:27:02 +00:00
_LRU . push_front ( res ) ;
2016-12-31 20:39:57 -06:00
_memoryLRU + = res - > size ( ) ;
2010-09-28 20:30:50 +00:00
# if SCI_VERBOSE_RESMAN
2016-02-17 18:35:01 -06:00
debug ( " Adding %s (%d bytes) to lru control: %d bytes total " ,
res - > _id . toString ( ) . c_str ( ) , res - > size ,
_memoryLRU ) ;
2009-02-15 06:10:59 +00:00
# endif
2010-02-17 23:37:59 +00:00
res - > _status = kResStatusEnqueued ;
2009-02-15 06:10:59 +00:00
}
2009-02-28 20:45:36 +00:00
void ResourceManager : : printLRU ( ) {
2009-02-15 06:10:59 +00:00
int mem = 0 ;
int entries = 0 ;
2009-03-05 23:27:02 +00:00
Common : : List < Resource * > : : iterator it = _LRU . begin ( ) ;
Resource * res ;
2009-02-15 06:10:59 +00:00
2009-03-05 23:27:02 +00:00
while ( it ! = _LRU . end ( ) ) {
res = * it ;
2017-03-30 14:02:27 -05:00
debug ( " \t %s: %u bytes " , res - > _id . toString ( ) . c_str ( ) , res - > size ( ) ) ;
2016-12-31 20:39:57 -06:00
mem + = res - > size ( ) ;
2010-01-13 23:58:25 +00:00
+ + entries ;
+ + it ;
2009-02-15 06:10:59 +00:00
}
2009-03-05 23:27:02 +00:00
debug ( " Total: %d entries, %d bytes (mgr says %d) " , entries , mem , _memoryLRU ) ;
2009-02-15 06:10:59 +00:00
}
2009-06-09 19:18:48 +00:00
void ResourceManager : : freeOldResources ( ) {
2016-02-17 18:47:28 -06:00
while ( _maxMemoryLRU < _memoryLRU ) {
2009-06-09 19:18:48 +00:00
assert ( ! _LRU . empty ( ) ) ;
2017-06-04 22:02:30 -05:00
Resource * goner = _LRU . back ( ) ;
2009-02-28 20:45:36 +00:00
removeFromLRU ( goner ) ;
2009-03-03 23:07:06 +00:00
goner - > unalloc ( ) ;
2010-09-28 20:30:50 +00:00
# ifdef SCI_VERBOSE_RESMAN
2016-02-17 18:35:01 -06:00
debug ( " resMan-debug: LRU: Freeing %s (%d bytes) " , goner - > _id . toString ( ) . c_str ( ) , goner - > size ) ;
2009-02-15 06:10:59 +00:00
# endif
}
}
2012-01-15 18:25:00 +01:00
Common : : List < ResourceId > ResourceManager : : listResources ( ResourceType type , int mapNumber ) {
Common : : List < ResourceId > resources ;
2009-06-07 19:15:55 +00:00
ResourceMap : : iterator itr = _resMap . begin ( ) ;
while ( itr ! = _resMap . end ( ) ) {
2010-06-15 12:35:17 +00:00
if ( ( itr - > _value - > getType ( ) = = type ) & & ( ( mapNumber = = - 1 ) | | ( itr - > _value - > getNumber ( ) = = mapNumber ) ) )
2012-01-15 18:25:00 +01:00
resources . push_back ( itr - > _value - > _id ) ;
2010-01-13 23:58:25 +00:00
+ + itr ;
2009-06-07 19:15:55 +00:00
}
return resources ;
}
Resource * ResourceManager : : findResource ( ResourceId id , bool lock ) {
2009-07-07 13:39:24 +00:00
Resource * retval = testResource ( id ) ;
2009-02-15 06:10:59 +00:00
if ( ! retval )
return NULL ;
2010-02-17 23:37:59 +00:00
if ( retval - > _status = = kResStatusNoMalloc )
2009-03-05 23:27:02 +00:00
loadResource ( retval ) ;
2010-02-17 23:37:59 +00:00
else if ( retval - > _status = = kResStatusEnqueued )
2016-06-19 21:37:07 -05:00
// The resource is removed from its current position
// in the LRU list because it has been requested
// again. Below, it will either be locked, or it
// will be added back to the LRU list at the 'most
// recent' position.
2009-02-28 20:45:36 +00:00
removeFromLRU ( retval ) ;
2016-06-19 21:37:07 -05:00
2010-06-15 12:33:20 +00:00
// Unless an error occurred, the resource is now either
2009-02-21 12:33:24 +00:00
// locked or allocated, but never queued or freed.
2009-02-15 06:10:59 +00:00
2009-06-09 19:18:48 +00:00
freeOldResources ( ) ;
2009-02-15 06:10:59 +00:00
if ( lock ) {
2010-02-17 23:37:59 +00:00
if ( retval - > _status = = kResStatusAllocated ) {
retval - > _status = kResStatusLocked ;
retval - > _lockers = 0 ;
2016-12-31 20:39:57 -06:00
_memoryLocked + = retval - > _size ;
2009-02-15 06:10:59 +00:00
}
2010-02-17 23:37:59 +00:00
retval - > _lockers + + ;
} else if ( retval - > _status ! = kResStatusLocked ) { // Don't lock it
if ( retval - > _status = = kResStatusAllocated )
2009-02-28 20:45:36 +00:00
addToLRU ( retval ) ;
2009-02-15 06:10:59 +00:00
}
2016-12-31 20:39:57 -06:00
if ( retval - > data ( ) )
2009-02-15 06:10:59 +00:00
return retval ;
else {
2010-02-17 23:37:59 +00:00
warning ( " resMan: Failed to read %s " , retval - > _id . toString ( ) . c_str ( ) ) ;
2016-12-31 20:39:57 -06:00
return nullptr ;
2009-02-15 06:10:59 +00:00
}
}
2009-06-07 19:15:55 +00:00
void ResourceManager : : unlockResource ( Resource * res ) {
assert ( res ) ;
2009-02-15 06:10:59 +00:00
2010-02-17 23:37:59 +00:00
if ( res - > _status ! = kResStatusLocked ) {
2010-08-07 00:59:40 +00:00
debugC ( kDebugLevelResMan , 2 , " [resMan] Attempt to unlock unlocked resource %s " , res - > _id . toString ( ) . c_str ( ) ) ;
2009-02-15 06:10:59 +00:00
return ;
}
2010-02-17 23:37:59 +00:00
if ( ! - - res - > _lockers ) { // No more lockers?
res - > _status = kResStatusAllocated ;
2016-12-31 20:39:57 -06:00
_memoryLocked - = res - > size ( ) ;
2009-02-28 20:45:36 +00:00
addToLRU ( res ) ;
2009-02-15 06:10:59 +00:00
}
2009-06-09 19:18:48 +00:00
freeOldResources ( ) ;
2009-02-15 06:10:59 +00:00
}
2009-08-15 00:28:59 +00:00
const char * ResourceManager : : versionDescription ( ResVersion version ) const {
switch ( version ) {
case kResVersionUnknown :
return " Unknown " ;
case kResVersionSci0Sci1Early :
return " SCI0 / Early SCI1 " ;
case kResVersionSci1Middle :
return " Middle SCI1 " ;
2011-02-15 21:47:29 -05:00
case kResVersionKQ5FMT :
return " KQ5 FM Towns " ;
2009-08-15 00:28:59 +00:00
case kResVersionSci1Late :
return " Late SCI1 " ;
case kResVersionSci11 :
return " SCI1.1 " ;
2010-05-10 18:29:13 +00:00
case kResVersionSci11Mac :
return " Mac SCI1.1+ " ;
2010-11-09 14:25:39 +00:00
case kResVersionSci2 :
2010-11-09 16:23:08 +00:00
return " SCI2/2.1 " ;
2010-11-09 14:25:39 +00:00
case kResVersionSci3 :
2010-11-09 16:23:08 +00:00
return " SCI3 " ;
2009-08-15 00:28:59 +00:00
}
return " Version not valid " ;
}
2010-06-15 12:19:39 +00:00
ResVersion ResourceManager : : detectMapVersion ( ) {
2009-08-18 14:10:31 +00:00
Common : : SeekableReadStream * fileStream = 0 ;
2009-03-07 00:59:38 +00:00
byte buff [ 6 ] ;
2009-07-01 20:51:34 +00:00
ResourceSource * rsrc = 0 ;
2009-06-08 12:20:36 +00:00
for ( Common : : List < ResourceSource * > : : iterator it = _sources . begin ( ) ; it ! = _sources . end ( ) ; + + it ) {
rsrc = * it ;
2010-06-15 12:09:30 +00:00
if ( rsrc - > getSourceType ( ) = = kSourceExtMap ) {
2010-06-15 12:13:07 +00:00
if ( rsrc - > _resourceFile ) {
fileStream = rsrc - > _resourceFile - > createReadStream ( ) ;
2009-08-18 14:10:31 +00:00
} else {
2010-06-02 13:17:36 +00:00
Common : : File * file = new Common : : File ( ) ;
2010-06-15 12:09:51 +00:00
file - > open ( rsrc - > getLocationName ( ) ) ;
2009-08-18 14:10:31 +00:00
if ( file - > isOpen ( ) )
fileStream = file ;
}
2009-03-07 00:59:38 +00:00
break ;
2010-12-14 23:30:22 +00:00
} else if ( rsrc - > getSourceType ( ) = = kSourceMacResourceFork ) {
2010-05-10 18:29:13 +00:00
return kResVersionSci11Mac ;
2010-12-14 23:30:22 +00:00
}
2009-03-07 00:59:38 +00:00
}
2009-08-18 09:12:41 +00:00
2011-09-10 13:47:02 +03:00
if ( ! fileStream ) {
warning ( " Failed to open resource map file " ) ;
return kResVersionUnknown ;
}
2009-08-21 18:12:13 +00:00
2009-03-07 00:59:38 +00:00
// detection
// SCI0 and SCI01 maps have last 6 bytes set to FF
2009-08-18 14:10:31 +00:00
fileStream - > seek ( - 4 , SEEK_END ) ;
uint32 uEnd = fileStream - > readUint32LE ( ) ;
2009-03-07 00:59:38 +00:00
if ( uEnd = = 0xFFFFFFFF ) {
2011-02-15 21:47:29 -05:00
// check if the last 7 bytes are all ff, indicating a KQ5 FM-Towns map
fileStream - > seek ( - 7 , SEEK_END ) ;
fileStream - > read ( buff , 3 ) ;
if ( buff [ 0 ] = = 0xff & & buff [ 1 ] = = 0xff & & buff [ 2 ] = = 0xff ) {
delete fileStream ;
return kResVersionKQ5FMT ;
}
2009-03-07 00:59:38 +00:00
// check if 0 or 01 - try to read resources in SCI0 format and see if exists
2009-08-18 14:10:31 +00:00
fileStream - > seek ( 0 , SEEK_SET ) ;
while ( fileStream - > read ( buff , 6 ) = = 6 & & ! ( buff [ 0 ] = = 0xFF & & buff [ 1 ] = = 0xFF & & buff [ 2 ] = = 0xFF ) ) {
2010-12-14 23:30:22 +00:00
if ( findVolume ( rsrc , ( buff [ 5 ] & 0xFC ) > > 2 ) = = NULL ) {
delete fileStream ;
2009-08-15 00:28:59 +00:00
return kResVersionSci1Middle ;
2010-12-14 23:30:22 +00:00
}
2009-03-07 00:59:38 +00:00
}
2010-12-14 23:30:22 +00:00
delete fileStream ;
2009-08-15 00:28:59 +00:00
return kResVersionSci0Sci1Early ;
2009-03-07 00:59:38 +00:00
}
2009-07-09 15:54:18 +00:00
// SCI1 and SCI1.1 maps consist of a fixed 3-byte header, a directory list (3-bytes each) that has one entry
// of id FFh and points to EOF. The actual entries have 6-bytes on SCI1 and 5-bytes on SCI1.1
byte directoryType = 0 ;
uint16 directoryOffset = 0 ;
uint16 lastDirectoryOffset = 0 ;
uint16 directorySize = 0 ;
2009-08-15 00:28:59 +00:00
ResVersion mapDetected = kResVersionUnknown ;
2009-08-18 14:10:31 +00:00
fileStream - > seek ( 0 , SEEK_SET ) ;
while ( ! fileStream - > eos ( ) ) {
directoryType = fileStream - > readByte ( ) ;
directoryOffset = fileStream - > readUint16LE ( ) ;
2009-08-21 19:48:48 +00:00
2009-08-21 18:12:13 +00:00
// Only SCI32 has directory type < 0x80
2010-11-09 14:25:39 +00:00
if ( directoryType < 0x80 & & ( mapDetected = = kResVersionUnknown | | mapDetected = = kResVersionSci2 ) )
mapDetected = kResVersionSci2 ;
2009-08-25 23:02:57 +00:00
else if ( directoryType < 0x80 | | ( ( directoryType & 0x7f ) > 0x20 & & directoryType ! = 0xFF ) )
2009-08-21 18:12:13 +00:00
break ;
2009-12-27 12:17:43 +00:00
2009-07-09 15:54:18 +00:00
// Offset is above file size? -> definitely not SCI1/SCI1.1
2009-08-18 14:10:31 +00:00
if ( directoryOffset > fileStream - > size ( ) )
2009-07-09 15:54:18 +00:00
break ;
2009-08-21 18:12:13 +00:00
2009-08-25 23:02:57 +00:00
if ( lastDirectoryOffset & & mapDetected = = kResVersionUnknown ) {
2009-07-09 15:54:18 +00:00
directorySize = directoryOffset - lastDirectoryOffset ;
if ( ( directorySize % 5 ) & & ( directorySize % 6 = = 0 ) )
2009-08-15 00:28:59 +00:00
mapDetected = kResVersionSci1Late ;
2009-07-09 15:54:18 +00:00
if ( ( directorySize % 5 = = 0 ) & & ( directorySize % 6 ) )
2009-08-15 00:28:59 +00:00
mapDetected = kResVersionSci11 ;
2009-03-07 00:59:38 +00:00
}
2009-12-27 12:17:43 +00:00
2009-08-18 14:10:31 +00:00
if ( directoryType = = 0xFF ) {
2009-07-09 15:54:18 +00:00
// FFh entry needs to point to EOF
2009-08-18 14:10:31 +00:00
if ( directoryOffset ! = fileStream - > size ( ) )
2009-07-09 15:54:18 +00:00
break ;
2009-08-18 14:10:31 +00:00
delete fileStream ;
if ( mapDetected )
2009-07-09 15:54:18 +00:00
return mapDetected ;
2009-08-15 00:28:59 +00:00
return kResVersionSci1Late ;
2009-07-09 15:54:18 +00:00
}
2009-08-21 18:12:13 +00:00
2009-07-09 15:54:18 +00:00
lastDirectoryOffset = directoryOffset ;
2009-03-07 00:59:38 +00:00
}
2009-03-30 07:53:32 +00:00
2009-08-18 14:10:31 +00:00
delete fileStream ;
2009-08-15 00:28:59 +00:00
return kResVersionUnknown ;
2009-03-07 00:59:38 +00:00
}
2010-06-15 12:19:39 +00:00
ResVersion ResourceManager : : detectVolVersion ( ) {
2009-08-18 14:10:31 +00:00
Common : : SeekableReadStream * fileStream = 0 ;
2009-06-08 12:20:36 +00:00
ResourceSource * rsrc ;
2009-08-18 14:10:31 +00:00
2009-06-08 12:20:36 +00:00
for ( Common : : List < ResourceSource * > : : iterator it = _sources . begin ( ) ; it ! = _sources . end ( ) ; + + it ) {
rsrc = * it ;
2011-06-20 00:59:48 +02:00
2010-06-15 12:09:30 +00:00
if ( rsrc - > getSourceType ( ) = = kSourceVolume ) {
2010-06-15 12:13:07 +00:00
if ( rsrc - > _resourceFile ) {
fileStream = rsrc - > _resourceFile - > createReadStream ( ) ;
2009-08-18 14:10:31 +00:00
} else {
2010-06-02 13:17:36 +00:00
Common : : File * file = new Common : : File ( ) ;
2010-06-15 12:09:51 +00:00
file - > open ( rsrc - > getLocationName ( ) ) ;
2009-08-18 14:10:31 +00:00
if ( file - > isOpen ( ) )
fileStream = file ;
}
2009-03-07 00:59:38 +00:00
break ;
2010-06-15 12:09:30 +00:00
} else if ( rsrc - > getSourceType ( ) = = kSourceMacResourceFork )
2010-05-10 18:29:13 +00:00
return kResVersionSci11Mac ;
2009-03-07 00:59:38 +00:00
}
2010-05-10 18:29:13 +00:00
2009-08-18 14:10:31 +00:00
if ( ! fileStream ) {
2011-09-10 13:47:02 +03:00
warning ( " Failed to open volume file - if you got resource.p01/resource.p02/etc. files, merge them together into resource.000 " ) ;
2010-01-18 23:07:56 +00:00
// resource.p01/resource.p02/etc. may be there when directly copying the files from the original floppies
// the sierra installer would merge those together (perhaps we could do this as well?)
// possible TODO
// example for such game: Laura Bow 2
2009-08-15 00:28:59 +00:00
return kResVersionUnknown ;
2009-03-07 00:59:38 +00:00
}
2009-08-18 14:10:31 +00:00
2009-03-07 05:44:36 +00:00
// SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes
// SCI1 volume format: {bResType wResNumber wPacked+4 wUnpacked wCompression} = 9 bytes
2009-03-10 21:44:03 +00:00
// SCI1.1 volume format: {bResType wResNumber wPacked wUnpacked wCompression} = 9 bytes
2009-12-27 22:41:33 +00:00
// SCI32 volume format: {bResType wResNumber dwPacked dwUnpacked wCompression} = 13 bytes
2009-03-07 00:59:38 +00:00
// Try to parse volume with SCI0 scheme to see if it make sense
// Checking 1MB of data should be enough to determine the version
2011-04-27 13:12:05 +03:00
uint16 wCompression ;
2009-03-07 05:44:36 +00:00
uint32 dwPacked , dwUnpacked ;
2009-08-15 00:28:59 +00:00
ResVersion curVersion = kResVersionSci0Sci1Early ;
2009-05-14 21:47:38 +00:00
bool failed = false ;
2009-08-30 19:33:34 +00:00
bool sci11Align = false ;
2009-03-30 07:53:32 +00:00
2010-11-09 14:25:39 +00:00
// Check for SCI0, SCI1, SCI1.1, SCI32 v2 (Gabriel Knight 1 CD) and SCI32 v3 (LSL7) formats
2009-08-18 14:10:31 +00:00
while ( ! fileStream - > eos ( ) & & fileStream - > pos ( ) < 0x100000 ) {
2009-08-15 00:28:59 +00:00
if ( curVersion > kResVersionSci0Sci1Early )
2009-08-18 14:10:31 +00:00
fileStream - > readByte ( ) ;
2011-04-27 13:12:05 +03:00
fileStream - > skip ( 2 ) ; // resId
2010-11-09 14:25:39 +00:00
dwPacked = ( curVersion < kResVersionSci2 ) ? fileStream - > readUint16LE ( ) : fileStream - > readUint32LE ( ) ;
dwUnpacked = ( curVersion < kResVersionSci2 ) ? fileStream - > readUint16LE ( ) : fileStream - > readUint32LE ( ) ;
// The compression field is present, but bogus when
// loading SCI3 volumes, the format is otherwise
// identical to SCI2. We therefore get the compression
// indicator here, but disregard it in the following
2011-06-20 00:59:48 +02:00
// code.
2009-12-27 22:41:33 +00:00
wCompression = fileStream - > readUint16LE ( ) ;
2010-11-09 14:25:39 +00:00
2009-08-18 14:10:31 +00:00
if ( fileStream - > eos ( ) ) {
delete fileStream ;
2009-05-14 21:47:38 +00:00
return curVersion ;
2009-08-18 14:10:31 +00:00
}
2009-05-14 21:47:38 +00:00
2010-11-09 14:25:39 +00:00
int chk ;
2010-11-09 16:23:08 +00:00
if ( curVersion = = kResVersionSci0Sci1Early )
2010-11-09 14:25:39 +00:00
chk = 4 ;
2010-11-09 16:23:08 +00:00
else if ( curVersion < kResVersionSci2 )
2010-11-09 14:25:39 +00:00
chk = 20 ;
2010-11-09 16:23:08 +00:00
else
2010-11-09 14:25:39 +00:00
chk = 32 ; // We don't need this, but include it for completeness
2010-11-09 16:23:08 +00:00
2009-08-15 00:28:59 +00:00
int offs = curVersion < kResVersionSci11 ? 4 : 0 ;
2010-11-09 14:25:39 +00:00
if ( ( curVersion < kResVersionSci2 & & wCompression > chk )
| | ( curVersion = = kResVersionSci2 & & wCompression ! = 0 & & wCompression ! = 32 )
2009-05-14 21:47:38 +00:00
| | ( wCompression = = 0 & & dwPacked ! = dwUnpacked + offs )
| | ( dwUnpacked < dwPacked - offs ) ) {
// Retry with a newer SCI version
2009-08-15 00:28:59 +00:00
if ( curVersion = = kResVersionSci0Sci1Early ) {
curVersion = kResVersionSci1Late ;
} else if ( curVersion = = kResVersionSci1Late ) {
curVersion = kResVersionSci11 ;
2009-08-30 19:33:34 +00:00
} else if ( curVersion = = kResVersionSci11 & & ! sci11Align ) {
2009-10-08 08:22:19 +00:00
// Later versions (e.g. QFG1VGA) have resources word-aligned
2009-08-30 19:33:34 +00:00
sci11Align = true ;
2009-08-15 00:28:59 +00:00
} else if ( curVersion = = kResVersionSci11 ) {
2010-11-09 14:25:39 +00:00
curVersion = kResVersionSci2 ;
} else if ( curVersion = = kResVersionSci2 ) {
curVersion = kResVersionSci3 ;
2009-05-14 21:47:38 +00:00
} else {
// All version checks failed, exit loop
failed = true ;
break ;
}
2009-08-21 18:12:13 +00:00
fileStream - > seek ( 0 ) ;
2009-05-14 21:47:38 +00:00
continue ;
2009-03-07 05:44:36 +00:00
}
2009-05-14 21:47:38 +00:00
2009-08-15 00:28:59 +00:00
if ( curVersion < kResVersionSci11 )
2009-08-18 14:10:31 +00:00
fileStream - > seek ( dwPacked - 4 , SEEK_CUR ) ;
2009-08-15 00:28:59 +00:00
else if ( curVersion = = kResVersionSci11 )
2009-08-30 19:33:34 +00:00
fileStream - > seek ( sci11Align & & ( ( 9 + dwPacked ) % 2 ) ? dwPacked + 1 : dwPacked , SEEK_CUR ) ;
2010-11-09 14:25:39 +00:00
else if ( curVersion > = kResVersionSci2 )
2009-12-27 22:41:33 +00:00
fileStream - > seek ( dwPacked , SEEK_CUR ) ;
2009-03-07 05:44:36 +00:00
}
2009-05-14 21:47:38 +00:00
2009-08-18 14:10:31 +00:00
delete fileStream ;
2009-05-14 21:47:38 +00:00
if ( ! failed )
return curVersion ;
2009-03-07 00:59:38 +00:00
2009-03-07 05:44:36 +00:00
// Failed to detect volume version
2009-08-15 00:28:59 +00:00
return kResVersionUnknown ;
2009-03-07 00:59:38 +00:00
}
2017-07-23 15:56:27 -05:00
bool ResourceManager : : isBlacklistedPatch ( const ResourceId & resId ) const {
switch ( g_sci - > getGameId ( ) ) {
case GID_SHIVERS :
// The SFX resource map patch in the Shivers interactive demo has
// broken offsets for some sounds; ignore it so that the correct map
// from RESSCI.000 will be used instead.
return g_sci - > isDemo ( ) & &
resId . getType ( ) = = kResourceTypeMap & &
resId . getNumber ( ) = = 65535 ;
case GID_PHANTASMAGORIA :
// The GOG release of Phantasmagoria 1 merges all resources into a
2017-08-25 16:30:58 -05:00
// single-disc bundle, but they also include the 65535.MAP from the
// original game's CD 1, which does not contain the entries for sound
// effects from later CDs. So, just ignore this map patch since the
// correct maps will be found in the RESSCI.000 file. This also helps
// eliminate user error when copying files from the original CDs, since
// each CD had a different 65535.MAP patch file.
return resId . getType ( ) = = kResourceTypeMap & & resId . getNumber ( ) = = 65535 ;
2017-07-23 15:56:27 -05:00
default :
return false ;
}
}
2009-03-07 04:49:34 +00:00
// version-agnostic patch application
2010-06-07 21:55:57 +00:00
void ResourceManager : : processPatch ( ResourceSource * source , ResourceType resourceType , uint16 resourceNr , uint32 tuple ) {
2010-06-02 13:17:36 +00:00
Common : : SeekableReadStream * fileStream = 0 ;
2010-06-10 15:06:25 +00:00
Resource * newrsc = 0 ;
2010-06-07 21:55:57 +00:00
ResourceId resId = ResourceId ( resourceType , resourceNr , tuple ) ;
2010-06-08 21:15:53 +00:00
ResourceType checkForType = resourceType ;
2009-03-07 04:49:34 +00:00
2017-07-23 15:56:27 -05:00
if ( isBlacklistedPatch ( resId ) ) {
debug ( " Skipping blacklisted patch file %s " , source - > getLocationName ( ) . c_str ( ) ) ;
2017-05-01 12:30:02 -05:00
delete source ;
return ;
}
2010-06-07 17:44:17 +00:00
// base36 encoded patches (i.e. audio36 and sync36) have the same type as their non-base36 encoded counterparts
2010-06-08 21:15:53 +00:00
if ( checkForType = = kResourceTypeAudio36 )
checkForType = kResourceTypeAudio ;
2010-06-10 15:06:25 +00:00
else if ( checkForType = = kResourceTypeSync36 )
2010-06-08 21:15:53 +00:00
checkForType = kResourceTypeSync ;
2010-06-07 17:44:17 +00:00
2010-06-15 12:13:07 +00:00
if ( source - > _resourceFile ) {
fileStream = source - > _resourceFile - > createReadStream ( ) ;
2010-06-02 13:17:36 +00:00
} else {
Common : : File * file = new Common : : File ( ) ;
2010-06-15 12:09:51 +00:00
if ( ! file - > open ( source - > getLocationName ( ) ) ) {
warning ( " ResourceManager::processPatch(): failed to open %s " , source - > getLocationName ( ) . c_str ( ) ) ;
2016-06-14 20:26:32 -05:00
delete source ;
2016-07-22 12:20:54 -05:00
delete file ;
2010-06-02 13:17:36 +00:00
return ;
}
fileStream = file ;
2009-03-07 04:49:34 +00:00
}
2010-06-10 15:06:25 +00:00
int fsize = fileStream - > size ( ) ;
2009-03-07 04:49:34 +00:00
if ( fsize < 3 ) {
2010-06-15 12:09:51 +00:00
debug ( " Patching %s failed - file too small " , source - > getLocationName ( ) . c_str ( ) ) ;
2016-06-14 20:26:32 -05:00
delete source ;
2016-07-22 12:20:54 -05:00
delete fileStream ;
2009-03-07 04:49:34 +00:00
return ;
}
2009-03-11 01:40:08 +00:00
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
byte patchType ;
if ( fileStream - > readUint32BE ( ) = = MKTAG ( ' R ' , ' I ' , ' F ' , ' F ' ) ) {
fileStream - > seek ( - 4 , SEEK_CUR ) ;
patchType = kResourceTypeAudio ;
2016-11-20 16:09:27 -06:00
} else {
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
fileStream - > seek ( - 4 , SEEK_CUR ) ;
patchType = convertResType ( fileStream - > readByte ( ) ) ;
}
enum {
kExtraHeaderSize = 2 , ///< extra header used in gfx resources
kViewHeaderSize = 22 ///< extra header used in view resources
} ;
int32 patchDataOffset = kResourceHeaderSize ;
2017-05-08 23:25:37 -05:00
if ( _volVersion < kResVersionSci11 ) {
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
patchDataOffset + = fileStream - > readByte ( ) ;
2017-05-08 23:25:37 -05:00
} else {
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
switch ( patchType ) {
case kResourceTypeView :
fileStream - > seek ( 3 , SEEK_SET ) ;
patchDataOffset + = fileStream - > readByte ( ) + kViewHeaderSize + kExtraHeaderSize ;
break ;
case kResourceTypePic :
2017-05-08 23:25:37 -05:00
if ( _volVersion < kResVersionSci2 ) {
fileStream - > seek ( 3 , SEEK_SET ) ;
patchDataOffset + = fileStream - > readByte ( ) + kViewHeaderSize + kExtraHeaderSize ;
} else {
patchDataOffset + = kExtraHeaderSize ;
}
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
break ;
case kResourceTypePalette :
fileStream - > seek ( 3 , SEEK_SET ) ;
patchDataOffset + = fileStream - > readByte ( ) + kExtraHeaderSize ;
break ;
case kResourceTypeAudio :
case kResourceTypeAudio36 :
2017-05-09 00:25:02 -05:00
# ifdef ENABLE_SCI32
case kResourceTypeWave :
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
case kResourceTypeVMD :
case kResourceTypeDuck :
case kResourceTypeClut :
case kResourceTypeTGA :
case kResourceTypeZZZ :
case kResourceTypeEtc :
2017-05-09 00:25:02 -05:00
# endif
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
patchDataOffset = 0 ;
break ;
default :
fileStream - > seek ( 1 , SEEK_SET ) ;
patchDataOffset + = fileStream - > readByte ( ) ;
break ;
}
2016-11-20 16:09:27 -06:00
}
2010-06-08 21:15:53 +00:00
2010-06-02 13:17:36 +00:00
delete fileStream ;
2009-03-07 04:49:34 +00:00
2010-06-08 21:15:53 +00:00
if ( patchType ! = checkForType ) {
2010-06-15 12:09:51 +00:00
debug ( " Patching %s failed - resource type mismatch " , source - > getLocationName ( ) . c_str ( ) ) ;
2016-06-14 20:26:32 -05:00
delete source ;
2010-06-08 21:15:53 +00:00
return ;
}
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
if ( patchDataOffset > = fsize ) {
2009-03-07 04:49:34 +00:00
debug ( " Patching %s failed - patch starting at offset %d can't be in file of size %d " ,
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
source - > getLocationName ( ) . c_str ( ) , patchDataOffset , fsize ) ;
2016-06-14 20:26:32 -05:00
delete source ;
2009-03-07 04:49:34 +00:00
return ;
}
2010-06-10 15:06:25 +00:00
2009-03-07 04:49:34 +00:00
// Overwrite everything, because we're patching
2017-05-07 14:09:58 -05:00
newrsc = updateResource ( resId , source , 0 , fsize - patchDataOffset , source - > getLocationName ( ) ) ;
2010-06-07 21:55:57 +00:00
newrsc - > _headerSize = patchDataOffset ;
2010-06-15 12:18:31 +00:00
2010-06-15 12:09:51 +00:00
debugC ( 1 , kDebugLevelResMan , " Patching %s - OK " , source - > getLocationName ( ) . c_str ( ) ) ;
2009-03-07 04:49:34 +00:00
}
2011-02-02 00:23:46 +00:00
static ResourceId convertPatchNameBase36 ( ResourceType type , const Common : : String & filename ) {
// The base36 encoded resource contains the following:
// uint16 resourceId, byte noun, byte verb, byte cond, byte seq
// Skip patch type character
uint16 resourceNr = strtol ( Common : : String ( filename . c_str ( ) + 1 , 3 ) . c_str ( ) , 0 , 36 ) ; // 3 characters
uint16 noun = strtol ( Common : : String ( filename . c_str ( ) + 4 , 2 ) . c_str ( ) , 0 , 36 ) ; // 2 characters
uint16 verb = strtol ( Common : : String ( filename . c_str ( ) + 6 , 2 ) . c_str ( ) , 0 , 36 ) ; // 2 characters
// Skip '.'
uint16 cond = strtol ( Common : : String ( filename . c_str ( ) + 9 , 2 ) . c_str ( ) , 0 , 36 ) ; // 2 characters
uint16 seq = strtol ( Common : : String ( filename . c_str ( ) + 11 , 1 ) . c_str ( ) , 0 , 36 ) ; // 1 character
return ResourceId ( type , resourceNr , noun , verb , cond , seq ) ;
}
2010-06-15 12:19:39 +00:00
void ResourceManager : : readResourcePatchesBase36 ( ) {
2010-06-07 09:10:22 +00:00
// The base36 encoded audio36 and sync36 resources use a different naming scheme, because they
// cannot be described with a single resource number, but are a result of a
// <number, noun, verb, cond, seq> tuple. Please don't be confused with the normal audio patches
// (*.aud) and normal sync patches (*.syn). audio36 patches can be seen for example in the AUD
// folder of GK1CD, and are like this file: @0CS0M00.0X1. GK1CD is the first game where these
// have been observed. The actual audio36 and sync36 resources exist in SCI1.1 as well, but the
// first game where external patch files for them have been found is GK1CD. The names of these
2010-06-07 11:44:52 +00:00
// files are base36 encoded, and we handle their decoding here. audio36 files start with a '@',
// whereas sync36 start with a '#'. Mac versions begin with 'A' (probably meaning AIFF). Torin
// has several that begin with 'B'.
2010-06-07 09:10:22 +00:00
2010-06-07 11:44:52 +00:00
Common : : String name , inputName ;
2010-06-07 09:10:22 +00:00
Common : : ArchiveMemberList files ;
2010-06-07 17:44:17 +00:00
ResourceSource * psrcPatch ;
2010-06-07 09:10:22 +00:00
for ( int i = kResourceTypeAudio36 ; i < = kResourceTypeSync36 ; + + i ) {
2010-06-07 17:44:17 +00:00
files . clear ( ) ;
2010-06-07 11:44:52 +00:00
// audio36 resources start with a @, A, or B
2016-03-18 22:55:56 -05:00
// sync36 resources start with a #, S, or T
2010-06-07 11:44:52 +00:00
if ( i = = kResourceTypeAudio36 ) {
SearchMan . listMatchingMembers ( files , " @???????.??? " ) ;
SearchMan . listMatchingMembers ( files , " A???????.??? " ) ;
SearchMan . listMatchingMembers ( files , " B???????.??? " ) ;
2016-03-18 22:55:56 -05:00
} else {
2010-06-07 11:44:52 +00:00
SearchMan . listMatchingMembers ( files , " #???????.??? " ) ;
2016-03-18 22:55:56 -05:00
# ifdef ENABLE_SCI32
SearchMan . listMatchingMembers ( files , " S???????.??? " ) ;
SearchMan . listMatchingMembers ( files , " T???????.??? " ) ;
# endif
}
2010-06-07 09:10:22 +00:00
for ( Common : : ArchiveMemberList : : const_iterator x = files . begin ( ) ; x ! = files . end ( ) ; + + x ) {
name = ( * x ) - > getName ( ) ;
2017-05-08 00:02:53 -05:00
name . toUppercase ( ) ;
2010-06-10 15:06:25 +00:00
2016-08-19 10:21:33 -05:00
// The S/T prefixes often conflict with non-patch files and generate
// spurious warnings about invalid patches
2017-05-14 11:22:26 -05:00
if ( name . hasSuffix ( " .DLL " ) | | name . hasSuffix ( " .EXE " ) | | name . hasSuffix ( " .TXT " ) | | name . hasSuffix ( " .OLD " ) | | name . hasSuffix ( " .WIN " ) | | name . hasSuffix ( " .DOS " ) ) {
2016-08-19 10:21:33 -05:00
continue ;
}
2011-02-02 00:23:46 +00:00
ResourceId resource36 = convertPatchNameBase36 ( ( ResourceType ) i , name ) ;
2011-06-20 00:59:48 +02:00
2011-02-02 00:23:46 +00:00
/*
if ( i = = kResourceTypeAudio36 )
debug ( " audio36 patch: %s => %s. tuple:%d, %s \n " , name . c_str ( ) , inputName . c_str ( ) , resource36 . tuple , resource36 . toString ( ) . c_str ( ) ) ;
else
debug ( " sync36 patch: %s => %s. tuple:%d, %s \n " , name . c_str ( ) , inputName . c_str ( ) , resource36 . tuple , resource36 . toString ( ) . c_str ( ) ) ;
*/
2010-06-10 15:06:25 +00:00
2011-02-02 00:23:46 +00:00
// Make sure that the audio patch is a valid resource
if ( i = = kResourceTypeAudio36 ) {
Common : : SeekableReadStream * stream = SearchMan . createReadStreamForMember ( name ) ;
uint32 tag = stream - > readUint32BE ( ) ;
2011-04-12 16:53:15 +02:00
if ( tag = = MKTAG ( ' R ' , ' I ' , ' F ' , ' F ' ) | | tag = = MKTAG ( ' F ' , ' O ' , ' R ' , ' M ' ) ) {
2010-06-10 15:06:25 +00:00
delete stream ;
2011-02-02 00:23:46 +00:00
processWavePatch ( resource36 , name ) ;
continue ;
2010-06-10 15:06:25 +00:00
}
2011-02-02 00:23:46 +00:00
// Check for SOL as well
tag = ( tag < < 16 ) | stream - > readUint16BE ( ) ;
2011-06-20 00:59:48 +02:00
2011-04-12 16:53:15 +02:00
if ( tag ! = MKTAG ( ' S ' , ' O ' , ' L ' , 0 ) ) {
2011-02-02 00:23:46 +00:00
delete stream ;
continue ;
}
delete stream ;
2010-06-08 15:08:45 +00:00
}
2011-02-02 00:23:46 +00:00
psrcPatch = new PatchResourceSource ( name ) ;
processPatch ( psrcPatch , ( ResourceType ) i , resource36 . getNumber ( ) , resource36 . getTuple ( ) ) ;
2010-06-07 09:10:22 +00:00
}
}
}
2010-06-15 12:19:39 +00:00
void ResourceManager : : readResourcePatches ( ) {
2010-05-27 07:50:23 +00:00
// Note: since some SCI1 games(KQ5 floppy, SQ4) might use SCI0 naming scheme for patch files
// this function tries to read patch file with any supported naming scheme,
// regardless of s_sciVersion value
2009-03-07 04:49:34 +00:00
Common : : String mask , name ;
Common : : ArchiveMemberList files ;
2010-06-07 21:55:57 +00:00
uint16 resourceNr = 0 ;
2009-03-07 04:49:34 +00:00
const char * szResType ;
ResourceSource * psrcPatch ;
2010-07-20 21:53:58 +00:00
for ( int i = kResourceTypeView ; i < kResourceTypeInvalid ; + + i ) {
2010-07-18 00:16:19 +00:00
// Ignore the types that can't be patched (and Robot/VMD is handled externally for now)
2010-11-13 00:20:23 +00:00
if ( ! s_resourceTypeSuffixes [ i ] | | ( i > = kResourceTypeRobot & & i ! = kResourceTypeChunk ) )
2010-07-18 00:16:19 +00:00
continue ;
2009-03-07 04:49:34 +00:00
files . clear ( ) ;
szResType = getResourceTypeName ( ( ResourceType ) i ) ;
// SCI0 naming - type.nnn
mask = szResType ;
mask + = " .??? " ;
SearchMan . listMatchingMembers ( files , mask ) ;
// SCI1 and later naming - nnn.typ
mask = " *. " ;
2010-11-11 23:46:53 +00:00
mask + = s_resourceTypeSuffixes [ i ] ;
2009-03-07 04:49:34 +00:00
SearchMan . listMatchingMembers ( files , mask ) ;
2010-06-07 09:10:22 +00:00
2011-03-04 17:16:54 +02:00
if ( i = = kResourceTypeView ) {
SearchMan . listMatchingMembers ( files , " *.v16 " ) ; // EGA SCI1 view patches
SearchMan . listMatchingMembers ( files , " *.v32 " ) ; // Amiga SCI1 view patches
2011-03-05 18:39:17 +02:00
SearchMan . listMatchingMembers ( files , " *.v64 " ) ; // Amiga AGA SCI1 (i.e. Longbow) view patches
2011-03-04 17:16:54 +02:00
} else if ( i = = kResourceTypePic ) {
SearchMan . listMatchingMembers ( files , " *.p16 " ) ; // EGA SCI1 picture patches
SearchMan . listMatchingMembers ( files , " *.p32 " ) ; // Amiga SCI1 picture patches
2011-03-05 18:39:17 +02:00
SearchMan . listMatchingMembers ( files , " *.p64 " ) ; // Amiga AGA SCI1 (i.e. Longbow) picture patches
2011-03-04 17:16:54 +02:00
} else if ( i = = kResourceTypeScript ) {
2017-02-16 12:58:47 -06:00
// SCI3 (we can't use getSciVersion() at this point)
SearchMan . listMatchingMembers ( files , " *.csc " ) ;
2010-11-11 23:46:53 +00:00
}
2010-01-13 23:58:25 +00:00
for ( Common : : ArchiveMemberList : : const_iterator x = files . begin ( ) ; x ! = files . end ( ) ; + + x ) {
2009-12-08 20:44:57 +00:00
bool bAdd = false ;
2009-03-07 04:49:34 +00:00
name = ( * x ) - > getName ( ) ;
2010-06-20 22:30:20 +00:00
2009-03-11 22:52:54 +00:00
// SCI1 scheme
2012-02-20 16:03:39 +01:00
if ( Common : : isDigit ( name [ 0 ] ) ) {
2010-06-20 23:41:54 +00:00
char * end = 0 ;
resourceNr = strtol ( name . c_str ( ) , & end , 10 ) ;
bAdd = ( * end = = ' . ' ) ; // Ensure the next character is the period
2009-03-07 04:49:34 +00:00
} else {
// SCI0 scheme
int resname_len = strlen ( szResType ) ;
2009-05-20 17:53:31 +00:00
if ( scumm_strnicmp ( name . c_str ( ) , szResType , resname_len ) = = 0
2012-02-20 16:03:39 +01:00
& & ! Common : : isAlpha ( name [ resname_len + 1 ] ) ) {
2010-06-07 21:55:57 +00:00
resourceNr = atoi ( name . c_str ( ) + resname_len + 1 ) ;
2009-03-11 22:52:54 +00:00
bAdd = true ;
2009-03-07 04:49:34 +00:00
}
}
2009-03-11 22:52:54 +00:00
if ( bAdd ) {
2010-06-15 12:10:41 +00:00
psrcPatch = new PatchResourceSource ( name ) ;
2010-06-07 21:55:57 +00:00
processPatch ( psrcPatch , ( ResourceType ) i , resourceNr ) ;
2009-03-11 22:52:54 +00:00
}
2009-03-07 04:49:34 +00:00
}
}
}
2010-01-25 01:39:44 +00:00
2009-03-07 04:49:34 +00:00
int ResourceManager : : readResourceMapSCI0 ( ResourceSource * map ) {
2010-06-05 14:09:52 +00:00
Common : : SeekableReadStream * fileStream = 0 ;
2011-02-19 20:30:57 +02:00
ResourceType type = kResourceTypeInvalid ; // to silence a false positive in MSVC
2009-03-07 04:49:34 +00:00
uint16 number , id ;
uint32 offset ;
2010-06-15 12:13:07 +00:00
if ( map - > _resourceFile ) {
fileStream = map - > _resourceFile - > createReadStream ( ) ;
2010-06-05 14:09:52 +00:00
if ( ! fileStream )
return SCI_ERROR_RESMAP_NOT_FOUND ;
} else {
Common : : File * file = new Common : : File ( ) ;
2010-06-15 12:09:51 +00:00
if ( ! file - > open ( map - > getLocationName ( ) ) )
2010-06-05 14:09:52 +00:00
return SCI_ERROR_RESMAP_NOT_FOUND ;
fileStream = file ;
}
2009-03-07 04:49:34 +00:00
2010-06-05 14:09:52 +00:00
fileStream - > seek ( 0 , SEEK_SET ) ;
2009-03-07 04:49:34 +00:00
2011-02-15 21:47:29 -05:00
byte bMask = ( _mapVersion > = kResVersionSci1Middle ) ? 0xF0 : 0xFC ;
byte bShift = ( _mapVersion > = kResVersionSci1Middle ) ? 28 : 26 ;
2009-03-07 04:49:34 +00:00
do {
2011-02-15 21:47:29 -05:00
// King's Quest 5 FM-Towns uses a 7 byte version of the SCI1 Middle map,
// splitting the type from the id.
if ( _mapVersion = = kResVersionKQ5FMT )
type = convertResType ( fileStream - > readByte ( ) ) ;
2010-06-05 14:09:52 +00:00
id = fileStream - > readUint16LE ( ) ;
offset = fileStream - > readUint32LE ( ) ;
2009-03-07 04:49:34 +00:00
2010-06-05 14:09:52 +00:00
if ( fileStream - > eos ( ) | | fileStream - > err ( ) ) {
delete fileStream ;
2010-06-15 12:09:51 +00:00
warning ( " Error while reading %s " , map - > getLocationName ( ) . c_str ( ) ) ;
2009-03-07 04:49:34 +00:00
return SCI_ERROR_RESMAP_NOT_FOUND ;
}
2011-06-20 00:59:48 +02:00
2009-03-11 01:40:08 +00:00
if ( offset = = 0xFFFFFFFF )
2009-03-07 04:49:34 +00:00
break ;
2011-02-15 21:47:29 -05:00
if ( _mapVersion = = kResVersionKQ5FMT ) {
number = id ;
} else {
type = convertResType ( id > > 11 ) ;
number = id & 0x7FF ;
}
2009-06-07 02:18:38 +00:00
ResourceId resId = ResourceId ( type , number ) ;
2009-03-07 04:49:34 +00:00
// adding a new resource
if ( _resMap . contains ( resId ) = = false ) {
2010-06-15 12:18:31 +00:00
ResourceSource * source = findVolume ( map , offset > > bShift ) ;
if ( ! source ) {
2009-08-20 19:49:52 +00:00
warning ( " Could not get volume for resource %d, VolumeID %d " , id , offset > > bShift ) ;
2009-08-26 09:38:51 +00:00
if ( _mapVersion ! = _volVersion ) {
warning ( " Retrying with the detected volume version instead " ) ;
warning ( " Map version was: %d, retrying with: %d " , _mapVersion , _volVersion ) ;
_mapVersion = _volVersion ;
bMask = ( _mapVersion = = kResVersionSci1Middle ) ? 0xF0 : 0xFC ;
bShift = ( _mapVersion = = kResVersionSci1Middle ) ? 28 : 26 ;
2010-06-15 12:18:31 +00:00
source = findVolume ( map , offset > > bShift ) ;
2017-05-07 14:09:58 -05:00
if ( ! source ) {
delete fileStream ;
warning ( " Still couldn't find the volume " ) ;
return SCI_ERROR_NO_RESOURCE_FILES_FOUND ;
}
} else {
delete fileStream ;
return SCI_ERROR_NO_RESOURCE_FILES_FOUND ;
2009-08-26 09:38:51 +00:00
}
2009-07-09 15:54:18 +00:00
}
2010-06-15 12:18:31 +00:00
2017-05-07 14:09:58 -05:00
addResource ( resId , source , offset & ( ( ( ~ bMask ) < < 24 ) | 0xFFFFFF ) , 0 , map - > getLocationName ( ) ) ;
2009-03-07 04:49:34 +00:00
}
2010-06-05 14:09:52 +00:00
} while ( ! fileStream - > eos ( ) ) ;
delete fileStream ;
2009-03-07 04:49:34 +00:00
return 0 ;
}
2009-03-26 13:11:30 +00:00
int ResourceManager : : readResourceMapSCI1 ( ResourceSource * map ) {
2010-06-05 14:09:52 +00:00
Common : : SeekableReadStream * fileStream = 0 ;
2010-06-15 12:13:07 +00:00
if ( map - > _resourceFile ) {
fileStream = map - > _resourceFile - > createReadStream ( ) ;
2010-06-05 14:09:52 +00:00
if ( ! fileStream )
return SCI_ERROR_RESMAP_NOT_FOUND ;
} else {
Common : : File * file = new Common : : File ( ) ;
2010-06-15 12:09:51 +00:00
if ( ! file - > open ( map - > getLocationName ( ) ) )
2010-06-05 14:09:52 +00:00
return SCI_ERROR_RESMAP_NOT_FOUND ;
fileStream = file ;
}
2009-03-07 04:49:34 +00:00
2009-03-07 14:40:58 +00:00
resource_index_t resMap [ 32 ] ;
memset ( resMap , 0 , sizeof ( resource_index_t ) * 32 ) ;
2009-03-07 04:49:34 +00:00
byte type = 0 , prevtype = 0 ;
2009-08-15 00:28:59 +00:00
byte nEntrySize = _mapVersion = = kResVersionSci11 ? SCI11_RESMAP_ENTRIES_SIZE : SCI1_RESMAP_ENTRIES_SIZE ;
2009-06-07 02:18:38 +00:00
ResourceId resId ;
2009-03-07 04:49:34 +00:00
// Read resource type and offsets to resource offsets block from .MAP file
// The last entry has type=0xFF (0x1F) and offset equals to map file length
do {
2010-06-05 14:09:52 +00:00
type = fileStream - > readByte ( ) & 0x1F ;
resMap [ type ] . wOffset = fileStream - > readUint16LE ( ) ;
2017-05-07 14:09:58 -05:00
if ( fileStream - > eos ( ) ) {
delete fileStream ;
warning ( " Premature end of file %s " , map - > getLocationName ( ) . c_str ( ) ) ;
2014-10-18 15:33:07 +03:00
return SCI_ERROR_RESMAP_NOT_FOUND ;
2017-05-07 14:09:58 -05:00
}
2014-10-18 15:33:07 +03:00
2009-03-07 04:49:34 +00:00
resMap [ prevtype ] . wSize = ( resMap [ type ] . wOffset
2009-03-11 01:40:08 +00:00
- resMap [ prevtype ] . wOffset ) / nEntrySize ;
2009-03-07 04:49:34 +00:00
prevtype = type ;
} while ( type ! = 0x1F ) ; // the last entry is FF
// reading each type's offsets
2010-08-21 13:36:09 +00:00
uint32 fileOffset = 0 ;
2009-03-07 14:40:58 +00:00
for ( type = 0 ; type < 32 ; type + + ) {
2009-03-07 04:49:34 +00:00
if ( resMap [ type ] . wOffset = = 0 ) // this resource does not exist in map
continue ;
2010-06-05 14:09:52 +00:00
fileStream - > seek ( resMap [ type ] . wOffset ) ;
2009-03-07 04:49:34 +00:00
for ( int i = 0 ; i < resMap [ type ] . wSize ; i + + ) {
2010-06-05 14:09:52 +00:00
uint16 number = fileStream - > readUint16LE ( ) ;
2009-04-07 13:38:51 +00:00
int volume_nr = 0 ;
2009-08-15 00:28:59 +00:00
if ( _mapVersion = = kResVersionSci11 ) {
2009-04-07 13:38:51 +00:00
// offset stored in 3 bytes
2010-08-21 13:36:09 +00:00
fileOffset = fileStream - > readUint16LE ( ) ;
fileOffset | = fileStream - > readByte ( ) < < 16 ;
fileOffset < < = 1 ;
2009-04-07 13:38:51 +00:00
} else {
// offset/volume stored in 4 bytes
2010-08-21 13:36:09 +00:00
fileOffset = fileStream - > readUint32LE ( ) ;
2009-08-15 00:28:59 +00:00
if ( _mapVersion < kResVersionSci11 ) {
2010-08-21 13:36:09 +00:00
volume_nr = fileOffset > > 28 ; // most significant 4 bits
fileOffset & = 0x0FFFFFFF ; // least significant 28 bits
2009-04-07 13:38:51 +00:00
} else {
// in SCI32 it's a plain offset
}
}
2010-06-05 14:09:52 +00:00
if ( fileStream - > eos ( ) | | fileStream - > err ( ) ) {
delete fileStream ;
2010-06-15 12:09:51 +00:00
warning ( " Error while reading %s " , map - > getLocationName ( ) . c_str ( ) ) ;
2009-03-07 04:49:34 +00:00
return SCI_ERROR_RESMAP_NOT_FOUND ;
}
2010-07-18 00:16:19 +00:00
resId = ResourceId ( convertResType ( type ) , number ) ;
2010-08-21 13:36:09 +00:00
// NOTE: We add the map's volume number here to the specified volume number
// for SCI2.1 and SCI3 maps that are not resmap.000. The resmap.* files' numbers
// need to be used in concurrence with the volume specified in the map to get
// the actual resource file.
int mapVolumeNr = volume_nr + map - > _volumeNumber ;
ResourceSource * source = findVolume ( map , mapVolumeNr ) ;
2010-11-10 03:34:08 +00:00
2017-05-07 14:09:58 -05:00
if ( ! source ) {
delete fileStream ;
warning ( " Could not get volume for resource %d, VolumeID %d " , number , mapVolumeNr ) ;
return SCI_ERROR_NO_RESOURCE_FILES_FOUND ;
}
2010-08-21 13:36:09 +00:00
Resource * resource = _resMap . getVal ( resId , NULL ) ;
if ( ! resource ) {
2017-05-07 14:09:58 -05:00
addResource ( resId , source , fileOffset , 0 , map - > getLocationName ( ) ) ;
2010-08-21 13:36:09 +00:00
} else {
2011-08-26 01:56:50 +03:00
// If the resource is already present in a volume, change it to
// the new content (but only in a volume, so as not to overwrite
// external patches - refer to bug #3366295).
// This is needed at least for the German version of Pharkas.
// That version contains several duplicate resources INSIDE the
// resource data files like fonts, views, scripts, etc. Thus,
// if we use the first entries in the resource file, half of the
// game will be English and umlauts will also be missing :P
if ( resource - > _source - > getSourceType ( ) = = kSourceVolume ) {
2017-05-07 14:09:58 -05:00
updateResource ( resId , source , fileOffset , 0 , map - > getLocationName ( ) ) ;
2011-08-26 01:56:50 +03:00
}
2009-03-07 04:49:34 +00:00
}
2016-08-19 10:05:05 -05:00
# ifdef ENABLE_SCI32
// Different CDs may have different audio maps on each disc. The
// ResourceManager does not know how to deal with this; it expects
// each resource ID to be unique across an entire game. To work
// around this problem, all audio maps from this disc must be
// processed immediately, since they will be replaced by the audio
// map from the next disc on the next call to readResourceMapSCI1
if ( _multiDiscAudio & & resId . getType ( ) = = kResourceTypeMap ) {
2017-05-07 14:09:58 -05:00
IntMapResourceSource * audioMap = new IntMapResourceSource ( source - > getLocationName ( ) , mapVolumeNr , resId . getNumber ( ) ) ;
addSource ( audioMap ) ;
2016-08-19 10:05:05 -05:00
Common : : String volumeName ;
2017-07-30 22:59:41 -05:00
if ( mapVolumeNr = = kResPatVolumeNumber ) {
if ( resId . getNumber ( ) = = 65535 ) {
volumeName = " RESSCI.PAT " ;
} else {
volumeName = " RESAUD.001 " ;
}
} else if ( resId . getNumber ( ) = = 65535 ) {
2016-08-19 10:05:05 -05:00
volumeName = Common : : String : : format ( " RESSFX.%03d " , mapVolumeNr ) ;
} else {
volumeName = Common : : String : : format ( " RESAUD.%03d " , mapVolumeNr ) ;
}
ResourceSource * audioVolume = addSource ( new AudioVolumeResourceSource ( this , volumeName , audioMap , mapVolumeNr ) ) ;
if ( ! audioMap - > _scanned ) {
audioVolume - > _scanned = true ;
audioMap - > _scanned = true ;
audioMap - > scanSource ( this ) ;
}
}
# endif
2009-03-07 04:49:34 +00:00
}
}
2010-06-05 14:09:52 +00:00
delete fileStream ;
2009-03-07 04:49:34 +00:00
return 0 ;
}
2010-10-31 17:23:53 +00:00
struct MacResTag {
2010-05-18 04:17:58 +00:00
uint32 tag ;
ResourceType type ;
2010-10-31 17:23:53 +00:00
} ;
static const MacResTag macResTagMap [ ] = {
2011-04-12 16:53:15 +02:00
{ MKTAG ( ' V ' , ' 5 ' , ' 6 ' , ' ' ) , kResourceTypeView } ,
{ MKTAG ( ' P ' , ' 5 ' , ' 6 ' , ' ' ) , kResourceTypePic } ,
{ MKTAG ( ' S ' , ' C ' , ' R ' , ' ' ) , kResourceTypeScript } ,
{ MKTAG ( ' T ' , ' E ' , ' X ' , ' ' ) , kResourceTypeText } ,
{ MKTAG ( ' S ' , ' N ' , ' D ' , ' ' ) , kResourceTypeSound } ,
{ MKTAG ( ' V ' , ' O ' , ' C ' , ' ' ) , kResourceTypeVocab } ,
{ MKTAG ( ' F ' , ' O ' , ' N ' , ' ' ) , kResourceTypeFont } ,
{ MKTAG ( ' C ' , ' U ' , ' R ' , ' S ' ) , kResourceTypeCursor } ,
{ MKTAG ( ' c ' , ' r ' , ' s ' , ' r ' ) , kResourceTypeCursor } ,
{ MKTAG ( ' P ' , ' a ' , ' t ' , ' ' ) , kResourceTypePatch } ,
{ MKTAG ( ' P ' , ' A ' , ' L ' , ' ' ) , kResourceTypePalette } ,
{ MKTAG ( ' s ' , ' n ' , ' d ' , ' ' ) , kResourceTypeAudio } ,
{ MKTAG ( ' M ' , ' S ' , ' G ' , ' ' ) , kResourceTypeMessage } ,
{ MKTAG ( ' H ' , ' E ' , ' P ' , ' ' ) , kResourceTypeHeap } ,
{ MKTAG ( ' I ' , ' B ' , ' I ' , ' N ' ) , kResourceTypeMacIconBarPictN } ,
{ MKTAG ( ' I ' , ' B ' , ' I ' , ' S ' ) , kResourceTypeMacIconBarPictS } ,
{ MKTAG ( ' P ' , ' I ' , ' C ' , ' T ' ) , kResourceTypeMacPict } ,
{ MKTAG ( ' S ' , ' Y ' , ' N ' , ' ' ) , kResourceTypeSync } ,
{ MKTAG ( ' S ' , ' Y ' , ' N ' , ' C ' ) , kResourceTypeSync }
2010-05-10 18:29:13 +00:00
} ;
2010-08-14 04:21:09 +00:00
static Common : : Array < uint32 > resTypeToMacTags ( ResourceType type ) {
Common : : Array < uint32 > tags ;
2010-05-18 04:17:58 +00:00
for ( uint32 i = 0 ; i < ARRAYSIZE ( macResTagMap ) ; i + + )
if ( macResTagMap [ i ] . type = = type )
2010-08-14 04:21:09 +00:00
tags . push_back ( macResTagMap [ i ] . tag ) ;
2010-05-10 18:29:13 +00:00
2010-08-14 04:21:09 +00:00
return tags ;
2010-05-10 18:29:13 +00:00
}
2010-06-15 16:50:10 +00:00
void MacResourceForkResourceSource : : scanSource ( ResourceManager * resMan ) {
2010-06-15 12:12:44 +00:00
if ( ! _macResMan - > open ( getLocationName ( ) . c_str ( ) ) )
error ( " %s is not a valid Mac resource fork " , getLocationName ( ) . c_str ( ) ) ;
2010-05-10 18:29:13 +00:00
2010-06-15 12:12:44 +00:00
Common : : MacResTagArray tagArray = _macResMan - > getResTagArray ( ) ;
2010-05-10 18:29:13 +00:00
for ( uint32 i = 0 ; i < tagArray . size ( ) ; i + + ) {
ResourceType type = kResourceTypeInvalid ;
// Map the Mac tags to our ResourceType
2010-05-18 04:17:58 +00:00
for ( uint32 j = 0 ; j < ARRAYSIZE ( macResTagMap ) ; j + + )
if ( tagArray [ i ] = = macResTagMap [ j ] . tag ) {
type = macResTagMap [ j ] . type ;
2010-05-10 18:29:13 +00:00
break ;
}
if ( type = = kResourceTypeInvalid )
continue ;
2010-06-15 12:12:44 +00:00
Common : : MacResIDArray idArray = _macResMan - > getResIDArray ( tagArray [ i ] ) ;
2010-05-10 18:29:13 +00:00
for ( uint32 j = 0 ; j < idArray . size ( ) ; j + + ) {
2011-02-02 00:23:46 +00:00
ResourceId resId ;
// Check to see if we've got a base36 encoded resource name
if ( type = = kResourceTypeAudio ) {
Common : : String resourceName = _macResMan - > getResName ( tagArray [ i ] , idArray [ j ] ) ;
// If we have a file name on an audio resource, we've got an audio36
// resource. Parse the file name to get the id.
2011-02-03 12:42:52 +00:00
if ( ! resourceName . empty ( ) & & resourceName [ 0 ] = = ' @ ' )
2011-02-02 00:23:46 +00:00
resId = convertPatchNameBase36 ( kResourceTypeAudio36 , resourceName ) ;
else
resId = ResourceId ( type , idArray [ j ] ) ;
} else if ( type = = kResourceTypeSync ) {
Common : : String resourceName = _macResMan - > getResName ( tagArray [ i ] , idArray [ j ] ) ;
// Same as with audio36 above
2011-02-03 12:42:52 +00:00
if ( ! resourceName . empty ( ) & & resourceName [ 0 ] = = ' # ' )
2011-02-02 00:23:46 +00:00
resId = convertPatchNameBase36 ( kResourceTypeSync36 , resourceName ) ;
else
resId = ResourceId ( type , idArray [ j ] ) ;
} else {
// Otherwise, we're just going with the id that was given
resId = ResourceId ( type , idArray [ j ] ) ;
}
2010-05-10 18:29:13 +00:00
2010-06-15 12:18:31 +00:00
// Overwrite Resource instance. Resource forks may contain patches.
2011-02-09 17:54:34 +00:00
// The size will be filled in later by decompressResource()
2017-05-07 14:09:58 -05:00
resMan - > updateResource ( resId , this , 0 , getLocationName ( ) ) ;
}
}
}
bool ResourceManager : : validateResource ( const ResourceId & resourceId , const Common : : String & sourceMapLocation , const Common : : String & sourceName , const uint32 offset , const uint32 size , const uint32 sourceSize ) const {
if ( size ! = 0 ) {
if ( offset + size > sourceSize ) {
warning ( " Resource %s from %s points beyond end of %s (%u + %u > %u) " , resourceId . toString ( ) . c_str ( ) , sourceMapLocation . c_str ( ) , sourceName . c_str ( ) , offset , size , sourceSize ) ;
return false ;
}
} else {
if ( offset > = sourceSize ) {
warning ( " Resource %s from %s points beyond end of %s (%u >= %u) " , resourceId . toString ( ) . c_str ( ) , sourceMapLocation . c_str ( ) , sourceName . c_str ( ) , offset , sourceSize ) ;
return false ;
2010-05-10 18:29:13 +00:00
}
}
2017-05-07 14:09:58 -05:00
return true ;
2010-05-10 18:29:13 +00:00
}
2017-05-10 15:29:10 -05:00
Resource * ResourceManager : : addResource ( ResourceId resId , ResourceSource * src , uint32 offset , uint32 size , const Common : : String & sourceMapLocation ) {
2009-06-07 19:15:55 +00:00
// Adding new resource only if it does not exist
if ( _resMap . contains ( resId ) = = false ) {
2017-05-10 15:29:10 -05:00
return updateResource ( resId , src , offset , size , sourceMapLocation ) ;
} else {
return _resMap . getVal ( resId ) ;
2009-06-07 19:15:55 +00:00
}
}
2009-06-06 00:07:18 +00:00
2017-05-07 14:09:58 -05:00
Resource * ResourceManager : : updateResource ( ResourceId resId , ResourceSource * src , uint32 size , const Common : : String & sourceMapLocation ) {
uint32 offset = 0 ;
if ( _resMap . contains ( resId ) ) {
const Resource * res = _resMap . getVal ( resId ) ;
offset = res - > _fileOffset ;
}
return updateResource ( resId , src , offset , size , sourceMapLocation ) ;
}
Resource * ResourceManager : : updateResource ( ResourceId resId , ResourceSource * src , uint32 offset , uint32 size , const Common : : String & sourceMapLocation ) {
2010-06-15 12:18:31 +00:00
// Update a patched resource, whether it exists or not
2017-05-07 14:09:58 -05:00
Resource * res = _resMap . getVal ( resId , nullptr ) ;
2010-06-15 12:18:31 +00:00
2017-05-07 14:09:58 -05:00
Common : : SeekableReadStream * volumeFile = getVolumeFile ( src ) ;
if ( volumeFile = = nullptr ) {
error ( " Could not open %s for reading " , src - > getLocationName ( ) . c_str ( ) ) ;
2010-06-15 12:18:31 +00:00
}
2017-05-10 00:33:43 -05:00
AudioVolumeResourceSource * avSrc = dynamic_cast < AudioVolumeResourceSource * > ( src ) ;
if ( avSrc ! = nullptr & & ! avSrc - > relocateMapOffset ( offset , size ) ) {
warning ( " Compressed volume %s does not contain a valid entry for %s (map offset %u) " , src - > getLocationName ( ) . c_str ( ) , resId . toString ( ) . c_str ( ) , offset ) ;
_hasBadResources = true ;
2017-05-13 22:07:53 -05:00
disposeVolumeFileStream ( volumeFile , src ) ;
2017-05-10 00:33:43 -05:00
return res ;
}
2017-05-07 14:09:58 -05:00
if ( validateResource ( resId , sourceMapLocation , src - > getLocationName ( ) , offset , size , volumeFile - > size ( ) ) ) {
if ( res = = nullptr ) {
res = new Resource ( this , resId ) ;
_resMap . setVal ( resId , res ) ;
}
res - > _status = kResStatusNoMalloc ;
res - > _source = src ;
res - > _headerSize = 0 ;
res - > _fileOffset = offset ;
res - > _size = size ;
} else {
_hasBadResources = true ;
}
2010-06-15 12:18:31 +00:00
2017-05-13 22:07:53 -05:00
disposeVolumeFileStream ( volumeFile , src ) ;
2010-06-15 12:18:31 +00:00
return res ;
}
2010-06-16 00:24:16 +00:00
int Resource : : readResourceInfo ( ResVersion volVersion , Common : : SeekableReadStream * file ,
2010-06-15 12:14:15 +00:00
uint32 & szPacked , ResourceCompression & compression ) {
2009-03-10 21:44:03 +00:00
// SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes
// SCI1 volume format: {bResType wResNumber wPacked+4 wUnpacked wCompression} = 9 bytes
// SCI1.1 volume format: {bResType wResNumber wPacked wUnpacked wCompression} = 9 bytes
// SCI32 volume format : {bResType wResNumber dwPacked dwUnpacked wCompression} = 13 bytes
2010-01-03 04:46:00 +00:00
uint16 w , number ;
uint32 wCompression , szUnpacked ;
2009-03-10 21:44:03 +00:00
ResourceType type ;
2012-05-15 03:05:08 +03:00
if ( file - > size ( ) = = 0 )
return SCI_ERROR_EMPTY_RESOURCE ;
2010-06-16 00:24:16 +00:00
switch ( volVersion ) {
2009-08-15 00:28:59 +00:00
case kResVersionSci0Sci1Early :
case kResVersionSci1Middle :
2009-03-10 21:44:03 +00:00
w = file - > readUint16LE ( ) ;
2010-08-03 14:58:57 +00:00
type = _resMan - > convertResType ( w > > 11 ) ;
2009-03-10 21:44:03 +00:00
number = w & 0x7FF ;
szPacked = file - > readUint16LE ( ) - 4 ;
szUnpacked = file - > readUint16LE ( ) ;
wCompression = file - > readUint16LE ( ) ;
break ;
2009-08-15 00:28:59 +00:00
case kResVersionSci1Late :
2010-08-03 14:58:57 +00:00
type = _resMan - > convertResType ( file - > readByte ( ) ) ;
2009-03-10 21:44:03 +00:00
number = file - > readUint16LE ( ) ;
szPacked = file - > readUint16LE ( ) - 4 ;
szUnpacked = file - > readUint16LE ( ) ;
wCompression = file - > readUint16LE ( ) ;
break ;
2009-08-15 00:28:59 +00:00
case kResVersionSci11 :
2010-08-03 14:58:57 +00:00
type = _resMan - > convertResType ( file - > readByte ( ) ) ;
2009-03-10 21:44:03 +00:00
number = file - > readUint16LE ( ) ;
szPacked = file - > readUint16LE ( ) ;
szUnpacked = file - > readUint16LE ( ) ;
wCompression = file - > readUint16LE ( ) ;
break ;
2009-03-30 07:53:32 +00:00
# ifdef ENABLE_SCI32
2010-11-09 14:25:39 +00:00
case kResVersionSci2 :
case kResVersionSci3 :
2010-08-03 14:58:57 +00:00
type = _resMan - > convertResType ( file - > readByte ( ) ) ;
2009-03-10 21:44:03 +00:00
number = file - > readUint16LE ( ) ;
szPacked = file - > readUint32LE ( ) ;
szUnpacked = file - > readUint32LE ( ) ;
2010-11-09 14:25:39 +00:00
// The same comment applies here as in
// detectVolVersion regarding SCI3. We ignore the
// compression field for SCI3 games, but must presume
// it exists in the file.
2009-03-10 21:44:03 +00:00
wCompression = file - > readUint16LE ( ) ;
2010-11-09 14:25:39 +00:00
if ( volVersion = = kResVersionSci3 )
wCompression = szPacked ! = szUnpacked ? 32 : 0 ;
2009-03-10 21:44:03 +00:00
break ;
2009-03-30 07:53:32 +00:00
# endif
2009-03-10 21:44:03 +00:00
default :
2012-05-15 03:05:08 +03:00
return SCI_ERROR_RESMAP_INVALID_ENTRY ;
2009-03-10 21:44:03 +00:00
}
2010-05-10 18:29:13 +00:00
2009-03-10 21:44:03 +00:00
// check if there were errors while reading
2009-12-15 08:18:57 +00:00
if ( ( file - > eos ( ) | | file - > err ( ) ) )
2009-03-10 21:44:03 +00:00
return SCI_ERROR_IO_ERROR ;
2010-05-10 18:29:13 +00:00
2010-06-15 12:19:39 +00:00
_id = ResourceId ( type , number ) ;
2016-12-31 20:39:57 -06:00
_size = szUnpacked ;
2010-05-10 18:29:13 +00:00
2009-03-10 21:44:03 +00:00
// checking compression method
2009-03-14 04:17:03 +00:00
switch ( wCompression ) {
case 0 :
2009-03-10 21:44:03 +00:00
compression = kCompNone ;
2009-03-11 01:40:08 +00:00
break ;
2009-03-14 04:17:03 +00:00
case 1 :
2009-09-23 10:55:35 +00:00
compression = ( getSciVersion ( ) < = SCI_VERSION_01 ) ? kCompLZW : kCompHuffman ;
2009-03-11 01:40:08 +00:00
break ;
2009-03-14 04:17:03 +00:00
case 2 :
2009-09-23 10:55:35 +00:00
compression = ( getSciVersion ( ) < = SCI_VERSION_01 ) ? kCompHuffman : kCompLZW1 ;
2009-03-11 01:40:08 +00:00
break ;
2009-03-14 04:17:03 +00:00
case 3 :
compression = kCompLZW1View ;
break ;
case 4 :
compression = kCompLZW1Pic ;
break ;
case 18 :
case 19 :
case 20 :
compression = kCompDCL ;
break ;
2009-03-30 07:53:32 +00:00
# ifdef ENABLE_SCI32
2009-03-14 04:17:03 +00:00
case 32 :
compression = kCompSTACpack ;
2009-03-11 01:40:08 +00:00
break ;
2009-03-30 07:53:32 +00:00
# endif
2009-03-11 01:40:08 +00:00
default :
compression = kCompUnknown ;
2009-03-10 21:44:03 +00:00
}
2012-05-15 03:05:08 +03:00
return ( compression = = kCompUnknown ) ? SCI_ERROR_UNKNOWN_COMPRESSION : SCI_ERROR_NONE ;
2009-03-10 21:44:03 +00:00
}
2010-06-16 00:24:16 +00:00
int Resource : : decompress ( ResVersion volVersion , Common : : SeekableReadStream * file ) {
2010-06-17 23:52:17 +00:00
int errorNum ;
2009-03-10 21:44:03 +00:00
uint32 szPacked = 0 ;
ResourceCompression compression = kCompUnknown ;
// fill resource info
2010-06-17 23:52:17 +00:00
errorNum = readResourceInfo ( volVersion , file , szPacked , compression ) ;
if ( errorNum )
return errorNum ;
2010-05-10 18:29:13 +00:00
2009-03-10 21:44:03 +00:00
// getting a decompressor
Decompressor * dec = NULL ;
switch ( compression ) {
case kCompNone :
dec = new Decompressor ;
break ;
case kCompHuffman :
dec = new DecompressorHuffman ;
break ;
2009-03-14 04:17:03 +00:00
case kCompLZW :
case kCompLZW1 :
case kCompLZW1View :
case kCompLZW1Pic :
dec = new DecompressorLZW ( compression ) ;
2009-03-10 21:44:03 +00:00
break ;
case kCompDCL :
dec = new DecompressorDCL ;
break ;
2009-03-30 07:53:32 +00:00
# ifdef ENABLE_SCI32
2009-03-14 04:17:03 +00:00
case kCompSTACpack :
dec = new DecompressorLZS ;
break ;
2009-03-30 07:53:32 +00:00
# endif
2009-03-10 21:44:03 +00:00
default :
2010-06-17 23:45:38 +00:00
error ( " Resource %s: Compression method %d not supported " , _id . toString ( ) . c_str ( ) , compression ) ;
2009-03-14 04:17:03 +00:00
return SCI_ERROR_UNKNOWN_COMPRESSION ;
2009-03-10 21:44:03 +00:00
}
2009-03-11 01:40:08 +00:00
2016-12-31 20:39:57 -06:00
byte * ptr = new byte [ _size ] ;
_data = ptr ;
2010-06-15 12:19:39 +00:00
_status = kResStatusAllocated ;
2016-12-31 20:39:57 -06:00
errorNum = ptr ? dec - > unpack ( file , ptr , szPacked , _size ) : SCI_ERROR_RESOURCE_TOO_BIG ;
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
if ( errorNum ) {
2010-06-15 12:19:39 +00:00
unalloc ( ) ;
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
} else {
2017-01-02 11:16:39 -06:00
// At least Lighthouse puts sound effects in RESSCI.00n/RESSCI.PAT
// instead of using a RESOURCE.SFX
if ( getType ( ) = = kResourceTypeAudio ) {
SCI32: Fix audio, wave, VMD, Duck, CLUT, TGA, ZZZ, Etc patches
Specifically, audio patches are used in at least PQ:SWAT
(40103.AUD), Lighthouse (9103.AUD), and the GK2 demo (300.AUD).
2017-01-08 22:59:30 -06:00
const uint8 headerSize = ptr [ 1 ] ;
2017-05-07 14:09:58 -05:00
if ( headerSize < 11 ) {
error ( " Unexpected audio header size for %s: should be >= 11, but got %d " , _id . toString ( ) . c_str ( ) , headerSize ) ;
}
const uint32 audioSize = READ_LE_UINT32 ( ptr + 9 ) ;
const uint32 calculatedTotalSize = audioSize + headerSize + kResourceHeaderSize ;
if ( calculatedTotalSize ! = _size ) {
2017-08-05 13:53:02 -05:00
warning ( " Unexpected audio file size: the size of %s in %s is %d, but the volume says it should be %d " , _id . toString ( ) . c_str ( ) , _source - > getLocationName ( ) . c_str ( ) , calculatedTotalSize , _size ) ;
2017-05-07 14:09:58 -05:00
}
2017-08-05 13:53:02 -05:00
_size = MIN ( _size - kResourceHeaderSize , headerSize + audioSize ) ;
2017-01-02 11:16:39 -06:00
}
}
2009-03-11 01:40:08 +00:00
2009-03-10 21:44:03 +00:00
delete dec ;
2010-06-17 23:52:17 +00:00
return errorNum ;
2009-03-10 21:44:03 +00:00
}
2009-08-16 19:18:19 +00:00
ResourceCompression ResourceManager : : getViewCompression ( ) {
int viewsTested = 0 ;
// Test 10 views to see if any are compressed
for ( int i = 0 ; i < 1000 ; i + + ) {
2010-06-05 14:09:52 +00:00
Common : : SeekableReadStream * fileStream = 0 ;
2009-08-16 19:18:19 +00:00
Resource * res = testResource ( ResourceId ( kResourceTypeView , i ) ) ;
if ( ! res )
continue ;
2010-06-15 12:09:30 +00:00
if ( res - > _source - > getSourceType ( ) ! = kSourceVolume )
2009-08-16 19:18:19 +00:00
continue ;
2010-06-06 13:04:24 +00:00
fileStream = getVolumeFile ( res - > _source ) ;
2010-06-05 14:09:52 +00:00
if ( ! fileStream )
2009-08-16 19:18:19 +00:00
continue ;
2010-06-05 14:09:52 +00:00
fileStream - > seek ( res - > _fileOffset , SEEK_SET ) ;
2009-08-16 19:18:19 +00:00
uint32 szPacked ;
ResourceCompression compression ;
2010-06-16 00:24:16 +00:00
if ( res - > readResourceInfo ( _volVersion , fileStream , szPacked , compression ) ) {
2017-05-13 22:07:53 -05:00
disposeVolumeFileStream ( fileStream , res - > _source ) ;
2009-08-16 19:18:19 +00:00
continue ;
2010-06-05 14:09:52 +00:00
}
2017-05-13 22:07:53 -05:00
disposeVolumeFileStream ( fileStream , res - > _source ) ;
2009-08-16 19:18:19 +00:00
if ( compression ! = kCompNone )
return compression ;
if ( + + viewsTested = = 10 )
break ;
}
return kCompNone ;
}
2009-08-19 21:08:17 +00:00
ViewType ResourceManager : : detectViewType ( ) {
2009-08-16 19:18:19 +00:00
for ( int i = 0 ; i < 1000 ; i + + ) {
Resource * res = findResource ( ResourceId ( kResourceTypeView , i ) , 0 ) ;
2009-08-19 21:08:17 +00:00
2009-08-16 19:18:19 +00:00
if ( res ) {
2010-06-18 18:49:06 +00:00
// Skip views coming from patch files
if ( res - > _source - > getSourceType ( ) = = kSourcePatch )
continue ;
2016-12-31 20:39:57 -06:00
switch ( res - > getUint8At ( 1 ) ) {
2009-08-19 21:08:17 +00:00
case 128 :
2011-03-07 13:23:52 +02:00
// If the 2nd byte is 128, it's a VGA game.
// However, Longbow Amiga (AGA, 64 colors), also sets this byte
// to 128, but it's a mixed VGA/Amiga format. Detect this from
// the platform here.
if ( g_sci & & g_sci - > getPlatform ( ) = = Common : : kPlatformAmiga )
return kViewAmiga64 ;
2009-08-16 19:18:19 +00:00
return kViewVga ;
2009-08-19 21:08:17 +00:00
case 0 :
// EGA or Amiga, try to read as Amiga view
2016-12-31 20:39:57 -06:00
if ( res - > size ( ) < 10 )
2009-08-19 21:08:17 +00:00
return kViewUnknown ;
// Read offset of first loop
2016-12-31 20:39:57 -06:00
uint16 offset = res - > getUint16LEAt ( 8 ) ;
2009-08-19 21:08:17 +00:00
2016-12-31 20:39:57 -06:00
if ( offset + 6U > = res - > size ( ) )
2009-08-19 21:08:17 +00:00
return kViewUnknown ;
// Read offset of first cel
2016-12-31 20:39:57 -06:00
offset = res - > getUint16LEAt ( offset + 4 ) ;
2009-08-19 21:08:17 +00:00
2016-12-31 20:39:57 -06:00
if ( offset + 4U > = res - > size ( ) )
2009-08-19 21:08:17 +00:00
return kViewUnknown ;
// Check palette offset, amiga views have no palette
2016-12-31 20:39:57 -06:00
if ( res - > getUint16LEAt ( 6 ) ! = 0 )
2009-08-19 21:08:17 +00:00
return kViewEga ;
2016-12-31 20:39:57 -06:00
uint16 width = res - > getUint16LEAt ( offset ) ;
2009-08-19 21:08:17 +00:00
offset + = 2 ;
2016-12-31 20:39:57 -06:00
uint16 height = res - > getUint16LEAt ( offset ) ;
2009-08-19 21:08:17 +00:00
offset + = 6 ;
2010-01-08 13:45:44 +00:00
// To improve the heuristic, we skip very small views
if ( height < 10 )
continue ;
2009-08-19 21:08:17 +00:00
// Check that the RLE data stays within bounds
int y ;
for ( y = 0 ; y < height ; y + + ) {
int x = 0 ;
2016-12-31 20:39:57 -06:00
while ( ( x < width ) & & ( offset < res - > size ( ) ) ) {
byte op = res - > getUint8At ( offset + + ) ;
2009-08-19 21:08:17 +00:00
x + = ( op & 0x07 ) ? op & 0x07 : op > > 3 ;
}
// Make sure we got exactly the right number of pixels for this row
if ( x ! = width )
return kViewEga ;
}
return kViewAmiga ;
2009-08-16 19:18:19 +00:00
}
}
}
2010-06-19 22:14:15 +00:00
// this may happen if there are serious system issues (or trying to add a broken game)
warning ( " resMan: Couldn't find any views " ) ;
2009-08-19 21:08:17 +00:00
return kViewUnknown ;
2009-08-16 19:18:19 +00:00
}
2015-12-29 01:44:11 +01:00
// to detect selector "wordFail" in LE vocab resource
static const byte detectSci21EarlySignature [ ] = {
10 , // size of signature
0x08 , 0x00 , ' w ' , ' o ' , ' r ' , ' d ' , ' F ' , ' a ' , ' i ' , ' l '
} ;
// to detect selector "wordFail" in BE vocab resource (SCI2.1 Early)
static const byte detectSci21EarlyBESignature [ ] = {
10 , // size of signature
0x00 , 0x08 , ' w ' , ' o ' , ' r ' , ' d ' , ' F ' , ' a ' , ' i ' , ' l '
} ;
// to detect new kString calling to detect SCI2.1 Late
static const byte detectSci21NewStringSignature [ ] = {
8 , // size of signature
0x78 , // push1
0x78 , // push1
0x39 , 0x09 , // pushi 09
0x59 , 0x01 , // rest 01
0x43 , 0x5c , // callk String
} ;
bool ResourceManager : : checkResourceDataForSignature ( Resource * resource , const byte * signature ) {
byte signatureSize = * signature ;
signature + + ; // skip over size byte
if ( signatureSize < 4 )
error ( " resource signature is too small, internal error " ) ;
2016-12-31 20:39:57 -06:00
if ( signatureSize > resource - > size ( ) )
2015-12-29 01:44:11 +01:00
return false ;
2017-02-18 15:06:45 +01:00
const uint32 signatureDWord = READ_UINT32 ( signature ) ;
2015-12-29 01:44:11 +01:00
signature + = 4 ; signatureSize - = 4 ;
2016-12-31 20:39:57 -06:00
const uint32 searchLimit = resource - > size ( ) - signatureSize + 1 ;
2015-12-29 01:44:11 +01:00
uint32 DWordOffset = 0 ;
while ( DWordOffset < searchLimit ) {
2016-12-31 20:39:57 -06:00
if ( signatureDWord = = resource - > getUint32At ( DWordOffset ) ) {
2015-12-29 01:44:11 +01:00
// magic DWORD found, check if the rest matches as well
uint32 offset = DWordOffset + 4 ;
uint32 signaturePos = 0 ;
while ( signaturePos < signatureSize ) {
2016-12-31 20:39:57 -06:00
if ( resource - > getUint8At ( offset ) ! = signature [ signaturePos ] )
2015-12-29 01:44:11 +01:00
break ;
offset + + ;
signaturePos + + ;
}
if ( signaturePos > = signatureSize )
return true ; // signature found
}
DWordOffset + + ;
}
return false ;
}
bool ResourceManager : : checkResourceForSignatures ( ResourceType resourceType , uint16 resourceNr , const byte * signature1 , const byte * signature2 ) {
Resource * resource = findResource ( ResourceId ( resourceType , resourceNr ) , false ) ;
if ( resource ) {
// resource found and loaded, check for signatures
if ( signature1 ) {
if ( checkResourceDataForSignature ( resource , signature1 ) )
return true ;
}
if ( signature2 ) {
if ( checkResourceDataForSignature ( resource , signature2 ) )
return true ;
}
}
return false ;
}
2009-09-23 10:55:35 +00:00
void ResourceManager : : detectSciVersion ( ) {
2009-09-23 12:12:37 +00:00
// We use the view compression to set a preliminary s_sciVersion for the sake of getResourceInfo
2009-08-16 19:18:19 +00:00
// Pretend we have a SCI0 game
2009-09-23 12:12:37 +00:00
s_sciVersion = SCI_VERSION_0_EARLY ;
2009-08-16 19:18:19 +00:00
bool oldDecompressors = true ;
2010-09-18 09:47:18 +00:00
ResourceCompression viewCompression ;
2011-06-20 00:59:48 +02:00
# ifdef ENABLE_SCI32
2010-09-18 09:47:18 +00:00
viewCompression = getViewCompression ( ) ;
# else
2010-11-09 14:25:39 +00:00
if ( _volVersion > = kResVersionSci2 ) {
2010-09-18 09:47:18 +00:00
// SCI32 support isn't built in, thus view detection will fail
viewCompression = kCompUnknown ;
} else {
viewCompression = getViewCompression ( ) ;
}
# endif
2009-08-16 19:18:19 +00:00
if ( viewCompression ! = kCompLZW ) {
// If it's a different compression type from kCompLZW, the game is probably
2011-02-27 16:48:53 +02:00
// SCI_VERSION_1_EGA_ONLY or later. If the views are uncompressed, it is
2009-08-16 19:18:19 +00:00
// likely not an early disk game.
2011-02-27 16:48:53 +02:00
s_sciVersion = SCI_VERSION_1_EGA_ONLY ;
2009-08-16 19:18:19 +00:00
oldDecompressors = false ;
}
// Set view type
2009-12-27 12:17:43 +00:00
if ( viewCompression = = kCompDCL
2010-01-27 22:55:30 +00:00
| | _volVersion = = kResVersionSci11 // pq4demo
2010-05-18 04:17:58 +00:00
| | _volVersion = = kResVersionSci11Mac
2009-08-25 23:15:18 +00:00
# ifdef ENABLE_SCI32
| | viewCompression = = kCompSTACpack
2010-11-09 14:25:39 +00:00
| | _volVersion = = kResVersionSci2 // kq7
2009-08-25 23:15:18 +00:00
# endif
) {
2009-08-16 19:18:19 +00:00
// SCI1.1 VGA views
_viewType = kViewVga11 ;
} else {
2010-09-18 09:47:18 +00:00
# ifdef ENABLE_SCI32
2009-08-16 19:18:19 +00:00
// Otherwise we detect it from a view
_viewType = detectViewType ( ) ;
2010-09-18 09:47:18 +00:00
# else
2010-11-09 14:25:39 +00:00
if ( _volVersion = = kResVersionSci2 & & viewCompression = = kCompUnknown ) {
2010-09-18 09:47:18 +00:00
// A SCI32 game, but SCI32 support is disabled. Force the view type
// to kViewVga11, as we can't read from the game's resource files
_viewType = kViewVga11 ;
} else {
_viewType = detectViewType ( ) ;
}
# endif
2009-08-16 19:18:19 +00:00
}
2011-06-20 00:59:48 +02:00
2010-05-10 18:29:13 +00:00
if ( _volVersion = = kResVersionSci11Mac ) {
2011-10-15 01:00:58 +03:00
Resource * res = testResource ( ResourceId ( kResourceTypeScript , 64920 ) ) ;
// Distinguish between SCI1.1 and SCI32 games here. SCI32 games will
// always include script 64920 (the Array class). Note that there are
// no Mac SCI2 games. Yes, that means that GK1 Mac is SCI2.1 and not SCI2.
2011-01-31 23:09:22 +00:00
// TODO: Decide between SCI2.1 and SCI3
2015-12-29 01:44:11 +01:00
if ( res ) {
s_sciVersion = SCI_VERSION_2_1_EARLY ; // we check for SCI2.1 specifics a bit later
} else {
2011-10-15 01:00:58 +03:00
s_sciVersion = SCI_VERSION_1_1 ;
2015-12-29 01:44:11 +01:00
return ;
}
2010-05-10 18:29:13 +00:00
}
2009-12-27 12:17:43 +00:00
2009-08-25 23:02:57 +00:00
// Handle SCI32 versions here
2015-12-29 01:44:11 +01:00
if ( s_sciVersion ! = SCI_VERSION_2_1_EARLY ) {
if ( _volVersion > = kResVersionSci2 ) {
Common : : List < ResourceId > heaps = listResources ( kResourceTypeHeap ) ;
bool hasHeapResources = ! heaps . empty ( ) ;
// SCI2.1/3 and SCI1 Late resource maps are the same, except that
// SCI1 Late resource maps have the resource types or'd with
// 0x80. We differentiate between SCI2 and SCI2.1/3 based on that.
if ( _mapVersion = = kResVersionSci1Late ) {
s_sciVersion = SCI_VERSION_2 ;
return ;
} else if ( hasHeapResources ) {
s_sciVersion = SCI_VERSION_2_1_EARLY ; // exact SCI2.1 version is checked a bit later
} else {
s_sciVersion = SCI_VERSION_3 ;
return ;
}
}
}
if ( s_sciVersion = = SCI_VERSION_2_1_EARLY ) {
// we only know that it's SCI2.1, not which exact version it is
// check, if selector "wordFail" inside vocab 997 exists, if it does it's SCI2.1 Early
if ( ( checkResourceForSignatures ( kResourceTypeVocab , 997 , detectSci21EarlySignature , detectSci21EarlyBESignature ) ) ) {
// found -> it is SCI2.1 early
2009-09-23 10:55:35 +00:00
return ;
2015-12-29 01:44:11 +01:00
}
s_sciVersion = SCI_VERSION_2_1_MIDDLE ;
if ( checkResourceForSignatures ( kResourceTypeScript , 64918 , detectSci21NewStringSignature , nullptr ) ) {
// new kString call detected, it's SCI2.1 late
// TODO: this call seems to be different on Mac
s_sciVersion = SCI_VERSION_2_1_LATE ;
2010-11-10 14:28:45 +00:00
return ;
2009-09-23 10:55:35 +00:00
}
2015-12-29 01:44:11 +01:00
return ;
2009-08-25 23:02:57 +00:00
}
2009-08-16 19:18:19 +00:00
2009-10-26 21:41:41 +00:00
// Check for transitive SCI1/SCI1.1 games, like PQ1 here
// If the game has any heap file (here we check for heap file 0), then
// it definitely uses a SCI1.1 kernel
if ( testResource ( ResourceId ( kResourceTypeHeap , 0 ) ) ) {
s_sciVersion = SCI_VERSION_1_1 ;
return ;
}
2009-08-16 19:18:19 +00:00
switch ( _mapVersion ) {
case kResVersionSci0Sci1Early :
if ( _viewType = = kViewVga ) {
// VGA
2009-09-23 12:12:37 +00:00
s_sciVersion = SCI_VERSION_1_EARLY ;
2009-09-23 10:55:35 +00:00
return ;
2009-08-16 19:18:19 +00:00
}
// EGA
2009-09-23 10:55:35 +00:00
if ( hasOldScriptHeader ( ) ) {
2009-09-23 12:12:37 +00:00
s_sciVersion = SCI_VERSION_0_EARLY ;
2009-09-23 10:55:35 +00:00
return ;
}
2009-08-16 19:18:19 +00:00
2010-08-02 18:25:48 +00:00
if ( hasSci0Voc999 ( ) ) {
s_sciVersion = SCI_VERSION_0_LATE ;
return ;
}
2009-08-16 19:18:19 +00:00
if ( oldDecompressors ) {
// It's either SCI_VERSION_0_LATE or SCI_VERSION_01
// We first check for SCI1 vocab.999
if ( testResource ( ResourceId ( kResourceTypeVocab , 999 ) ) ) {
2010-08-02 18:25:48 +00:00
s_sciVersion = SCI_VERSION_01 ;
return ;
2009-08-16 19:18:19 +00:00
}
// If vocab.999 is missing, we try vocab.900
if ( testResource ( ResourceId ( kResourceTypeVocab , 900 ) ) ) {
if ( hasSci1Voc900 ( ) ) {
2009-09-23 12:12:37 +00:00
s_sciVersion = SCI_VERSION_01 ;
2009-09-23 10:55:35 +00:00
return ;
2009-08-16 19:18:19 +00:00
} else {
2009-09-23 12:12:37 +00:00
s_sciVersion = SCI_VERSION_0_LATE ;
2009-09-23 10:55:35 +00:00
return ;
2009-08-16 19:18:19 +00:00
}
}
2010-06-17 23:45:38 +00:00
error ( " Failed to accurately determine SCI version " ) ;
2009-08-16 19:18:19 +00:00
}
2011-02-27 16:48:53 +02:00
// New decompressors. It's either SCI_VERSION_1_EGA_ONLY or SCI_VERSION_1_EARLY.
2010-08-02 14:14:06 +00:00
if ( hasSci1Voc900 ( ) ) {
2011-02-27 16:48:53 +02:00
s_sciVersion = SCI_VERSION_1_EGA_ONLY ;
2010-08-02 14:14:06 +00:00
return ;
2009-09-23 10:55:35 +00:00
}
2009-08-16 19:18:19 +00:00
2010-07-28 15:34:34 +00:00
// SCI_VERSION_1_EARLY EGA versions lack the parser vocab
2009-09-23 12:12:37 +00:00
s_sciVersion = SCI_VERSION_1_EARLY ;
2009-09-23 10:55:35 +00:00
return ;
2009-08-16 19:18:19 +00:00
case kResVersionSci1Middle :
2011-02-15 21:47:29 -05:00
case kResVersionKQ5FMT :
2009-09-23 12:12:37 +00:00
s_sciVersion = SCI_VERSION_1_MIDDLE ;
2011-03-04 14:27:25 +02:00
// Amiga SCI1 middle games are actually SCI1 late
2011-03-07 13:23:52 +02:00
if ( _viewType = = kViewAmiga | | _viewType = = kViewAmiga64 )
2011-03-04 14:27:25 +02:00
s_sciVersion = SCI_VERSION_1_LATE ;
2011-03-13 23:50:40 +02:00
// Same goes for Mac SCI1 middle games
if ( g_sci & & g_sci - > getPlatform ( ) = = Common : : kPlatformMacintosh )
s_sciVersion = SCI_VERSION_1_LATE ;
2009-09-23 10:55:35 +00:00
return ;
2009-08-16 19:18:19 +00:00
case kResVersionSci1Late :
2009-09-23 10:55:35 +00:00
if ( _volVersion = = kResVersionSci11 ) {
2009-09-23 12:12:37 +00:00
s_sciVersion = SCI_VERSION_1_1 ;
2009-09-23 10:55:35 +00:00
return ;
}
2009-09-23 12:12:37 +00:00
s_sciVersion = SCI_VERSION_1_LATE ;
2009-09-23 10:55:35 +00:00
return ;
2009-08-16 19:18:19 +00:00
case kResVersionSci11 :
2009-09-23 12:12:37 +00:00
s_sciVersion = SCI_VERSION_1_1 ;
2009-09-23 10:55:35 +00:00
return ;
2009-08-16 19:18:19 +00:00
default :
2010-01-03 22:50:39 +00:00
s_sciVersion = SCI_VERSION_NONE ;
2010-11-09 19:37:42 +00:00
error ( " detectSciVersion(): Unable to detect the game's SCI version " ) ;
2009-08-16 19:18:19 +00:00
}
}
2010-04-17 20:43:09 +00:00
bool ResourceManager : : detectFontExtended ( ) {
Resource * res = findResource ( ResourceId ( kResourceTypeFont , 0 ) , 0 ) ;
if ( res ) {
2016-12-31 20:39:57 -06:00
if ( res - > size ( ) > = 4 ) {
uint16 numChars = READ_LE_UINT16 ( res - > data ( ) + 2 ) ;
2010-04-17 20:43:09 +00:00
if ( numChars > 0x80 )
return true ;
}
}
return false ;
}
2010-06-25 10:04:31 +00:00
// detects, if SCI1.1 game uses palette merging or copying - this is supposed to only get used on SCI1.1 games
2011-10-19 23:44:34 +03:00
bool ResourceManager : : detectPaletteMergingSci11 ( ) {
2010-06-25 10:04:31 +00:00
// Load palette 999 (default palette)
Resource * res = findResource ( ResourceId ( kResourceTypePalette , 999 ) , false ) ;
2016-12-31 20:39:57 -06:00
if ( res & & res - > size ( ) > 30 ) {
2010-06-25 10:04:31 +00:00
// Old palette format used in palette resource? -> it's merging
2016-12-31 20:39:57 -06:00
if ( ( res - > getUint8At ( 0 ) = = 0 & & res - > getUint8At ( 1 ) = = 1 ) | |
( res - > getUint8At ( 0 ) = = 0 & & res - > getUint8At ( 1 ) = = 0 & & res - > getUint16LEAt ( 29 ) = = 0 ) ) {
2010-06-25 10:04:31 +00:00
return true ;
2016-12-31 20:39:57 -06:00
}
2014-10-28 22:37:57 +01:00
// Hardcoded: Laura Bow 2 floppy uses new palette resource, but still palette merging + 16 bit color matching
2016-12-31 20:39:57 -06:00
if ( g_sci - > getGameId ( ) = = GID_LAURABOW2 & & ! g_sci - > isCD ( ) & & ! g_sci - > isDemo ( ) ) {
2014-10-28 22:37:57 +01:00
return true ;
2016-12-31 20:39:57 -06:00
}
2010-06-25 10:04:31 +00:00
}
2016-12-31 20:39:57 -06:00
2010-06-25 10:04:31 +00:00
return false ;
}
2010-09-04 08:51:10 +00:00
// is called on SCI0EARLY games to make sure that sound resources are in fact also SCI0EARLY
bool ResourceManager : : detectEarlySound ( ) {
2016-12-31 20:39:57 -06:00
Resource * res = findResource ( ResourceId ( kResourceTypeSound , 1 ) , false ) ;
if ( res & &
res - > size ( ) > = 0x22 & &
res - > getUint16LEAt ( 0x1f ) = = 0 & & // channel 15 voice count + play mask is 0 in SCI0LATE
res - > getUint8At ( 0x21 ) = = 0 ) { // last byte right before actual data is 0 as well
return false ;
2010-09-04 08:51:10 +00:00
}
2016-12-31 20:39:57 -06:00
2010-09-04 08:51:10 +00:00
return true ;
}
2009-08-16 19:18:19 +00:00
// Functions below are based on PD code by Brian Provinciano (SCI Studio)
bool ResourceManager : : hasOldScriptHeader ( ) {
2016-12-31 20:39:57 -06:00
Resource * res = findResource ( ResourceId ( kResourceTypeScript , 0 ) , false ) ;
2009-08-16 19:18:19 +00:00
if ( ! res ) {
2016-02-20 16:55:01 +02:00
// Script 0 missing -> corrupted / non-SCI resource files.
// Don't error out here, because this might have been called
// from the fallback detector
2009-08-16 19:18:19 +00:00
return false ;
}
uint offset = 2 ;
const int objTypes = 17 ;
2016-12-31 20:39:57 -06:00
while ( offset < res - > size ( ) ) {
uint16 objType = res - > getUint16LEAt ( offset ) ;
2009-08-16 19:18:19 +00:00
if ( ! objType ) {
offset + = 2 ;
// We should be at the end of the resource now
2016-12-31 20:39:57 -06:00
return offset = = res - > size ( ) ;
2009-08-16 19:18:19 +00:00
}
if ( objType > = objTypes ) {
// Invalid objType
return false ;
}
2016-12-31 20:39:57 -06:00
int skip = res - > getUint16LEAt ( offset + 2 ) ;
2009-08-16 19:18:19 +00:00
if ( skip < 2 ) {
// Invalid size
return false ;
}
offset + = skip ;
}
2009-08-16 19:20:09 +00:00
return false ;
}
2009-08-16 19:18:19 +00:00
bool ResourceManager : : hasSci0Voc999 ( ) {
2016-12-31 20:39:57 -06:00
Resource * res = findResource ( ResourceId ( kResourceTypeVocab , 999 ) , false ) ;
2009-08-16 19:18:19 +00:00
if ( ! res ) {
// No vocab present, possibly a demo version
return false ;
}
2016-12-31 20:39:57 -06:00
if ( res - > size ( ) < 2 )
2009-08-16 19:18:19 +00:00
return false ;
2016-12-31 20:39:57 -06:00
uint16 count = res - > getUint16LEAt ( 0 ) ;
2009-08-16 19:18:19 +00:00
// Make sure there's enough room for the pointers
2016-12-31 20:39:57 -06:00
if ( res - > size ( ) < ( uint ) count * 2 )
2009-08-16 19:18:19 +00:00
return false ;
// Iterate over all pointers
for ( uint i = 0 ; i < count ; i + + ) {
// Offset to string
2016-12-31 20:39:57 -06:00
uint16 offset = res - > getUint16LEAt ( 2 + count * 2 ) ;
2009-08-16 19:18:19 +00:00
// Look for end of string
do {
2016-12-31 20:39:57 -06:00
if ( offset > = res - > size ( ) ) {
2009-08-16 19:18:19 +00:00
// Out of bounds
return false ;
}
2016-12-31 20:39:57 -06:00
} while ( res - > getUint8At ( offset + + ) ) ;
2009-08-16 19:18:19 +00:00
}
return true ;
}
bool ResourceManager : : hasSci1Voc900 ( ) {
Resource * res = findResource ( ResourceId ( kResourceTypeVocab , 900 ) , 0 ) ;
if ( ! res )
return false ;
2016-12-31 20:39:57 -06:00
if ( res - > size ( ) < 0x1fe )
2009-08-16 19:18:19 +00:00
return false ;
uint16 offset = 0x1fe ;
2016-12-31 20:39:57 -06:00
while ( offset < res - > size ( ) ) {
2009-08-16 19:18:19 +00:00
offset + + ;
do {
2016-12-31 20:39:57 -06:00
if ( offset > = res - > size ( ) ) {
2009-08-16 19:18:19 +00:00
// Out of bounds;
return false ;
}
2016-12-31 20:39:57 -06:00
} while ( res - > getUint8At ( offset + + ) ) ;
2009-08-16 19:18:19 +00:00
offset + = 3 ;
}
2016-12-31 20:39:57 -06:00
return offset = = res - > size ( ) ;
2009-08-16 19:20:09 +00:00
}
2009-08-16 19:18:19 +00:00
2010-11-04 23:19:23 +00:00
// Same function as Script::findBlockSCI0(). Slight code
2010-06-02 13:17:36 +00:00
// duplication here, but this has been done to keep the resource
// manager independent from the rest of the engine
2016-12-31 20:39:57 -06:00
static SciSpan < const byte > : : const_iterator findSci0ExportsBlock ( const SciSpan < const byte > & buffer ) {
SciSpan < const byte > : : const_iterator buf = buffer . cbegin ( ) ;
2010-06-02 13:17:36 +00:00
bool oldScriptHeader = ( getSciVersion ( ) = = SCI_VERSION_0_EARLY ) ;
if ( oldScriptHeader )
buf + = 2 ;
2016-12-31 20:39:57 -06:00
for ( ; ; ) {
int seekerType = buf . getUint16LE ( ) ;
2010-06-02 13:17:36 +00:00
if ( seekerType = = 0 )
break ;
if ( seekerType = = 7 ) // exports
return buf ;
2016-12-31 20:39:57 -06:00
int seekerSize = ( buf + 2 ) . getUint16LE ( ) ;
2010-06-02 13:17:36 +00:00
assert ( seekerSize > 0 ) ;
buf + = seekerSize ;
2016-12-31 20:39:57 -06:00
}
2010-06-02 13:17:36 +00:00
2016-12-31 20:39:57 -06:00
return buffer . cend ( ) ;
2010-06-02 13:17:36 +00:00
}
2010-05-23 10:28:03 +00:00
2010-11-17 11:17:38 +00:00
// This code duplicates Script::relocateOffsetSci3, but we can't use
// that here since we can't instantiate scripts at this point.
2017-02-08 11:52:36 -06:00
static int relocateOffsetSci3 ( const SciSpan < const byte > & buf , uint32 offset , const bool isBE ) {
2016-12-31 20:39:57 -06:00
int relocStart = buf . getUint32LEAt ( 8 ) ;
int relocCount = buf . getUint16LEAt ( 18 ) ;
SciSpan < const byte > : : const_iterator seeker = buf . cbegin ( ) + relocStart ;
2010-11-17 11:17:38 +00:00
for ( int i = 0 ; i < relocCount ; + + i ) {
2017-02-08 11:52:36 -06:00
const uint32 candidateOffset = isBE ? seeker . getUint32BE ( ) : seeker . getUint32LE ( ) ;
if ( candidateOffset = = offset ) {
if ( isBE ) {
return buf . getUint16BEAt ( offset ) + ( seeker + 4 ) . getUint32BE ( ) ;
} else {
return buf . getUint16LEAt ( offset ) + ( seeker + 4 ) . getUint32LE ( ) ;
}
2010-11-17 11:17:38 +00:00
}
seeker + = 10 ;
}
return - 1 ;
}
2017-02-08 11:52:36 -06:00
reg_t ResourceManager : : findGameObject ( const bool addSci11ScriptOffset , const bool isBE ) {
2010-05-23 10:28:03 +00:00
Resource * script = findResource ( ResourceId ( kResourceTypeScript , 0 ) , false ) ;
2010-06-02 02:55:55 +00:00
if ( ! script )
return NULL_REG ;
2016-12-31 20:39:57 -06:00
SciSpan < const byte > : : const_iterator offsetPtr ;
2010-06-02 13:17:36 +00:00
2010-11-17 11:17:38 +00:00
if ( getSciVersion ( ) < = SCI_VERSION_1_LATE ) {
2016-12-31 20:39:57 -06:00
SciSpan < const byte > buf = ( getSciVersion ( ) = = SCI_VERSION_0_EARLY ) ? script - > subspan ( 2 ) : * script ;
2010-06-02 13:17:36 +00:00
// Check if the first block is the exports block (in most cases, it is)
2016-12-31 20:39:57 -06:00
bool exportsIsFirst = buf . getUint16LEAt ( 4 ) = = 7 ;
2010-06-02 13:17:36 +00:00
if ( exportsIsFirst ) {
2016-12-31 20:39:57 -06:00
offsetPtr = buf . subspan ( 4 + 2 ) . cbegin ( ) ;
2010-06-02 13:17:36 +00:00
} else {
2016-12-31 20:39:57 -06:00
offsetPtr = findSci0ExportsBlock ( * script ) ;
if ( offsetPtr = = buf . cend ( ) )
2010-06-02 13:17:36 +00:00
error ( " Unable to find exports block from script 0 " ) ;
offsetPtr + = 4 + 2 ;
}
2010-11-17 11:17:38 +00:00
2016-12-31 20:39:57 -06:00
int16 offset = ! isSci11Mac ( ) ? offsetPtr . getUint16LE ( ) : offsetPtr . getUint16BE ( ) ;
2010-11-17 11:17:38 +00:00
return make_reg ( 1 , offset ) ;
2015-12-29 01:44:11 +01:00
} else if ( getSciVersion ( ) > = SCI_VERSION_1_1 & & getSciVersion ( ) < = SCI_VERSION_2_1_LATE ) {
2016-12-31 20:39:57 -06:00
offsetPtr = script - > cbegin ( ) + 4 + 2 + 2 ;
2010-05-29 14:03:08 +00:00
2010-11-17 11:17:38 +00:00
// In SCI1.1 - SCI2.1, the heap is appended at the end of the script,
// so adjust the offset accordingly if requested
2016-12-31 20:39:57 -06:00
int16 offset = ! isSci11Mac ( ) ? offsetPtr . getUint16LE ( ) : offsetPtr . getUint16BE ( ) ;
2010-11-17 11:17:38 +00:00
if ( addSci11ScriptOffset ) {
2016-12-31 20:39:57 -06:00
offset + = script - > size ( ) ;
2010-05-29 14:03:08 +00:00
2010-11-17 11:17:38 +00:00
// Ensure that the start of the heap is word-aligned - same as in Script::init()
2016-12-31 20:39:57 -06:00
if ( script - > size ( ) & 2 )
2010-11-17 11:17:38 +00:00
offset + + ;
}
2010-05-30 15:17:29 +00:00
2010-11-17 11:17:38 +00:00
return make_reg ( 1 , offset ) ;
} else {
2017-02-08 11:52:36 -06:00
return make_reg ( 1 , relocateOffsetSci3 ( * script , 22 , isBE ) ) ;
2010-11-17 11:17:38 +00:00
}
2010-05-29 14:03:08 +00:00
}
2017-02-08 11:52:36 -06:00
Common : : String ResourceManager : : findSierraGameId ( const bool isBE ) {
2010-11-17 08:53:02 +00:00
// In SCI0-SCI1, the heap is embedded in the script. In SCI1.1 - SCI2.1,
// it's in a separate heap resource
2017-02-08 11:52:36 -06:00
Resource * heap = nullptr ;
int nameSelector = - 1 ;
2010-05-23 10:28:03 +00:00
if ( getSciVersion ( ) < SCI_VERSION_1_1 ) {
2010-05-29 14:03:08 +00:00
heap = findResource ( ResourceId ( kResourceTypeScript , 0 ) , false ) ;
2017-02-08 11:52:36 -06:00
nameSelector = 3 ;
2015-12-29 01:44:11 +01:00
} else if ( getSciVersion ( ) > = SCI_VERSION_1_1 & & getSciVersion ( ) < = SCI_VERSION_2_1_LATE ) {
2010-05-23 10:28:03 +00:00
heap = findResource ( ResourceId ( kResourceTypeHeap , 0 ) , false ) ;
2017-02-08 11:52:36 -06:00
nameSelector = 8 ;
2010-11-17 08:30:08 +00:00
} else if ( getSciVersion ( ) = = SCI_VERSION_3 ) {
2017-02-08 11:52:36 -06:00
heap = findResource ( ResourceId ( kResourceTypeScript , 0 ) , false ) ;
Resource * vocab = findResource ( ResourceId ( kResourceTypeVocab , VOCAB_RESOURCE_SELECTORS ) , false ) ;
const uint16 numSelectors = isBE ? vocab - > getUint16BEAt ( 0 ) : vocab - > getUint16LEAt ( 0 ) ;
for ( uint16 i = 0 ; i < numSelectors ; + + i ) {
uint16 selectorOffset ;
uint16 selectorSize ;
if ( isBE ) {
selectorOffset = vocab - > getUint16BEAt ( ( i + 1 ) * sizeof ( uint16 ) ) ;
selectorSize = vocab - > getUint16BEAt ( selectorOffset ) ;
} else {
selectorOffset = vocab - > getUint16LEAt ( ( i + 1 ) * sizeof ( uint16 ) ) ;
selectorSize = vocab - > getUint16LEAt ( selectorOffset ) ;
}
Common : : String selectorName = Common : : String ( ( const char * ) vocab - > getUnsafeDataAt ( selectorOffset + 2 , selectorSize ) , selectorSize ) ;
if ( selectorName = = " name " ) {
nameSelector = i ;
break ;
}
}
2010-05-23 10:28:03 +00:00
}
2017-02-08 11:52:36 -06:00
if ( ! heap | | nameSelector = = - 1 )
2010-06-02 02:55:55 +00:00
return " " ;
2017-02-08 11:52:36 -06:00
int16 gameObjectOffset = findGameObject ( false , isBE ) . getOffset ( ) ;
2010-05-29 15:01:33 +00:00
2010-06-02 02:55:55 +00:00
if ( ! gameObjectOffset )
return " " ;
2017-02-08 11:52:36 -06:00
int32 offset ;
if ( getSciVersion ( ) = = SCI_VERSION_3 ) {
offset = relocateOffsetSci3 ( * heap , gameObjectOffset + /* base selector offset */ 0x110 + nameSelector * sizeof ( uint16 ) , isBE ) ;
} else {
// Seek to the name selector of the first export
SciSpan < const byte > : : const_iterator offsetPtr = heap - > cbegin ( ) + gameObjectOffset + nameSelector * sizeof ( uint16 ) ;
offset = ! isSci11Mac ( ) ? offsetPtr . getUint16LE ( ) : offsetPtr . getUint16BE ( ) ;
}
2016-12-31 20:39:57 -06:00
return heap - > getStringAt ( offset ) ;
2010-05-23 10:28:03 +00:00
}
2010-09-10 14:07:32 +00:00
const Common : : String & Resource : : getResourceLocation ( ) const {
return _source - > getLocationName ( ) ;
}
2009-02-21 10:23:36 +00:00
} // End of namespace Sci