2007-05-30 21:56:52 +00:00
/* ScummVM - Graphic Adventure Engine
2006-05-23 23:43:52 +00:00
*
2007-05-30 21:56:52 +00:00
* 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 .
2006-05-23 23:43:52 +00:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
2014-02-18 02:34:17 +01:00
*
2006-05-23 23:43:52 +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:17 +01:00
*
2006-05-23 23:43:52 +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-06-06 17:39:13 +00:00
//
// Savegame support by Vasyl Tsvirkunov <vasyl@pacbell.net>
// Multi-slots by Claudio Matsuoka <claudio@helllabs.org>
//
2006-05-23 23:43:52 +00:00
2006-12-06 19:27:02 +00:00
# include "common/file.h"
2009-07-22 15:55:33 +00:00
# include "common/config-manager.h"
2010-01-03 20:15:44 +00:00
# include "common/savefile.h"
2011-04-24 11:34:27 +03:00
# include "common/textconsole.h"
2012-03-27 00:21:16 +03:00
# include "common/translation.h"
# include "gui/saveload.h"
2010-11-19 01:37:04 +00:00
# include "graphics/thumbnail.h"
# include "graphics/surface.h"
2006-05-23 23:43:52 +00:00
# include "agi/agi.h"
# include "agi/graphics.h"
2016-01-29 13:13:40 +01:00
# include "agi/text.h"
2006-05-23 23:43:52 +00:00
# include "agi/sprite.h"
# include "agi/keyboard.h"
# include "agi/menu.h"
2016-01-29 13:13:40 +01:00
# include "agi/systemui.h"
# include "agi/words.h"
2006-05-23 23:43:52 +00:00
2016-02-19 02:00:03 +01:00
# define SAVEGAME_CURRENT_VERSION 11
2006-05-23 23:43:52 +00:00
2009-06-06 17:39:13 +00:00
//
2016-02-13 20:42:30 +01:00
// Version 0 (Sarien): view table has 64 entries
// Version 1 (Sarien): view table has 256 entries (needed in KQ3)
// Version 2 (ScummVM): first ScummVM version
// Version 3 (ScummVM): added AGIPAL save/load support
// Version 4 (ScummVM): added thumbnails and save creation date/time
// Version 5 (ScummVM): Added game md5
// Version 6 (ScummVM): Added game played time
// Version 7 (ScummVM): Added controller key mappings
// required for some games for quick-loading from ScummVM main menu
// for games, that do not set all key mappings right at the start
// Added automatic save data (for command SetSimple)
// Version 8 (ScummVM): Added Hold-Key-Mode boolean
// required for at least Mixed Up Mother Goose
// gets set at the start of the game only
// Version 9 (ScummVM): Added seconds to saved game time stamp
// Version 10 (ScummVM): Added priorityTableSet boolean
2006-05-23 23:43:52 +00:00
2007-01-12 02:29:20 +00:00
namespace Agi {
2016-02-02 18:43:36 +01:00
static const uint32 AGIflag = MKTAG ( ' A ' , ' G ' , ' I ' , ' : ' ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
int AgiEngine : : saveGame ( const Common : : String & fileName , const Common : : String & descriptionString ) {
2009-06-06 17:39:13 +00:00
char gameIDstring [ 8 ] = " gameIDX " ;
2007-01-12 02:29:20 +00:00
int i ;
Common : : OutSaveFile * out ;
2007-08-04 06:18:28 +00:00
int result = errOK ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
debugC ( 3 , kDebugLevelMain | kDebugLevelSavegame , " AgiEngine::saveGame(%s, %s) " , fileName . c_str ( ) , descriptionString . c_str ( ) ) ;
2007-01-12 02:29:20 +00:00
if ( ! ( out = _saveFileMan - > openForSaving ( fileName ) ) ) {
2011-09-25 21:25:25 +03:00
warning ( " Can't create file '%s', game not saved " , fileName . c_str ( ) ) ;
2007-01-16 12:40:51 +00:00
return errBadFileOpen ;
2007-01-12 02:29:20 +00:00
} else {
2011-09-25 21:25:25 +03:00
debugC ( 3 , kDebugLevelMain | kDebugLevelSavegame , " Successfully opened %s for writing " , fileName . c_str ( ) ) ;
2007-01-12 02:29:20 +00:00
}
out - > writeUint32BE ( AGIflag ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
// Write description of saved game, limited to SAVEDGAME_DESCRIPTION_LEN characters + terminating NUL
char description [ SAVEDGAME_DESCRIPTION_LEN + 1 ] ;
memset ( description , 0 , sizeof ( description ) ) ;
strncpy ( description , descriptionString . c_str ( ) , SAVEDGAME_DESCRIPTION_LEN ) ;
assert ( SAVEDGAME_DESCRIPTION_LEN + 1 = = 31 ) ; // safety
out - > write ( description , 31 ) ;
out - > writeByte ( SAVEGAME_CURRENT_VERSION ) ;
debugC ( 5 , kDebugLevelMain | kDebugLevelSavegame , " Writing save game version (%d) " , SAVEGAME_CURRENT_VERSION ) ;
2006-05-23 23:43:52 +00:00
2008-11-10 19:02:47 +00:00
// Thumbnail
Graphics : : saveThumbnail ( * out ) ;
// Creation date/time
2009-10-08 19:41:38 +00:00
TimeDate curTime ;
2008-11-10 19:02:47 +00:00
_system - > getTimeAndDate ( curTime ) ;
2008-11-29 18:01:16 +00:00
uint32 saveDate = ( ( curTime . tm_mday & 0xFF ) < < 24 ) | ( ( ( curTime . tm_mon + 1 ) & 0xFF ) < < 16 ) | ( ( curTime . tm_year + 1900 ) & 0xFFFF ) ;
uint16 saveTime = ( ( curTime . tm_hour & 0xFF ) < < 8 ) | ( ( curTime . tm_min ) & 0xFF ) ;
2012-03-27 00:50:29 +03:00
uint32 playTime = g_engine - > getTotalPlayTime ( ) / 1000 ;
2008-11-10 19:02:47 +00:00
out - > writeUint32BE ( saveDate ) ;
debugC ( 5 , kDebugLevelMain | kDebugLevelSavegame , " Writing save date (%d) " , saveDate ) ;
out - > writeUint16BE ( saveTime ) ;
debugC ( 5 , kDebugLevelMain | kDebugLevelSavegame , " Writing save time (%d) " , saveTime ) ;
2016-02-13 13:40:47 +01:00
// Version 9+: save seconds of current time as well
out - > writeByte ( curTime . tm_sec & 0xFF ) ;
2012-03-27 00:50:29 +03:00
out - > writeUint32BE ( playTime ) ;
debugC ( 5 , kDebugLevelMain | kDebugLevelSavegame , " Writing play time (%d) " , playTime ) ;
2008-11-10 19:02:47 +00:00
2016-02-03 03:07:50 +01:00
out - > writeByte ( 2 ) ; // was _game.state, 2 = STATE_RUNNING
2007-09-19 08:40:12 +00:00
2016-05-10 13:41:40 +02:00
Common : : strlcpy ( gameIDstring , _game . id , 8 ) ;
2007-01-12 02:29:20 +00:00
out - > write ( gameIDstring , 8 ) ;
2007-01-16 12:40:51 +00:00
debugC ( 5 , kDebugLevelMain | kDebugLevelSavegame , " Writing game id (%s, %s) " , gameIDstring , _game . id ) ;
2006-05-23 23:43:52 +00:00
2009-06-06 17:41:50 +00:00
const char * tmp = getGameMD5 ( ) ;
2009-09-23 13:17:08 +00:00
// As reported in bug report #2849084 "AGI: Crash when saving fallback-matched game"
// getGameMD5 will return NULL for fallback matched games. Since there is also no
// filename available we can not compute any MD5 here either. Thus we will just set
// the MD5 sum in the savegame to all zero, when getGameMD5 returns NULL.
if ( ! tmp ) {
for ( i = 0 ; i < 32 ; + + i )
out - > writeByte ( 0 ) ;
} else {
for ( i = 0 ; i < 32 ; + + i )
out - > writeByte ( tmp [ i ] ) ;
}
2009-06-06 17:41:50 +00:00
2016-01-29 13:13:40 +01:00
// Version 7+: Save automatic saving state (set.simple opcode)
out - > writeByte ( _game . automaticSave ) ;
out - > write ( _game . automaticSaveDescription , 31 ) ;
2016-01-31 17:56:53 +01:00
// touch VM_VAR_SECONDS, so that it gets updated
getVar ( VM_VAR_SECONDS ) ;
2006-05-23 23:43:52 +00:00
for ( i = 0 ; i < MAX_FLAGS ; i + + )
2007-01-16 12:40:51 +00:00
out - > writeByte ( _game . flags [ i ] ) ;
2006-05-23 23:43:52 +00:00
for ( i = 0 ; i < MAX_VARS ; i + + )
2007-01-16 12:40:51 +00:00
out - > writeByte ( _game . vars [ i ] ) ;
2006-05-23 23:43:52 +00:00
2007-01-16 12:40:51 +00:00
out - > writeSint16BE ( ( int8 ) _game . horizon ) ;
2016-01-29 13:13:40 +01:00
out - > writeSint16BE ( ( int16 ) _text - > statusRow_Get ( ) ) ;
out - > writeSint16BE ( ( int16 ) _text - > promptRow_Get ( ) ) ;
out - > writeSint16BE ( ( int16 ) _text - > getWindowRowMin ( ) ) ;
2007-01-12 02:29:20 +00:00
2016-02-02 23:02:50 +01:00
out - > writeSint16BE ( 1 ) ; // was _game.inputMode, we set it to 1, which was INPUTMODE_NORMAL
2016-02-01 16:21:13 +01:00
out - > writeSint16BE ( ( int16 ) _game . curLogicNr ) ;
2006-05-23 23:43:52 +00:00
2007-01-16 12:40:51 +00:00
out - > writeSint16BE ( ( int16 ) _game . playerControl ) ;
2008-09-30 12:27:38 +00:00
out - > writeSint16BE ( ( int16 ) shouldQuit ( ) ) ;
2016-01-29 13:13:40 +01:00
if ( _text - > statusEnabled ( ) ) {
out - > writeSint16BE ( 0x7FFF ) ;
} else {
out - > writeSint16BE ( 0 ) ;
}
2016-02-01 15:54:32 +01:00
out - > writeSint16BE ( 1 ) ; // was clock enabled
// (previous in-game-timer, in-game-timer is always enabled during the regular game, so need to save/load it)
2007-01-16 12:40:51 +00:00
out - > writeSint16BE ( ( int16 ) _game . exitAllLogics ) ;
out - > writeSint16BE ( ( int16 ) _game . pictureShown ) ;
2016-02-01 16:13:19 +01:00
out - > writeSint16BE ( ( int16 ) _text - > promptIsEnabled ( ) ) ; // was "_game.hasPrompt", no longer needed
2007-01-16 12:40:51 +00:00
out - > writeSint16BE ( ( int16 ) _game . gameFlags ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
if ( _text - > promptIsEnabled ( ) ) {
out - > writeSint16BE ( 0x7FFF ) ;
} else {
out - > writeSint16BE ( 0 ) ;
}
2007-01-12 02:29:20 +00:00
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < SCRIPT_HEIGHT ; i + + )
2016-02-13 20:42:30 +01:00
out - > writeByte ( _gfx - > saveLoadGetPriority ( i ) ) ;
// Version 10+: Save, if priority table got modified (set.pri.base opcode)
out - > writeSint16BE ( ( int16 ) _gfx - > saveLoadWasPriorityTableModified ( ) ) ;
2007-09-19 08:40:12 +00:00
2007-01-16 12:40:51 +00:00
out - > writeSint16BE ( ( int16 ) _game . gfxMode ) ;
2016-01-29 13:13:40 +01:00
out - > writeByte ( _text - > inputGetCursorChar ( ) ) ;
out - > writeSint16BE ( ( int16 ) _text - > charAttrib_GetForeground ( ) ) ;
out - > writeSint16BE ( ( int16 ) _text - > charAttrib_GetBackground ( ) ) ;
2006-05-23 23:43:52 +00:00
2009-06-06 17:39:13 +00:00
// game.hires
// game.sbuf
// game.ego_words
// game.num_ego_words
2006-05-23 23:43:52 +00:00
2007-01-16 12:40:51 +00:00
out - > writeSint16BE ( ( int16 ) _game . numObjects ) ;
for ( i = 0 ; i < ( int16 ) _game . numObjects ; i + + )
out - > writeSint16BE ( ( int16 ) objectGetLocation ( i ) ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
// Version 7+: save controller key mappings
// required for games, that do not set all key mappings right at the start
// when quick restoring is used from ScummVM menu, only 1 cycle is executed
for ( i = 0 ; i < MAX_CONTROLLER_KEYMAPPINGS ; i + + ) {
out - > writeUint16BE ( _game . controllerKeyMapping [ i ] . keycode ) ;
out - > writeByte ( _game . controllerKeyMapping [ i ] . controllerSlot ) ;
}
2016-02-04 22:53:15 +01:00
// Version 8+: hold-key-mode
// required for at least Mixed Up Mother Goose
out - > writeByte ( _keyHoldMode ) ;
2009-06-06 17:39:13 +00:00
// game.ev_keyp
2006-05-23 23:43:52 +00:00
for ( i = 0 ; i < MAX_STRINGS ; i + + )
2007-01-16 12:40:51 +00:00
out - > write ( _game . strings [ i ] , MAX_STRINGLEN ) ;
2006-05-23 23:43:52 +00:00
2009-06-06 17:39:13 +00:00
// record info about loaded resources
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < MAX_DIRECTORY_ENTRIES ; i + + ) {
2007-01-16 12:40:51 +00:00
out - > writeByte ( _game . dirLogic [ i ] . flags ) ;
out - > writeSint16BE ( ( int16 ) _game . logics [ i ] . sIP ) ;
out - > writeSint16BE ( ( int16 ) _game . logics [ i ] . cIP ) ;
2006-05-23 23:43:52 +00:00
}
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < MAX_DIRECTORY_ENTRIES ; i + + )
2007-01-16 12:40:51 +00:00
out - > writeByte ( _game . dirPic [ i ] . flags ) ;
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < MAX_DIRECTORY_ENTRIES ; i + + )
2007-01-16 12:40:51 +00:00
out - > writeByte ( _game . dirView [ i ] . flags ) ;
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < MAX_DIRECTORY_ENTRIES ; i + + )
2007-01-16 12:40:51 +00:00
out - > writeByte ( _game . dirSound [ i ] . flags ) ;
2006-05-23 23:43:52 +00:00
2009-06-06 17:39:13 +00:00
// game.pictures
// game.logics
// game.views
// game.sounds
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < SCREENOBJECTS_MAX ; i + + ) {
ScreenObjEntry * screenObj = & _game . screenObjTable [ i ] ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
out - > writeByte ( screenObj - > stepTime ) ;
out - > writeByte ( screenObj - > stepTimeCount ) ;
out - > writeByte ( screenObj - > objectNr ) ;
out - > writeSint16BE ( screenObj - > xPos ) ;
out - > writeSint16BE ( screenObj - > yPos ) ;
out - > writeByte ( screenObj - > currentViewNr ) ;
2006-05-23 23:43:52 +00:00
2009-06-06 17:39:13 +00:00
// v->view_data
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
out - > writeByte ( screenObj - > currentLoopNr ) ;
out - > writeByte ( screenObj - > loopCount ) ;
2006-05-23 23:43:52 +00:00
2009-06-06 17:39:13 +00:00
// v->loop_data
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
out - > writeByte ( screenObj - > currentCelNr ) ;
out - > writeByte ( screenObj - > celCount ) ;
2006-05-23 23:43:52 +00:00
2009-06-06 17:39:13 +00:00
// v->cel_data
// v->cel_data_2
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
out - > writeSint16BE ( screenObj - > xPos_prev ) ;
out - > writeSint16BE ( screenObj - > yPos_prev ) ;
2006-05-23 23:43:52 +00:00
2009-06-06 17:39:13 +00:00
// v->s
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
out - > writeSint16BE ( screenObj - > xSize ) ;
out - > writeSint16BE ( screenObj - > ySize ) ;
out - > writeByte ( screenObj - > stepSize ) ;
out - > writeByte ( screenObj - > cycleTime ) ;
out - > writeByte ( screenObj - > cycleTimeCount ) ;
out - > writeByte ( screenObj - > direction ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
out - > writeByte ( screenObj - > motionType ) ;
out - > writeByte ( screenObj - > cycle ) ;
2016-02-19 02:00:03 +01:00
// Version 11+: loop_flag, was saved previously under vt.parm1
out - > writeByte ( screenObj - > loop_flag ) ;
2016-01-29 13:13:40 +01:00
out - > writeByte ( screenObj - > priority ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
out - > writeUint16BE ( screenObj - > flags ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
// this was done so that saved games compatibility isn't broken
switch ( screenObj - > motionType ) {
case kMotionNormal :
out - > writeByte ( 0 ) ;
out - > writeByte ( 0 ) ;
out - > writeByte ( 0 ) ;
out - > writeByte ( 0 ) ;
break ;
case kMotionWander :
out - > writeByte ( screenObj - > wander_count ) ;
out - > writeByte ( 0 ) ;
out - > writeByte ( 0 ) ;
out - > writeByte ( 0 ) ;
break ;
case kMotionFollowEgo :
out - > writeByte ( screenObj - > follow_stepSize ) ;
out - > writeByte ( screenObj - > follow_flag ) ;
out - > writeByte ( screenObj - > follow_count ) ;
out - > writeByte ( 0 ) ;
break ;
case kMotionEgo :
case kMotionMoveObj :
out - > writeByte ( ( byte ) screenObj - > move_x ) ; // problematic! int16 -> byte
out - > writeByte ( ( byte ) screenObj - > move_y ) ;
out - > writeByte ( screenObj - > move_stepSize ) ;
out - > writeByte ( screenObj - > move_flag ) ;
break ;
default :
error ( " unknown motion-type " ) ;
}
2006-05-23 23:43:52 +00:00
}
2009-06-06 17:39:13 +00:00
// Save image stack
2006-05-23 23:43:52 +00:00
2012-02-22 20:20:55 +01:00
for ( Common : : Stack < ImageStackElement > : : size_type j = 0 ; j < _imageStack . size ( ) ; + + j ) {
const ImageStackElement & ise = _imageStack [ j ] ;
2007-11-22 10:32:36 +00:00
out - > writeByte ( ise . type ) ;
out - > writeSint16BE ( ise . parm1 ) ;
out - > writeSint16BE ( ise . parm2 ) ;
out - > writeSint16BE ( ise . parm3 ) ;
out - > writeSint16BE ( ise . parm4 ) ;
out - > writeSint16BE ( ise . parm5 ) ;
out - > writeSint16BE ( ise . parm6 ) ;
out - > writeSint16BE ( ise . parm7 ) ;
2006-05-23 23:43:52 +00:00
}
2007-01-12 02:29:20 +00:00
out - > writeByte ( 0 ) ;
2007-03-22 22:03:21 +00:00
//Write which file number AGIPAL is using (0 if not being used)
2007-03-22 22:29:00 +00:00
out - > writeSint16BE ( _gfx - > getAGIPalFileNum ( ) ) ;
2007-03-22 22:03:21 +00:00
2007-02-17 18:55:51 +00:00
out - > finalize ( ) ;
2009-05-19 11:42:14 +00:00
if ( out - > err ( ) ) {
2011-09-25 21:25:25 +03:00
warning ( " Can't write file '%s'. (Disk full?) " , fileName . c_str ( ) ) ;
2007-08-04 06:18:28 +00:00
result = errIOError ;
} else
2016-01-29 13:13:40 +01:00
debugC ( 1 , kDebugLevelMain | kDebugLevelSavegame , " Saved game %s in file %s " , descriptionString . c_str ( ) , fileName . c_str ( ) ) ;
2007-09-19 08:40:12 +00:00
2007-01-12 02:29:20 +00:00
delete out ;
2011-09-25 21:25:25 +03:00
debugC ( 3 , kDebugLevelMain | kDebugLevelSavegame , " Closed %s " , fileName . c_str ( ) ) ;
2008-08-10 22:53:43 +00:00
2007-08-04 06:18:28 +00:00
return result ;
2006-05-23 23:43:52 +00:00
}
2011-09-26 09:27:15 +02:00
int AgiEngine : : loadGame ( const Common : : String & fileName , bool checkId ) {
2016-01-29 13:13:40 +01:00
char description [ SAVEDGAME_DESCRIPTION_LEN + 1 ] ;
byte saveVersion = 0 ;
char loadId [ 8 ] ;
int i , vtEntries = SCREENOBJECTS_MAX ;
2006-05-23 23:43:52 +00:00
uint8 t ;
int16 parm [ 7 ] ;
2007-01-12 02:29:20 +00:00
Common : : InSaveFile * in ;
2016-01-31 20:53:36 +01:00
bool totalPlayTimeWasSet = false ;
2016-02-19 02:00:03 +01:00
byte oldLoopFlag = 0 ;
2006-05-23 23:43:52 +00:00
2011-09-25 21:25:25 +03:00
debugC ( 3 , kDebugLevelMain | kDebugLevelSavegame , " AgiEngine::loadGame(%s) " , fileName . c_str ( ) ) ;
2006-05-23 23:43:52 +00:00
2007-01-12 02:29:20 +00:00
if ( ! ( in = _saveFileMan - > openForLoading ( fileName ) ) ) {
2011-09-25 21:25:25 +03:00
warning ( " Can't open file '%s', game not loaded " , fileName . c_str ( ) ) ;
2007-01-16 12:40:51 +00:00
return errBadFileOpen ;
2007-01-12 02:29:20 +00:00
} else {
2011-09-25 21:25:25 +03:00
debugC ( 3 , kDebugLevelMain | kDebugLevelSavegame , " Successfully opened %s for reading " , fileName . c_str ( ) ) ;
2006-05-23 23:43:52 +00:00
}
2007-01-12 02:29:20 +00:00
uint32 typea = in - > readUint32BE ( ) ;
if ( typea = = AGIflag ) {
debugC ( 6 , kDebugLevelMain | kDebugLevelSavegame , " Has AGI flag, good start " ) ;
} else {
warning ( " This doesn't appear to be an AGI savegame, game not restored " ) ;
2007-09-19 08:40:12 +00:00
delete in ;
2007-01-16 12:40:51 +00:00
return errOK ;
2007-09-19 08:40:12 +00:00
}
2007-01-12 02:29:20 +00:00
2016-01-29 13:13:40 +01:00
assert ( SAVEDGAME_DESCRIPTION_LEN + 1 = = 31 ) ; // safety
in - > read ( description , 31 ) ; // skip description
2007-09-19 08:40:12 +00:00
2016-01-29 13:13:40 +01:00
// check, if there is a terminating NUL inside description
uint16 descriptionPos = 0 ;
while ( description [ descriptionPos ] ) {
descriptionPos + + ;
if ( descriptionPos > = sizeof ( description ) )
error ( " saved game description is corrupt " ) ;
}
2007-01-12 02:29:20 +00:00
debugC ( 6 , kDebugLevelMain | kDebugLevelSavegame , " Description is: %s " , description ) ;
2007-09-19 08:40:12 +00:00
2007-01-12 02:29:20 +00:00
saveVersion = in - > readByte ( ) ;
2016-02-02 18:43:36 +01:00
if ( saveVersion < 2 ) // is the save game pre-ScummVM?
2016-01-29 13:13:40 +01:00
warning ( " Old save game version (%d, current version is %d). Will try and read anyway, but don't be surprised if bad things happen " , saveVersion , SAVEGAME_CURRENT_VERSION ) ;
2007-01-12 02:29:20 +00:00
2008-11-10 19:02:47 +00:00
if ( saveVersion < 3 )
warning ( " This save game contains no AGIPAL data, if the game is using the AGIPAL hack, it won't work correctly " ) ;
2016-01-29 13:13:40 +01:00
if ( saveVersion > SAVEGAME_CURRENT_VERSION )
error ( " Saved game was created with a newer version of ScummVM. Unable to load. " ) ;
2008-11-10 19:02:47 +00:00
if ( saveVersion > = 4 ) {
// We don't need the thumbnail here, so just read it and discard it
2011-08-07 14:34:15 +02:00
Graphics : : skipThumbnail ( * in ) ;
2008-11-10 19:02:47 +00:00
2016-02-02 18:43:36 +01:00
in - > readUint32BE ( ) ; // save date
2016-02-13 13:40:47 +01:00
in - > readUint16BE ( ) ; // save time (hour + minute)
if ( saveVersion > = 9 ) {
in - > readByte ( ) ; // save time seconds
}
2012-03-27 00:50:29 +03:00
if ( saveVersion > = 6 ) {
uint32 playTime = in - > readUint32BE ( ) ;
2016-01-31 20:53:36 +01:00
inGameTimerReset ( playTime * 1000 ) ;
totalPlayTimeWasSet = true ;
2012-03-27 00:50:29 +03:00
}
2008-11-10 19:02:47 +00:00
}
2016-02-03 03:07:50 +01:00
in - > readByte ( ) ; // was _game.state, not needed anymore
2007-09-19 08:40:12 +00:00
2007-01-12 02:29:20 +00:00
in - > read ( loadId , 8 ) ;
2011-11-02 22:20:40 +00:00
if ( strcmp ( loadId , _game . id ) ! = 0 & & checkId ) {
2007-09-19 08:40:12 +00:00
delete in ;
2007-01-16 12:40:51 +00:00
warning ( " This save seems to be from a different AGI game (save from %s, running %s), not loaded " , loadId , _game . id ) ;
return errBadFileOpen ;
2006-05-23 23:43:52 +00:00
}
2013-04-27 08:44:20 +03:00
Common : : strlcpy ( _game . id , loadId , 8 ) ;
2009-01-07 20:18:38 +00:00
2009-06-06 17:41:50 +00:00
if ( saveVersion > = 5 ) {
char md5 [ 32 + 1 ] ;
for ( i = 0 ; i < 32 ; i + + ) {
md5 [ i ] = in - > readByte ( ) ;
}
md5 [ i ] = 0 ; // terminate
2009-09-23 13:17:08 +00:00
// As noted above in AgiEngine::saveGame the MD5 sum field may be all zero
// when the save was made via a fallback matched game. In this case we will
// replace the MD5 sum with a nicer string, so that the user can easily see
// this fact in the debug output. The string saved in "md5" will never match
// any valid MD5 sum, thus it is safe to do that here.
if ( md5 [ 0 ] = = 0 )
strcpy ( md5 , " fallback matched " ) ;
debug ( 0 , " Saved game MD5: \" %s \" " , md5 ) ;
if ( ! getGameMD5 ( ) ) {
2010-09-18 10:55:16 +00:00
warning ( " Since your game was only detected via the fallback detector, there is no possibility to assure the save is compatible with your game version " ) ;
2009-06-06 17:41:50 +00:00
2009-09-23 13:17:08 +00:00
debug ( 0 , " The game used for saving is \" %s \" . " , md5 ) ;
2011-11-02 22:20:40 +00:00
} else if ( strcmp ( md5 , getGameMD5 ( ) ) ! = 0 ) {
2009-06-06 17:41:50 +00:00
warning ( " Game was saved with different gamedata - you may encounter problems " ) ;
2009-09-23 13:17:08 +00:00
debug ( 0 , " Your game is \" %s \" and save is \" %s \" . " , getGameMD5 ( ) , md5 ) ;
2009-06-06 17:41:50 +00:00
}
}
2010-01-25 01:39:44 +00:00
2016-01-29 13:13:40 +01:00
if ( saveVersion > = 7 ) {
// Restore automatic saving state (set.simple opcode)
_game . automaticSave = in - > readByte ( ) ;
in - > read ( _game . automaticSaveDescription , 31 ) ;
} else {
_game . automaticSave = false ;
_game . automaticSaveDescription [ 0 ] = 0 ;
}
2006-05-23 23:43:52 +00:00
for ( i = 0 ; i < MAX_FLAGS ; i + + )
2007-01-16 12:40:51 +00:00
_game . flags [ i ] = in - > readByte ( ) ;
2006-05-23 23:43:52 +00:00
for ( i = 0 ; i < MAX_VARS ; i + + )
2007-01-16 12:40:51 +00:00
_game . vars [ i ] = in - > readByte ( ) ;
2006-05-23 23:43:52 +00:00
2016-01-31 20:53:36 +01:00
if ( ! totalPlayTimeWasSet ) {
// If we haven't gotten total play time by now, try to calculate it by using VM Variables
// This will happen for at least saves before version 6
// Direct access because otherwise we would trigger an update to these variables according to ScummVM total play time
byte playTimeSeconds = _game . vars [ VM_VAR_SECONDS ] ;
byte playTimeMinutes = _game . vars [ VM_VAR_MINUTES ] ;
byte playTimeHours = _game . vars [ VM_VAR_HOURS ] ;
byte playTimeDays = _game . vars [ VM_VAR_DAYS ] ;
uint32 playTime = ( playTimeSeconds + ( playTimeMinutes * 60 ) + ( playTimeHours * 3600 ) + ( playTimeDays * 86400 ) ) * 1000 ;
inGameTimerReset ( playTime ) ;
}
2016-01-29 13:13:40 +01:00
setVar ( VM_VAR_FREE_PAGES , 180 ) ; // Set amount of free memory to realistic value (Overwriting the just loaded value)
2008-01-02 00:35:32 +00:00
2007-01-16 12:40:51 +00:00
_game . horizon = in - > readSint16BE ( ) ;
2016-01-29 13:13:40 +01:00
_text - > statusRow_Set ( in - > readSint16BE ( ) ) ;
_text - > promptRow_Set ( in - > readSint16BE ( ) ) ;
_text - > configureScreen ( in - > readSint16BE ( ) ) ;
2007-09-19 08:40:12 +00:00
2009-06-06 17:39:13 +00:00
// These are never saved
2016-01-29 13:13:40 +01:00
_text - > promptReset ( ) ;
2016-02-02 23:02:50 +01:00
in - > readSint16BE ( ) ; // was _game.inputMode, not needed anymore
2016-01-29 13:13:40 +01:00
2016-02-01 16:21:13 +01:00
_game . curLogicNr = in - > readSint16BE ( ) ;
2007-01-16 12:40:51 +00:00
_game . playerControl = in - > readSint16BE ( ) ;
2008-07-07 23:24:12 +00:00
if ( in - > readSint16BE ( ) )
2008-07-09 02:27:05 +00:00
quitGame ( ) ;
2016-01-29 13:13:40 +01:00
if ( in - > readSint16BE ( ) ) {
_text - > statusEnable ( ) ;
} else {
_text - > statusDisable ( ) ;
}
2016-02-01 15:54:32 +01:00
in - > readSint16BE ( ) ; // was clock enabled, no longer needed
2007-01-16 12:40:51 +00:00
_game . exitAllLogics = in - > readSint16BE ( ) ;
2016-01-29 13:13:40 +01:00
in - > readSint16BE ( ) ; // was _game.pictureShown
2016-02-01 16:13:19 +01:00
in - > readSint16BE ( ) ; // was _game.hasPrompt, no longer needed
2007-01-16 12:40:51 +00:00
_game . gameFlags = in - > readSint16BE ( ) ;
2016-01-29 13:13:40 +01:00
if ( in - > readSint16BE ( ) ) {
_text - > promptEnable ( ) ;
} else {
_text - > promptDisable ( ) ;
}
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < SCRIPT_HEIGHT ; i + + )
2016-02-13 20:42:30 +01:00
_gfx - > saveLoadSetPriority ( i , in - > readByte ( ) ) ;
if ( saveVersion > = 10 ) {
// Version 10+: priority table was modified by scripts
int16 priorityTableWasModified = in - > readSint16BE ( ) ;
if ( priorityTableWasModified ) {
_gfx - > saveLoadSetPriorityTableModifiedBool ( true ) ;
} else {
_gfx - > saveLoadSetPriorityTableModifiedBool ( false ) ;
}
} else {
// Try to figure it out by ourselves
_gfx - > saveLoadFigureOutPriorityTableModifiedBool ( ) ;
}
2006-12-06 19:27:02 +00:00
2016-01-29 13:13:40 +01:00
_text - > closeWindow ( ) ;
2006-05-23 23:43:52 +00:00
2007-01-16 12:40:51 +00:00
_game . block . active = false ;
2006-05-23 23:43:52 +00:00
2007-01-16 12:40:51 +00:00
_game . gfxMode = in - > readSint16BE ( ) ;
2016-01-29 13:13:40 +01:00
_text - > inputSetCursorChar ( in - > readByte ( ) ) ;
int16 textForeground = in - > readSint16BE ( ) ;
int16 textBackground = in - > readSint16BE ( ) ;
_text - > charAttrib_Set ( textForeground , textBackground ) ;
2006-05-23 23:43:52 +00:00
2009-06-06 17:39:13 +00:00
// game.ego_words - fixed by clean_input
// game.num_ego_words - fixed by clean_input
2006-05-23 23:43:52 +00:00
2007-01-16 12:40:51 +00:00
_game . numObjects = in - > readSint16BE ( ) ;
for ( i = 0 ; i < ( int16 ) _game . numObjects ; i + + )
objectSetLocation ( i , in - > readSint16BE ( ) ) ;
2006-05-23 23:43:52 +00:00
2009-06-06 17:39:13 +00:00
// Those are not serialized
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < MAX_CONTROLLERS ; i + + ) {
2009-06-06 17:48:09 +00:00
_game . controllerOccured [ i ] = false ;
2006-05-23 23:43:52 +00:00
}
2016-01-29 13:13:40 +01:00
if ( saveVersion > = 7 ) {
// For old saves, we just keep the current controllers
for ( i = 0 ; i < MAX_CONTROLLER_KEYMAPPINGS ; i + + ) {
_game . controllerKeyMapping [ i ] . keycode = in - > readUint16BE ( ) ;
_game . controllerKeyMapping [ i ] . controllerSlot = in - > readByte ( ) ;
}
}
2016-02-04 22:53:15 +01:00
if ( saveVersion > = 8 ) {
// Version 8+: hold-key-mode
if ( in - > readByte ( ) ) {
_keyHoldMode = true ;
} else {
_keyHoldMode = false ;
}
}
2006-05-23 23:43:52 +00:00
for ( i = 0 ; i < MAX_STRINGS ; i + + )
2007-01-16 12:40:51 +00:00
in - > read ( _game . strings [ i ] , MAX_STRINGLEN ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < MAX_DIRECTORY_ENTRIES ; i + + ) {
2007-01-12 02:29:20 +00:00
if ( in - > readByte ( ) & RES_LOADED )
2016-01-29 13:13:40 +01:00
agiLoadResource ( RESOURCETYPE_LOGIC , i ) ;
2006-05-23 23:43:52 +00:00
else
2016-01-29 13:13:40 +01:00
agiUnloadResource ( RESOURCETYPE_LOGIC , i ) ;
2007-01-16 12:40:51 +00:00
_game . logics [ i ] . sIP = in - > readSint16BE ( ) ;
_game . logics [ i ] . cIP = in - > readSint16BE ( ) ;
2006-05-23 23:43:52 +00:00
}
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < MAX_DIRECTORY_ENTRIES ; i + + ) {
2007-01-12 02:29:20 +00:00
if ( in - > readByte ( ) & RES_LOADED )
2016-01-29 13:13:40 +01:00
agiLoadResource ( RESOURCETYPE_PICTURE , i ) ;
2006-05-23 23:43:52 +00:00
else
2016-01-29 13:13:40 +01:00
agiUnloadResource ( RESOURCETYPE_PICTURE , i ) ;
2006-05-23 23:43:52 +00:00
}
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < MAX_DIRECTORY_ENTRIES ; i + + ) {
2007-01-12 02:29:20 +00:00
if ( in - > readByte ( ) & RES_LOADED )
2016-01-29 13:13:40 +01:00
agiLoadResource ( RESOURCETYPE_VIEW , i ) ;
2006-05-23 23:43:52 +00:00
else
2016-01-29 13:13:40 +01:00
agiUnloadResource ( RESOURCETYPE_VIEW , i ) ;
2006-05-23 23:43:52 +00:00
}
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < MAX_DIRECTORY_ENTRIES ; i + + ) {
2007-01-12 02:29:20 +00:00
if ( in - > readByte ( ) & RES_LOADED )
2016-01-29 13:13:40 +01:00
agiLoadResource ( RESOURCETYPE_SOUND , i ) ;
2006-05-23 23:43:52 +00:00
else
2016-01-29 13:13:40 +01:00
agiUnloadResource ( RESOURCETYPE_SOUND , i ) ;
2006-05-23 23:43:52 +00:00
}
2009-06-06 17:39:13 +00:00
// game.pictures - loaded above
// game.logics - loaded above
// game.views - loaded above
// game.sounds - loaded above
2006-05-23 23:43:52 +00:00
2007-01-16 12:40:51 +00:00
for ( i = 0 ; i < vtEntries ; i + + ) {
2016-01-29 13:13:40 +01:00
ScreenObjEntry * screenObj = & _game . screenObjTable [ i ] ;
screenObj - > stepTime = in - > readByte ( ) ;
screenObj - > stepTimeCount = in - > readByte ( ) ;
screenObj - > objectNr = in - > readByte ( ) ;
screenObj - > xPos = in - > readSint16BE ( ) ;
screenObj - > yPos = in - > readSint16BE ( ) ;
screenObj - > currentViewNr = in - > readByte ( ) ;
// screenObj->view_data - fixed below
screenObj - > currentLoopNr = in - > readByte ( ) ;
screenObj - > loopCount = in - > readByte ( ) ;
// screenObj->loop_data - fixed below
screenObj - > currentCelNr = in - > readByte ( ) ;
screenObj - > celCount = in - > readByte ( ) ;
// screenObj->cel_data - fixed below
// screenObj->cel_data_2 - fixed below
screenObj - > xPos_prev = in - > readSint16BE ( ) ;
screenObj - > yPos_prev = in - > readSint16BE ( ) ;
// screenObj->s - fixed below
screenObj - > xSize = in - > readSint16BE ( ) ;
screenObj - > ySize = in - > readSint16BE ( ) ;
screenObj - > stepSize = in - > readByte ( ) ;
screenObj - > cycleTime = in - > readByte ( ) ;
screenObj - > cycleTimeCount = in - > readByte ( ) ;
screenObj - > direction = in - > readByte ( ) ;
screenObj - > motionType = ( MotionType ) in - > readByte ( ) ;
screenObj - > cycle = ( CycleType ) in - > readByte ( ) ;
2016-02-19 02:00:03 +01:00
if ( saveVersion > = 11 ) {
// Version 11+: loop_flag, was previously vt.parm1
screenObj - > loop_flag = in - > readByte ( ) ;
}
2016-01-29 13:13:40 +01:00
screenObj - > priority = in - > readByte ( ) ;
screenObj - > flags = in - > readUint16BE ( ) ;
// this was done so that saved games compatibility isn't broken
switch ( screenObj - > motionType ) {
case kMotionNormal :
2016-02-19 02:00:03 +01:00
oldLoopFlag = in - > readByte ( ) ;
2016-01-29 13:13:40 +01:00
in - > readByte ( ) ;
in - > readByte ( ) ;
in - > readByte ( ) ;
break ;
case kMotionWander :
screenObj - > wander_count = in - > readByte ( ) ;
in - > readByte ( ) ;
in - > readByte ( ) ;
in - > readByte ( ) ;
2016-02-19 02:00:03 +01:00
oldLoopFlag = screenObj - > wander_count ;
2016-01-29 13:13:40 +01:00
break ;
case kMotionFollowEgo :
screenObj - > follow_stepSize = in - > readByte ( ) ;
screenObj - > follow_flag = in - > readByte ( ) ;
screenObj - > follow_count = in - > readByte ( ) ;
in - > readByte ( ) ;
2016-02-19 02:00:03 +01:00
oldLoopFlag = screenObj - > follow_stepSize ;
2016-01-29 13:13:40 +01:00
break ;
case kMotionEgo :
case kMotionMoveObj :
screenObj - > move_x = in - > readByte ( ) ; // problematic! int16 -> byte
screenObj - > move_y = in - > readByte ( ) ;
screenObj - > move_stepSize = in - > readByte ( ) ;
screenObj - > move_flag = in - > readByte ( ) ;
2016-02-19 02:00:03 +01:00
oldLoopFlag = screenObj - > move_x ;
2016-01-29 13:13:40 +01:00
break ;
default :
error ( " unknown motion-type " ) ;
}
2016-02-19 02:00:03 +01:00
if ( saveVersion < 11 ) {
if ( saveVersion < 7 ) {
// Recreate loop_flag from motion-type (was previously vt.parm1)
// vt.parm1 was shared for multiple uses
screenObj - > loop_flag = oldLoopFlag ;
} else {
// for Version 7-10 we can't really do anything, it was not saved
screenObj - > loop_flag = 0 ; // set it to 0
}
}
2006-05-23 23:43:52 +00:00
}
2016-01-29 13:13:40 +01:00
// Fix some pointers in screenObjTable
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
for ( i = 0 ; i < SCREENOBJECTS_MAX ; i + + ) {
ScreenObjEntry * screenObj = & _game . screenObjTable [ i ] ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
if ( _game . dirView [ screenObj - > currentViewNr ] . offset = = _EMPTY )
2006-05-23 23:43:52 +00:00
continue ;
2016-01-29 13:13:40 +01:00
if ( ! ( _game . dirView [ screenObj - > currentViewNr ] . flags & RES_LOADED ) )
agiLoadResource ( RESOURCETYPE_VIEW , screenObj - > currentViewNr ) ;
2006-05-23 23:43:52 +00:00
2016-02-02 18:43:36 +01:00
setView ( screenObj , screenObj - > currentViewNr ) ; // Fix v->view_data
setLoop ( screenObj , screenObj - > currentLoopNr ) ; // Fix v->loop_data
setCel ( screenObj , screenObj - > currentCelNr ) ; // Fix v->cel_data
2006-05-23 23:43:52 +00:00
}
2016-01-29 13:13:40 +01:00
_sprites - > eraseSprites ( ) ;
_game . pictureShown = false ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
_gfx - > clearDisplay ( 0 , false ) ; // clear display screen, but not copy it to actual screen for now b/c transition
2006-05-23 23:43:52 +00:00
2009-06-06 17:39:13 +00:00
// Recreate background from saved image stack
2007-01-16 12:40:51 +00:00
clearImageStack ( ) ;
2007-01-12 02:29:20 +00:00
while ( ( t = in - > readByte ( ) ) ! = 0 ) {
2006-05-23 23:43:52 +00:00
for ( i = 0 ; i < 7 ; i + + )
2007-01-12 02:29:20 +00:00
parm [ i ] = in - > readSint16BE ( ) ;
2007-01-16 12:40:51 +00:00
replayImageStackCall ( t , parm [ 0 ] , parm [ 1 ] , parm [ 2 ] ,
2016-02-02 18:43:36 +01:00
parm [ 3 ] , parm [ 4 ] , parm [ 5 ] , parm [ 6 ] ) ;
2006-05-23 23:43:52 +00:00
}
2009-06-06 17:39:13 +00:00
// Load AGIPAL Data
2007-03-22 22:29:00 +00:00
if ( saveVersion > = 3 )
2007-03-22 22:03:21 +00:00
_gfx - > setAGIPal ( in - > readSint16BE ( ) ) ;
2007-09-19 08:40:12 +00:00
delete in ;
2011-09-25 21:25:25 +03:00
debugC ( 3 , kDebugLevelMain | kDebugLevelSavegame , " Closed %s " , fileName . c_str ( ) ) ;
2006-05-23 23:43:52 +00:00
2016-01-31 17:56:53 +01:00
setFlag ( VM_FLAG_RESTORE_JUST_RAN , true ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
_words - > clearEgoWords ( ) ;
2009-05-19 21:09:43 +00:00
2016-01-29 13:13:40 +01:00
// don't delay anything right after restoring a game
2016-02-11 17:26:25 +01:00
artificialDelay_Reset ( ) ;
2011-12-10 20:12:35 +00:00
2016-01-29 13:13:40 +01:00
_sprites - > eraseSprites ( ) ;
_sprites - > buildAllSpriteLists ( ) ;
_sprites - > drawAllSpriteLists ( ) ;
_picture - > showPicWithTransition ( ) ;
_game . pictureShown = true ;
_text - > statusDraw ( ) ;
_text - > promptRedraw ( ) ;
2011-12-10 20:12:35 +00:00
2016-01-29 13:13:40 +01:00
// copy everything over (we should probably only copy over the remaining parts of the screen w/o play screen
2016-02-10 19:54:03 +01:00
_gfx - > copyDisplayToScreen ( ) ;
2011-12-10 20:12:35 +00:00
2016-01-31 22:14:35 +01:00
// Sync volume settings from ScummVM system settings, so that VM volume variable is overwritten
setVolumeViaSystemSetting ( ) ;
2016-01-29 13:13:40 +01:00
return errOK ;
2006-05-23 23:43:52 +00:00
}
2012-03-27 00:21:16 +03:00
int AgiEngine : : scummVMSaveLoadDialog ( bool isSave ) {
GUI : : SaveLoadChooser * dialog ;
Common : : String desc ;
int slot ;
if ( isSave ) {
2012-06-10 04:14:17 +02:00
dialog = new GUI : : SaveLoadChooser ( _ ( " Save game: " ) , _ ( " Save " ) , true ) ;
2012-03-27 00:21:16 +03:00
2012-06-10 04:49:42 +02:00
slot = dialog - > runModalWithCurrentTarget ( ) ;
2012-03-27 00:21:16 +03:00
desc = dialog - > getResultString ( ) ;
if ( desc . empty ( ) ) {
// create our own description for the saved game, the user didnt enter it
2012-06-10 05:04:59 +02:00
desc = dialog - > createDefaultSaveDescription ( slot ) ;
2012-03-27 00:21:16 +03:00
}
if ( desc . size ( ) > 28 )
desc = Common : : String ( desc . c_str ( ) , 28 ) ;
} else {
2012-06-10 04:14:17 +02:00
dialog = new GUI : : SaveLoadChooser ( _ ( " Restore game: " ) , _ ( " Restore " ) , false ) ;
2012-06-10 04:49:42 +02:00
slot = dialog - > runModalWithCurrentTarget ( ) ;
2012-03-27 00:21:16 +03:00
}
delete dialog ;
2012-05-02 23:51:02 +03:00
if ( slot < 0 )
return true ;
2012-09-26 04:17:31 +02:00
2012-03-27 00:21:16 +03:00
if ( isSave )
return doSave ( slot , desc ) ;
else
return doLoad ( slot , false ) ;
}
int AgiEngine : : doSave ( int slot , const Common : : String & desc ) {
2020-02-05 07:02:14 -08:00
Common : : String fileName = getSaveStateName ( slot ) ;
2012-03-27 00:21:16 +03:00
debugC ( 8 , kDebugLevelMain | kDebugLevelResources , " file is [%s] " , fileName . c_str ( ) ) ;
// Make sure all graphics was blitted to screen. This fixes bug
// #2960567: "AGI: Ego partly erased in Load/Save thumbnails"
2016-01-29 13:13:40 +01:00
_gfx - > updateScreen ( ) ;
// _gfx->doUpdate();
2012-03-27 00:21:16 +03:00
return saveGame ( fileName , desc ) ;
}
int AgiEngine : : doLoad ( int slot , bool showMessages ) {
2020-02-05 07:02:14 -08:00
Common : : String fileName = getSaveStateName ( slot ) ;
2012-03-27 00:21:16 +03:00
debugC ( 8 , kDebugLevelMain | kDebugLevelResources , " file is [%s] " , fileName . c_str ( ) ) ;
2016-01-29 13:13:40 +01:00
_sprites - > eraseSprites ( ) ;
2012-03-27 00:21:16 +03:00
_sound - > stopSound ( ) ;
2016-01-29 13:13:40 +01:00
_text - > closeWindow ( ) ;
2012-03-27 00:21:16 +03:00
int result = loadGame ( fileName ) ;
if ( result = = errOK ) {
2016-02-01 16:15:07 +01:00
_game . exitAllLogics = true ;
2016-01-29 13:13:40 +01:00
_menu - > itemEnableAll ( ) ;
2012-03-27 00:21:16 +03:00
} else {
if ( showMessages )
2016-01-29 13:13:40 +01:00
_text - > messageBox ( " Error restoring game. " ) ;
2012-03-27 00:21:16 +03:00
}
return result ;
}
2016-01-29 13:13:40 +01:00
SavedGameSlotIdArray AgiEngine : : getSavegameSlotIds ( ) {
Common : : StringArray filenames ;
int16 numberPos = _targetName . size ( ) + 1 ;
int16 slotId = 0 ;
SavedGameSlotIdArray slotIdArray ;
2012-03-27 00:21:16 +03:00
2016-01-29 13:13:40 +01:00
// search for saved game filenames...
filenames = _saveFileMan - > listSavefiles ( _targetName + " .### " ) ;
Common : : StringArray : : iterator it ;
2016-05-25 05:37:07 +02:00
Common : : StringArray : : iterator end = filenames . end ( ) ;
2016-01-29 13:13:40 +01:00
// convert to lower-case, just to be sure
for ( it = filenames . begin ( ) ; it ! = end ; it + + ) {
it - > toLowercase ( ) ;
2006-05-23 23:43:52 +00:00
}
2016-01-29 13:13:40 +01:00
// sort
Common : : sort ( filenames . begin ( ) , filenames . end ( ) ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
// now extract slot-Ids
for ( it = filenames . begin ( ) ; it ! = end ; it + + ) {
slotId = atoi ( it - > c_str ( ) + numberPos ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
slotIdArray . push_back ( slotId ) ;
}
return slotIdArray ;
}
2006-05-23 23:43:52 +00:00
2016-02-13 13:40:47 +01:00
bool AgiEngine : : getSavegameInformation ( int16 slotId , Common : : String & saveDescription , uint32 & saveDate , uint32 & saveTime , bool & saveIsValid ) {
2016-01-29 13:13:40 +01:00
Common : : InSaveFile * in ;
2020-02-05 07:02:14 -08:00
Common : : String fileName = getSaveStateName ( slotId ) ;
2016-01-29 13:13:40 +01:00
char saveGameDescription [ 31 ] ;
int16 curPos = 0 ;
byte saveVersion = 0 ;
2012-03-27 00:21:16 +03:00
2016-01-29 13:13:40 +01:00
saveDescription . clear ( ) ;
saveDate = 0 ;
saveTime = 0 ;
saveIsValid = false ;
2011-09-25 19:33:01 +03:00
2016-01-29 13:13:40 +01:00
debugC ( 4 , kDebugLevelMain | kDebugLevelSavegame , " Current game id is %s " , _targetName . c_str ( ) ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
if ( ! ( in = _saveFileMan - > openForLoading ( fileName ) ) ) {
debugC ( 4 , kDebugLevelMain | kDebugLevelSavegame , " File %s does not exist " , fileName . c_str ( ) ) ;
return false ;
} else {
debugC ( 4 , kDebugLevelMain | kDebugLevelSavegame , " Successfully opened %s for reading " , fileName . c_str ( ) ) ;
2012-03-27 00:21:16 +03:00
2016-01-29 13:13:40 +01:00
uint32 type = in - > readUint32BE ( ) ;
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
if ( type ! = AGIflag ) {
warning ( " This doesn't appear to be an AGI savegame " ) ;
saveDescription + = " [ScummVM: not an AGI save] " ;
delete in ;
return true ;
}
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
debugC ( 6 , kDebugLevelMain | kDebugLevelSavegame , " Has AGI flag, good start " ) ;
if ( in - > read ( saveGameDescription , 31 ) ! = 31 ) {
warning ( " unexpected EOF " ) ;
delete in ;
saveDescription + = " [ScummVM: invalid save] " ;
return true ;
}
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
for ( curPos = 0 ; curPos < 31 ; curPos + + ) {
if ( ! saveGameDescription [ curPos ] )
break ;
}
if ( curPos > = 31 ) {
warning ( " corrupted description " ) ;
delete in ;
saveDescription + = " [ScummVM: invalid save] " ;
return true ;
}
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
saveVersion = in - > readByte ( ) ;
if ( saveVersion > SAVEGAME_CURRENT_VERSION ) {
warning ( " save from a future ScummVM, not supported " ) ;
delete in ;
saveDescription + = " [ScummVM: not supported] " ;
return true ;
}
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
if ( saveVersion > = 4 ) {
// We don't need the thumbnail here, so just read it and discard it
Graphics : : skipThumbnail ( * in ) ;
2009-05-19 21:09:43 +00:00
2016-01-29 13:13:40 +01:00
saveDate = in - > readUint32BE ( ) ;
2016-02-13 13:40:47 +01:00
saveTime = in - > readUint16BE ( ) < < 8 ;
if ( saveVersion > = 9 ) {
saveTime | = in - > readByte ( ) ; // add seconds (only available since saved game version 9+)
}
2016-01-29 13:13:40 +01:00
// save date is DDMMYYYY, we need a proper format
byte saveDateDay = saveDate > > 24 ;
byte saveDateMonth = ( saveDate > > 16 ) & 0xFF ;
uint16 saveDateYear = saveDate & 0xFFFF ;
saveDate = ( saveDateYear < < 16 ) | ( saveDateMonth < < 8 ) | saveDateDay ;
} else {
saveDate = 0 ;
saveTime = 0 ;
}
saveDescription + = saveGameDescription ;
saveIsValid = true ;
delete in ;
return true ;
2006-05-23 23:43:52 +00:00
}
2016-01-29 13:13:40 +01:00
}
2006-05-23 23:43:52 +00:00
2016-01-29 13:13:40 +01:00
bool AgiEngine : : loadGameAutomatic ( ) {
int16 automaticRestoreGameSlotId = 0 ;
automaticRestoreGameSlotId = _systemUI - > figureOutAutomaticRestoreGameSlot ( _game . automaticSaveDescription ) ;
if ( automaticRestoreGameSlotId > = 0 ) {
if ( doLoad ( automaticRestoreGameSlotId , true ) = = errOK ) {
return true ;
}
}
return false ;
2007-01-12 02:29:20 +00:00
}
2016-01-29 13:13:40 +01:00
bool AgiEngine : : loadGameDialog ( ) {
int16 restoreGameSlotId = 0 ;
2012-03-27 00:21:16 +03:00
if ( ! ConfMan . getBool ( " originalsaveload " ) )
return scummVMSaveLoadDialog ( false ) ;
2016-01-29 13:13:40 +01:00
restoreGameSlotId = _systemUI - > askForRestoreGameSlot ( ) ;
if ( restoreGameSlotId > = 0 ) {
if ( doLoad ( restoreGameSlotId , true ) = = errOK ) {
return true ;
}
}
return errOK ;
}
// Try to figure out either the slot, that is currently using the automatic saved game description
// or get a new slot.
// If we fail, return false, so that the regular saved game dialog is called
// Original AGI was limited to 12 saves, we are effectively limited to 100 saves at the moment.
//
// btw. this also means that entering an existant name in Mixed Up Mother Goose will effectively overwrite
// that saved game. This is also what original AGI did.
bool AgiEngine : : saveGameAutomatic ( ) {
int16 automaticSaveGameSlotId = 0 ;
automaticSaveGameSlotId = _systemUI - > figureOutAutomaticSaveGameSlot ( _game . automaticSaveDescription ) ;
if ( automaticSaveGameSlotId > = 0 ) {
Common : : String slotDescription ( _game . automaticSaveDescription ) ;
// WORKAROUND: Remove window in case one is currently shown, otherwise it would get saved in the thumbnail
// Happens for Mixed Up Mother Goose. The scripts close the window after saving.
// Original interpreter obviously did not do this, but original interpreter also did not save thumbnails.
_text - > closeWindow ( ) ;
if ( doSave ( automaticSaveGameSlotId , slotDescription ) = = errOK ) {
return true ;
}
}
return false ;
2006-05-23 23:43:52 +00:00
}
2016-01-29 13:13:40 +01:00
bool AgiEngine : : saveGameDialog ( ) {
int16 saveGameSlotId = 0 ;
Common : : String slotDescription ;
if ( ! ConfMan . getBool ( " originalsaveload " ) )
return scummVMSaveLoadDialog ( true ) ;
saveGameSlotId = _systemUI - > askForSaveGameSlot ( ) ;
if ( saveGameSlotId > = 0 ) {
if ( _systemUI - > askForSaveGameDescription ( saveGameSlotId , slotDescription ) ) {
if ( doSave ( saveGameSlotId , slotDescription ) = = errOK ) {
return true ;
}
}
}
return false ;
}
2009-07-22 15:55:33 +00:00
void AgiEngine : : recordImageStackCall ( uint8 type , int16 p1 , int16 p2 , int16 p3 ,
2016-02-02 18:43:36 +01:00
int16 p4 , int16 p5 , int16 p6 , int16 p7 ) {
2009-07-22 15:55:33 +00:00
ImageStackElement pnew ;
pnew . type = type ;
pnew . pad = 0 ;
pnew . parm1 = p1 ;
pnew . parm2 = p2 ;
pnew . parm3 = p3 ;
pnew . parm4 = p4 ;
pnew . parm5 = p5 ;
pnew . parm6 = p6 ;
pnew . parm7 = p7 ;
_imageStack . push ( pnew ) ;
}
void AgiEngine : : replayImageStackCall ( uint8 type , int16 p1 , int16 p2 , int16 p3 ,
2016-02-02 18:43:36 +01:00
int16 p4 , int16 p5 , int16 p6 , int16 p7 ) {
2009-07-22 15:55:33 +00:00
switch ( type ) {
case ADD_PIC :
debugC ( 8 , kDebugLevelMain , " --- decoding picture %d --- " , p1 ) ;
2016-01-29 13:13:40 +01:00
agiLoadResource ( RESOURCETYPE_PICTURE , p1 ) ;
2009-07-22 15:55:33 +00:00
_picture - > decodePicture ( p1 , p2 , p3 ! = 0 ) ;
break ;
case ADD_VIEW :
2016-01-29 13:13:40 +01:00
agiLoadResource ( RESOURCETYPE_VIEW , p1 ) ;
2009-07-22 15:55:33 +00:00
_sprites - > addToPic ( p1 , p2 , p3 , p4 , p5 , p6 , p7 ) ;
break ;
2019-10-14 03:54:10 +01:00
default :
break ;
2009-07-22 15:55:33 +00:00
}
}
2009-11-02 21:54:57 +00:00
void AgiEngine : : clearImageStack ( ) {
2009-07-22 15:55:33 +00:00
_imageStack . clear ( ) ;
}
2009-11-02 21:54:57 +00:00
void AgiEngine : : releaseImageStack ( ) {
2009-07-22 15:55:33 +00:00
_imageStack . clear ( ) ;
}
void AgiEngine : : checkQuickLoad ( ) {
if ( ConfMan . hasKey ( " save_slot " ) ) {
2020-02-05 07:02:14 -08:00
Common : : String saveNameBuffer = getSaveStateName ( ConfMan . getInt ( " save_slot " ) ) ;
2009-07-22 15:55:33 +00:00
2016-01-29 13:13:40 +01:00
_sprites - > eraseSprites ( ) ;
2009-08-03 17:18:18 +00:00
_sound - > stopSound ( ) ;
2016-02-02 18:43:36 +01:00
if ( loadGame ( saveNameBuffer , false ) = = errOK ) { // Do not check game id
2016-02-01 16:15:07 +01:00
_game . exitAllLogics = true ;
2016-01-29 13:13:40 +01:00
_menu - > itemEnableAll ( ) ;
2009-07-22 15:55:33 +00:00
}
}
}
2009-08-03 17:18:18 +00:00
Common : : Error AgiEngine : : loadGameState ( int slot ) {
2020-02-05 07:02:14 -08:00
Common : : String saveLoadSlot = getSaveStateName ( slot ) ;
2009-08-03 17:18:18 +00:00
2016-01-29 13:13:40 +01:00
_sprites - > eraseSprites ( ) ;
2009-08-03 17:18:18 +00:00
_sound - > stopSound ( ) ;
if ( loadGame ( saveLoadSlot ) = = errOK ) {
2016-02-01 16:15:07 +01:00
_game . exitAllLogics = true ;
2016-01-29 13:13:40 +01:00
_menu - > itemEnableAll ( ) ;
2009-08-03 17:18:18 +00:00
return Common : : kNoError ;
} else {
return Common : : kUnknownError ;
}
}
2020-02-04 22:13:33 -08:00
Common : : Error AgiEngine : : saveGameState ( int slot , const Common : : String & description , bool isAutosave ) {
2020-02-05 07:02:14 -08:00
Common : : String saveLoadSlot = getSaveStateName ( slot ) ;
2016-01-29 13:13:40 +01:00
if ( saveGame ( saveLoadSlot , description ) = = errOK )
2009-08-03 17:18:18 +00:00
return Common : : kNoError ;
else
return Common : : kUnknownError ;
}
2007-01-16 12:40:51 +00:00
} // End of namespace Agi