2007-05-30 21:56:52 +00:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
2001-10-09 14:30:12 +00:00
*
2021-12-26 18:47:58 +01:00
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
2014-02-18 02:34:24 +01:00
*
2001-10-09 14:30:12 +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
*
2001-10-09 14:30:12 +00:00
* You should have received a copy of the GNU General Public License
2021-12-26 18:47:58 +01:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2001-10-09 14:30:12 +00:00
*
*/
2021-07-12 14:28:47 +02:00
# include "common/md5.h"
2002-12-24 16:10:31 +00:00
# include "common/str.h"
2021-07-12 14:28:47 +02:00
# include "common/memstream.h"
2021-05-07 10:11:06 +02:00
# include "common/macresman.h"
2010-05-04 12:02:33 +00:00
# ifndef MACOSX
# include "common/config-manager.h"
# endif
2005-04-10 12:59:17 +00:00
2005-05-13 10:23:56 +00:00
# include "scumm/charset.h"
2003-09-11 10:32:15 +00:00
# include "scumm/dialogs.h"
2006-03-03 15:16:02 +00:00
# include "scumm/file.h"
2021-11-15 15:05:18 -08:00
# include "scumm/imuse_digi/dimuse_engine.h"
2006-02-15 00:57:50 +00:00
# include "scumm/he/intern_he.h"
2003-09-11 10:32:15 +00:00
# include "scumm/object.h"
# include "scumm/resource.h"
# include "scumm/scumm.h"
2009-03-20 16:33:58 +00:00
# include "scumm/scumm_v5.h"
# include "scumm/scumm_v8.h"
2003-09-11 10:32:15 +00:00
# include "scumm/sound.h"
2005-04-10 12:59:17 +00:00
# include "scumm/util.h"
2003-09-11 10:32:15 +00:00
# include "scumm/verbs.h"
2005-04-10 12:59:17 +00:00
2003-10-03 18:33:57 +00:00
namespace Scumm {
2005-10-17 15:00:23 +00:00
enum {
RF_LOCK = 0x80 ,
RF_USAGE = 0x7F ,
2006-01-27 00:27:57 +00:00
RF_USAGE_MAX = RF_USAGE ,
2011-08-28 10:40:23 +10:00
RS_MODIFIED = 0x10 ,
RF_OFFHEAP = 0x40
2005-10-17 15:00:23 +00:00
} ;
2011-05-13 14:04:59 +02:00
extern const char * nameOfResType ( ResType type ) ;
2005-04-03 22:10:10 +00:00
2005-03-06 13:23:29 +00:00
static uint16 newTag2Old ( uint32 newTag ) ;
2005-03-31 23:11:39 +00:00
static const byte * findResourceSmall ( uint32 tag , const byte * searchin ) ;
2002-07-16 21:03:14 +00:00
2005-10-19 19:26:53 +00:00
static bool checkTryMedia ( BaseScummFile * handle ) ;
2004-07-26 17:14:57 +00:00
2001-10-09 14:30:12 +00:00
/* Open a room */
2005-04-10 17:38:09 +00:00
void ScummEngine : : openRoom ( const int room ) {
2003-04-29 03:32:08 +00:00
bool result ;
2003-07-28 16:55:06 +00:00
byte encByte = 0 ;
2001-10-09 14:30:12 +00:00
2004-01-10 11:01:47 +00:00
debugC ( DEBUG_GENERAL , " openRoom(%d) " , room ) ;
2002-12-22 21:58:16 +00:00
assert ( room > = 0 ) ;
2001-10-09 14:30:12 +00:00
/* Don't load the same room again */
if ( _lastLoadedRoom = = room )
return ;
_lastLoadedRoom = room ;
/* Room -1 means close file */
2002-04-11 17:19:16 +00:00
if ( room = = - 1 ) {
2001-10-09 14:30:12 +00:00
deleteRoomOffsets ( ) ;
2005-03-30 21:59:12 +00:00
_fileHandle - > close ( ) ;
2001-10-09 14:30:12 +00:00
return ;
}
2006-04-19 18:14:28 +00:00
// Load the disk numer / room offs (special case for room 0 exists because
// room 0 contains the data which is used to create the roomno / roomoffs
// tables -- hence obviously we mustn't use those when loading room 0.
2011-05-13 14:48:01 +02:00
const uint32 diskNumber = room ? _res - > _types [ rtRoom ] [ room ] . _roomno : 0 ;
const uint32 room_offs = room ? _res - > _types [ rtRoom ] [ room ] . _roomoffs : 0 ;
2005-04-10 17:38:09 +00:00
2006-04-23 17:33:16 +00:00
// FIXME: Since room_offs is const, clearly the following loop either
// is never entered, or loops forever (if it wasn't for the return/error
// statements in it, that is). -> This should be cleaned up!
2006-04-23 20:02:33 +00:00
// Maybe we should re-enabled the looping properly, to deal with disc
// changes in COMI ?
2007-04-01 15:58:34 +00:00
while ( room_offs ! = RES_INVALID_OFFSET ) {
2001-10-09 14:30:12 +00:00
2006-02-20 16:51:30 +00:00
if ( room_offs ! = 0 & & room ! = 0 & & _game . heversion < 98 ) {
2011-05-13 14:48:01 +02:00
_fileOffset = _res - > _types [ rtRoom ] [ room ] . _roomoffs ;
2005-03-02 05:01:52 +00:00
return ;
2001-10-09 14:30:12 +00:00
}
2007-09-19 08:40:12 +00:00
2006-04-23 17:33:16 +00:00
Common : : String filename ( generateFilename ( room ) ) ;
// Determine the encryption, if any.
2006-04-11 22:41:44 +00:00
if ( _game . features & GF_USE_KEY ) {
if ( _game . version < = 3 )
encByte = 0xFF ;
else if ( ( _game . version = = 4 ) & & ( room = = 0 | | room > = 900 ) )
encByte = 0 ;
else
encByte = 0x69 ;
} else
encByte = 0 ;
if ( room > 0 & & ( _game . version = = 8 ) )
VAR ( VAR_CURRENTDISK ) = diskNumber ;
2002-02-12 21:28:07 +00:00
2006-04-23 17:33:16 +00:00
// Try to open the file
2006-04-23 17:39:31 +00:00
result = openResourceFile ( filename , encByte ) ;
2005-07-30 21:11:48 +00:00
2003-04-29 03:32:08 +00:00
if ( result ) {
2002-04-11 17:19:16 +00:00
if ( room = = 0 )
2001-10-09 14:30:12 +00:00
return ;
2005-04-03 22:56:02 +00:00
deleteRoomOffsets ( ) ;
2001-10-09 14:30:12 +00:00
readRoomsOffsets ( ) ;
2011-05-13 14:48:01 +02:00
_fileOffset = _res - > _types [ rtRoom ] [ room ] . _roomoffs ;
2001-10-09 14:30:12 +00:00
if ( _fileOffset ! = 8 )
return ;
2006-04-23 17:33:16 +00:00
error ( " Room %d not in %s " , room , filename . c_str ( ) ) ;
2001-10-09 14:30:12 +00:00
return ;
}
2006-04-23 17:33:16 +00:00
askForDisk ( filename . c_str ( ) , diskNumber ) ;
2001-10-09 14:30:12 +00:00
}
do {
2006-04-23 17:33:16 +00:00
char buf [ 16 ] ;
snprintf ( buf , sizeof ( buf ) , " %.3d.lfl " , room ) ;
2003-07-28 16:55:06 +00:00
encByte = 0 ;
if ( openResourceFile ( buf , encByte ) )
2001-10-09 14:30:12 +00:00
break ;
2005-04-10 17:38:09 +00:00
askForDisk ( buf , diskNumber ) ;
2002-04-11 17:19:16 +00:00
} while ( 1 ) ;
2001-10-09 14:30:12 +00:00
deleteRoomOffsets ( ) ;
2002-12-22 21:58:16 +00:00
_fileOffset = 0 ; // start of file
}
2003-10-02 22:42:03 +00:00
void ScummEngine : : closeRoom ( ) {
2002-12-22 21:58:16 +00:00
if ( _lastLoadedRoom ! = - 1 ) {
_lastLoadedRoom = - 1 ;
deleteRoomOffsets ( ) ;
2005-03-30 21:59:12 +00:00
_fileHandle - > close ( ) ;
2002-12-22 21:58:16 +00:00
}
2001-10-09 14:30:12 +00:00
}
2003-05-30 20:13:29 +00:00
/** Delete the currently loaded room offsets. */
2003-10-02 22:42:03 +00:00
void ScummEngine : : deleteRoomOffsets ( ) {
2003-12-26 23:11:35 +00:00
for ( int i = 0 ; i < _numRooms ; i + + ) {
2011-05-13 14:48:01 +02:00
if ( _res - > _types [ rtRoom ] [ i ] . _roomoffs ! = RES_INVALID_OFFSET )
_res - > _types [ rtRoom ] [ i ] . _roomoffs = 0 ;
2001-10-09 14:30:12 +00:00
}
}
2003-05-30 20:13:29 +00:00
/** Read room offsets */
2003-10-02 22:42:03 +00:00
void ScummEngine : : readRoomsOffsets ( ) {
2006-02-20 16:51:30 +00:00
if ( _game . features & GF_SMALL_HEADER ) {
2005-03-30 21:59:12 +00:00
_fileHandle - > seek ( 12 , SEEK_SET ) ; // Directly searching for the room offset block would be more generic...
2005-04-10 00:33:31 +00:00
} else {
_fileHandle - > seek ( 16 , SEEK_SET ) ;
2002-04-11 17:19:16 +00:00
}
2001-10-09 14:30:12 +00:00
2011-04-05 13:44:50 +02:00
int num = _fileHandle - > readByte ( ) ;
2002-12-24 02:18:02 +00:00
while ( num - - ) {
2011-04-05 13:44:50 +02:00
int room = _fileHandle - > readByte ( ) ;
int offset = _fileHandle - > readUint32LE ( ) ;
2011-05-13 14:48:01 +02:00
if ( _res - > _types [ rtRoom ] [ room ] . _roomoffs ! = RES_INVALID_OFFSET ) {
_res - > _types [ rtRoom ] [ room ] . _roomoffs = offset ;
2001-10-09 14:30:12 +00:00
}
}
}
2006-04-23 17:39:31 +00:00
bool ScummEngine : : openFile ( BaseScummFile & file , const Common : : String & filename , bool resourceFile ) {
2004-07-26 17:14:57 +00:00
bool result = false ;
2006-03-28 09:42:54 +00:00
if ( ! _containerFile . empty ( ) ) {
2004-07-26 23:15:01 +00:00
file . close ( ) ;
2006-04-14 01:48:51 +00:00
file . open ( _containerFile ) ;
2004-07-26 23:15:01 +00:00
assert ( file . isOpen ( ) ) ;
2005-06-19 18:16:27 +00:00
2006-04-23 17:33:16 +00:00
result = file . openSubFile ( filename ) ;
2004-07-26 17:14:57 +00:00
}
2005-07-30 21:11:48 +00:00
2004-07-26 17:14:57 +00:00
if ( ! result ) {
2004-07-26 23:15:01 +00:00
file . close ( ) ;
result = file . open ( filename ) ;
2001-10-09 14:30:12 +00:00
}
2005-07-30 21:11:48 +00:00
2004-07-26 17:14:57 +00:00
return result ;
2001-10-09 14:30:12 +00:00
}
2006-04-23 17:39:31 +00:00
bool ScummEngine : : openResourceFile ( const Common : : String & filename , byte encByte ) {
debugC ( DEBUG_GENERAL , " openResourceFile(%s) " , filename . c_str ( ) ) ;
2005-07-30 21:11:48 +00:00
2005-11-19 03:37:34 +00:00
if ( openFile ( * _fileHandle , filename , true ) ) {
2005-03-30 21:59:12 +00:00
_fileHandle - > setEnc ( encByte ) ;
2004-07-26 23:15:01 +00:00
return true ;
}
return false ;
}
2003-10-02 22:42:03 +00:00
void ScummEngine : : askForDisk ( const char * filename , int disknum ) {
2003-01-02 10:36:17 +00:00
char buf [ 128 ] ;
2006-02-20 16:51:30 +00:00
if ( _game . version = = 8 ) {
2008-05-06 03:00:26 +00:00
# ifdef ENABLE_SCUMM_7_8
2003-01-02 10:36:17 +00:00
char result ;
2004-04-26 07:47:12 +00:00
_imuseDigital - > stopAllSounds ( ) ;
2003-04-20 00:56:23 +00:00
2003-05-24 22:17:45 +00:00
# ifdef MACOSX
2022-10-23 15:26:54 +02:00
Common : : sprintf_s ( buf , " Cannot find file: '%s' \n Please insert disc %d. \n Press OK to retry, Quit to exit " , filename , disknum ) ;
2003-05-24 22:17:45 +00:00
# else
2022-10-23 15:26:54 +02:00
Common : : sprintf_s ( buf , " Cannot find file: '%s' \n Insert disc %d into drive %s \n Press OK to retry, Quit to exit " , filename , disknum , ConfMan . get ( " path " ) . c_str ( ) ) ;
2003-05-24 22:17:45 +00:00
# endif
2003-01-02 10:36:17 +00:00
2009-07-27 16:04:35 +00:00
result = displayMessage ( " Quit " , " %s " , buf ) ;
2004-08-11 21:49:58 +00:00
if ( ! result ) {
2004-04-08 11:48:35 +00:00
error ( " Cannot find file: '%s' " , filename ) ;
2004-04-04 12:36:50 +00:00
}
2005-05-14 23:28:22 +00:00
# endif
2005-07-30 21:11:48 +00:00
} else {
2022-10-23 15:26:54 +02:00
Common : : sprintf_s ( buf , " Cannot find file: '%s' " , filename ) ;
2020-07-04 03:13:05 +05:30
InfoDialog dialog ( this , Common : : U32String ( buf ) ) ;
2003-09-11 10:32:15 +00:00
runDialog ( dialog ) ;
2004-04-08 11:48:35 +00:00
error ( " Cannot find file: '%s' " , filename ) ;
2003-01-02 10:36:17 +00:00
}
2001-10-09 14:30:12 +00:00
}
2003-10-02 22:42:03 +00:00
void ScummEngine : : readIndexFile ( ) {
2002-04-11 17:19:16 +00:00
uint32 blocktype , itemsize ;
2001-10-09 14:30:12 +00:00
int numblock = 0 ;
2004-01-10 11:01:47 +00:00
debugC ( DEBUG_GENERAL , " readIndexFile() " ) ;
2001-10-09 14:30:12 +00:00
2002-12-22 21:58:16 +00:00
closeRoom ( ) ;
2001-10-09 14:30:12 +00:00
openRoom ( 0 ) ;
2006-02-20 16:51:30 +00:00
if ( _game . version < = 5 ) {
2005-04-10 00:33:31 +00:00
// Figure out the sizes of various resources
2009-07-20 20:55:28 +00:00
while ( true ) {
2006-02-25 02:12:58 +00:00
blocktype = _fileHandle - > readUint32BE ( ) ;
2005-03-30 21:59:12 +00:00
itemsize = _fileHandle - > readUint32BE ( ) ;
2009-07-20 20:55:28 +00:00
if ( _fileHandle - > eos ( ) | | _fileHandle - > err ( ) )
2001-11-26 19:57:57 +00:00
break ;
2002-04-11 17:19:16 +00:00
switch ( blocktype ) {
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' O ' , ' B ' , ' J ' ) :
2005-03-30 21:59:12 +00:00
_numGlobalObjects = _fileHandle - > readUint16LE ( ) ;
2002-04-11 17:19:16 +00:00
itemsize - = 2 ;
2001-11-26 19:57:57 +00:00
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' R ' , ' O ' , ' O ' ) :
2005-03-30 21:59:12 +00:00
_numRooms = _fileHandle - > readUint16LE ( ) ;
2002-04-11 17:19:16 +00:00
itemsize - = 2 ;
2001-11-26 19:57:57 +00:00
break ;
2001-10-09 14:30:12 +00:00
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' S ' , ' C ' , ' R ' ) :
2005-03-30 21:59:12 +00:00
_numScripts = _fileHandle - > readUint16LE ( ) ;
2002-04-11 17:19:16 +00:00
itemsize - = 2 ;
2001-11-26 19:57:57 +00:00
break ;
2001-10-09 14:30:12 +00:00
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' C ' , ' O ' , ' S ' ) :
2005-03-30 21:59:12 +00:00
_numCostumes = _fileHandle - > readUint16LE ( ) ;
2002-04-11 17:19:16 +00:00
itemsize - = 2 ;
2001-11-26 19:57:57 +00:00
break ;
2001-10-09 14:30:12 +00:00
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' S ' , ' O ' , ' U ' ) :
2005-03-30 21:59:12 +00:00
_numSounds = _fileHandle - > readUint16LE ( ) ;
2002-04-11 17:19:16 +00:00
itemsize - = 2 ;
2001-10-09 14:30:12 +00:00
break ;
2019-12-23 11:56:16 +00:00
default :
break ;
2001-10-09 14:30:12 +00:00
}
2005-03-30 21:59:12 +00:00
_fileHandle - > seek ( itemsize - 8 , SEEK_CUR ) ;
2001-10-09 14:30:12 +00:00
}
2005-03-30 21:59:12 +00:00
_fileHandle - > seek ( 0 , SEEK_SET ) ;
2001-10-09 14:30:12 +00:00
}
2005-10-19 19:26:53 +00:00
if ( checkTryMedia ( _fileHandle ) ) {
2021-11-13 23:40:38 +02:00
displayMessage ( nullptr , " You're trying to run game encrypted by ActiveMark. This is not supported. " ) ;
2008-07-11 01:22:37 +00:00
quitGame ( ) ;
2005-10-19 19:26:53 +00:00
return ;
}
2005-04-17 22:59:43 +00:00
while ( true ) {
2006-02-25 02:12:58 +00:00
blocktype = _fileHandle - > readUint32BE ( ) ;
2005-04-17 22:59:43 +00:00
itemsize = _fileHandle - > readUint32BE ( ) ;
2001-10-16 10:01:48 +00:00
2009-07-20 20:55:28 +00:00
if ( _fileHandle - > eos ( ) | | _fileHandle - > err ( ) )
2001-10-16 10:01:48 +00:00
break ;
numblock + + ;
2011-05-11 15:37:00 +02:00
debug ( 2 , " Reading index block of type '%s', size %d " , tag2str ( blocktype ) , itemsize ) ;
2005-04-17 22:59:43 +00:00
readIndexBlock ( blocktype , itemsize ) ;
2001-10-16 10:01:48 +00:00
}
2002-04-11 17:19:16 +00:00
// if (numblock!=9)
// error("Not enough blocks read from directory");
2001-10-16 10:01:48 +00:00
2002-12-22 21:58:16 +00:00
closeRoom ( ) ;
2001-10-16 10:01:48 +00:00
}
2005-10-19 19:26:53 +00:00
# define TRYMEDIA_MARK_LEN 6
bool checkTryMedia ( BaseScummFile * handle ) {
byte buf [ TRYMEDIA_MARK_LEN ] ;
2005-10-21 15:26:57 +00:00
bool matched = true ;
2007-09-19 08:40:12 +00:00
const byte magic [ 2 ] [ TRYMEDIA_MARK_LEN ] =
2005-10-19 19:26:53 +00:00
{ { 0x00 , ' T ' , ' M ' , ' S ' , ' A ' , ' M ' } ,
{ ' i ' , ' = ' , ' $ ' , ' : ' , ' ( ' , ' $ ' } } ; // Same but 0x69 xored
handle - > read ( buf , TRYMEDIA_MARK_LEN ) ;
for ( int i = 0 ; i < 2 ; i + + ) {
matched = true ;
for ( int j = 0 ; j < TRYMEDIA_MARK_LEN ; j + + )
2005-10-21 15:26:57 +00:00
if ( buf [ j ] ! = magic [ i ] [ j ] ) {
2005-10-19 19:26:53 +00:00
matched = false ;
2005-10-21 15:26:57 +00:00
break ;
}
2005-10-19 19:26:53 +00:00
if ( matched )
break ;
}
if ( matched )
return true ;
handle - > seek ( 0 , SEEK_SET ) ;
return false ;
}
2008-05-06 03:00:26 +00:00
# ifdef ENABLE_SCUMM_7_8
2005-04-20 23:33:35 +00:00
void ScummEngine_v7 : : readIndexBlock ( uint32 blocktype , uint32 itemsize ) {
int num ;
char * ptr ;
switch ( blocktype ) {
2011-04-12 16:53:15 +02:00
case MKTAG ( ' A ' , ' N ' , ' A ' , ' M ' ) : // Used by: The Dig, FT
2005-04-20 23:33:35 +00:00
num = _fileHandle - > readUint16LE ( ) ;
2012-02-15 09:53:31 -06:00
ptr = ( char * ) malloc ( num * 9 ) ;
2005-04-20 23:33:35 +00:00
_fileHandle - > read ( ptr , num * 9 ) ;
_imuseDigital - > setAudioNames ( num , ptr ) ;
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' R ' , ' S ' , ' C ' ) : // Used by: COMI
2007-02-24 19:29:40 +00:00
readResTypeList ( rtRoomScripts ) ;
2006-01-16 08:50:03 +00:00
break ;
2005-04-20 23:33:35 +00:00
default :
ScummEngine : : readIndexBlock ( blocktype , itemsize ) ;
}
}
2005-05-14 22:56:41 +00:00
# endif
2005-04-20 23:33:35 +00:00
2005-04-17 22:59:43 +00:00
void ScummEngine_v70he : : readIndexBlock ( uint32 blocktype , uint32 itemsize ) {
int i ;
switch ( blocktype ) {
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' I ' , ' R ' , ' I ' ) :
2007-02-24 19:29:40 +00:00
readResTypeList ( rtRoomImage ) ;
2006-01-16 08:50:03 +00:00
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' I ' , ' R ' , ' M ' ) :
2007-02-24 19:29:40 +00:00
readResTypeList ( rtImage ) ;
2006-01-16 08:50:03 +00:00
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' I ' , ' R ' , ' T ' ) :
2007-02-24 19:29:40 +00:00
readResTypeList ( rtTalkie ) ;
2006-01-16 08:50:03 +00:00
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' L ' , ' F ' , ' L ' ) :
2005-04-17 22:59:43 +00:00
i = _fileHandle - > readUint16LE ( ) ;
_fileHandle - > seek ( - 2 , SEEK_CUR ) ;
_heV7RoomOffsets = ( byte * ) calloc ( 2 + ( i * 4 ) , 1 ) ;
_fileHandle - > read ( _heV7RoomOffsets , ( 2 + ( i * 4 ) ) ) ;
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' I ' , ' S ' , ' K ' ) :
2005-04-17 22:59:43 +00:00
i = _fileHandle - > readUint16LE ( ) ;
_heV7DiskOffsets = ( byte * ) calloc ( i , 1 ) ;
_fileHandle - > read ( _heV7DiskOffsets , i ) ;
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' S ' , ' V ' , ' E ' , ' R ' ) :
2006-01-16 08:50:03 +00:00
// Index version number
2006-01-16 00:40:49 +00:00
_fileHandle - > seek ( itemsize - 8 , SEEK_CUR ) ;
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' I ' , ' N ' , ' I ' , ' B ' ) :
2006-01-16 00:40:49 +00:00
_fileHandle - > seek ( itemsize - 8 , SEEK_CUR ) ;
debug ( 2 , " INIB index block not yet handled, skipping " ) ;
break ;
2005-04-17 22:59:43 +00:00
default :
ScummEngine : : readIndexBlock ( blocktype , itemsize ) ;
}
}
void ScummEngine : : readIndexBlock ( uint32 blocktype , uint32 itemsize ) {
int i ;
switch ( blocktype ) {
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' C ' , ' H ' , ' R ' ) :
case MKTAG ( ' D ' , ' I ' , ' R ' , ' F ' ) :
2007-02-24 19:29:40 +00:00
readResTypeList ( rtCharset ) ;
2005-04-17 22:59:43 +00:00
break ;
2005-07-30 21:11:48 +00:00
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' O ' , ' B ' , ' J ' ) :
2005-04-17 22:59:43 +00:00
readGlobalObjects ( ) ;
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' R ' , ' N ' , ' A ' , ' M ' ) :
2005-04-17 22:59:43 +00:00
// Names of rooms. Maybe we should put them into a table, for use by the debugger?
2006-02-20 16:51:30 +00:00
if ( _game . heversion > = 80 ) {
2005-04-17 22:59:43 +00:00
for ( int room ; ( room = _fileHandle - > readUint16LE ( ) ) ; ) {
2005-10-22 10:48:14 +00:00
char buf [ 100 ] ;
2005-04-17 22:59:43 +00:00
i = 0 ;
2005-10-22 10:48:14 +00:00
for ( byte s ; ( s = _fileHandle - > readByte ( ) ) & & i < ARRAYSIZE ( buf ) - 1 ; ) {
2005-04-17 22:59:43 +00:00
buf [ i + + ] = s ;
}
buf [ i ] = 0 ;
debug ( 5 , " Room %d: '%s' " , room , buf ) ;
}
} else {
for ( int room ; ( room = _fileHandle - > readByte ( ) ) ; ) {
char buf [ 10 ] ;
_fileHandle - > read ( buf , 9 ) ;
buf [ 9 ] = 0 ;
for ( i = 0 ; i < 9 ; i + + )
buf [ i ] ^ = 0xFF ;
debug ( 5 , " Room %d: '%s' " , room , buf ) ;
}
}
break ;
2005-07-30 21:11:48 +00:00
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' R ' , ' O ' , ' O ' ) :
case MKTAG ( ' D ' , ' I ' , ' R ' , ' R ' ) :
2007-02-24 19:29:40 +00:00
readResTypeList ( rtRoom ) ;
2005-04-17 22:59:43 +00:00
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' S ' , ' C ' , ' R ' ) :
case MKTAG ( ' D ' , ' I ' , ' R ' , ' S ' ) :
2007-02-24 19:29:40 +00:00
readResTypeList ( rtScript ) ;
2005-04-17 22:59:43 +00:00
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' C ' , ' O ' , ' S ' ) :
case MKTAG ( ' D ' , ' I ' , ' R ' , ' C ' ) :
2007-02-24 19:29:40 +00:00
readResTypeList ( rtCostume ) ;
2005-04-17 22:59:43 +00:00
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' M ' , ' A ' , ' X ' , ' S ' ) :
2005-04-17 22:59:43 +00:00
readMAXS ( itemsize ) ;
allocateArrays ( ) ;
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' D ' , ' I ' , ' R ' , ' N ' ) :
case MKTAG ( ' D ' , ' S ' , ' O ' , ' U ' ) :
2007-02-24 19:29:40 +00:00
readResTypeList ( rtSound ) ;
2005-04-17 22:59:43 +00:00
break ;
2011-04-12 16:53:15 +02:00
case MKTAG ( ' A ' , ' A ' , ' R ' , ' Y ' ) :
2005-04-17 22:59:43 +00:00
readArrayFromIndexFile ( ) ;
break ;
default :
2009-05-31 10:02:16 +00:00
error ( " Bad ID %04X('%s') found in index file directory " , blocktype ,
2005-04-17 22:59:43 +00:00
tag2str ( blocktype ) ) ;
}
}
2003-10-02 22:42:03 +00:00
void ScummEngine : : readArrayFromIndexFile ( ) {
2004-01-19 20:27:31 +00:00
error ( " readArrayFromIndexFile() not supported in pre-V6 games " ) ;
2001-10-16 10:01:48 +00:00
}
2011-05-13 14:02:53 +02:00
int ScummEngine : : readResTypeList ( ResType type ) {
uint num ;
ResId idx ;
2002-04-11 17:19:16 +00:00
2006-02-20 16:51:30 +00:00
if ( _game . version = = 8 )
2005-03-30 21:59:12 +00:00
num = _fileHandle - > readUint32LE ( ) ;
2002-08-29 16:57:43 +00:00
else
2005-04-03 22:10:10 +00:00
num = _fileHandle - > readUint16LE ( ) ;
2001-10-09 14:30:12 +00:00
2011-05-13 14:48:01 +02:00
if ( num ! = _res - > _types [ type ] . size ( ) ) {
2011-05-13 14:04:59 +02:00
error ( " Invalid number of %ss (%d) in directory " , nameOfResType ( type ) , num ) ;
2001-10-09 14:30:12 +00:00
}
2002-02-12 21:28:07 +00:00
2011-05-13 14:04:59 +02:00
debug ( 2 , " readResTypeList(%s): %d entries " , nameOfResType ( type ) , num ) ;
2011-05-11 15:37:00 +02:00
2011-05-13 14:02:53 +02:00
for ( idx = 0 ; idx < num ; idx + + ) {
2011-05-13 14:48:01 +02:00
_res - > _types [ type ] [ idx ] . _roomno = _fileHandle - > readByte ( ) ;
2007-02-24 19:20:15 +00:00
}
2011-05-13 14:02:53 +02:00
for ( idx = 0 ; idx < num ; idx + + ) {
2011-05-13 14:48:01 +02:00
_res - > _types [ type ] [ idx ] . _roomoffs = _fileHandle - > readUint32LE ( ) ;
2007-02-24 19:20:15 +00:00
}
2004-06-06 02:20:53 +00:00
2011-04-05 13:44:50 +02:00
return num ;
}
2011-05-13 14:02:53 +02:00
int ScummEngine_v70he : : readResTypeList ( ResType type ) {
uint num ;
ResId idx ;
2011-04-05 13:44:50 +02:00
2011-05-13 14:02:53 +02:00
num = ScummEngine : : readResTypeList ( type ) ;
2011-04-05 13:44:50 +02:00
2011-05-13 14:02:53 +02:00
if ( type = = rtRoom )
for ( idx = 0 ; idx < num ; idx + + ) {
2011-05-13 14:48:01 +02:00
_heV7RoomIntOffsets [ idx ] = _res - > _types [ rtRoom ] [ idx ] . _roomoffs ;
2004-06-06 02:36:44 +00:00
}
2011-04-05 13:44:50 +02:00
2011-05-13 14:02:53 +02:00
for ( idx = 0 ; idx < num ; idx + + ) {
2011-05-13 14:40:45 +02:00
// The globsize is currently not being used
2011-05-13 14:48:01 +02:00
/*_res->_types[type][idx]._globsize =*/ _fileHandle - > readUint32LE ( ) ;
2002-11-28 23:47:58 +00:00
}
2011-04-05 13:44:50 +02:00
return num ;
2001-10-09 14:30:12 +00:00
}
2011-05-13 14:02:53 +02:00
void ResourceManager : : allocResTypeData ( ResType type , uint32 tag , int num , ResTypeMode mode ) {
2011-05-13 14:04:59 +02:00
debug ( 2 , " allocResTypeData(%s,%s,%d,%d) " , nameOfResType ( type ) , tag2str ( TO_BE_32 ( tag ) ) , num , mode ) ;
2011-05-13 14:02:53 +02:00
assert ( type > = 0 & & type < ( int ) ( ARRAYSIZE ( _types ) ) ) ;
2006-09-09 18:20:28 +00:00
2011-05-11 17:07:31 +02:00
if ( num > = 8000 )
2011-05-13 14:04:59 +02:00
error ( " Too many %s resources (%d) in directory " , nameOfResType ( type ) , num ) ;
2006-09-09 18:20:28 +00:00
2011-05-13 14:02:53 +02:00
_types [ type ] . _mode = mode ;
_types [ type ] . _tag = tag ;
2011-05-13 14:39:34 +02:00
// If there was data in there, let's clear it out completely. This is important
// in case we are restarting the game.
2011-05-13 14:48:01 +02:00
_types [ type ] . clear ( ) ;
_types [ type ] . resize ( num ) ;
2006-09-09 18:20:28 +00:00
2011-05-13 11:45:42 +02:00
/*
TODO : Use multiple Resource subclasses , one for each res mode ; then ,
given them serializability .
2011-05-11 17:07:31 +02:00
if ( mode ) {
2011-05-13 14:02:53 +02:00
_types [ type ] . roomno = ( byte * ) calloc ( num , sizeof ( byte ) ) ;
_types [ type ] . roomoffs = ( uint32 * ) calloc ( num , sizeof ( uint32 ) ) ;
2001-10-09 14:30:12 +00:00
}
2004-06-07 00:26:08 +00:00
2006-09-09 18:20:28 +00:00
if ( _vm - > _game . heversion > = 70 ) {
2011-05-13 14:02:53 +02:00
_types [ type ] . globsize = ( uint32 * ) calloc ( num , sizeof ( uint32 ) ) ;
2004-06-22 13:52:39 +00:00
}
2011-05-13 11:45:42 +02:00
*/
2001-10-09 14:30:12 +00:00
}
2003-10-02 22:42:03 +00:00
void ScummEngine : : loadCharset ( int no ) {
2002-04-13 04:39:04 +00:00
int i ;
2001-10-09 14:30:12 +00:00
byte * ptr ;
2002-04-11 17:19:16 +00:00
2004-01-10 11:01:47 +00:00
debugC ( DEBUG_GENERAL , " loadCharset(%d) " , no ) ;
2002-03-31 09:06:51 +00:00
2002-05-11 20:00:32 +00:00
/* FIXME - hack around crash in Indy4 (occurs if you try to load after dieing) */
2006-02-20 16:51:30 +00:00
if ( _game . id = = GID_INDY4 & & no = = 0 )
2002-05-11 20:00:32 +00:00
no = 1 ;
2002-04-11 15:22:02 +00:00
2004-06-26 05:08:02 +00:00
/* for Humongous catalogs */
2006-02-20 16:51:30 +00:00
if ( _game . heversion > = 70 & & _numCharsets = = 1 ) {
2005-08-14 01:41:52 +00:00
debug ( 0 , " Not loading charset as it doesn't seem to exist? " ) ;
2004-06-26 05:08:02 +00:00
return ;
}
2002-07-07 20:32:26 +00:00
assert ( no < ( int ) sizeof ( _charsetData ) / 16 ) ;
2006-09-16 13:38:43 +00:00
assertRange ( 1 , no , _numCharsets - 1 , " charset " ) ;
2002-04-11 17:19:16 +00:00
2002-08-29 20:01:27 +00:00
ptr = getResourceAddress ( rtCharset , no ) ;
2001-10-09 14:30:12 +00:00
2002-04-11 17:19:16 +00:00
for ( i = 0 ; i < 15 ; i + + ) {
_charsetData [ no ] [ i + 1 ] = ptr [ i + 14 ] ;
2001-10-09 14:30:12 +00:00
}
}
2003-10-02 22:42:03 +00:00
void ScummEngine : : nukeCharset ( int i ) {
2006-09-16 13:38:43 +00:00
assertRange ( 1 , i , _numCharsets - 1 , " charset " ) ;
2006-09-17 20:36:48 +00:00
_res - > nukeResource ( rtCharset , i ) ;
2001-10-09 14:30:12 +00:00
}
2011-05-13 14:02:53 +02:00
void ScummEngine : : ensureResourceLoaded ( ResType type , ResId idx ) {
2022-10-04 11:20:35 +02:00
Common : : StackLock lock ( _resourceAccessMutex ) ;
2011-05-13 14:04:59 +02:00
debugC ( DEBUG_RESOURCE , " ensureResourceLoaded(%s,%d) " , nameOfResType ( type ) , idx ) ;
2001-10-09 14:30:12 +00:00
2011-05-13 14:02:53 +02:00
if ( ( type = = rtRoom ) & & idx > 0x7F & & _game . version < 7 & & _game . heversion < = 71 ) {
idx = _resourceMapper [ idx & 0x7F ] ;
2001-10-09 14:30:12 +00:00
}
2011-05-13 14:02:53 +02:00
// FIXME: This check used to be "idx==0". However, that causes
2003-05-01 11:03:41 +00:00
// problems when using this function to ensure charset 0 is loaded.
2003-05-01 14:35:21 +00:00
// This is done for many games, e.g. Zak256 or Indy3 (EGA and VGA).
// For now we restrict the check to anything which is not a charset.
2003-05-01 11:40:21 +00:00
// Question: Why was this check like that in the first place?
2003-05-01 14:35:21 +00:00
// Answer: costumes with an index of zero in the newer games at least.
// TODO: determine why the heck anything would try to load a costume
// with id 0. Is that "normal", or is it caused by yet another bug in
// our code base? After all we also have to add special cases for many
2005-07-30 21:11:48 +00:00
// of our script opcodes that check for the (invalid) actor 0... so
2003-05-01 14:35:21 +00:00
// maybe both issues are related...
2011-05-13 14:02:53 +02:00
if ( type ! = rtCharset & & idx = = 0 )
2001-10-09 14:30:12 +00:00
return ;
2011-05-13 14:48:01 +02:00
if ( idx < = _res - > _types [ type ] . size ( ) & & _res - > _types [ type ] [ idx ] . _address )
2001-10-09 14:30:12 +00:00
return ;
2022-10-04 11:20:35 +02:00
# ifdef ENABLE_SCUMM_7_8
_resourceAccessMutex . unlock ( ) ;
if ( _imuseDigital ) {
2022-10-04 13:50:49 +02:00
int32 bufSize , criticalSize , freeSpace ;
int paused ;
2022-10-04 11:20:35 +02:00
if ( _imuseDigital - > isFTSoundEngine ( ) & & _imuseDigital - > queryNextSoundFile ( bufSize , criticalSize , freeSpace , paused ) ) {
_imuseDigital - > fillStreamsWhileMusicCritical ( 5 ) ;
} else {
_imuseDigital - > fillStreamsWhileMusicCritical ( _game . id = = GID_DIG ? 30 : 20 ) ;
}
}
_resourceAccessMutex . lock ( ) ;
# endif
2011-05-13 14:02:53 +02:00
loadResource ( type , idx ) ;
2001-10-09 14:30:12 +00:00
2011-05-13 14:02:53 +02:00
if ( _game . version = = 5 & & type = = rtRoom & & ( int ) idx = = _roomResource )
2005-03-02 11:57:11 +00:00
VAR ( VAR_ROOM_FLAG ) = 1 ;
2001-10-09 14:30:12 +00:00
}
2011-05-13 14:02:53 +02:00
int ScummEngine : : loadResource ( ResType type , ResId idx ) {
2002-11-28 23:47:58 +00:00
int roomNr ;
2007-04-01 15:58:34 +00:00
uint32 fileOffs ;
2001-10-09 14:30:12 +00:00
uint32 size , tag ;
2011-05-13 14:04:59 +02:00
debugC ( DEBUG_RESOURCE , " loadResource(%s,%d) " , nameOfResType ( type ) , idx ) ;
2002-04-11 17:19:16 +00:00
2006-02-20 16:51:30 +00:00
if ( type = = rtCharset & & ( _game . features & GF_SMALL_HEADER ) ) {
2002-04-11 17:19:16 +00:00
loadCharset ( idx ) ;
2007-12-24 14:20:53 +00:00
return 1 ;
2002-04-11 17:19:16 +00:00
}
2002-02-12 21:28:07 +00:00
2002-03-08 08:42:11 +00:00
roomNr = getResourceRoomNr ( type , idx ) ;
2002-05-08 06:57:27 +00:00
2011-05-13 14:48:01 +02:00
if ( idx > = _res - > _types [ type ] . size ( ) )
error ( " %s %d undefined %d %d " , nameOfResType ( type ) , idx , _res - > _types [ type ] . size ( ) , roomNr ) ;
2002-05-08 06:57:27 +00:00
if ( roomNr = = 0 )
roomNr = _roomResource ;
2001-10-09 14:30:12 +00:00
2011-04-05 13:44:50 +02:00
fileOffs = getResourceRoomOffset ( type , idx ) ;
if ( fileOffs = = RES_INVALID_OFFSET )
return 0 ;
2002-04-11 17:19:16 +00:00
2002-11-28 23:47:58 +00:00
openRoom ( roomNr ) ;
2002-04-11 17:19:16 +00:00
2005-03-30 21:59:12 +00:00
_fileHandle - > seek ( fileOffs + _fileOffset , SEEK_SET ) ;
2002-08-29 20:01:27 +00:00
2006-02-20 16:51:30 +00:00
if ( _game . features & GF_OLD_BUNDLE ) {
if ( ( _game . version = = 3 ) & & ! ( _game . platform = = Common : : kPlatformAmiga ) & & ( type = = rtSound ) ) {
2007-12-24 14:20:53 +00:00
return readSoundResourceSmallHeader ( idx ) ;
2003-08-12 16:09:41 +00:00
} else {
2020-05-05 14:04:36 +02:00
// WORKAROUND: Apple //gs MM has malformed sound resource #68
if ( _fileHandle - > pos ( ) + 2 > _fileHandle - > size ( ) ) {
warning ( " loadResource(%s,%d): resource is too short " , nameOfResType ( type ) , idx ) ;
size = 0 ;
} else {
size = _fileHandle - > readUint16LE ( ) ;
_fileHandle - > seek ( - 2 , SEEK_CUR ) ;
}
2003-08-12 16:09:41 +00:00
}
2006-02-20 16:51:30 +00:00
} else if ( _game . features & GF_SMALL_HEADER ) {
if ( _game . version = = 4 )
2005-03-30 21:59:12 +00:00
_fileHandle - > seek ( 8 , SEEK_CUR ) ;
size = _fileHandle - > readUint32LE ( ) ;
tag = _fileHandle - > readUint16LE ( ) ;
_fileHandle - > seek ( - 6 , SEEK_CUR ) ;
2006-02-20 16:51:30 +00:00
if ( ( type = = rtSound ) & & ! ( _game . platform = = Common : : kPlatformAmiga ) & & ! ( _game . platform = = Common : : kPlatformFMTowns ) ) {
2007-12-24 14:20:53 +00:00
return readSoundResourceSmallHeader ( idx ) ;
2003-05-03 12:48:18 +00:00
}
2002-11-28 23:47:58 +00:00
} else {
if ( type = = rtSound ) {
2007-12-24 14:20:53 +00:00
return readSoundResource ( idx ) ;
2002-11-28 23:47:58 +00:00
}
2011-05-11 18:19:05 +02:00
// Sanity check: Is this the right tag for this resource type?
2011-05-13 14:26:35 +02:00
//
// Currently disabled for newer HE games because they use different
// tags. For example, for rtRoom, 'ROOM' changed to 'RMDA'; and for
// rtImage, 'AWIZ' and 'MULT' can both occur simultaneously.
// On the long run, it would be preferable to not turn this check off,
// but instead to explicitly support the variations in the HE games.
2006-02-25 02:31:49 +00:00
tag = _fileHandle - > readUint32BE ( ) ;
2011-05-11 18:19:05 +02:00
if ( tag ! = _res - > _types [ type ] . _tag & & _game . heversion < 70 ) {
2011-05-13 14:26:35 +02:00
error ( " Unknown res tag '%s' encountered (expected '%s') "
" while trying to load res (%s,%d) in room %d at %d+%d in file %s " ,
tag2str ( tag ) , tag2str ( _res - > _types [ type ] . _tag ) ,
2011-05-13 14:04:59 +02:00
nameOfResType ( type ) , idx , roomNr ,
2008-09-30 09:12:02 +00:00
_fileOffset , fileOffs , _fileHandle - > getName ( ) ) ;
2002-08-29 20:01:27 +00:00
}
2005-03-30 21:59:12 +00:00
size = _fileHandle - > readUint32BE ( ) ;
_fileHandle - > seek ( - 8 , SEEK_CUR ) ;
2002-11-28 23:47:58 +00:00
}
2006-09-17 20:36:48 +00:00
_fileHandle - > read ( _res - > createResource ( type , idx , size ) , size ) ;
2002-11-28 23:47:58 +00:00
2020-04-16 13:43:53 +03:00
applyWorkaroundIfNeeded ( type , idx ) ;
2021-07-12 14:28:47 +02:00
// NB: The workaround may have changed the resource size, so don't rely on 'size' after this.
2002-12-31 02:09:57 +00:00
// dump the resource if requested
if ( _dumpScripts & & type = = rtScript ) {
2002-11-28 23:47:58 +00:00
dumpResource ( " script- " , idx , getResourceAddress ( rtScript , idx ) ) ;
}
2001-10-09 14:30:12 +00:00
2011-04-05 13:44:50 +02:00
if ( _fileHandle - > err ( ) | | _fileHandle - > eos ( ) ) {
error ( " Cannot read resource " ) ;
2002-08-29 20:01:27 +00:00
}
2011-04-05 13:44:50 +02:00
return 1 ;
2001-10-09 14:30:12 +00:00
}
2011-05-13 14:02:53 +02:00
int ScummEngine : : getResourceRoomNr ( ResType type , ResId idx ) {
2006-02-20 16:51:30 +00:00
if ( type = = rtRoom & & _game . heversion < 70 )
2002-03-08 08:42:11 +00:00
return idx ;
2011-05-13 14:48:01 +02:00
return _res - > _types [ type ] [ idx ] . _roomno ;
2001-10-09 14:30:12 +00:00
}
2011-05-13 14:02:53 +02:00
uint32 ScummEngine : : getResourceRoomOffset ( ResType type , ResId idx ) {
2011-04-05 13:44:50 +02:00
if ( type = = rtRoom ) {
return ( _game . version = = 8 ) ? 8 : 0 ;
}
2011-05-13 14:48:01 +02:00
return _res - > _types [ type ] [ idx ] . _roomoffs ;
2011-04-05 13:44:50 +02:00
}
2011-05-13 14:02:53 +02:00
uint32 ScummEngine_v70he : : getResourceRoomOffset ( ResType type , ResId idx ) {
2011-04-05 13:44:50 +02:00
if ( type = = rtRoom ) {
return _heV7RoomIntOffsets [ idx ] ;
}
2011-05-13 14:48:01 +02:00
return _res - > _types [ type ] [ idx ] . _roomoffs ;
2011-04-05 13:44:50 +02:00
}
2011-05-13 14:02:53 +02:00
int ScummEngine : : getResourceSize ( ResType type , ResId idx ) {
2021-11-15 15:05:18 -08:00
Common : : StackLock lock ( _resourceAccessMutex ) ;
2003-07-21 21:28:47 +00:00
byte * ptr = getResourceAddress ( type , idx ) ;
2007-02-23 02:35:01 +00:00
assert ( ptr ) ;
2011-05-13 14:48:01 +02:00
return _res - > _types [ type ] [ idx ] . _size ;
2003-07-21 21:28:47 +00:00
}
2011-05-13 14:02:53 +02:00
byte * ScummEngine : : getResourceAddress ( ResType type , ResId idx ) {
2021-11-15 15:05:18 -08:00
Common : : StackLock lock ( _resourceAccessMutex ) ;
2001-10-09 14:30:12 +00:00
byte * ptr ;
2002-07-07 20:32:26 +00:00
2006-02-20 16:51:30 +00:00
if ( _game . heversion > = 80 & & type = = rtString )
2005-10-14 12:36:46 +00:00
idx & = ~ 0x33539000 ;
2006-09-17 20:36:48 +00:00
if ( ! _res - > validateResource ( " getResourceAddress " , type , idx ) )
2021-11-13 23:40:38 +02:00
return nullptr ;
2002-09-25 03:04:28 +00:00
2011-05-11 17:07:31 +02:00
// If the resource is missing, but loadable from the game data files, try to do so.
2011-05-13 14:48:01 +02:00
if ( ! _res - > _types [ type ] [ idx ] . _address & & _res - > _types [ type ] . _mode ! = kDynamicResTypeMode ) {
2002-03-08 08:42:11 +00:00
ensureResourceLoaded ( type , idx ) ;
2002-04-11 17:19:16 +00:00
}
2001-10-09 14:30:12 +00:00
2011-05-13 14:48:01 +02:00
ptr = ( byte * ) _res - > _types [ type ] [ idx ] . _address ;
2011-05-11 17:07:31 +02:00
if ( ! ptr ) {
2011-05-13 14:04:59 +02:00
debugC ( DEBUG_RESOURCE , " getResourceAddress(%s,%d) == NULL " , nameOfResType ( type ) , idx ) ;
2021-11-13 23:40:38 +02:00
return nullptr ;
2002-04-26 14:13:39 +00:00
}
2001-10-09 14:30:12 +00:00
2006-09-17 20:36:48 +00:00
_res - > setResourceCounter ( type , idx , 1 ) ;
2001-11-14 18:40:39 +00:00
2016-01-07 14:52:11 +02:00
debugC ( DEBUG_RESOURCE , " getResourceAddress(%s,%d) == %p " , nameOfResType ( type ) , idx , ( void * ) ptr ) ;
2011-05-11 18:06:30 +02:00
return ptr ;
2001-10-09 14:30:12 +00:00
}
2011-05-13 14:02:53 +02:00
byte * ScummEngine : : getStringAddress ( ResId idx ) {
byte * addr = getResourceAddress ( rtString , idx ) ;
2005-04-09 20:22:31 +00:00
return addr ;
}
2011-05-13 14:02:53 +02:00
byte * ScummEngine_v6 : : getStringAddress ( ResId idx ) {
byte * addr = getResourceAddress ( rtString , idx ) ;
2021-11-13 23:40:38 +02:00
if ( addr = = nullptr )
return nullptr ;
2006-10-20 19:44:27 +00:00
// Skip over the ArrayHeader
return addr + 6 ;
2001-10-24 20:12:52 +00:00
}
2003-10-02 22:42:03 +00:00
byte * ScummEngine : : getStringAddressVar ( int i ) {
2004-11-27 13:10:41 +00:00
return getStringAddress ( _scummVars [ i ] ) ;
2002-04-21 19:38:00 +00:00
}
2006-09-09 18:20:28 +00:00
void ResourceManager : : increaseExpireCounter ( ) {
2011-05-12 15:03:58 +02:00
+ + _expireCounter ;
if ( _expireCounter = = 0 ) { // overflow?
increaseResourceCounters ( ) ;
2006-09-09 18:20:28 +00:00
}
}
2011-05-12 15:03:58 +02:00
void ResourceManager : : increaseResourceCounters ( ) {
2011-05-13 14:02:53 +02:00
for ( ResType type = rtFirst ; type < = rtLast ; type = ResType ( type + 1 ) ) {
2011-05-13 14:48:01 +02:00
ResId idx = _types [ type ] . size ( ) ;
2011-05-13 14:02:53 +02:00
while ( idx - - > 0 ) {
2011-05-13 14:48:01 +02:00
byte counter = _types [ type ] [ idx ] . getResourceCounter ( ) ;
2005-03-31 21:39:31 +00:00
if ( counter & & counter < RF_USAGE_MAX ) {
2011-05-13 14:02:53 +02:00
setResourceCounter ( type , idx , counter + 1 ) ;
2005-03-31 21:39:31 +00:00
}
}
}
}
2011-05-13 14:02:53 +02:00
void ResourceManager : : setResourceCounter ( ResType type , ResId idx , byte counter ) {
2011-05-13 14:48:01 +02:00
_types [ type ] [ idx ] . setResourceCounter ( counter ) ;
2011-05-13 11:45:42 +02:00
}
void ResourceManager : : Resource : : setResourceCounter ( byte counter ) {
_flags & = RF_LOCK ; // Clear lower 7 bits, preserve the lock bit.
_flags | = counter ; // Update the usage counter
}
byte ResourceManager : : Resource : : getResourceCounter ( ) const {
return _flags & RF_USAGE ;
2001-10-09 14:30:12 +00:00
}
2001-11-05 19:21:49 +00:00
/* 2 bytes safety area to make "precaching" of bytes in the gdi drawer easier */
# define SAFETY_AREA 2
2011-05-13 14:02:53 +02:00
byte * ResourceManager : : createResource ( ResType type , ResId idx , uint32 size ) {
2011-05-13 14:04:59 +02:00
debugC ( DEBUG_RESOURCE , " _res->createResource(%s,%d,%d) " , nameOfResType ( type ) , idx , size ) ;
2001-10-09 14:30:12 +00:00
2005-04-06 17:31:35 +00:00
if ( ! validateResource ( " allocating " , type , idx ) )
2021-11-13 23:40:38 +02:00
return nullptr ;
2005-08-14 20:46:02 +00:00
2006-02-20 16:51:30 +00:00
if ( _vm - > _game . version < = 2 ) {
2005-08-14 20:46:02 +00:00
// Nuking and reloading a resource can be harmful in some
// cases. For instance, Zak tries to reload the intro music
2021-02-28 01:28:23 -08:00
// while it's playing. See bug #2115.
2005-08-14 20:46:02 +00:00
2011-05-13 14:48:01 +02:00
if ( _types [ type ] [ idx ] . _address & & ( type = = rtSound | | type = = rtScript | | type = = rtCostume ) )
return _types [ type ] [ idx ] . _address ;
2005-08-14 20:46:02 +00:00
}
2005-04-06 17:31:35 +00:00
nukeResource ( type , idx ) ;
2001-10-10 10:02:33 +00:00
2001-11-05 19:21:49 +00:00
expireResources ( size ) ;
2021-11-02 08:48:22 +02:00
byte * ptr = new byte [ size + SAFETY_AREA ] ( ) ;
2021-11-13 23:40:38 +02:00
if ( ptr = = nullptr ) {
2011-05-13 14:04:59 +02:00
error ( " createResource(%s,%d): Out of memory while allocating %d " , nameOfResType ( type ) , idx , size ) ;
2001-10-09 14:30:12 +00:00
}
2005-04-06 17:31:35 +00:00
_allocatedSize + = size ;
2001-10-09 14:30:12 +00:00
2011-05-13 14:48:01 +02:00
_types [ type ] [ idx ] . _address = ptr ;
_types [ type ] [ idx ] . _size = size ;
2005-04-06 17:31:35 +00:00
setResourceCounter ( type , idx , 1 ) ;
2011-05-11 18:06:30 +02:00
return ptr ;
2001-10-09 14:30:12 +00:00
}
2011-05-13 11:45:42 +02:00
ResourceManager : : Resource : : Resource ( ) {
2021-11-13 23:40:38 +02:00
_address = nullptr ;
2011-05-13 11:45:42 +02:00
_size = 0 ;
_flags = 0 ;
_status = 0 ;
_roomno = 0 ;
_roomoffs = 0 ;
}
ResourceManager : : Resource : : ~ Resource ( ) {
2011-06-14 23:55:57 +02:00
delete [ ] _address ;
2021-11-13 23:40:38 +02:00
_address = nullptr ;
2011-05-13 11:45:42 +02:00
}
void ResourceManager : : Resource : : nuke ( ) {
2011-06-14 23:55:57 +02:00
delete [ ] _address ;
2021-11-13 23:40:38 +02:00
_address = nullptr ;
2011-05-13 11:45:42 +02:00
_size = 0 ;
_flags = 0 ;
_status & = ~ RS_MODIFIED ;
}
2011-05-11 17:19:26 +02:00
ResourceManager : : ResTypeData : : ResTypeData ( ) {
2011-05-13 11:45:42 +02:00
_mode = kDynamicResTypeMode ;
_tag = 0 ;
2011-05-11 17:19:26 +02:00
}
ResourceManager : : ResTypeData : : ~ ResTypeData ( ) {
}
ResourceManager : : ResourceManager ( ScummEngine * vm ) : _vm ( vm ) {
_allocatedSize = 0 ;
_maxHeapThreshold = 0 ;
_minHeapThreshold = 0 ;
_expireCounter = 0 ;
2005-03-31 21:39:31 +00:00
}
2006-09-09 18:20:28 +00:00
ResourceManager : : ~ ResourceManager ( ) {
freeResources ( ) ;
}
void ResourceManager : : setHeapThreshold ( int min , int max ) {
assert ( 0 < max ) ;
assert ( min < = max ) ;
_maxHeapThreshold = max ;
_minHeapThreshold = min ;
}
2011-05-13 14:02:53 +02:00
bool ResourceManager : : validateResource ( const char * str , ResType type , ResId idx ) const {
2011-05-13 14:48:01 +02:00
if ( type < rtFirst | | type > rtLast | | ( uint ) idx > = ( uint ) _types [ type ] . size ( ) ) {
2018-05-17 12:17:09 +02:00
warning ( " %s Illegal Glob type %s (%d) num %d " , str , nameOfResType ( type ) , type , idx ) ;
2002-09-25 03:04:28 +00:00
return false ;
2001-10-09 14:30:12 +00:00
}
2002-09-25 03:04:28 +00:00
return true ;
2001-10-09 14:30:12 +00:00
}
2011-05-13 14:02:53 +02:00
void ResourceManager : : nukeResource ( ResType type , ResId idx ) {
2011-05-13 14:48:01 +02:00
byte * ptr = _types [ type ] [ idx ] . _address ;
2021-11-13 23:40:38 +02:00
if ( ptr ! = nullptr ) {
2011-05-13 14:04:59 +02:00
debugC ( DEBUG_RESOURCE , " nukeResource(%s,%d) " , nameOfResType ( type ) , idx ) ;
2011-05-13 14:48:01 +02:00
_allocatedSize - = _types [ type ] [ idx ] . _size ;
_types [ type ] [ idx ] . nuke ( ) ;
2001-10-09 14:30:12 +00:00
}
}
2003-10-02 22:42:03 +00:00
const byte * ScummEngine : : findResourceData ( uint32 tag , const byte * ptr ) {
2006-02-20 16:51:30 +00:00
if ( _game . features & GF_OLD_BUNDLE )
2003-04-28 13:16:37 +00:00
error ( " findResourceData must not be used in GF_OLD_BUNDLE games " ) ;
2006-02-20 16:51:30 +00:00
else if ( _game . features & GF_SMALL_HEADER )
2003-06-26 00:47:39 +00:00
ptr = findResourceSmall ( tag , ptr ) ;
2002-08-29 20:01:27 +00:00
else
2003-06-26 00:47:39 +00:00
ptr = findResource ( tag , ptr ) ;
2002-02-12 21:28:07 +00:00
2021-11-13 23:40:38 +02:00
if ( ptr = = nullptr )
return nullptr ;
2002-08-29 20:01:27 +00:00
return ptr + _resourceHeaderSize ;
2001-11-26 19:57:57 +00:00
}
2003-10-02 22:42:03 +00:00
int ScummEngine : : getResourceDataSize ( const byte * ptr ) const {
2021-11-13 23:40:38 +02:00
if ( ptr = = nullptr )
2001-12-27 17:51:58 +00:00
return 0 ;
2002-02-12 21:28:07 +00:00
2006-02-20 16:51:30 +00:00
if ( _game . features & GF_OLD_BUNDLE )
2005-03-31 23:11:39 +00:00
return READ_LE_UINT16 ( ptr ) - _resourceHeaderSize ;
2006-02-20 16:51:30 +00:00
else if ( _game . features & GF_SMALL_HEADER )
2005-03-31 23:11:39 +00:00
return READ_LE_UINT32 ( ptr ) - _resourceHeaderSize ;
2002-04-11 17:19:16 +00:00
else
2005-03-31 23:11:39 +00:00
return READ_BE_UINT32 ( ptr - 4 ) - _resourceHeaderSize ;
2001-12-27 17:51:58 +00:00
}
2011-05-13 14:02:53 +02:00
void ResourceManager : : lock ( ResType type , ResId idx ) {
if ( ! validateResource ( " Locking " , type , idx ) )
2002-09-25 03:04:28 +00:00
return ;
2011-05-13 14:48:01 +02:00
_types [ type ] [ idx ] . lock ( ) ;
2001-10-09 14:30:12 +00:00
}
2011-05-13 14:02:53 +02:00
void ResourceManager : : unlock ( ResType type , ResId idx ) {
if ( ! validateResource ( " Unlocking " , type , idx ) )
2002-09-25 03:04:28 +00:00
return ;
2011-05-13 14:48:01 +02:00
_types [ type ] [ idx ] . unlock ( ) ;
2001-11-05 19:21:49 +00:00
}
2011-05-13 14:02:53 +02:00
bool ResourceManager : : isLocked ( ResType type , ResId idx ) const {
if ( ! validateResource ( " isLocked " , type , idx ) )
2005-10-17 15:00:23 +00:00
return false ;
2011-05-13 14:48:01 +02:00
return _types [ type ] [ idx ] . isLocked ( ) ;
2011-05-13 11:45:42 +02:00
}
void ResourceManager : : Resource : : lock ( ) {
_flags | = RF_LOCK ;
}
void ResourceManager : : Resource : : unlock ( ) {
_flags & = ~ RF_LOCK ;
}
bool ResourceManager : : Resource : : isLocked ( ) const {
return ( _flags & RF_LOCK ) ! = 0 ;
2005-10-17 15:00:23 +00:00
}
2011-05-13 14:02:53 +02:00
bool ScummEngine : : isResourceInUse ( ResType type , ResId idx ) const {
if ( ! _res - > validateResource ( " isResourceInUse " , type , idx ) )
2002-09-25 03:04:28 +00:00
return false ;
2002-04-11 17:19:16 +00:00
switch ( type ) {
2001-11-05 19:21:49 +00:00
case rtRoom :
2011-05-13 14:02:53 +02:00
return _roomResource = = ( byte ) idx ;
2005-05-23 03:44:10 +00:00
case rtRoomImage :
2011-05-13 14:02:53 +02:00
return _roomResource = = ( byte ) idx ;
2002-12-27 19:48:30 +00:00
case rtRoomScripts :
2011-05-13 14:02:53 +02:00
return _roomResource = = ( byte ) idx ;
2001-11-05 19:21:49 +00:00
case rtScript :
2011-05-13 14:02:53 +02:00
return isScriptInUse ( idx ) ;
2001-11-05 19:21:49 +00:00
case rtCostume :
2011-05-13 14:02:53 +02:00
return isCostumeInUse ( idx ) ;
2001-11-05 19:21:49 +00:00
case rtSound :
2009-06-05 00:21:10 +00:00
// Sound resource 1 is used for queued speech
2011-05-13 14:02:53 +02:00
if ( _game . heversion > = 60 & & idx = = 1 )
2009-06-05 00:21:10 +00:00
return true ;
else
2011-05-13 14:02:53 +02:00
return _sound - > isSoundInUse ( idx ) ;
2005-05-13 10:23:56 +00:00
case rtCharset :
2011-05-13 14:02:53 +02:00
return _charset - > getCurID ( ) = = ( int ) idx ;
2006-01-27 00:27:57 +00:00
case rtImage :
2011-05-13 14:02:53 +02:00
return _res - > isModified ( type , idx ) ! = 0 ;
2006-01-05 07:06:47 +00:00
case rtSpoolBuffer :
2011-05-13 14:02:53 +02:00
return _sound - > isSoundRunning ( 10000 + idx ) ! = 0 ;
2001-11-05 19:21:49 +00:00
default :
return false ;
}
}
2011-05-13 14:02:53 +02:00
void ResourceManager : : setModified ( ResType type , ResId idx ) {
if ( ! validateResource ( " Modified " , type , idx ) )
2006-01-27 00:27:57 +00:00
return ;
2011-05-13 14:48:01 +02:00
_types [ type ] [ idx ] . setModified ( ) ;
2006-01-27 00:27:57 +00:00
}
2011-08-28 11:39:05 +10:00
void ResourceManager : : setOffHeap ( ResType type , ResId idx ) {
if ( ! validateResource ( " setOffHeap " , type , idx ) )
return ;
_types [ type ] [ idx ] . setOffHeap ( ) ;
}
void ResourceManager : : setOnHeap ( ResType type , ResId idx ) {
if ( ! validateResource ( " setOnHeap " , type , idx ) )
return ;
_types [ type ] [ idx ] . setOnHeap ( ) ;
}
2011-05-13 14:02:53 +02:00
bool ResourceManager : : isModified ( ResType type , ResId idx ) const {
if ( ! validateResource ( " isModified " , type , idx ) )
2006-01-27 00:27:57 +00:00
return false ;
2011-05-13 14:48:01 +02:00
return _types [ type ] [ idx ] . isModified ( ) ;
2011-05-13 11:45:42 +02:00
}
2011-08-28 11:39:05 +10:00
bool ResourceManager : : Resource : : isModified ( ) const {
return ( _status & RS_MODIFIED ) ! = 0 ;
}
bool ResourceManager : : Resource : : isOffHeap ( ) const {
return ( _status & RF_OFFHEAP ) ! = 0 ;
}
2011-05-13 11:45:42 +02:00
void ResourceManager : : Resource : : setModified ( ) {
_status | = RS_MODIFIED ;
}
2011-08-28 11:39:05 +10:00
void ResourceManager : : Resource : : setOffHeap ( ) {
_status | = RF_OFFHEAP ;
}
void ResourceManager : : Resource : : setOnHeap ( ) {
_status & = ~ RF_OFFHEAP ;
2006-01-27 00:27:57 +00:00
}
2005-04-06 17:31:35 +00:00
void ResourceManager : : expireResources ( uint32 size ) {
2001-11-05 19:21:49 +00:00
byte best_counter ;
2011-05-13 14:02:53 +02:00
ResType best_type ;
int best_res = 0 ;
2001-11-06 21:29:23 +00:00
uint32 oldAllocatedSize ;
2001-11-05 19:21:49 +00:00
2005-04-06 17:31:35 +00:00
if ( _expireCounter ! = 0xFF ) {
_expireCounter = 0xFF ;
2011-05-12 15:03:58 +02:00
increaseResourceCounters ( ) ;
2001-11-05 19:21:49 +00:00
}
2005-04-06 17:31:35 +00:00
if ( size + _allocatedSize < _maxHeapThreshold )
2001-11-05 19:21:49 +00:00
return ;
2005-04-06 17:31:35 +00:00
oldAllocatedSize = _allocatedSize ;
2001-11-06 21:29:23 +00:00
2001-11-05 19:21:49 +00:00
do {
2011-05-13 14:02:53 +02:00
best_type = rtInvalid ;
2001-11-05 19:21:49 +00:00
best_counter = 2 ;
2011-05-13 14:02:53 +02:00
for ( ResType type = rtFirst ; type < = rtLast ; type = ResType ( type + 1 ) ) {
if ( _types [ type ] . _mode ! = kDynamicResTypeMode ) {
2011-05-11 17:07:31 +02:00
// Resources of this type can be reloaded from the data files,
// so we can potentially unload them to free memory.
2011-05-13 14:48:01 +02:00
ResId idx = _types [ type ] . size ( ) ;
2011-05-13 14:02:53 +02:00
while ( idx - - > 0 ) {
2011-05-13 14:48:01 +02:00
Resource & tmp = _types [ type ] [ idx ] ;
2011-05-13 11:45:42 +02:00
byte counter = tmp . getResourceCounter ( ) ;
2011-08-28 10:40:23 +10:00
if ( ! tmp . isLocked ( ) & & counter > = best_counter & & tmp . _address & & ! _vm - > isResourceInUse ( type , idx ) & & ! tmp . isOffHeap ( ) ) {
2011-05-13 11:45:42 +02:00
best_counter = counter ;
2011-05-13 14:02:53 +02:00
best_type = type ;
best_res = idx ;
2001-11-05 19:21:49 +00:00
}
}
}
2011-05-13 14:02:53 +02:00
}
2001-11-05 19:21:49 +00:00
if ( ! best_type )
break ;
2005-04-06 17:31:35 +00:00
nukeResource ( best_type , best_res ) ;
} while ( size + _allocatedSize > _minHeapThreshold ) ;
2001-11-06 21:29:23 +00:00
2011-05-12 15:03:58 +02:00
increaseResourceCounters ( ) ;
2001-11-07 18:10:52 +00:00
2005-04-06 17:31:35 +00:00
debugC ( DEBUG_RESOURCE , " Expired resources, mem %d -> %d " , oldAllocatedSize , _allocatedSize ) ;
2001-11-05 19:21:49 +00:00
}
2005-03-31 21:39:31 +00:00
void ResourceManager : : freeResources ( ) {
2011-05-13 14:02:53 +02:00
for ( ResType type = rtFirst ; type < = rtLast ; type = ResType ( type + 1 ) ) {
2011-05-13 14:48:01 +02:00
ResId idx = _types [ type ] . size ( ) ;
2011-05-13 14:02:53 +02:00
while ( idx - - > 0 ) {
if ( isResourceLoaded ( type , idx ) )
nukeResource ( type , idx ) ;
2001-11-05 19:21:49 +00:00
}
2011-05-13 14:48:01 +02:00
_types [ type ] . clear ( ) ;
2004-06-22 13:52:39 +00:00
}
2001-10-09 14:30:12 +00:00
}
2011-05-13 14:02:53 +02:00
void ScummEngine : : loadPtrToResource ( ResType type , ResId idx , const byte * source ) {
2002-02-24 17:25:03 +00:00
byte * alloced ;
2010-10-18 18:55:24 +00:00
int len ;
2001-10-09 14:30:12 +00:00
2020-11-15 15:45:07 +09:00
bool sourceWasNull = ! source ;
int originalLen ;
2011-05-13 14:02:53 +02:00
_res - > nukeResource ( type , idx ) ;
2001-10-09 14:30:12 +00:00
2002-12-28 01:57:19 +00:00
len = resStrLen ( source ) + 1 ;
2001-11-09 22:45:19 +00:00
if ( len < = 0 )
2001-10-09 14:30:12 +00:00
return ;
2020-11-15 15:45:07 +09:00
originalLen = len ;
// Translate resource text
byte translateBuffer [ 512 ] ;
if ( isScummvmKorTarget ( ) ) {
if ( ! source ) {
refreshScriptPointer ( ) ;
source = _scriptPointer ;
}
2022-08-19 15:11:52 +02:00
translateText ( source , translateBuffer , sizeof ( translateBuffer ) ) ;
2020-11-15 15:45:07 +09:00
source = translateBuffer ;
len = resStrLen ( source ) + 1 ;
}
2011-05-13 14:02:53 +02:00
alloced = _res - > createResource ( type , idx , len ) ;
2001-10-09 14:30:12 +00:00
if ( ! source ) {
2010-10-18 18:55:24 +00:00
// Need to refresh the script pointer, since createResource may
// have caused the script resource to expire.
refreshScriptPointer ( ) ;
2020-11-15 15:45:07 +09:00
memcpy ( alloced , _scriptPointer , originalLen ) ;
_scriptPointer + = originalLen ;
} else if ( sourceWasNull ) {
refreshScriptPointer ( ) ;
memcpy ( alloced , source , len ) ;
_scriptPointer + = originalLen ;
2001-10-09 14:30:12 +00:00
} else {
2010-10-18 18:55:24 +00:00
memcpy ( alloced , source , len ) ;
2001-10-09 14:30:12 +00:00
}
}
2011-05-13 14:02:53 +02:00
bool ResourceManager : : isResourceLoaded ( ResType type , ResId idx ) const {
2004-07-31 23:20:37 +00:00
if ( ! validateResource ( " isResourceLoaded " , type , idx ) )
2002-09-25 03:04:28 +00:00
return false ;
2021-11-13 23:40:38 +02:00
return _types [ type ] [ idx ] . _address ! = nullptr ;
2001-11-05 19:21:49 +00:00
}
2005-04-06 17:31:35 +00:00
void ResourceManager : : resourceStats ( ) {
2001-11-07 18:10:52 +00:00
uint32 lockedSize = 0 , lockedNum = 0 ;
2002-04-11 17:19:16 +00:00
2011-05-13 14:02:53 +02:00
for ( ResType type = rtFirst ; type < = rtLast ; type = ResType ( type + 1 ) ) {
2011-05-13 14:48:01 +02:00
ResId idx = _types [ type ] . size ( ) ;
2011-05-13 14:02:53 +02:00
while ( idx - - > 0 ) {
2011-05-13 14:48:01 +02:00
Resource & tmp = _types [ type ] [ idx ] ;
2011-05-13 11:45:42 +02:00
if ( tmp . isLocked ( ) & & tmp . _address ) {
lockedSize + = tmp . _size ;
2001-11-07 18:10:52 +00:00
lockedNum + + ;
}
}
2011-05-13 14:02:53 +02:00
}
2002-04-11 17:19:16 +00:00
2005-04-06 17:31:35 +00:00
debug ( 1 , " Total allocated size=%d, locked=%d(%d) " , _allocatedSize , lockedSize , lockedNum ) ;
2001-11-05 19:21:49 +00:00
}
2005-04-09 20:26:51 +00:00
void ScummEngine_v5 : : readMAXS ( int blockSize ) {
2005-04-03 23:53:34 +00:00
_numVariables = _fileHandle - > readUint16LE ( ) ; // 800
_fileHandle - > readUint16LE ( ) ; // 16
_numBitVariables = _fileHandle - > readUint16LE ( ) ; // 2048
_numLocalObjects = _fileHandle - > readUint16LE ( ) ; // 200
_numArray = 50 ;
_numVerbs = 100 ;
// Used to be 50, which wasn't enough for MI2 and FOA. See bugs
2021-06-07 17:44:05 -06:00
// #1591, #1600 and #1607.
2005-04-03 23:53:34 +00:00
_numNewNames = 150 ;
2021-11-13 23:40:38 +02:00
_objectRoomTable = nullptr ;
2004-06-06 02:20:53 +00:00
2005-04-03 23:53:34 +00:00
_fileHandle - > readUint16LE ( ) ; // 50
_numCharsets = _fileHandle - > readUint16LE ( ) ; // 9
_fileHandle - > readUint16LE ( ) ; // 100
_fileHandle - > readUint16LE ( ) ; // 50
_numInventory = _fileHandle - > readUint16LE ( ) ; // 80
_numGlobalScripts = 200 ;
_shadowPaletteSize = 256 ;
_numFlObject = 50 ;
2001-10-16 10:01:48 +00:00
2001-12-27 17:51:58 +00:00
if ( _shadowPaletteSize )
2002-05-01 17:16:47 +00:00
_shadowPalette = ( byte * ) calloc ( _shadowPaletteSize , 1 ) ;
2001-10-16 10:01:48 +00:00
}
2008-05-06 03:00:26 +00:00
# ifdef ENABLE_SCUMM_7_8
2005-04-09 20:22:31 +00:00
void ScummEngine_v8 : : readMAXS ( int blockSize ) {
2022-06-21 18:08:22 +02:00
_fileHandle - > read ( _engineVersionString , 50 ) ;
_fileHandle - > read ( _dataFileVersionString , 50 ) ;
2005-04-09 20:22:31 +00:00
_numVariables = _fileHandle - > readUint32LE ( ) ; // 1500
_numBitVariables = _fileHandle - > readUint32LE ( ) ; // 2048
_fileHandle - > readUint32LE ( ) ; // 40
_numScripts = _fileHandle - > readUint32LE ( ) ; // 458
_numSounds = _fileHandle - > readUint32LE ( ) ; // 789
_numCharsets = _fileHandle - > readUint32LE ( ) ; // 1
_numCostumes = _fileHandle - > readUint32LE ( ) ; // 446
_numRooms = _fileHandle - > readUint32LE ( ) ; // 95
_fileHandle - > readUint32LE ( ) ; // 80
_numGlobalObjects = _fileHandle - > readUint32LE ( ) ; // 1401
_fileHandle - > readUint32LE ( ) ; // 60
_numLocalObjects = _fileHandle - > readUint32LE ( ) ; // 200
_numNewNames = _fileHandle - > readUint32LE ( ) ; // 100
_numFlObject = _fileHandle - > readUint32LE ( ) ; // 128
_numInventory = _fileHandle - > readUint32LE ( ) ; // 80
_numArray = _fileHandle - > readUint32LE ( ) ; // 200
_numVerbs = _fileHandle - > readUint32LE ( ) ; // 50
_objectRoomTable = ( byte * ) calloc ( _numGlobalObjects , 1 ) ;
_numGlobalScripts = 2000 ;
_shadowPaletteSize = NUM_SHADOW_PALETTE * 256 ;
_shadowPalette = ( byte * ) calloc ( _shadowPaletteSize , 1 ) ;
}
void ScummEngine_v7 : : readMAXS ( int blockSize ) {
2022-06-21 18:08:22 +02:00
_fileHandle - > read ( _engineVersionString , 50 ) ;
_fileHandle - > read ( _dataFileVersionString , 50 ) ;
2005-04-09 20:22:31 +00:00
_numVariables = _fileHandle - > readUint16LE ( ) ;
_numBitVariables = _fileHandle - > readUint16LE ( ) ;
_fileHandle - > readUint16LE ( ) ;
_numGlobalObjects = _fileHandle - > readUint16LE ( ) ;
_numLocalObjects = _fileHandle - > readUint16LE ( ) ;
_numNewNames = _fileHandle - > readUint16LE ( ) ;
_numVerbs = _fileHandle - > readUint16LE ( ) ;
_numFlObject = _fileHandle - > readUint16LE ( ) ;
_numInventory = _fileHandle - > readUint16LE ( ) ;
_numArray = _fileHandle - > readUint16LE ( ) ;
_numRooms = _fileHandle - > readUint16LE ( ) ;
_numScripts = _fileHandle - > readUint16LE ( ) ;
_numSounds = _fileHandle - > readUint16LE ( ) ;
_numCharsets = _fileHandle - > readUint16LE ( ) ;
_numCostumes = _fileHandle - > readUint16LE ( ) ;
_objectRoomTable = ( byte * ) calloc ( _numGlobalObjects , 1 ) ;
2006-02-20 16:51:30 +00:00
if ( ( _game . id = = GID_FT ) & & ( _game . features & GF_DEMO ) & &
2013-05-02 18:26:58 -04:00
( _game . platform = = Common : : kPlatformDOS ) )
2005-04-09 20:22:31 +00:00
_numGlobalScripts = 300 ;
else
_numGlobalScripts = 2000 ;
_shadowPaletteSize = NUM_SHADOW_PALETTE * 256 ;
_shadowPalette = ( byte * ) calloc ( _shadowPaletteSize , 1 ) ;
}
2005-05-14 22:56:41 +00:00
# endif
2005-04-09 20:22:31 +00:00
void ScummEngine_v6 : : readMAXS ( int blockSize ) {
2006-02-15 23:04:48 +00:00
if ( blockSize = = 38 ) {
_numVariables = _fileHandle - > readUint16LE ( ) ;
_fileHandle - > readUint16LE ( ) ;
_numBitVariables = _fileHandle - > readUint16LE ( ) ;
_numLocalObjects = _fileHandle - > readUint16LE ( ) ;
_numArray = _fileHandle - > readUint16LE ( ) ;
_fileHandle - > readUint16LE ( ) ;
_numVerbs = _fileHandle - > readUint16LE ( ) ;
_numFlObject = _fileHandle - > readUint16LE ( ) ;
_numInventory = _fileHandle - > readUint16LE ( ) ;
_numRooms = _fileHandle - > readUint16LE ( ) ;
_numScripts = _fileHandle - > readUint16LE ( ) ;
_numSounds = _fileHandle - > readUint16LE ( ) ;
_numCharsets = _fileHandle - > readUint16LE ( ) ;
_numCostumes = _fileHandle - > readUint16LE ( ) ;
_numGlobalObjects = _fileHandle - > readUint16LE ( ) ;
_numNewNames = 50 ;
2021-11-13 23:40:38 +02:00
_objectRoomTable = nullptr ;
2006-02-15 23:04:48 +00:00
_numGlobalScripts = 200 ;
2005-04-09 20:22:31 +00:00
2006-02-20 16:51:30 +00:00
if ( _game . heversion > = 70 ) {
2006-02-15 23:04:48 +00:00
_objectRoomTable = ( byte * ) calloc ( _numGlobalObjects , 1 ) ;
}
2005-04-09 20:22:31 +00:00
2006-02-20 16:51:30 +00:00
if ( _game . heversion < = 70 ) {
2006-02-15 23:04:48 +00:00
_shadowPaletteSize = 256 ;
_shadowPalette = ( byte * ) calloc ( _shadowPaletteSize , 1 ) ;
}
2006-04-23 22:39:55 +00:00
} else
error ( " readMAXS(%d) failed to read MAXS data " , blockSize ) ;
2005-04-09 20:22:31 +00:00
}
2005-04-03 23:53:34 +00:00
void ScummEngine : : readGlobalObjects ( ) {
int i ;
int num = _fileHandle - > readUint16LE ( ) ;
assert ( num = = _numGlobalObjects ) ;
2006-04-23 22:39:55 +00:00
assert ( _objectStateTable ) ;
assert ( _objectOwnerTable ) ;
2005-04-03 23:53:34 +00:00
_fileHandle - > read ( _objectOwnerTable , num ) ;
for ( i = 0 ; i < num ; i + + ) {
_objectStateTable [ i ] = _objectOwnerTable [ i ] > > OF_STATE_SHL ;
_objectOwnerTable [ i ] & = OF_OWNER_MASK ;
}
_fileHandle - > read ( _classData , num * sizeof ( uint32 ) ) ;
# if defined(SCUMM_BIG_ENDIAN)
// Correct the endianess if necessary
for ( i = 0 ; i ! = num ; i + + )
_classData [ i ] = FROM_LE_32 ( _classData [ i ] ) ;
# endif
}
2008-05-06 03:00:26 +00:00
# ifdef ENABLE_SCUMM_7_8
2005-04-09 20:22:31 +00:00
void ScummEngine_v8 : : readGlobalObjects ( ) {
int i ;
int num = _fileHandle - > readUint32LE ( ) ;
assert ( num = = _numGlobalObjects ) ;
2006-04-23 22:39:55 +00:00
assert ( _objectStateTable ) ;
assert ( _objectOwnerTable ) ;
2005-04-09 20:22:31 +00:00
2005-04-23 16:52:11 +00:00
_objectIDMap = new ObjectNameId [ num ] ;
_objectIDMapSize = num ;
2005-04-09 20:22:31 +00:00
for ( i = 0 ; i < num ; i + + ) {
2005-04-23 16:52:11 +00:00
// Add to object name-to-id map
_fileHandle - > read ( _objectIDMap [ i ] . name , 40 ) ;
_objectIDMap [ i ] . id = i ;
2005-04-09 20:22:31 +00:00
_objectStateTable [ i ] = _fileHandle - > readByte ( ) ;
_objectRoomTable [ i ] = _fileHandle - > readByte ( ) ;
_classData [ i ] = _fileHandle - > readUint32LE ( ) ;
}
memset ( _objectOwnerTable , 0xFF , num ) ;
2005-07-30 21:11:48 +00:00
2005-04-23 16:52:11 +00:00
// Finally, sort the object name->ID map, so we can later use
// bsearch on it. For this we (ab)use strcmp, which works fine
// since the table entries start with a string.
qsort ( _objectIDMap , _objectIDMapSize , sizeof ( ObjectNameId ) ,
( int ( * ) ( const void * , const void * ) ) strcmp ) ;
2005-04-09 20:22:31 +00:00
}
void ScummEngine_v7 : : readGlobalObjects ( ) {
int num = _fileHandle - > readUint16LE ( ) ;
assert ( num = = _numGlobalObjects ) ;
2006-04-23 22:39:55 +00:00
assert ( _objectStateTable ) ;
assert ( _objectOwnerTable ) ;
2005-04-09 20:22:31 +00:00
_fileHandle - > read ( _objectStateTable , num ) ;
_fileHandle - > read ( _objectRoomTable , num ) ;
memset ( _objectOwnerTable , 0xFF , num ) ;
_fileHandle - > read ( _classData , num * sizeof ( uint32 ) ) ;
# if defined(SCUMM_BIG_ENDIAN)
// Correct the endianess if necessary
for ( int i = 0 ; i ! = num ; i + + )
_classData [ i ] = FROM_LE_32 ( _classData [ i ] ) ;
# endif
}
2005-05-14 22:56:41 +00:00
# endif
2005-04-09 20:22:31 +00:00
2003-10-02 22:42:03 +00:00
void ScummEngine : : allocateArrays ( ) {
2002-03-14 13:57:28 +00:00
// Note: Buffers are now allocated in scummMain to allow for
2002-04-11 17:19:16 +00:00
// early GUI init.
2002-05-01 17:16:47 +00:00
_objectOwnerTable = ( byte * ) calloc ( _numGlobalObjects , 1 ) ;
_objectStateTable = ( byte * ) calloc ( _numGlobalObjects , 1 ) ;
_classData = ( uint32 * ) calloc ( _numGlobalObjects , sizeof ( uint32 ) ) ;
_newNames = ( uint16 * ) calloc ( _numNewNames , sizeof ( uint16 ) ) ;
_inventory = ( uint16 * ) calloc ( _numInventory , sizeof ( uint16 ) ) ;
_verbs = ( VerbSlot * ) calloc ( _numVerbs , sizeof ( VerbSlot ) ) ;
_objs = ( ObjectData * ) calloc ( _numLocalObjects , sizeof ( ObjectData ) ) ;
2004-08-26 23:30:28 +00:00
_roomVars = ( int32 * ) calloc ( _numRoomVariables , sizeof ( int32 ) ) ;
2003-05-08 15:48:50 +00:00
_scummVars = ( int32 * ) calloc ( _numVariables , sizeof ( int32 ) ) ;
2002-05-01 17:16:47 +00:00
_bitVars = ( byte * ) calloc ( _numBitVariables > > 3 , 1 ) ;
2006-02-20 16:51:30 +00:00
if ( _game . heversion > = 60 ) {
2004-08-02 05:09:53 +00:00
_arraySlot = ( byte * ) calloc ( _numArray , 1 ) ;
2005-10-19 12:15:36 +00:00
}
2002-04-11 17:19:16 +00:00
2011-04-12 16:53:15 +02:00
_res - > allocResTypeData ( rtCostume , ( _game . features & GF_NEW_COSTUMES ) ? MKTAG ( ' A ' , ' K ' , ' O ' , ' S ' ) : MKTAG ( ' C ' , ' O ' , ' S ' , ' T ' ) ,
2011-05-11 18:19:05 +02:00
_numCostumes , kStaticResTypeMode ) ;
_res - > allocResTypeData ( rtRoom , MKTAG ( ' R ' , ' O ' , ' O ' , ' M ' ) , _numRooms , kStaticResTypeMode ) ;
_res - > allocResTypeData ( rtRoomImage , MKTAG ( ' R ' , ' M ' , ' I ' , ' M ' ) , _numRooms , kStaticResTypeMode ) ;
_res - > allocResTypeData ( rtRoomScripts , MKTAG ( ' R ' , ' M ' , ' S ' , ' C ' ) , _numRooms , kStaticResTypeMode ) ;
_res - > allocResTypeData ( rtSound , MKTAG ( ' S ' , ' O ' , ' U ' , ' N ' ) , _numSounds , kSoundResTypeMode ) ;
_res - > allocResTypeData ( rtScript , MKTAG ( ' S ' , ' C ' , ' R ' , ' P ' ) , _numScripts , kStaticResTypeMode ) ;
_res - > allocResTypeData ( rtCharset , MKTAG ( ' C ' , ' H ' , ' A ' , ' R ' ) , _numCharsets , kStaticResTypeMode ) ;
_res - > allocResTypeData ( rtObjectName , 0 , _numNewNames , kDynamicResTypeMode ) ;
_res - > allocResTypeData ( rtInventory , 0 , _numInventory , kDynamicResTypeMode ) ;
_res - > allocResTypeData ( rtTemp , 0 , 10 , kDynamicResTypeMode ) ;
_res - > allocResTypeData ( rtScaleTable , 0 , 5 , kDynamicResTypeMode ) ;
_res - > allocResTypeData ( rtActorName , 0 , _numActors , kDynamicResTypeMode ) ;
_res - > allocResTypeData ( rtVerb , 0 , _numVerbs , kDynamicResTypeMode ) ;
_res - > allocResTypeData ( rtString , 0 , _numArray , kDynamicResTypeMode ) ;
_res - > allocResTypeData ( rtFlObject , 0 , _numFlObject , kDynamicResTypeMode ) ;
_res - > allocResTypeData ( rtMatrix , 0 , 10 , kDynamicResTypeMode ) ;
_res - > allocResTypeData ( rtImage , MKTAG ( ' A ' , ' W ' , ' I ' , ' Z ' ) , _numImages , kStaticResTypeMode ) ;
_res - > allocResTypeData ( rtTalkie , MKTAG ( ' T ' , ' L ' , ' K ' , ' E ' ) , _numTalkies , kStaticResTypeMode ) ;
2011-04-05 13:44:50 +02:00
}
2004-09-07 13:23:26 +00:00
2011-04-05 13:44:50 +02:00
void ScummEngine_v70he : : allocateArrays ( ) {
ScummEngine : : allocateArrays ( ) ;
2011-05-11 18:19:05 +02:00
_res - > allocResTypeData ( rtSpoolBuffer , 0 , 9 , kStaticResTypeMode ) ;
2011-04-05 13:44:50 +02:00
_heV7RoomIntOffsets = ( uint32 * ) calloc ( _numRooms , sizeof ( uint32 ) ) ;
2001-11-06 21:41:56 +00:00
}
2002-02-12 18:20:37 +00:00
2011-04-05 13:44:50 +02:00
2011-05-13 14:02:53 +02:00
void ScummEngine : : dumpResource ( const char * tag , int id , const byte * ptr , int length ) {
2003-07-19 18:18:01 +00:00
char buf [ 256 ] ;
2008-07-29 16:09:10 +00:00
Common : : DumpFile out ;
2003-07-19 18:18:01 +00:00
uint32 size ;
if ( length > = 0 )
size = length ;
2006-02-20 16:51:30 +00:00
else if ( _game . features & GF_OLD_BUNDLE )
2003-07-19 18:18:01 +00:00
size = READ_LE_UINT16 ( ptr ) ;
2006-02-20 16:51:30 +00:00
else if ( _game . features & GF_SMALL_HEADER )
2003-07-19 18:18:01 +00:00
size = READ_LE_UINT32 ( ptr ) ;
else
size = READ_BE_UINT32 ( ptr + 4 ) ;
2022-10-23 15:26:54 +02:00
Common : : sprintf_s ( buf , " dumps/%s%d.dmp " , tag , id ) ;
2003-07-19 18:18:01 +00:00
2008-07-29 16:09:10 +00:00
out . open ( buf ) ;
2003-12-25 15:09:23 +00:00
if ( out . isOpen ( ) = = false )
return ;
out . write ( ptr , size ) ;
2003-07-19 18:18:01 +00:00
out . close ( ) ;
}
2003-06-26 13:54:29 +00:00
ResourceIterator : : ResourceIterator ( const byte * searchin , bool smallHeader )
: _ptr ( searchin ) , _smallHeader ( smallHeader ) {
assert ( searchin ) ;
if ( _smallHeader ) {
_size = READ_LE_UINT32 ( searchin ) ;
_pos = 6 ;
_ptr = searchin + 6 ;
} else {
_size = READ_BE_UINT32 ( searchin + 4 ) ;
_pos = 8 ;
_ptr = searchin + 8 ;
}
2005-07-30 21:11:48 +00:00
2003-06-26 13:54:29 +00:00
}
const byte * ResourceIterator : : findNext ( uint32 tag ) {
uint32 size = 0 ;
2021-11-13 23:40:38 +02:00
const byte * result = nullptr ;
2005-07-30 21:11:48 +00:00
2003-06-26 13:54:29 +00:00
if ( _smallHeader ) {
uint16 smallTag = newTag2Old ( tag ) ;
do {
if ( _pos > = _size )
2021-11-13 23:40:38 +02:00
return nullptr ;
2005-07-30 21:11:48 +00:00
2003-06-26 13:54:29 +00:00
result = _ptr ;
size = READ_LE_UINT32 ( result ) ;
if ( ( int32 ) size < = 0 )
2021-11-13 23:40:38 +02:00
return nullptr ; // Avoid endless loop
2005-07-30 21:11:48 +00:00
2003-06-26 13:54:29 +00:00
_pos + = size ;
_ptr + = size ;
} while ( READ_LE_UINT16 ( result + 4 ) ! = smallTag ) ;
} else {
do {
if ( _pos > = _size )
2021-11-13 23:40:38 +02:00
return nullptr ;
2005-07-30 21:11:48 +00:00
2003-06-26 13:54:29 +00:00
result = _ptr ;
size = READ_BE_UINT32 ( result + 4 ) ;
if ( ( int32 ) size < = 0 )
2021-11-13 23:40:38 +02:00
return nullptr ; // Avoid endless loop
2005-07-30 21:11:48 +00:00
2003-06-26 13:54:29 +00:00
_pos + = size ;
_ptr + = size ;
2006-02-25 02:12:58 +00:00
} while ( READ_BE_UINT32 ( result ) ! = tag ) ;
2003-06-26 13:54:29 +00:00
}
return result ;
}
2004-06-23 01:36:57 +00:00
const byte * ScummEngine : : findResource ( uint32 tag , const byte * searchin ) {
2021-11-15 15:05:18 -08:00
Common : : StackLock lock ( _resourceAccessMutex ) ;
2003-06-26 13:54:29 +00:00
uint32 curpos , totalsize , size ;
2009-07-03 10:40:49 +00:00
debugC ( DEBUG_RESOURCE , " findResource(%s, %p) " , tag2str ( tag ) , ( const void * ) searchin ) ;
2003-06-26 13:54:29 +00:00
2004-06-23 01:36:57 +00:00
if ( ! searchin ) {
2006-02-20 16:51:30 +00:00
if ( _game . heversion > = 70 ) {
2004-06-23 01:36:57 +00:00
searchin = _resourceLastSearchBuf ;
totalsize = _resourceLastSearchSize ;
curpos = 0 ;
} else {
assert ( searchin ) ;
2021-11-13 23:40:38 +02:00
return nullptr ;
2004-06-23 01:36:57 +00:00
}
} else {
searchin + = 4 ;
_resourceLastSearchSize = totalsize = READ_BE_UINT32 ( searchin ) ;
curpos = 8 ;
searchin + = 4 ;
}
2003-06-26 13:54:29 +00:00
while ( curpos < totalsize ) {
2006-02-25 02:12:58 +00:00
if ( READ_BE_UINT32 ( searchin ) = = tag ) {
2004-06-23 01:36:57 +00:00
_resourceLastSearchBuf = searchin ;
2003-06-26 13:54:29 +00:00
return searchin ;
2004-06-23 01:36:57 +00:00
}
2003-06-26 13:54:29 +00:00
size = READ_BE_UINT32 ( searchin + 4 ) ;
if ( ( int32 ) size < = 0 ) {
2003-10-17 16:28:29 +00:00
error ( " (%s) Not found in %d... illegal block len %d " , tag2str ( tag ) , 0 , size ) ;
2021-11-13 23:40:38 +02:00
return nullptr ;
2003-06-26 13:54:29 +00:00
}
curpos + = size ;
searchin + = size ;
}
2021-11-13 23:40:38 +02:00
return nullptr ;
2003-06-26 13:54:29 +00:00
}
const byte * findResourceSmall ( uint32 tag , const byte * searchin ) {
uint32 curpos , totalsize , size ;
uint16 smallTag ;
smallTag = newTag2Old ( tag ) ;
2003-07-17 14:03:15 +00:00
if ( smallTag = = 0 )
2021-11-13 23:40:38 +02:00
return nullptr ;
2003-06-26 13:54:29 +00:00
assert ( searchin ) ;
totalsize = READ_LE_UINT32 ( searchin ) ;
searchin + = 6 ;
curpos = 6 ;
while ( curpos < totalsize ) {
size = READ_LE_UINT32 ( searchin ) ;
if ( READ_LE_UINT16 ( searchin + 4 ) = = smallTag )
return searchin ;
if ( ( int32 ) size < = 0 ) {
2003-10-17 16:28:29 +00:00
error ( " (%s) Not found in %d... illegal block len %d " , tag2str ( tag ) , 0 , size ) ;
2021-11-13 23:40:38 +02:00
return nullptr ;
2003-06-26 13:54:29 +00:00
}
curpos + = size ;
searchin + = size ;
}
2021-11-13 23:40:38 +02:00
return nullptr ;
2003-06-26 13:54:29 +00:00
}
2005-03-06 13:23:29 +00:00
uint16 newTag2Old ( uint32 newTag ) {
switch ( newTag ) {
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' R ' , ' M ' , ' H ' , ' D ' ) ) :
2002-07-28 11:57:33 +00:00
return ( 0x4448 ) ; // HD
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' I ' , ' M ' , ' 0 ' , ' 0 ' ) ) :
2002-07-28 11:57:33 +00:00
return ( 0x4D42 ) ; // BM
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' E ' , ' X ' , ' C ' , ' D ' ) ) :
2002-07-28 11:57:33 +00:00
return ( 0x5845 ) ; // EX
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' E ' , ' N ' , ' C ' , ' D ' ) ) :
2002-07-28 11:57:33 +00:00
return ( 0x4E45 ) ; // EN
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' S ' , ' C ' , ' A ' , ' L ' ) ) :
2002-07-28 11:57:33 +00:00
return ( 0x4153 ) ; // SA
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' L ' , ' S ' , ' C ' , ' R ' ) ) :
2002-07-28 11:57:33 +00:00
return ( 0x534C ) ; // LS
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' O ' , ' B ' , ' C ' , ' D ' ) ) :
2002-07-28 11:57:33 +00:00
return ( 0x434F ) ; // OC
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' O ' , ' B ' , ' I ' , ' M ' ) ) :
2002-07-28 11:57:33 +00:00
return ( 0x494F ) ; // OI
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' S ' , ' M ' , ' A ' , ' P ' ) ) :
2002-07-28 11:57:33 +00:00
return ( 0x4D42 ) ; // BM
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' C ' , ' L ' , ' U ' , ' T ' ) ) :
2002-07-28 11:57:33 +00:00
return ( 0x4150 ) ; // PA
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' B ' , ' O ' , ' X ' , ' D ' ) ) :
2002-07-28 11:57:33 +00:00
return ( 0x5842 ) ; // BX
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' C ' , ' Y ' , ' C ' , ' L ' ) ) :
2003-08-28 01:10:36 +00:00
return ( 0x4343 ) ; // CC
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' E ' , ' P ' , ' A ' , ' L ' ) ) :
2004-08-22 06:16:16 +00:00
return ( 0x5053 ) ; // SP
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' T ' , ' I ' , ' L ' , ' E ' ) ) :
2009-10-21 12:59:10 +00:00
return ( 0x4C54 ) ; // TL
2011-04-12 16:53:15 +02:00
case ( MKTAG ( ' Z ' , ' P ' , ' 0 ' , ' 0 ' ) ) :
2009-10-26 02:45:26 +00:00
return ( 0x505A ) ; // ZP
2002-04-11 17:19:16 +00:00
default :
return ( 0 ) ;
}
2002-02-12 18:20:37 +00:00
}
2002-04-21 19:38:00 +00:00
2011-05-13 14:04:59 +02:00
const char * nameOfResType ( ResType type ) {
2002-04-26 14:13:39 +00:00
static char buf [ 100 ] ;
2011-05-13 14:02:53 +02:00
switch ( type ) {
2002-07-07 20:32:26 +00:00
case rtRoom :
2003-06-25 23:49:54 +00:00
return " Room " ;
2002-07-07 20:32:26 +00:00
case rtScript :
2003-06-25 23:49:54 +00:00
return " Script " ;
2002-07-07 20:32:26 +00:00
case rtCostume :
2003-06-25 23:49:54 +00:00
return " Costume " ;
2002-07-07 20:32:26 +00:00
case rtSound :
2003-06-25 23:49:54 +00:00
return " Sound " ;
2002-07-07 20:32:26 +00:00
case rtInventory :
2003-06-25 23:49:54 +00:00
return " Inventory " ;
2002-07-07 20:32:26 +00:00
case rtCharset :
2003-06-25 23:49:54 +00:00
return " Charset " ;
2002-07-07 20:32:26 +00:00
case rtString :
2003-06-25 23:49:54 +00:00
return " String " ;
2002-07-07 20:32:26 +00:00
case rtVerb :
2003-06-25 23:49:54 +00:00
return " Verb " ;
2002-07-07 20:32:26 +00:00
case rtActorName :
2003-06-25 23:49:54 +00:00
return " ActorName " ;
2002-07-07 20:32:26 +00:00
case rtBuffer :
2003-06-25 23:49:54 +00:00
return " Buffer " ;
2002-07-07 20:32:26 +00:00
case rtScaleTable :
2003-06-25 23:49:54 +00:00
return " ScaleTable " ;
2002-07-07 20:32:26 +00:00
case rtTemp :
2003-06-25 23:49:54 +00:00
return " Temp " ;
2002-07-07 20:32:26 +00:00
case rtFlObject :
2003-06-25 23:49:54 +00:00
return " FlObject " ;
2002-07-07 20:32:26 +00:00
case rtMatrix :
2003-06-25 23:49:54 +00:00
return " Matrix " ;
2002-07-07 20:32:26 +00:00
case rtBox :
2003-06-25 23:49:54 +00:00
return " Box " ;
2004-08-30 00:10:36 +00:00
case rtObjectName :
return " ObjectName " ;
case rtRoomScripts :
return " RoomScripts " ;
case rtRoomImage :
return " RoomImage " ;
2004-06-26 12:57:11 +00:00
case rtImage :
return " Image " ;
2004-09-07 13:23:26 +00:00
case rtTalkie :
return " Talkie " ;
2006-01-05 07:06:47 +00:00
case rtSpoolBuffer :
return " SpoolBuffer " ;
2002-07-07 20:32:26 +00:00
default :
2022-10-23 15:26:54 +02:00
Common : : sprintf_s ( buf , " rt%d " , type ) ;
2003-06-25 23:49:54 +00:00
return buf ;
2002-04-26 14:13:39 +00:00
}
}
2003-10-03 18:33:57 +00:00
2020-04-16 13:43:53 +03:00
void ScummEngine : : applyWorkaroundIfNeeded ( ResType type , int idx ) {
2021-11-29 07:25:33 +01:00
// The resource isn't always loaded into memory, in which case no
// workaround is needed. This happens when loading some HE savegames
// where sound resource 1 isn't loaded. Possibly other cases as well.
if ( ! _res - > isResourceLoaded ( type , idx ) )
return ;
2021-07-12 14:28:47 +02:00
int size = getResourceSize ( type , idx ) ;
2020-04-16 13:43:53 +03:00
// WORKAROUND: FM-TOWNS Zak used the extra 40 pixels at the bottom to increase the inventory to 10 items
// if we trim to 200 pixels, we can show only 6 items
// therefore we patch the inventory script (20)
2021-10-31 15:10:19 +01:00
// replacing the 5 occurrences of 10 as limit to 6
2021-07-12 14:28:47 +02:00
if ( _game . platform = = Common : : kPlatformFMTowns & & _game . id = = GID_ZAK & & ConfMan . getBool ( " trim_fmtowns_to_200_pixels " ) ) {
2020-04-16 13:43:53 +03:00
if ( type = = rtScript & & idx = = 20 ) {
byte * ptr = getResourceAddress ( rtScript , idx ) ;
for ( int cnt = 5 ; cnt ; ptr + + ) {
if ( * ptr = = 10 ) {
* ptr = 6 ;
cnt - - ;
}
}
}
2021-07-12 14:28:47 +02:00
}
// WORKAROUND: The Mac version of Monkey Island 2 that was distributed
// on CD as the LucasArts Adventure Game Pack II is missing the part of
// the boot script that shows the copy protection and difficulty
// selection screen. Presumably it didn't include the code wheel. In
// fact, none of the games on this CD have any copy protection.
//
// The games on the first Game Pack CD does have copy protection, but
// since I only own the discs I can neither confirm nor deny if the
// necessary documentation was included.
//
// However, this means that there is no way to pick the difficulty
// level. Since ScummVM bypasses the copy protection check, there is
2022-04-19 13:57:37 +02:00
// no harm in showing the screen by simply re-inserting the missing
2021-07-12 14:28:47 +02:00
// part of the script.
else if ( _game . id = = GID_MONKEY2 & & _game . platform = = Common : : kPlatformMacintosh & & type = = rtScript & & idx = = 1 & & size = = 6718 ) {
byte * unpatchedScript = getResourceAddress ( type , idx ) ;
const byte patch [ ] = {
2022-04-19 14:11:55 +02:00
0x48 , 0x00 , 0x40 , 0x00 , 0x00 , 0x13 , 0x00 , // [0926] if (Local[0] == 0) {
0x33 , 0x03 , 0x00 , 0x00 , 0xc8 , 0x00 , // [092D] SetScreen(0,200);
0x0a , 0x82 , 0xff , // [0933] startScript(130,[]);
0x80 , // [0936] breakHere();
0x68 , 0x00 , 0x00 , 0x82 , // [0937] VAR_RESULT = isScriptRunning(130);
0x28 , 0x00 , 0x00 , 0xf6 , 0xff , // [093B] unless (!VAR_RESULT) goto 0936;
// [0940] }
0x48 , 0x00 , 0x40 , 0x3f , 0xe1 , 0x1d , 0x00 , // [0940] if (Local[0] == -7873) [
0x1a , 0x32 , 0x00 , 0x3f , 0x01 , // [0947] VAR_MAINMENU_KEY = 319;
0x33 , 0x03 , 0x00 , 0x00 , 0xc8 , 0x00 , // [094C] SetScreen(0,200);
0x0a , 0x82 , 0xff , // [0952] startScript(130,[]);
0x80 , // [0955] breakHere();
0x68 , 0x00 , 0x00 , 0x82 , // [0956] VAR_RESULT = isScriptRunning(130);
0x28 , 0x00 , 0x00 , 0xf6 , 0xff , // [095A] unless (!VAR_RESULT) goto 0955;
0x1a , 0x00 , 0x40 , 0x00 , 0x00 // [095F] Local[0] = 0;
// [0964] }
2021-07-12 14:28:47 +02:00
} ;
byte * patchedScript = new byte [ 6780 ] ;
memcpy ( patchedScript , unpatchedScript , 2350 ) ;
memcpy ( patchedScript + 2350 , patch , sizeof ( patch ) ) ;
memcpy ( patchedScript + 2350 + sizeof ( patch ) , unpatchedScript + 2350 , 6718 - 2350 ) ;
WRITE_BE_UINT32 ( patchedScript + 4 , 6780 ) ;
// Just to be completely safe, check that the patched script now
// matches the boot script from the other known Mac version.
// Only if it does can we replace the unpatched script.
2021-07-14 18:58:24 +02:00
if ( verifyMI2MacBootScript ( patchedScript , 6780 ) ) {
2021-07-12 14:28:47 +02:00
byte * newResource = _res - > createResource ( type , idx , 6780 ) ;
memcpy ( newResource , patchedScript , 6780 ) ;
} else
2021-07-14 18:58:24 +02:00
warning ( " Could not patch MI2 Mac boot script " ) ;
2021-07-12 14:28:47 +02:00
delete [ ] patchedScript ;
2021-07-17 09:20:10 +02:00
} else
2022-08-07 17:24:27 +02:00
// WORKAROUND: For some reason, the CD version of Monkey Island 1
// removes some of the text when giving the wimpy idol to the cannibals.
// It looks like a mistake, because one of the text that is printed is
// immediately overwritten. This probably affects all CD versions, so we
// just have to add further patches as they are reported.
2021-08-03 17:57:51 +02:00
2022-03-20 10:50:47 +01:00
if ( _game . id = = GID_MONKEY & & type = = rtRoom & & idx = = 25 & & _enableEnhancements ) {
2021-08-03 17:57:51 +02:00
tryPatchMI1CannibalScript ( getResourceAddress ( type , idx ) , size ) ;
} else
2022-08-07 17:24:27 +02:00
// WORKAROUND: There is a cracked version of Maniac Mansion v2 that
// attempts to remove the security door copy protection. With it, any
// code is accepted as long as you get the last digit wrong.
// Unfortunately, it changes a script that is used by all keypads in the
// game, which means some puzzles are completely nerfed.
2021-07-17 09:20:10 +02:00
//
2021-07-21 19:39:35 +02:00
// Even worse, this is the version that GOG and Steam are selling. No,
// seriously! I've reported this as a bug, but it remains unclear
// whether or not they will fix it.
2021-07-17 09:20:10 +02:00
if ( _game . id = = GID_MANIAC & & _game . version = = 2 & & _game . platform = = Common : : kPlatformDOS & & type = = rtScript & & idx = = 44 & & size = = 199 ) {
byte * data = getResourceAddress ( type , idx ) ;
if ( data [ 184 ] = = 0 ) {
Common : : MemoryReadStream stream ( data , size ) ;
Common : : String md5 = Common : : computeStreamMD5AsString ( stream ) ;
if ( md5 = = " 11adc9b47497b26ac2b9627e0982b3fe " ) {
warning ( " Removing bad copy protection crack from keypad script " ) ;
data [ 184 ] = 1 ;
}
}
2021-07-12 14:28:47 +02:00
}
2020-04-16 13:43:53 +03:00
}
2021-07-14 18:58:24 +02:00
bool ScummEngine : : verifyMI2MacBootScript ( ) {
return verifyMI2MacBootScript ( getResourceAddress ( rtScript , 1 ) , getResourceSize ( rtScript , 1 ) ) ;
}
bool ScummEngine : : verifyMI2MacBootScript ( byte * buf , int size ) {
if ( size = = 6780 ) {
Common : : MemoryReadStream stream ( buf , size ) ;
Common : : String md5 = Common : : computeStreamMD5AsString ( stream ) ;
if ( md5 ! = " 92b1cb7902b57d02b8e7434903d8508b " ) {
warning ( " Unexpected MI2 Mac boot script checksum: %s " , md5 . c_str ( ) ) ;
return false ;
}
} else {
warning ( " Unexpected MI2 Mac boot script length: %d " , size ) ;
return false ;
}
return true ;
}
2021-08-03 17:57:51 +02:00
bool ScummEngine : : tryPatchMI1CannibalScript ( byte * buf , int size ) {
assert ( _game . id = = GID_MONKEY ) ;
2021-08-04 11:19:06 +02:00
// The room resource is a collection of resources. We need to know the
// offset to the initial LSCR tag of the room-25-205 script, and its
// length up to (but not including) the LSCR tag of the next script.
// Furthermore we need to know the offset and length of the part of
// the script that we are going to replace. As an illustration, this
// is what that part of script looks like in the English CD version:
//
// [009C] (AE) WaitForMessage();
// [009E] (14) print(3,[Text("Oooh, that's nice.")]);
// [00B4] (14) print(3,[Text("And it says, `Made by Lemonhead`^" +
// wait() + "^just like one of mine!" + wait() +
// "We should take this to the Great Monkey.")]);
// [011C] (AE) WaitForMessage();
//
// What we want to do is make it behave like the script from the VGA
// floppy version:
//
// [009E] (AE) WaitForMessage();
// [00A0] (14) print(3,[Text("Oooh, that's nice." + wait() +
// "Simple. Just like one of mine." + wait() +
// "And little. Like mine.")]);
// [00F0] (AE) WaitForMessage();
// [00F2] (14) print(3,[Text("And it says, `Made by Lemonhead`^" +
// wait() + "^just like one of mine!" + wait() +
// "We should take this to the Great Monkey.")]);
// [015A] (AE) WaitForMessage();
//
// So we want to adjust the message, and insert a WaitForMessage().
// Unfortunately there isn't enough space to do that, and rather than
// modifying the length of the whole resource (which is easy to get
// wrong), we insert a placeholder message that gets replaced by
// decodeParseString().
//
// There should be enough space to do this even if we only change the
// first message. Any leftover space in the message is padded with
// spaces, since I can't find any NOP opcode.
2021-08-03 17:57:51 +02:00
int expectedSize = - 1 ;
int scriptOffset = - 1 ;
int scriptLength = - 1 ;
Common : : String expectedMd5 ;
int patchOffset = - 1 ;
int patchLength = - 1 ;
2021-08-04 18:04:53 +02:00
byte lang [ 3 ] ;
2021-08-03 17:57:51 +02:00
switch ( _language ) {
case Common : : EN_ANY :
expectedSize = 82906 ;
scriptOffset = 73883 ;
scriptLength = 607 ;
expectedMd5 = " 98b1126a836ef5bfefff10b605b20555 " ;
patchOffset = 167 ;
2021-08-04 11:19:06 +02:00
patchLength = 22 ;
2021-08-04 18:04:53 +02:00
lang [ 0 ] = ' E ' ;
lang [ 1 ] = ' N ' ;
lang [ 2 ] = ' G ' ;
2021-08-10 22:05:02 +02:00
// The Macintosh resource is 4 bytes shorter, which affects
// the script offset as well. Otherwise, both Mac versions
// that I have are identical to the DOS CD version in this
// particular case.
if ( _game . platform = = Common : : kPlatformMacintosh ) {
expectedSize - = 4 ;
scriptOffset - = 4 ;
2021-08-18 08:17:57 +02:00
} else if ( _game . platform = = Common : : kPlatformFMTowns ) {
expectedSize = 82817 ;
scriptOffset = 73794 ;
2021-08-18 18:35:33 +02:00
} else if ( _game . platform = = Common : : kPlatformSegaCD ) {
expectedSize = 61844 ;
scriptOffset = 51703 ;
2021-08-10 22:05:02 +02:00
}
2021-08-03 17:57:51 +02:00
break ;
2021-08-15 19:37:02 +02:00
case Common : : DE_DEU :
expectedSize = 83554 ;
scriptOffset = 74198 ;
scriptLength = 632 ;
expectedMd5 = " 27d6d8eab4e0f66792e10769090ae047 " ;
patchOffset = 170 ;
patchLength = 23 ;
lang [ 0 ] = ' D ' ;
lang [ 1 ] = ' E ' ;
lang [ 2 ] = ' U ' ;
break ;
2021-08-10 21:39:11 +02:00
case Common : : IT_ITA :
expectedSize = 83211 ;
scriptOffset = 73998 ;
scriptLength = 602 ;
expectedMd5 = " 39eb6116d67f2318f31d6fa98df2e931 " ;
patchOffset = 161 ;
patchLength = 20 ;
lang [ 0 ] = ' I ' ;
lang [ 1 ] = ' T ' ;
lang [ 2 ] = ' A ' ;
break ;
2021-08-10 22:33:26 +02:00
case Common : : ES_ESP :
expectedSize = 82829 ;
scriptOffset = 73905 ;
scriptLength = 579 ;
expectedMd5 = " 0e282d86f80d4e062a9a145601e6fed3 " ;
patchOffset = 161 ;
patchLength = 21 ;
lang [ 0 ] = ' E ' ;
lang [ 1 ] = ' S ' ;
lang [ 2 ] = ' P ' ;
break ;
2021-08-03 17:57:51 +02:00
default :
return false ;
}
2021-08-04 11:41:09 +02:00
// Note that the patch will not apply to the "Ultimate Talkie" edition
// since that script has been patched to a different length.
2021-08-03 17:57:51 +02:00
if ( size = = expectedSize ) {
// There isn't enough space in the script for the revised
// texts, so these abbreviations will be expanded in
// decodeParseString().
const byte patchData [ ] = {
2021-08-04 11:41:09 +02:00
0x14 , 0x03 , 0x0F , // print(3,[Text("/LH.$$$/");
2021-08-04 11:19:06 +02:00
0x2F , 0x4C , 0x48 , 0x2E ,
2021-08-04 11:41:09 +02:00
0x24 , 0x24 , 0x24 , 0x2F // No terminating 0x00!
2021-08-03 17:57:51 +02:00
} ;
byte * scriptPtr = buf + scriptOffset ;
// Check that the data is a local script.
if ( READ_BE_UINT32 ( scriptPtr ) ! = MKTAG ( ' L ' , ' S ' , ' C ' , ' R ' ) )
return false ;
// Check that the first instruction to be patched is o5_print
if ( scriptPtr [ patchOffset ] ! = 0x14 )
return false ;
// Check that the MD5 sum matches a known patchable script.
Common : : MemoryReadStream stream ( buf + scriptOffset , scriptLength ) ;
Common : : String md5 = Common : : computeStreamMD5AsString ( stream ) ;
if ( md5 ! = expectedMd5 )
return false ;
2021-08-04 11:41:09 +02:00
// Insert the script patch and tag it with the appropriate
// language.
memcpy ( scriptPtr + patchOffset , patchData , sizeof ( patchData ) ) ;
2021-08-04 18:04:53 +02:00
memcpy ( scriptPtr + patchOffset + 7 , lang , sizeof ( lang ) ) ;
2021-08-04 11:41:09 +02:00
2021-08-03 17:57:51 +02:00
// Pad the rest of the replaced script part with spaces before
2021-08-04 11:19:06 +02:00
// terminating the string. Finally, add WaitForMessage().
2021-08-03 17:57:51 +02:00
2021-08-04 12:44:26 +02:00
memset ( scriptPtr + patchOffset + sizeof ( patchData ) , 32 , patchLength - sizeof ( patchData ) - 3 ) ;
2021-08-04 11:19:06 +02:00
scriptPtr [ patchOffset + patchLength - 3 ] = 0 ;
scriptPtr [ patchOffset + patchLength - 2 ] = 0xAE ;
scriptPtr [ patchOffset + patchLength - 1 ] = 0x02 ;
2021-08-03 17:57:51 +02:00
}
return true ;
}
2020-04-16 13:43:53 +03:00
2003-10-03 18:33:57 +00:00
} // End of namespace Scumm