2009-02-17 15:02:16 +00:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
2014-02-18 02:34:24 +01:00
*
2009-02-17 15:02:16 +00:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2014-02-18 02:34:24 +01:00
*
2009-02-17 15:02:16 +00:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
*/
2009-02-15 06:10:59 +00:00
2015-04-26 09:08:46 +02:00
# include "common/savefile.h"
2009-02-21 22:50:35 +00:00
# include "common/stream.h"
# include "common/system.h"
2009-04-27 12:31:27 +00:00
# include "common/func.h"
2009-03-15 20:31:29 +00:00
# include "common/serializer.h"
2017-05-07 14:29:35 -05:00
# include "common/translation.h"
2009-10-11 15:51:43 +00:00
# include "graphics/thumbnail.h"
2009-03-15 20:31:29 +00:00
2009-05-13 16:52:41 +00:00
# include "sci/sci.h"
2009-12-04 17:42:32 +00:00
# include "sci/event.h"
2009-11-12 09:24:46 +00:00
2010-02-13 17:44:58 +00:00
# include "sci/engine/features.h"
2010-06-23 15:23:37 +00:00
# include "sci/engine/kernel.h"
2009-02-27 02:23:40 +00:00
# include "sci/engine/state.h"
2009-10-10 02:16:23 +00:00
# include "sci/engine/message.h"
2009-03-12 03:26:21 +00:00
# include "sci/engine/savegame.h"
2010-06-15 07:20:53 +00:00
# include "sci/engine/selector.h"
2009-11-12 09:24:46 +00:00
# include "sci/engine/vm_types.h"
2010-01-29 11:05:06 +00:00
# include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS
2010-11-01 16:09:33 +00:00
# include "sci/graphics/helpers.h"
2016-02-09 01:28:08 +01:00
# include "sci/graphics/menu.h"
2010-07-23 07:36:55 +00:00
# include "sci/graphics/palette.h"
2010-01-31 12:35:15 +00:00
# include "sci/graphics/ports.h"
2015-04-26 15:50:12 +02:00
# include "sci/graphics/screen.h"
2010-12-24 14:47:47 +00:00
# include "sci/parser/vocabulary.h"
2010-01-05 01:22:16 +00:00
# include "sci/sound/audio.h"
# include "sci/sound/music.h"
2009-02-15 06:10:59 +00:00
2011-02-14 22:38:12 -05:00
# ifdef ENABLE_SCI32
2017-09-08 19:09:45 -05:00
# include "common/config-manager.h"
2017-09-07 00:35:25 -05:00
# include "common/gui_options.h"
2017-02-05 12:08:56 -06:00
# include "sci/engine/guest_additions.h"
2016-07-31 13:41:05 -05:00
# include "sci/graphics/cursor32.h"
2011-02-14 22:38:12 -05:00
# include "sci/graphics/frameout.h"
2016-06-25 14:19:47 -05:00
# include "sci/graphics/palette32.h"
# include "sci/graphics/remap32.h"
2017-02-25 18:13:55 -06:00
# include "sci/graphics/video32.h"
2011-02-14 22:38:12 -05:00
# endif
2009-02-21 10:23:36 +00:00
namespace Sci {
2016-03-23 19:16:11 +01:00
// These are serialization functions for various objects.
void syncWithSerializer ( Common : : Serializer & s , Common : : Serializable & obj ) {
2009-05-04 15:05:11 +00:00
obj . saveLoadWithSerializer ( s ) ;
}
2017-06-07 13:07:27 -05:00
void syncWithSerializer ( Common : : Serializer & s , ResourceId & obj ) {
s . syncAsByte ( obj . _type ) ;
s . syncAsUint16LE ( obj . _number ) ;
s . syncAsUint32LE ( obj . _tuple ) ;
}
2016-03-23 19:16:11 +01:00
void syncWithSerializer ( Common : : Serializer & s , reg_t & obj ) {
s . syncAsUint16LE ( obj . _segment ) ;
s . syncAsUint16LE ( obj . _offset ) ;
}
void syncWithSerializer ( Common : : Serializer & s , synonym_t & obj ) {
s . syncAsUint16LE ( obj . replaceant ) ;
s . syncAsUint16LE ( obj . replacement ) ;
}
void syncWithSerializer ( Common : : Serializer & s , Class & obj ) {
s . syncAsSint32LE ( obj . script ) ;
syncWithSerializer ( s , obj . reg ) ;
}
2016-03-23 20:41:48 +01:00
void syncWithSerializer ( Common : : Serializer & s , List & obj ) {
2016-03-23 19:16:11 +01:00
syncWithSerializer ( s , obj . first ) ;
syncWithSerializer ( s , obj . last ) ;
}
2016-03-23 20:41:48 +01:00
void syncWithSerializer ( Common : : Serializer & s , Node & obj ) {
2016-03-23 19:16:11 +01:00
syncWithSerializer ( s , obj . pred ) ;
syncWithSerializer ( s , obj . succ ) ;
syncWithSerializer ( s , obj . key ) ;
syncWithSerializer ( s , obj . value ) ;
}
# pragma mark -
2009-05-04 15:05:11 +00:00
// By default, sync using syncWithSerializer, which in turn can easily be overloaded.
2012-02-10 21:14:48 -06:00
template < typename T >
2009-05-04 15:05:11 +00:00
struct DefaultSyncer : Common : : BinaryFunction < Common : : Serializer , T , void > {
2016-08-13 09:35:39 -05:00
void operator ( ) ( Common : : Serializer & s , T & obj , int ) const {
2009-05-04 15:05:11 +00:00
syncWithSerializer ( s , obj ) ;
}
} ;
2016-03-23 20:41:48 +01:00
// Syncer for entries in a segment obj table
template < typename T >
struct SegmentObjTableEntrySyncer : Common : : BinaryFunction < Common : : Serializer , typename T : : Entry & , void > {
2016-08-13 09:35:39 -05:00
void operator ( ) ( Common : : Serializer & s , typename T : : Entry & entry , int index ) const {
2016-03-23 20:41:48 +01:00
s . syncAsSint32LE ( entry . next_free ) ;
2017-01-16 07:22:32 +00:00
bool hasData = false ;
2016-08-13 09:35:39 -05:00
if ( s . getVersion ( ) > = 37 ) {
if ( s . isSaving ( ) ) {
hasData = entry . data ! = nullptr ;
}
s . syncAsByte ( hasData ) ;
} else {
hasData = ( entry . next_free = = index ) ;
}
if ( hasData ) {
if ( s . isLoading ( ) ) {
entry . data = new typename T : : value_type ;
}
syncWithSerializer ( s , * entry . data ) ;
} else if ( s . isLoading ( ) ) {
if ( s . getVersion ( ) < 37 ) {
typename T : : value_type dummy ;
syncWithSerializer ( s , dummy ) ;
}
entry . data = nullptr ;
}
2016-03-23 20:41:48 +01:00
}
} ;
2009-03-24 11:31:16 +00:00
/**
* Sync a Common : : Array using a Common : : Serializer .
* When saving , this writes the length of the array , then syncs ( writes ) all entries .
* When loading , it loads the length of the array , then resizes it accordingly , before
* syncing all entries .
*
2009-05-04 15:05:11 +00:00
* Note : This shouldn ' t be in common / array . h nor in common / serializer . h , after
* all , not all code using arrays wants to use the serializer , and vice versa .
* But we could put this into a separate header file in common / at some point .
* Something like common / serializer - extras . h or so .
2009-03-24 11:31:16 +00:00
*
* TODO : Add something like this for lists , queues . . . .
*/
2012-02-10 21:14:48 -06:00
template < typename T , class Syncer = DefaultSyncer < T > >
2009-05-04 15:05:11 +00:00
struct ArraySyncer : Common : : BinaryFunction < Common : : Serializer , T , void > {
void operator ( ) ( Common : : Serializer & s , Common : : Array < T > & arr ) const {
uint len = arr . size ( ) ;
s . syncAsUint32LE ( len ) ;
Syncer sync ;
// Resize the array if loading.
if ( s . isLoading ( ) )
arr . resize ( len ) ;
2016-08-13 16:09:49 -05:00
for ( uint i = 0 ; i < len ; + + i ) {
2016-08-13 09:35:39 -05:00
sync ( s , arr [ i ] , i ) ;
2009-05-04 15:05:11 +00:00
}
2009-03-24 11:31:16 +00:00
}
2009-05-04 15:05:11 +00:00
} ;
2009-02-28 11:07:36 +00:00
2009-05-04 15:05:11 +00:00
// Convenience wrapper
2016-04-06 23:22:44 +02:00
template < typename T >
void syncArray ( Common : : Serializer & s , Common : : Array < T > & arr ) {
ArraySyncer < T > sync ;
sync ( s , arr ) ;
}
template < typename T , class Syncer >
2009-05-04 15:05:11 +00:00
void syncArray ( Common : : Serializer & s , Common : : Array < T > & arr ) {
2016-03-23 20:41:48 +01:00
ArraySyncer < T , Syncer > sync ;
2009-05-04 15:05:11 +00:00
sync ( s , arr ) ;
2009-04-27 12:31:27 +00:00
}
2009-05-03 09:25:15 +00:00
void SegManager : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2011-02-11 17:05:27 +00:00
if ( s . isLoading ( ) ) {
2010-07-01 16:06:04 +00:00
resetSegMan ( ) ;
2009-09-06 13:00:30 +00:00
// Reset _scriptSegMap, to be restored below
_scriptSegMap . clear ( ) ;
}
2011-02-11 17:05:27 +00:00
s . skip ( 4 , VER ( 14 ) , VER ( 18 ) ) ; // OBSOLETE: Used to be _exportsAreWide
2009-05-03 09:25:15 +00:00
2009-05-11 13:32:00 +00:00
uint sync_heap_size = _heap . size ( ) ;
s . syncAsUint32LE ( sync_heap_size ) ;
2009-05-03 22:46:11 +00:00
_heap . resize ( sync_heap_size ) ;
2009-09-06 13:00:30 +00:00
for ( uint i = 0 ; i < sync_heap_size ; + + i ) {
2009-09-17 00:45:12 +00:00
SegmentObj * & mobj = _heap [ i ] ;
2009-09-06 13:00:30 +00:00
2009-09-17 13:22:46 +00:00
// Sync the segment type
2009-09-17 00:45:12 +00:00
SegmentType type = ( s . isSaving ( ) & & mobj ) ? mobj - > getType ( ) : SEG_TYPE_INVALID ;
2009-09-06 13:00:30 +00:00
s . syncAsUint32LE ( type ) ;
2011-02-15 01:30:33 +02:00
if ( type = = SEG_TYPE_HUNK ) {
// Don't save or load HunkTable segments
2009-09-06 13:00:30 +00:00
continue ;
2011-02-15 01:30:33 +02:00
} else if ( type = = SEG_TYPE_INVALID ) {
// If we were saving and mobj == 0, or if we are loading and this is an
// entry marked as empty -> skip to next
2009-10-13 18:54:20 +00:00
continue ;
2011-02-15 01:30:33 +02:00
} else if ( type = = 5 ) {
// Don't save or load the obsolete system string segments
2010-12-07 00:47:05 +00:00
if ( s . isSaving ( ) ) {
continue ;
} else {
// Old saved game. Skip the data.
Common : : String tmp ;
2010-12-07 01:12:48 +00:00
for ( int j = 0 ; j < 4 ; j + + ) {
2010-12-07 00:47:05 +00:00
s . syncString ( tmp ) ; // OBSOLETE: name
s . skip ( 4 ) ; // OBSOLETE: maxSize
s . syncString ( tmp ) ; // OBSOLETE: value
}
_heap [ i ] = NULL ; // set as freed
continue ;
}
2011-02-15 01:30:33 +02:00
# ifdef ENABLE_SCI32
2011-02-14 22:38:12 -05:00
} else if ( type = = SEG_TYPE_ARRAY ) {
2011-02-15 01:30:33 +02:00
_arraysSegId = i ;
2016-07-29 15:48:14 -05:00
} else if ( s . getVersion ( ) > = 36 & & type = = SEG_TYPE_BITMAP ) {
_bitmapSegId = i ;
2011-02-15 01:30:33 +02:00
# endif
2011-02-14 22:38:12 -05:00
}
2010-12-07 00:47:05 +00:00
2010-07-07 20:12:41 +00:00
if ( s . isLoading ( ) )
2009-09-17 00:45:12 +00:00
mobj = SegmentObj : : createSegmentObj ( type ) ;
2010-07-07 20:12:41 +00:00
2009-09-06 13:00:30 +00:00
assert ( mobj ) ;
2012-06-15 12:53:17 +03:00
// Let the object sync custom data. Scripts are loaded at this point.
2009-09-06 13:00:30 +00:00
mobj - > saveLoadWithSerializer ( s ) ;
2011-02-14 16:01:04 -05:00
if ( type = = SEG_TYPE_SCRIPT ) {
2010-11-08 00:18:34 +00:00
Script * scr = ( Script * ) mobj ;
2011-02-11 17:05:27 +00:00
if ( s . isLoading ( ) ) {
_scriptSegMap [ scr - > getScriptNumber ( ) ] = i ;
}
2010-11-08 00:18:34 +00:00
2011-02-14 16:01:04 -05:00
if ( s . getVersion ( ) > = 28 )
scr - > syncStringHeap ( s ) ;
2010-11-08 00:18:34 +00:00
}
2009-09-06 13:00:30 +00:00
}
2009-05-03 09:25:15 +00:00
2010-07-01 16:04:29 +00:00
s . syncAsSint32LE ( _clonesSegId ) ;
s . syncAsSint32LE ( _listsSegId ) ;
s . syncAsSint32LE ( _nodesSegId ) ;
2010-07-01 16:05:10 +00:00
syncArray < Class > ( s , _classTable ) ;
2010-11-08 00:18:34 +00:00
2017-02-08 18:52:20 -06:00
if ( s . isLoading ( ) ) {
// Now that all scripts are loaded, init their objects.
// Just like in Script::initializeObjectsSci0, we do two passes
// in case an object is loaded before its base.
int passes = getSciVersion ( ) < SCI_VERSION_1_1 ? 2 : 1 ;
for ( int pass = 1 ; pass < = passes ; + + pass ) {
for ( uint i = 0 ; i < _heap . size ( ) ; i + + ) {
if ( ! _heap [ i ] | | _heap [ i ] - > getType ( ) ! = SEG_TYPE_SCRIPT )
continue ;
Script * scr = ( Script * ) _heap [ i ] ;
scr - > syncLocalsBlock ( this ) ;
2013-10-27 01:17:28 +02:00
2017-05-20 19:50:56 -05:00
ObjMap & objects = scr - > getObjectMap ( ) ;
2017-02-08 18:52:20 -06:00
for ( ObjMap : : iterator it = objects . begin ( ) ; it ! = objects . end ( ) ; + + it ) {
reg_t addr = it - > _value . getPos ( ) ;
2017-07-15 16:34:39 -05:00
if ( pass = = 1 ) {
scr - > scriptObjInit ( addr , false ) ;
} else {
Object * obj = scr - > getObject ( addr . getOffset ( ) ) ;
2017-05-20 16:16:44 -05:00
// When a game disposes a script with kDisposeScript,
// the script is marked as deleted and its lockers are
// set to 0, which makes the GC stop using the script
// as a retainer of its own objects. Most of the time,
// this means that the script and all of its objects are
// cleaned up on the next GC cycle, but occasionally a
// game will retain a reference to an object within a
// disposed script somewhere else, which keeps the
// script (and all of its objects) alive. This does not
// prevent the GC from safely collecting other objects
// that had only been retained by now-unreachable script
// objects, so references held by these unreachable
// objects may be invalidated. If the superclass of one
// of these objects is GC'd (because it was the only
// retainer of the superclass), and a save game is
// created after kDisposeScript is called but before
// the script actually becomes collectable, it will
// cause the `initBaseObject` call to fail on restore,
// but this is fine because the object isn't reachable
// anyway (it is just waiting to be GC'd).
//
// For example, in EcoQuest floppy, after opening the
// gate for Delphineus at the beginning of the game,
// the game calls to dispose script 380, but there are
// still reachable references to the script 380 object
// `outsideGateLever` at `CueObj::client` and
// `OnMeAndLowY::theObj`, so script 380 (and all of its
// objects) are retained. However, the now-unreachable
// `fJump` object had been the only retainer of its
// superclass `JumpTo`, so the `JumpTo` class gets
// GC'd, and the `fJump` object is left with no valid
// superclass. If the game is saved and restored at this
// point, `initBaseObject` will fail on `fJump` because
// it has no superclass (but, again, this is fine
// because this is an unreachable object). Later,
// `outsideGateLever` becomes unreachable as the
// `CueObj::client` and `OnMeAndLowY::theObj` properties
// are changed, which means that all script 380 objects
// are finally unreachable and the script and its
// objects get fully disposed.
//
// All that said, if a script has lockers and the base
// object necessary for restoring the object is still
// missing, that is probably a real bug.
if ( ! obj - > initBaseObject ( this , addr , false ) & & scr - > getLockers ( ) ) {
warning ( " Failed to locate base object %04x:%04x for object %04x:%04x (%s); skipping " , PRINT_REG ( obj - > getSpeciesSelector ( ) ) , PRINT_REG ( addr ) , getObjectName ( addr ) ) ;
2017-02-08 18:52:20 -06:00
}
2013-10-27 01:17:28 +02:00
}
2010-11-08 00:18:34 +00:00
}
2017-02-05 12:08:56 -06:00
2017-02-08 21:45:13 -06:00
# ifdef ENABLE_SCI32
2017-02-08 18:52:20 -06:00
if ( pass = = passes ) {
g_sci - > _guestAdditions - > segManSaveLoadScriptHook ( * scr ) ;
}
2017-02-08 21:45:13 -06:00
# endif
2017-02-05 12:08:56 -06:00
}
2010-11-08 00:18:34 +00:00
}
}
2009-02-28 11:12:59 +00:00
}
2009-03-15 20:31:29 +00:00
static void sync_SavegameMetadata ( Common : : Serializer & s , SavegameMetadata & obj ) {
2010-10-31 01:45:24 +00:00
s . syncString ( obj . name ) ;
2009-09-06 12:59:56 +00:00
s . syncVersion ( CURRENT_SAVEGAME_VERSION ) ;
2010-10-31 01:45:24 +00:00
obj . version = s . getVersion ( ) ;
s . syncString ( obj . gameVersion ) ;
s . syncAsSint32LE ( obj . saveDate ) ;
s . syncAsSint32LE ( obj . saveTime ) ;
2010-06-15 08:25:51 +00:00
if ( s . getVersion ( ) < 22 ) {
2010-10-31 01:45:24 +00:00
obj . gameObjectOffset = 0 ;
obj . script0Size = 0 ;
2010-06-15 08:25:51 +00:00
} else {
2010-10-31 01:45:24 +00:00
s . syncAsUint16LE ( obj . gameObjectOffset ) ;
s . syncAsUint16LE ( obj . script0Size ) ;
}
// Playtime
obj . playTime = 0 ;
if ( s . isLoading ( ) ) {
if ( s . getVersion ( ) > = 26 )
s . syncAsUint32LE ( obj . playTime ) ;
} else {
2016-01-14 10:54:27 -06:00
if ( s . getVersion ( ) > = 34 ) {
obj . playTime = g_sci - > getTickCount ( ) ;
} else {
obj . playTime = g_engine - > getTotalPlayTime ( ) / 1000 ;
}
2010-10-31 01:45:24 +00:00
s . syncAsUint32LE ( obj . playTime ) ;
2010-06-15 08:25:51 +00:00
}
2016-09-15 21:08:44 -05:00
2016-09-21 16:16:21 -05:00
// Some games require additional metadata to display their restore screens
2016-09-21 12:10:06 -05:00
// correctly
2016-09-30 19:05:14 -05:00
if ( s . getVersion ( ) > = 39 ) {
2016-09-15 21:08:44 -05:00
if ( s . isSaving ( ) ) {
2016-09-21 12:10:06 -05:00
const reg_t * globals = g_sci - > getEngineState ( ) - > variables [ VAR_GLOBAL ] ;
2016-09-15 21:08:44 -05:00
if ( g_sci - > getGameId ( ) = = GID_SHIVERS ) {
2016-09-26 19:28:51 -05:00
obj . lowScore = globals [ kGlobalVarScore ] . toUint16 ( ) ;
obj . highScore = globals [ kGlobalVarShivers1Score ] . toUint16 ( ) ;
2016-09-21 16:16:21 -05:00
obj . avatarId = 0 ;
2016-09-21 12:10:06 -05:00
} else if ( g_sci - > getGameId ( ) = = GID_MOTHERGOOSEHIRES ) {
2016-09-21 16:16:21 -05:00
obj . lowScore = obj . highScore = 0 ;
2016-09-26 19:28:51 -05:00
obj . avatarId = readSelectorValue ( g_sci - > getEngineState ( ) - > _segMan , globals [ kGlobalVarEgo ] , SELECTOR ( view ) ) ;
2016-09-21 16:16:21 -05:00
} else {
obj . lowScore = obj . highScore = obj . avatarId = 0 ;
2016-09-15 21:08:44 -05:00
}
}
2016-09-21 16:16:21 -05:00
s . syncAsUint16LE ( obj . lowScore ) ;
s . syncAsUint16LE ( obj . highScore ) ;
s . syncAsByte ( obj . avatarId ) ;
2016-09-15 21:08:44 -05:00
}
2009-02-15 06:10:59 +00:00
}
2009-03-15 20:31:29 +00:00
void EngineState : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2009-09-25 13:02:11 +00:00
Common : : String tmp ;
2010-10-31 01:45:24 +00:00
s . syncString ( tmp , VER ( 14 ) , VER ( 23 ) ) ; // OBSOLETE: Used to be gameVersion
2009-02-15 22:28:12 +00:00
2010-07-23 08:22:13 +00:00
if ( getSciVersion ( ) < = SCI_VERSION_1_1 ) {
2010-06-10 10:27:13 +00:00
// Save/Load picPort as well for SCI0-SCI1.1. Necessary for Castle of Dr. Brain,
// as the picPort has been changed when loading during the intro
2009-12-08 20:54:18 +00:00
int16 picPortTop , picPortLeft ;
Common : : Rect picPortRect ;
2010-01-30 02:03:59 +00:00
2010-06-10 10:27:13 +00:00
if ( s . isSaving ( ) )
2010-02-13 17:43:31 +00:00
picPortRect = g_sci - > _gfxPorts - > kernelGetPicWindow ( picPortTop , picPortLeft ) ;
2009-12-08 20:54:18 +00:00
2009-12-09 07:28:04 +00:00
s . syncAsSint16LE ( picPortRect . top ) ;
s . syncAsSint16LE ( picPortRect . left ) ;
s . syncAsSint16LE ( picPortRect . bottom ) ;
s . syncAsSint16LE ( picPortRect . right ) ;
2009-12-08 20:54:18 +00:00
s . syncAsSint16LE ( picPortTop ) ;
s . syncAsSint16LE ( picPortLeft ) ;
2010-06-10 10:15:32 +00:00
2010-06-10 10:27:13 +00:00
if ( s . isLoading ( ) )
2010-06-10 10:15:32 +00:00
g_sci - > _gfxPorts - > kernelSetPicWindow ( picPortRect , picPortTop , picPortLeft , false ) ;
2009-12-08 20:54:18 +00:00
}
2017-02-25 18:13:55 -06:00
# ifdef ENABLE_SCI32
if ( getSciVersion ( ) > = SCI_VERSION_2 ) {
g_sci - > _video32 - > beforeSaveLoadWithSerializer ( s ) ;
}
# endif
2010-06-08 18:23:38 +00:00
_segMan - > saveLoadWithSerializer ( s ) ;
2009-03-15 20:31:29 +00:00
2010-06-29 09:00:08 +00:00
g_sci - > _soundCmd - > syncPlayList ( s ) ;
2016-07-09 12:41:12 -05:00
# ifdef ENABLE_SCI32
if ( getSciVersion ( ) > = SCI_VERSION_2 ) {
g_sci - > _gfxPalette32 - > saveLoadWithSerializer ( s ) ;
2016-08-06 10:02:02 -05:00
g_sci - > _gfxRemap32 - > saveLoadWithSerializer ( s ) ;
2016-08-28 13:04:02 -05:00
g_sci - > _gfxCursor32 - > saveLoadWithSerializer ( s ) ;
2017-06-16 16:32:16 -05:00
g_sci - > _audio32 - > saveLoadWithSerializer ( s ) ;
2017-02-25 18:13:55 -06:00
g_sci - > _video32 - > saveLoadWithSerializer ( s ) ;
2016-07-09 12:41:12 -05:00
} else
# endif
g_sci - > _gfxPalette16 - > saveLoadWithSerializer ( s ) ;
2009-02-15 06:10:59 +00:00
}
2010-12-24 14:47:47 +00:00
void Vocabulary : : saveLoadWithSerializer ( Common : : Serializer & s ) {
syncArray < synonym_t > ( s , _synonyms ) ;
}
2009-05-19 00:34:10 +00:00
void LocalVariables : : saveLoadWithSerializer ( Common : : Serializer & s ) {
s . syncAsSint32LE ( script_id ) ;
syncArray < reg_t > ( s , _locals ) ;
2009-02-15 06:10:59 +00:00
}
2009-10-10 15:58:51 +00:00
void Object : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2017-02-17 11:50:33 -06:00
s . syncAsSint32LE ( _isFreed ) ;
2010-08-04 08:26:09 +00:00
syncWithSerializer ( s , _pos ) ;
2009-10-10 15:58:51 +00:00
s . syncAsSint32LE ( _methodCount ) ; // that's actually a uint16
syncArray < reg_t > ( s , _variables ) ;
2017-04-17 11:25:31 -05:00
# ifdef ENABLE_SCI32
2017-02-09 19:11:58 -06:00
if ( s . getVersion ( ) > = 42 & & getSciVersion ( ) = = SCI_VERSION_3 ) {
2017-04-17 11:25:31 -05:00
// Obsolete mustSetViewVisible array
if ( s . getVersion ( ) = = 42 & & s . isLoading ( ) ) {
uint32 len ;
s . syncAsUint32LE ( len ) ;
s . skip ( len ) ;
}
2017-02-09 19:11:58 -06:00
syncWithSerializer ( s , _superClassPosSci3 ) ;
syncWithSerializer ( s , _speciesSelectorSci3 ) ;
syncWithSerializer ( s , _infoSelectorSci3 ) ;
}
2017-04-17 11:25:31 -05:00
# endif
2009-02-15 06:10:59 +00:00
}
2010-01-30 02:03:59 +00:00
2012-02-10 21:14:48 -06:00
template < typename T >
2009-05-11 13:32:00 +00:00
void sync_Table ( Common : : Serializer & s , T & obj ) {
2009-03-15 20:31:29 +00:00
s . syncAsSint32LE ( obj . first_free ) ;
s . syncAsSint32LE ( obj . entries_used ) ;
2009-02-15 06:10:59 +00:00
2016-03-23 20:41:48 +01:00
syncArray < typename T : : Entry , SegmentObjTableEntrySyncer < T > > ( s , obj . _table ) ;
2009-02-15 06:10:59 +00:00
}
2009-05-19 00:34:10 +00:00
void CloneTable : : saveLoadWithSerializer ( Common : : Serializer & s ) {
sync_Table < CloneTable > ( s , * this ) ;
}
void NodeTable : : saveLoadWithSerializer ( Common : : Serializer & s ) {
sync_Table < NodeTable > ( s , * this ) ;
}
void ListTable : : saveLoadWithSerializer ( Common : : Serializer & s ) {
sync_Table < ListTable > ( s , * this ) ;
}
void HunkTable : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2009-10-13 18:54:20 +00:00
// Do nothing, hunk tables are not actually saved nor loaded.
2009-05-19 00:34:10 +00:00
}
2010-11-08 00:18:34 +00:00
void Script : : syncStringHeap ( Common : : Serializer & s ) {
if ( getSciVersion ( ) < SCI_VERSION_1_1 ) {
2010-11-09 11:57:54 +00:00
// Sync all of the SCI_OBJ_STRINGS blocks
2017-04-30 12:38:50 -05:00
SciSpan < byte > buf = * _buf ;
2010-11-08 00:18:34 +00:00
bool oldScriptHeader = ( getSciVersion ( ) = = SCI_VERSION_0_EARLY ) ;
if ( oldScriptHeader )
buf + = 2 ;
2016-12-31 20:39:57 -06:00
for ( ; ; ) {
int blockType = buf . getUint16LEAt ( 0 ) ;
2010-11-08 11:02:43 +00:00
int blockSize ;
2010-11-08 00:18:34 +00:00
if ( blockType = = 0 )
break ;
2010-11-08 11:02:43 +00:00
2016-12-31 20:39:57 -06:00
blockSize = buf . getUint16LEAt ( 2 ) ;
2010-11-08 11:02:43 +00:00
assert ( blockSize > 0 ) ;
2010-11-08 00:18:34 +00:00
if ( blockType = = SCI_OBJ_STRINGS )
2016-12-31 20:39:57 -06:00
s . syncBytes ( buf . getUnsafeDataAt ( 0 , blockSize ) , blockSize ) ;
2010-11-08 00:18:34 +00:00
buf + = blockSize ;
2016-12-31 20:39:57 -06:00
}
2010-11-08 00:18:34 +00:00
2015-12-29 01:44:11 +01:00
} else if ( getSciVersion ( ) > = SCI_VERSION_1_1 & & getSciVersion ( ) < = SCI_VERSION_2_1_LATE ) {
2010-11-08 00:18:34 +00:00
// Strings in SCI1.1 come after the object instances
2017-04-30 12:38:50 -05:00
SciSpan < byte > buf = _heap . subspan ( 4 + _heap . getUint16SEAt ( 2 ) * 2 ) ;
2010-11-08 00:18:34 +00:00
// Skip all of the objects
2016-12-31 20:39:57 -06:00
while ( buf . getUint16SEAt ( 0 ) = = SCRIPT_OBJECT_MAGIC_NUMBER )
buf + = buf . getUint16SEAt ( 2 ) * 2 ;
2010-11-08 00:18:34 +00:00
// Now, sync everything till the end of the buffer
2016-12-31 20:39:57 -06:00
const int length = _heap . size ( ) - ( buf - _heap ) ;
s . syncBytes ( buf . getUnsafeDataAt ( 0 , length ) , length ) ;
2012-06-14 12:12:30 +03:00
} else if ( getSciVersion ( ) = = SCI_VERSION_3 ) {
2017-04-22 20:44:51 -05:00
const int stringOffset = _buf - > getInt32SEAt ( 4 ) ;
const int length = _buf - > getInt32SEAt ( 8 ) - stringOffset ;
2017-04-30 12:38:50 -05:00
s . syncBytes ( _buf - > getUnsafeDataAt ( stringOffset , length ) , length ) ;
2010-11-08 00:18:34 +00:00
}
}
2009-05-19 00:34:10 +00:00
void Script : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2009-09-16 23:32:27 +00:00
s . syncAsSint32LE ( _nr ) ;
2010-06-22 08:57:25 +00:00
if ( s . isLoading ( ) )
2013-12-04 20:42:16 +01:00
load ( _nr , g_sci - > getResMan ( ) , g_sci - > getScriptPatcher ( ) ) ;
2010-07-23 08:22:13 +00:00
s . skip ( 4 , VER ( 14 ) , VER ( 22 ) ) ; // OBSOLETE: Used to be _bufSize
s . skip ( 4 , VER ( 14 ) , VER ( 22 ) ) ; // OBSOLETE: Used to be _scriptSize
s . skip ( 4 , VER ( 14 ) , VER ( 22 ) ) ; // OBSOLETE: Used to be _heapSize
2009-05-03 09:25:15 +00:00
2010-07-23 08:22:13 +00:00
s . skip ( 4 , VER ( 14 ) , VER ( 19 ) ) ; // OBSOLETE: Used to be _numExports
s . skip ( 4 , VER ( 14 ) , VER ( 19 ) ) ; // OBSOLETE: Used to be _numSynonyms
2009-09-16 23:32:27 +00:00
s . syncAsSint32LE ( _lockers ) ;
2009-03-15 20:31:29 +00:00
2009-09-21 21:38:43 +00:00
// Sync _objects. This is a hashmap, and we use the following on disk format:
// First we store the number of items in the hashmap, then we store each
// object (which is an 'Object' instance). For loading, we take advantage
// of the fact that the key of each Object obj is just obj._pos.offset !
// By "chance" this format is identical to the format used to sync Common::Array<>,
// hence we can still old savegames with identical code :).
uint numObjs = _objects . size ( ) ;
s . syncAsUint32LE ( numObjs ) ;
if ( s . isLoading ( ) ) {
_objects . clear ( ) ;
Object tmp ;
for ( uint i = 0 ; i < numObjs ; + + i ) {
2010-08-04 08:26:09 +00:00
syncWithSerializer ( s , tmp ) ;
2012-06-18 05:21:59 +03:00
_objects [ tmp . getPos ( ) . getOffset ( ) ] = tmp ;
2009-09-21 21:38:43 +00:00
}
} else {
ObjMap : : iterator it ;
const ObjMap : : iterator end = _objects . end ( ) ;
for ( it = _objects . begin ( ) ; it ! = end ; + + it ) {
2010-08-04 08:26:09 +00:00
syncWithSerializer ( s , it - > _value ) ;
2009-09-21 21:38:43 +00:00
}
}
2009-03-15 20:31:29 +00:00
2010-07-23 08:22:13 +00:00
s . skip ( 4 , VER ( 14 ) , VER ( 20 ) ) ; // OBSOLETE: Used to be _localsOffset
2009-09-16 23:32:27 +00:00
s . syncAsSint32LE ( _localsSegment ) ;
2009-03-15 20:31:29 +00:00
2009-05-19 00:34:10 +00:00
s . syncAsSint32LE ( _markedAsDeleted ) ;
2009-02-24 02:59:50 +00:00
}
2009-05-19 00:34:10 +00:00
void DynMem : : saveLoadWithSerializer ( Common : : Serializer & s ) {
s . syncAsSint32LE ( _size ) ;
2009-09-14 22:34:53 +00:00
s . syncString ( _description ) ;
2009-05-19 00:34:10 +00:00
if ( ! _buf & & _size ) {
_buf = ( byte * ) calloc ( _size , 1 ) ;
2009-02-15 06:10:59 +00:00
}
2009-05-19 00:34:10 +00:00
if ( _size )
s . syncBytes ( _buf , _size ) ;
2009-03-15 20:31:29 +00:00
}
2009-02-15 06:10:59 +00:00
2009-05-19 00:34:10 +00:00
void DataStack : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2009-09-22 00:35:46 +00:00
s . syncAsUint32LE ( _capacity ) ;
2009-05-11 13:32:00 +00:00
if ( s . isLoading ( ) ) {
2010-06-06 23:00:33 +00:00
free ( _entries ) ;
2009-09-22 00:35:46 +00:00
_entries = ( reg_t * ) calloc ( _capacity , sizeof ( reg_t ) ) ;
2009-05-11 13:32:00 +00:00
}
}
2009-03-15 20:31:29 +00:00
# pragma mark -
2009-12-27 11:43:34 +00:00
void SciMusic : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2009-12-26 00:22:21 +00:00
// Sync song lib data. When loading, the actual song lib will be initialized
// afterwards in gamestate_restore()
2009-12-25 13:18:48 +00:00
int songcount = 0 ;
2009-12-27 14:11:26 +00:00
byte masterVolume = soundGetMasterVolume ( ) ;
2010-01-22 12:26:12 +00:00
byte reverb = _pMidiDrv - > getReverb ( ) ;
2009-12-27 14:11:26 +00:00
if ( s . isSaving ( ) ) {
s . syncAsByte ( _soundOn ) ;
s . syncAsByte ( masterVolume ) ;
2010-01-22 12:26:12 +00:00
s . syncAsByte ( reverb , VER ( 17 ) ) ;
2009-12-27 14:11:26 +00:00
} else if ( s . isLoading ( ) ) {
2010-01-01 14:22:07 +00:00
if ( s . getVersion ( ) > = 15 ) {
2009-12-27 14:11:26 +00:00
s . syncAsByte ( _soundOn ) ;
s . syncAsByte ( masterVolume ) ;
2010-01-22 12:26:12 +00:00
reverb = 0 ;
s . syncAsByte ( reverb , VER ( 17 ) ) ;
2009-12-27 14:11:26 +00:00
} else {
_soundOn = true ;
masterVolume = 15 ;
2010-01-22 12:26:12 +00:00
reverb = 0 ;
2009-12-27 14:11:26 +00:00
}
soundSetSoundOn ( _soundOn ) ;
soundSetMasterVolume ( masterVolume ) ;
2010-11-24 16:01:30 +00:00
setGlobalReverb ( reverb ) ;
2009-12-27 14:11:26 +00:00
}
2009-12-25 13:18:48 +00:00
if ( s . isSaving ( ) )
2009-12-27 11:43:34 +00:00
songcount = _playList . size ( ) ;
2009-12-25 13:18:48 +00:00
s . syncAsUint32LE ( songcount ) ;
2009-12-26 00:22:21 +00:00
2011-10-21 22:24:59 +02:00
if ( s . isLoading ( ) )
2009-12-27 23:46:11 +00:00
clearPlayList ( ) ;
2009-12-25 13:18:48 +00:00
2011-10-21 22:24:59 +02:00
Common : : StackLock lock ( _mutex ) ;
if ( s . isLoading ( ) ) {
2009-12-25 13:18:48 +00:00
for ( int i = 0 ; i < songcount ; i + + ) {
MusicEntry * curSong = new MusicEntry ( ) ;
2009-12-28 21:03:13 +00:00
curSong - > saveLoadWithSerializer ( s ) ;
2009-12-27 11:43:34 +00:00
_playList . push_back ( curSong ) ;
2009-12-25 13:18:48 +00:00
}
} else {
for ( int i = 0 ; i < songcount ; i + + ) {
2009-12-28 21:03:13 +00:00
_playList [ i ] - > saveLoadWithSerializer ( s ) ;
2009-12-25 13:18:48 +00:00
}
}
2009-12-27 12:12:14 +00:00
}
2009-03-15 20:31:29 +00:00
2010-08-04 08:25:39 +00:00
void MusicEntry : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2010-08-04 08:26:09 +00:00
syncWithSerializer ( s , soundObj ) ;
2010-08-04 08:25:39 +00:00
s . syncAsSint16LE ( resourceId ) ;
s . syncAsSint16LE ( dataInc ) ;
s . syncAsSint16LE ( ticker ) ;
s . syncAsSint16LE ( signal , VER ( 17 ) ) ;
2013-09-21 14:34:42 +02:00
if ( s . getVersion ( ) > = 31 ) // FE sound/music.h -> priority
2013-09-21 14:27:16 +02:00
s . syncAsSint16LE ( priority ) ;
else
s . syncAsByte ( priority ) ;
2010-08-04 08:25:39 +00:00
s . syncAsSint16LE ( loop , VER ( 17 ) ) ;
s . syncAsByte ( volume ) ;
s . syncAsByte ( hold , VER ( 17 ) ) ;
s . syncAsByte ( fadeTo ) ;
s . syncAsSint16LE ( fadeStep ) ;
s . syncAsSint32LE ( fadeTicker ) ;
s . syncAsSint32LE ( fadeTickerStep ) ;
s . syncAsByte ( status ) ;
2014-10-07 19:48:18 +02:00
if ( s . getVersion ( ) > = 32 )
s . syncAsByte ( playBed ) ;
else if ( s . isLoading ( ) )
playBed = false ;
2015-02-14 15:20:23 +01:00
if ( s . getVersion ( ) > = 33 )
s . syncAsByte ( overridePriority ) ;
else if ( s . isLoading ( ) )
overridePriority = false ;
2010-08-04 08:25:39 +00:00
// pMidiParser and pStreamAud will be initialized when the
// sound list is reconstructed in gamestate_restore()
if ( s . isLoading ( ) ) {
soundRes = 0 ;
pMidiParser = 0 ;
pStreamAud = 0 ;
2010-11-25 16:09:45 +00:00
reverb = - 1 ; // invalid reverb, will be initialized in processInitSound()
2010-08-04 08:25:39 +00:00
}
}
2010-06-15 07:20:53 +00:00
void SoundCommandParser : : syncPlayList ( Common : : Serializer & s ) {
_music - > saveLoadWithSerializer ( s ) ;
}
2010-10-31 20:57:50 +00:00
void SoundCommandParser : : reconstructPlayList ( ) {
2016-10-08 22:32:13 +02:00
_music - > _mutex . lock ( ) ;
2010-06-15 07:20:53 +00:00
2014-10-07 21:24:49 +02:00
// We store all songs here because starting songs may re-shuffle their order
MusicList songs ;
for ( MusicList : : iterator i = _music - > getPlayListStart ( ) ; i ! = _music - > getPlayListEnd ( ) ; + + i )
songs . push_back ( * i ) ;
2016-10-08 22:32:13 +02:00
// Done with main playlist, so release lock
_music - > _mutex . unlock ( ) ;
2014-10-07 21:24:49 +02:00
for ( MusicList : : iterator i = songs . begin ( ) ; i ! = songs . end ( ) ; + + i ) {
2016-11-02 14:30:23 -05:00
MusicEntry * entry = * i ;
initSoundResource ( entry ) ;
2011-09-26 19:57:50 +03:00
2016-11-02 14:30:23 -05:00
# ifdef ENABLE_SCI32
2017-06-16 16:32:16 -05:00
if ( _soundVersion > = SCI_VERSION_2 & & entry - > isSample ) {
2016-11-02 14:30:23 -05:00
const reg_t & soundObj = entry - > soundObj ;
2017-06-16 16:32:16 -05:00
if ( readSelectorValue ( _segMan , soundObj , SELECTOR ( loop ) ) = = 0xFFFF & &
2016-11-02 14:30:23 -05:00
readSelector ( _segMan , soundObj , SELECTOR ( handle ) ) ! = NULL_REG ) {
writeSelector ( _segMan , soundObj , SELECTOR ( handle ) , NULL_REG ) ;
processPlaySound ( soundObj , entry - > playBed ) ;
}
} else
# endif
if ( entry - > status = = kSoundPlaying ) {
2014-12-13 14:08:34 +01:00
// WORKAROUND: PQ3 (German?) scripts can set volume negative in the
// sound object directly without going through DoSound.
// Since we re-read this selector when re-playing the sound after loading,
// this will lead to unexpected behaviour. As a workaround we
// sync the sound object's selectors here. (See bug #5501)
2016-11-02 14:30:23 -05:00
writeSelectorValue ( _segMan , entry - > soundObj , SELECTOR ( loop ) , entry - > loop ) ;
writeSelectorValue ( _segMan , entry - > soundObj , SELECTOR ( priority ) , entry - > priority ) ;
2010-11-11 10:09:57 +00:00
if ( _soundVersion > = SCI_VERSION_1_EARLY )
2016-11-02 14:30:23 -05:00
writeSelectorValue ( _segMan , entry - > soundObj , SELECTOR ( vol ) , entry - > volume ) ;
2010-11-11 10:09:57 +00:00
2016-11-02 14:30:23 -05:00
processPlaySound ( entry - > soundObj , entry - > playBed ) ;
2010-06-15 07:20:53 +00:00
}
}
}
2010-01-30 02:03:59 +00:00
# ifdef ENABLE_SCI32
void ArrayTable : : saveLoadWithSerializer ( Common : : Serializer & ser ) {
if ( ser . getVersion ( ) < 18 )
return ;
sync_Table < ArrayTable > ( ser , * this ) ;
}
2016-09-04 16:30:47 -05:00
void SciArray : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2016-09-30 13:09:12 -05:00
uint16 savedSize ;
2016-09-04 16:30:47 -05:00
if ( s . isSaving ( ) ) {
2016-09-30 13:09:12 -05:00
savedSize = _size ;
2016-09-04 16:30:47 -05:00
}
s . syncAsByte ( _type ) ;
s . syncAsByte ( _elementSize ) ;
2016-09-30 13:09:12 -05:00
s . syncAsUint16LE ( savedSize ) ;
2010-01-30 02:03:59 +00:00
2016-09-04 16:30:47 -05:00
if ( s . isLoading ( ) ) {
2016-09-30 13:09:12 -05:00
resize ( savedSize ) ;
2016-09-04 16:30:47 -05:00
}
switch ( _type ) {
case kArrayTypeInt16 :
case kArrayTypeID :
2016-09-30 13:09:12 -05:00
for ( int i = 0 ; i < savedSize ; + + i ) {
2016-09-04 16:30:47 -05:00
syncWithSerializer ( s , ( ( reg_t * ) _data ) [ i ] ) ;
}
break ;
2016-10-03 16:02:59 -05:00
case kArrayTypeByte :
case kArrayTypeString :
s . syncBytes ( ( byte * ) _data , savedSize ) ;
break ;
2016-09-04 16:30:47 -05:00
default :
error ( " Attempt to sync invalid SciArray type %d " , _type ) ;
}
2010-01-30 02:03:59 +00:00
}
2016-07-29 15:48:14 -05:00
void BitmapTable : : saveLoadWithSerializer ( Common : : Serializer & ser ) {
2016-08-01 09:52:12 -05:00
if ( ser . getVersion ( ) < 36 ) {
2016-07-29 15:48:14 -05:00
return ;
2016-08-01 09:52:12 -05:00
}
2016-07-29 15:48:14 -05:00
sync_Table ( ser , * this ) ;
}
2016-08-01 09:52:12 -05:00
void SciBitmap : : saveLoadWithSerializer ( Common : : Serializer & s ) {
if ( s . getVersion ( ) < 36 ) {
return ;
}
s . syncAsByte ( _gc ) ;
s . syncAsUint32LE ( _dataSize ) ;
if ( s . isLoading ( ) ) {
_data = ( byte * ) malloc ( _dataSize ) ;
}
s . syncBytes ( _data , _dataSize ) ;
if ( s . isLoading ( ) ) {
2017-10-06 22:05:57 -05:00
_buffer . init ( getWidth ( ) , getHeight ( ) , getWidth ( ) , getPixels ( ) , Graphics : : PixelFormat : : createFormatCLUT8 ( ) ) ;
2016-08-01 09:52:12 -05:00
}
}
2010-01-30 02:03:59 +00:00
# endif
2010-07-27 17:51:44 +00:00
void GfxPalette : : palVarySaveLoadPalette ( Common : : Serializer & s , Palette * palette ) {
s . syncBytes ( palette - > mapping , 256 ) ;
s . syncAsUint32LE ( palette - > timestamp ) ;
for ( int i = 0 ; i < 256 ; i + + ) {
s . syncAsByte ( palette - > colors [ i ] . used ) ;
s . syncAsByte ( palette - > colors [ i ] . r ) ;
s . syncAsByte ( palette - > colors [ i ] . g ) ;
s . syncAsByte ( palette - > colors [ i ] . b ) ;
}
s . syncBytes ( palette - > intensity , 256 ) ;
}
2010-07-23 07:36:55 +00:00
void GfxPalette : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2010-09-24 09:56:50 +00:00
if ( s . getVersion ( ) > = 25 ) {
// We need to save intensity of the _sysPalette at least for kq6 when entering the dark cave (room 390)
// from room 340. scripts will set intensity to 60 for this room and restore them when leaving.
// Sierra SCI is also doing this (although obviously not for SCI0->SCI01 games, still it doesn't hurt
2013-04-27 14:02:49 +03:00
// to save it everywhere). Refer to bug #3072868
2010-09-24 09:56:50 +00:00
s . syncBytes ( _sysPalette . intensity , 256 ) ;
2010-07-23 08:08:27 +00:00
}
2010-09-24 09:56:50 +00:00
if ( s . getVersion ( ) > = 24 ) {
if ( s . isLoading ( ) & & _palVaryResourceId ! = - 1 )
palVaryRemoveTimer ( ) ;
s . syncAsSint32LE ( _palVaryResourceId ) ;
2016-12-18 20:14:12 +01:00
if ( _palVaryResourceId ! = - 1 | | s . getVersion ( ) > = 40 ) {
if ( _palVaryResourceId ! = - 1 ) {
palVarySaveLoadPalette ( s , & _palVaryOriginPalette ) ;
palVarySaveLoadPalette ( s , & _palVaryTargetPalette ) ;
}
2010-09-24 09:56:50 +00:00
s . syncAsSint16LE ( _palVaryStep ) ;
s . syncAsSint16LE ( _palVaryStepStop ) ;
s . syncAsSint16LE ( _palVaryDirection ) ;
s . syncAsUint16LE ( _palVaryTicks ) ;
s . syncAsSint32LE ( _palVaryPaused ) ;
2016-12-18 20:14:12 +01:00
if ( s . getVersion ( ) > = 40 )
s . syncAsSint32LE ( _palVarySignal ) ;
2010-09-24 09:56:50 +00:00
}
2010-07-23 07:36:55 +00:00
2016-12-18 20:14:12 +01:00
if ( s . isLoading ( ) & & s . getVersion ( ) < 40 ) {
// Reset _palVaryPaused to 0 when loading an old savegame.
// Before version 40, we didn't restore or reset _palVaryPaused.
// In QfG3 this could get it stuck at positive values (bug #9674).
//
// Other SCI11 games don't appear to use palVaryPaused at all.
// (Looked at eq2, freddy, kq6, lb2, mgoose11, pq1, qg1, sq4, sq5)
_palVaryPaused = 0 ;
// Clear any pending updates, since _palVarySignal also wasn't saved
// before version 40.
_palVarySignal = 0 ;
}
2012-11-15 14:57:55 +02:00
2010-09-24 09:56:50 +00:00
if ( s . isLoading ( ) & & _palVaryResourceId ! = - 1 ) {
palVaryInstallTimer ( ) ;
}
2010-07-23 08:08:27 +00:00
}
2010-07-23 07:36:55 +00:00
}
2016-01-14 10:50:41 -06:00
# ifdef ENABLE_SCI32
2017-10-05 20:53:27 -05:00
static void saveLoadPalette32 ( Common : : Serializer & s , Palette & palette ) {
s . syncAsUint32LE ( palette . timestamp ) ;
for ( int i = 0 ; i < ARRAYSIZE ( palette . colors ) ; + + i ) {
s . syncAsByte ( palette . colors [ i ] . used ) ;
s . syncAsByte ( palette . colors [ i ] . r ) ;
s . syncAsByte ( palette . colors [ i ] . g ) ;
s . syncAsByte ( palette . colors [ i ] . b ) ;
2016-01-14 10:50:41 -06:00
}
}
2017-10-05 20:53:27 -05:00
static void saveLoadOptionalPalette32 ( Common : : Serializer & s , Common : : ScopedPtr < Palette > & palette ) {
2017-01-16 07:22:32 +00:00
bool hasPalette = false ;
2016-01-14 10:50:41 -06:00
if ( s . isSaving ( ) ) {
2017-10-05 20:53:27 -05:00
hasPalette = palette ;
2016-01-14 10:50:41 -06:00
}
s . syncAsByte ( hasPalette ) ;
if ( hasPalette ) {
if ( s . isLoading ( ) ) {
2017-10-05 20:53:27 -05:00
palette . reset ( new Palette ) ;
2016-01-14 10:50:41 -06:00
}
saveLoadPalette32 ( s , * palette ) ;
}
}
void GfxPalette32 : : saveLoadWithSerializer ( Common : : Serializer & s ) {
if ( s . getVersion ( ) < 34 ) {
return ;
}
if ( s . isLoading ( ) ) {
+ + _version ;
2016-09-30 13:09:43 -05:00
for ( int i = 0 ; i < kNumCyclers ; + + i ) {
2017-10-05 20:53:27 -05:00
_cyclers [ i ] . reset ( ) ;
2016-09-30 13:09:43 -05:00
}
2017-10-05 20:53:27 -05:00
_varyTargetPalette . reset ( ) ;
_varyStartPalette . reset ( ) ;
2016-01-14 10:50:41 -06:00
}
s . syncAsSint16LE ( _varyDirection ) ;
s . syncAsSint16LE ( _varyPercent ) ;
s . syncAsSint16LE ( _varyTargetPercent ) ;
s . syncAsSint16LE ( _varyFromColor ) ;
s . syncAsSint16LE ( _varyToColor ) ;
s . syncAsUint16LE ( _varyNumTimesPaused ) ;
2016-07-09 12:41:12 -05:00
s . syncAsByte ( _needsUpdate ) ;
2016-01-14 10:50:41 -06:00
s . syncAsSint32LE ( _varyTime ) ;
s . syncAsUint32LE ( _varyLastTick ) ;
for ( int i = 0 ; i < ARRAYSIZE ( _fadeTable ) ; + + i ) {
s . syncAsByte ( _fadeTable [ i ] ) ;
}
for ( int i = 0 ; i < ARRAYSIZE ( _cycleMap ) ; + + i ) {
s . syncAsByte ( _cycleMap [ i ] ) ;
}
2017-02-24 12:15:57 -06:00
if ( g_sci - > _features - > hasLatePaletteCode ( ) & & s . getVersion ( ) > = 41 ) {
s . syncAsSint16LE ( _gammaLevel ) ;
2017-10-05 20:53:27 -05:00
saveLoadPalette32 ( s , _sourcePalette ) ;
2017-02-24 12:15:57 -06:00
+ + _version ;
_needsUpdate = true ;
_gammaChanged = true ;
}
2017-10-05 20:53:27 -05:00
saveLoadOptionalPalette32 ( s , _varyTargetPalette ) ;
saveLoadOptionalPalette32 ( s , _varyStartPalette ) ;
2017-02-24 12:15:57 -06:00
// _nextPalette is not saved by SSCI
2016-01-14 10:50:41 -06:00
for ( int i = 0 ; i < ARRAYSIZE ( _cyclers ) ; + + i ) {
2016-02-05 23:50:53 +02:00
PalCycler * cycler = nullptr ;
2016-01-14 10:50:41 -06:00
2017-01-16 07:22:32 +00:00
bool hasCycler = false ;
2016-01-14 10:50:41 -06:00
if ( s . isSaving ( ) ) {
2017-10-05 20:53:27 -05:00
cycler = _cyclers [ i ] . get ( ) ;
2016-01-14 10:50:41 -06:00
hasCycler = ( cycler ! = nullptr ) ;
}
s . syncAsByte ( hasCycler ) ;
if ( hasCycler ) {
if ( s . isLoading ( ) ) {
2017-10-05 20:53:27 -05:00
cycler = new PalCycler ;
_cyclers [ i ] . reset ( cycler ) ;
2016-01-14 10:50:41 -06:00
}
s . syncAsByte ( cycler - > fromColor ) ;
s . syncAsUint16LE ( cycler - > numColorsToCycle ) ;
s . syncAsByte ( cycler - > currentCycle ) ;
s . syncAsByte ( cycler - > direction ) ;
s . syncAsUint32LE ( cycler - > lastUpdateTick ) ;
s . syncAsSint16LE ( cycler - > delay ) ;
s . syncAsUint16LE ( cycler - > numTimesPaused ) ;
}
}
}
2016-06-25 14:19:47 -05:00
void GfxRemap32 : : saveLoadWithSerializer ( Common : : Serializer & s ) {
if ( s . getVersion ( ) < 35 ) {
return ;
}
s . syncAsByte ( _numActiveRemaps ) ;
s . syncAsByte ( _blockedRangeStart ) ;
s . syncAsSint16LE ( _blockedRangeCount ) ;
for ( uint i = 0 ; i < _remaps . size ( ) ; + + i ) {
SingleRemap & singleRemap = _remaps [ i ] ;
s . syncAsByte ( singleRemap . _type ) ;
if ( s . isLoading ( ) & & singleRemap . _type ! = kRemapNone ) {
singleRemap . reset ( ) ;
}
s . syncAsByte ( singleRemap . _from ) ;
s . syncAsByte ( singleRemap . _to ) ;
s . syncAsByte ( singleRemap . _delta ) ;
s . syncAsByte ( singleRemap . _percent ) ;
s . syncAsByte ( singleRemap . _gray ) ;
}
if ( s . isLoading ( ) ) {
_needsUpdate = true ;
}
}
2016-07-31 13:41:05 -05:00
void GfxCursor32 : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2016-08-28 13:04:02 -05:00
if ( s . getVersion ( ) < 38 ) {
2016-07-31 13:41:05 -05:00
return ;
}
2016-08-28 13:04:02 -05:00
int32 hideCount ;
if ( s . isSaving ( ) ) {
hideCount = _hideCount ;
}
s . syncAsSint32LE ( hideCount ) ;
2016-07-31 13:41:05 -05:00
s . syncAsSint16LE ( _restrictedArea . left ) ;
s . syncAsSint16LE ( _restrictedArea . top ) ;
s . syncAsSint16LE ( _restrictedArea . right ) ;
s . syncAsSint16LE ( _restrictedArea . bottom ) ;
s . syncAsUint16LE ( _cursorInfo . resourceId ) ;
s . syncAsUint16LE ( _cursorInfo . loopNo ) ;
s . syncAsUint16LE ( _cursorInfo . celNo ) ;
if ( s . isLoading ( ) ) {
hide ( ) ;
setView ( _cursorInfo . resourceId , _cursorInfo . loopNo , _cursorInfo . celNo ) ;
2016-08-28 13:04:02 -05:00
if ( ! hideCount ) {
2016-07-31 13:41:05 -05:00
show ( ) ;
2016-08-28 13:04:02 -05:00
} else {
_hideCount = hideCount ;
2016-07-31 13:41:05 -05:00
}
}
}
2017-02-25 18:13:55 -06:00
2017-06-07 20:59:34 -05:00
void Audio32 : : saveLoadWithSerializer ( Common : : Serializer & s ) {
if ( ! g_sci - > _features - > hasSci3Audio ( ) | | s . getVersion ( ) < 44 ) {
return ;
}
syncArray ( s , _lockedResourceIds ) ;
}
2017-02-25 18:13:55 -06:00
void Video32 : : beforeSaveLoadWithSerializer ( Common : : Serializer & s ) {
if ( getSciVersion ( ) < SCI_VERSION_3 | | s . isSaving ( ) ) {
return ;
}
_robotPlayer . close ( ) ;
}
void Video32 : : saveLoadWithSerializer ( Common : : Serializer & s ) {
if ( getSciVersion ( ) < SCI_VERSION_3 ) {
return ;
}
bool robotExists = _robotPlayer . getStatus ( ) ! = RobotDecoder : : kRobotStatusUninitialized ;
s . syncAsByte ( robotExists ) ;
if ( robotExists ) {
GuiResourceId robotId ;
reg_t planeId ;
Common : : Point position ;
int16 priority , scale ;
int frameNo ;
if ( s . isSaving ( ) ) {
robotId = _robotPlayer . getResourceId ( ) ;
planeId = _robotPlayer . getPlaneId ( ) ;
priority = _robotPlayer . getPriority ( ) ;
position = _robotPlayer . getPosition ( ) ;
scale = _robotPlayer . getScale ( ) ;
frameNo = _robotPlayer . getFrameNo ( ) ;
}
s . syncAsUint16LE ( robotId ) ;
syncWithSerializer ( s , planeId ) ;
s . syncAsSint16LE ( priority ) ;
s . syncAsSint16LE ( position . x ) ;
s . syncAsSint16LE ( position . y ) ;
s . syncAsSint16LE ( scale ) ;
s . syncAsSint32LE ( frameNo ) ;
if ( s . isLoading ( ) ) {
_robotPlayer . open ( robotId , planeId , priority , position . x , position . y , scale ) ;
_robotPlayer . showFrame ( frameNo , position . x , position . y , priority ) ;
}
}
}
2016-01-14 10:50:41 -06:00
# endif
2010-11-01 16:09:33 +00:00
void GfxPorts : : saveLoadWithSerializer ( Common : : Serializer & s ) {
2015-04-26 15:29:02 +02:00
// reset() is called directly way earlier in gamestate_restore()
2010-11-01 16:09:33 +00:00
if ( s . getVersion ( ) > = 27 ) {
uint windowCount = 0 ;
uint id = PORTS_FIRSTSCRIPTWINDOWID ;
if ( s . isSaving ( ) ) {
while ( id < _windowsById . size ( ) ) {
if ( _windowsById [ id ] )
windowCount + + ;
id + + ;
}
}
// Save/Restore window count
s . syncAsUint32LE ( windowCount ) ;
if ( s . isSaving ( ) ) {
id = PORTS_FIRSTSCRIPTWINDOWID ;
while ( id < _windowsById . size ( ) ) {
if ( _windowsById [ id ] ) {
Window * window = ( Window * ) _windowsById [ id ] ;
window - > saveLoadWithSerializer ( s ) ;
}
id + + ;
}
} else {
id = PORTS_FIRSTSCRIPTWINDOWID ;
while ( windowCount ) {
Window * window = new Window ( 0 ) ;
window - > saveLoadWithSerializer ( s ) ;
// add enough entries inside _windowsById as needed
while ( id < = window - > id ) {
_windowsById . push_back ( 0 ) ;
id + + ;
}
_windowsById [ window - > id ] = window ;
// _windowList may not be 100% correct using that way of restoring
// saving/restoring ports won't work perfectly anyway, because the contents
// of the window can only get repainted by the scripts and they dont do that
// so we will get empty, transparent windows instead. So perfect window order
// shouldn't really matter
2010-11-01 20:08:42 +00:00
if ( window - > counterTillFree ) {
_freeCounter + + ;
} else {
2015-04-23 18:54:07 +02:00
// we don't put the saved script windows into _windowList[], so that they aren't used
// by kernel functions. This is important and would cause issues otherwise.
// see Conquests of Camelot - bug #6744 - when saving on the map screen (room 103),
// restoring would result in a black window in place
// where the area name was displayed before
// In Sierra's SCI the behaviour is identical to us
// Sierra's SCI won't show those windows after restoring
// If this should cause issues in another game, we would have to add a flag to simply
// avoid any drawing operations for such windows
// We still have to restore script windows, because for example Conquests of Camelot
// will immediately delete windows, that were created before saving the game.
2010-11-01 20:08:42 +00:00
}
2010-11-01 16:09:33 +00:00
windowCount - - ;
}
}
}
}
2010-06-15 07:20:53 +00:00
void SegManager : : reconstructStack ( EngineState * s ) {
DataStack * stack = ( DataStack * ) ( _heap [ findSegmentByType ( SEG_TYPE_STACK ) ] ) ;
s - > stack_base = stack - > _entries ;
s - > stack_top = s - > stack_base + stack - > _capacity ;
}
void SegManager : : reconstructClones ( ) {
for ( uint i = 0 ; i < _heap . size ( ) ; i + + ) {
SegmentObj * mobj = _heap [ i ] ;
if ( mobj & & mobj - > getType ( ) = = SEG_TYPE_CLONES ) {
CloneTable * ct = ( CloneTable * ) mobj ;
for ( uint j = 0 ; j < ct - > _table . size ( ) ; j + + ) {
// Check if the clone entry is used
uint entryNum = ( uint ) ct - > first_free ;
bool isUsed = true ;
while ( entryNum ! = ( ( uint ) CloneTable : : HEAPENTRY_INVALID ) ) {
if ( entryNum = = j ) {
isUsed = false ;
break ;
}
entryNum = ct - > _table [ entryNum ] . next_free ;
}
2010-06-08 13:16:30 +00:00
2010-06-15 07:20:53 +00:00
if ( ! isUsed )
continue ;
2016-03-23 20:41:48 +01:00
CloneTable : : value_type & seeker = ct - > at ( j ) ;
2010-06-15 07:20:53 +00:00
const Object * baseObj = getObject ( seeker . getSpeciesSelector ( ) ) ;
seeker . cloneFromObject ( baseObj ) ;
2010-08-07 00:22:57 +00:00
if ( ! baseObj ) {
// Can happen when loading some KQ6 savegames
warning ( " Clone entry without a base class: %d " , j ) ;
}
2010-06-15 07:20:53 +00:00
} // end for
2010-07-06 23:24:36 +00:00
} // end if
2010-06-15 07:20:53 +00:00
} // end for
2010-06-08 13:16:30 +00:00
}
2010-06-15 07:25:09 +00:00
# pragma mark -
2011-06-02 14:11:38 +02:00
bool gamestate_save ( EngineState * s , Common : : WriteStream * fh , const Common : : String & savename , const Common : : String & version ) {
2017-09-23 01:40:25 -05:00
Common : : Serializer ser ( nullptr , fh ) ;
2017-09-24 11:10:03 -05:00
set_savegame_metadata ( ser , fh , savename , version ) ;
2010-06-15 07:25:09 +00:00
s - > saveLoadWithSerializer ( ser ) ; // FIXME: Error handling?
2010-11-01 16:09:33 +00:00
if ( g_sci - > _gfxPorts )
g_sci - > _gfxPorts - > saveLoadWithSerializer ( ser ) ;
2010-12-24 14:56:04 +00:00
Vocabulary * voc = g_sci - > getVocabulary ( ) ;
if ( voc )
voc - > saveLoadWithSerializer ( ser ) ;
2010-06-15 07:25:09 +00:00
2014-10-19 23:44:45 +02:00
// TODO: SSCI (at least JonesCD, presumably more) also stores the Menu state
2010-06-15 08:39:03 +00:00
return true ;
2010-06-15 07:25:09 +00:00
}
2010-08-17 20:33:22 +00:00
extern void showScummVMDialog ( const Common : : String & message ) ;
2016-02-09 01:28:08 +01:00
void gamestate_afterRestoreFixUp ( EngineState * s , int savegameId ) {
switch ( g_sci - > getGameId ( ) ) {
case GID_MOTHERGOOSE :
// WORKAROUND: Mother Goose SCI0
// Script 200 / rm200::newRoom will set global C5h directly right after creating a child to the
// current number of children plus 1.
// We can't trust that global, that's why we set the actual savedgame id right here directly after
// restoring a saved game.
// If we didn't, the game would always save to a new slot
s - > variables [ VAR_GLOBAL ] [ 0xC5 ] . setOffset ( SAVEGAMEID_OFFICIALRANGE_START + savegameId ) ;
break ;
case GID_MOTHERGOOSE256 :
// WORKAROUND: Mother Goose SCI1/SCI1.1 does some weird things for
// saving a previously restored game.
// We set the current savedgame-id directly and remove the script
// code concerning this via script patch.
s - > variables [ VAR_GLOBAL ] [ 0xB3 ] . setOffset ( SAVEGAMEID_OFFICIALRANGE_START + savegameId ) ;
break ;
case GID_JONES :
// HACK: The code that enables certain menu items isn't called when a game is restored from the
// launcher, or the "Restore game" option in the game's main menu - bugs #6537 and #6723.
// These menu entries are disabled when the game is launched, and are enabled when a new game is
// started. The code for enabling these entries is is all in script 1, room1::init, but that code
// path is never followed in these two cases (restoring game from the menu, or restoring a game
// from the ScummVM launcher). Thus, we perform the calls to enable the menus ourselves here.
// These two are needed when restoring from the launcher
// FIXME: The original interpreter saves and restores the menu state, so these attributes
// are automatically reset there. We may want to do the same.
g_sci - > _gfxMenu - > kernelSetAttribute ( 257 > > 8 , 257 & 0xFF , SCI_MENU_ATTRIBUTE_ENABLED , TRUE_REG ) ; // Sierra -> About Jones
g_sci - > _gfxMenu - > kernelSetAttribute ( 258 > > 8 , 258 & 0xFF , SCI_MENU_ATTRIBUTE_ENABLED , TRUE_REG ) ; // Sierra -> Help
// The rest are normally enabled from room1::init
g_sci - > _gfxMenu - > kernelSetAttribute ( 769 > > 8 , 769 & 0xFF , SCI_MENU_ATTRIBUTE_ENABLED , TRUE_REG ) ; // Options -> Delete current player
g_sci - > _gfxMenu - > kernelSetAttribute ( 513 > > 8 , 513 & 0xFF , SCI_MENU_ATTRIBUTE_ENABLED , TRUE_REG ) ; // Game -> Save Game
g_sci - > _gfxMenu - > kernelSetAttribute ( 515 > > 8 , 515 & 0xFF , SCI_MENU_ATTRIBUTE_ENABLED , TRUE_REG ) ; // Game -> Restore Game
g_sci - > _gfxMenu - > kernelSetAttribute ( 1025 > > 8 , 1025 & 0xFF , SCI_MENU_ATTRIBUTE_ENABLED , TRUE_REG ) ; // Status -> Statistics
g_sci - > _gfxMenu - > kernelSetAttribute ( 1026 > > 8 , 1026 & 0xFF , SCI_MENU_ATTRIBUTE_ENABLED , TRUE_REG ) ; // Status -> Goals
break ;
2016-03-16 14:49:21 +01:00
case GID_KQ6 :
if ( g_sci - > isCD ( ) ) {
// WORKAROUND:
// For the CD version of King's Quest 6, set global depending on current hires/lowres state
// The game sets a global at the start depending on it and some things check that global
// instead of checking platform like for example the game action menu.
// This never happened in the original interpreter, because the original DOS interpreter
// was only capable of lowres graphics and the original Windows 3.11 interpreter was only capable
// of hires graphics. Saved games were not compatible between those two.
// Which means saving during lowres mode, then going into hires mode and restoring that saved game,
// will result in some graphics being incorrect (lowres).
// That's why we are setting the global after restoring a saved game depending on hires/lowres state.
// The CD demo of KQ6 does the same and uses the exact same global.
if ( ( g_sci - > getPlatform ( ) = = Common : : kPlatformWindows ) | | ( g_sci - > forceHiresGraphics ( ) ) ) {
s - > variables [ VAR_GLOBAL ] [ 0xA9 ] . setOffset ( 1 ) ;
} else {
s - > variables [ VAR_GLOBAL ] [ 0xA9 ] . setOffset ( 0 ) ;
}
}
break ;
2016-02-09 01:28:08 +01:00
case GID_PQ2 :
2018-08-27 01:50:03 +03:00
// HACK: Same as in Jones - enable the save game menu option when loading in
// PQ2 (bug #6875). It gets disabled in the game's death screen.
2016-02-09 01:28:08 +01:00
g_sci - > _gfxMenu - > kernelSetAttribute ( 2 , 1 , SCI_MENU_ATTRIBUTE_ENABLED , TRUE_REG ) ; // Game -> Save Game
break ;
2017-09-07 00:35:25 -05:00
# ifdef ENABLE_SCI32
2018-09-01 13:05:02 +03:00
case GID_KQ7 :
if ( Common : : checkGameGUIOption ( GAMEOPTION_UPSCALE_VIDEOS , ConfMan . get ( " guioptions " ) ) ) {
uint16 value = ConfMan . getBool ( " enable_video_upscale " ) ? 32 : 0 ;
s - > variables [ VAR_GLOBAL ] [ kGlobalVarKQ7UpscaleVideos ] = make_reg ( 0 , value ) ;
}
break ;
2017-09-07 00:35:25 -05:00
case GID_PHANTASMAGORIA2 :
if ( Common : : checkGameGUIOption ( GAMEOPTION_ENABLE_CENSORING , ConfMan . get ( " guioptions " ) ) ) {
s - > variables [ VAR_GLOBAL ] [ kGlobalVarPhant2CensorshipFlag ] = make_reg ( 0 , ConfMan . getBool ( " enable_censoring " ) ) ;
}
break ;
2018-08-27 01:50:03 +03:00
case GID_SHIVERS :
// WORKAROUND: When loading a saved game from the GMM in the same scene in
// Shivers, we end up with the same draw list, but the scene palette is not
// set properly. Normally, Shivers does a room change when showing the saved
// game list, which does not occur when loading directly from the GMM. When
// loading from the GMM, at this point all of the visible planes and items
// are deleted, so calling frameOut here helps reset the game palette
// properly, like when changing a room.
g_sci - > _gfxFrameout - > frameOut ( true ) ;
break ;
2017-09-07 00:35:25 -05:00
# endif
2016-02-09 01:28:08 +01:00
default :
break ;
}
}
2010-01-31 01:26:06 +00:00
void gamestate_restore ( EngineState * s , Common : : SeekableReadStream * fh ) {
2009-02-28 11:07:36 +00:00
SavegameMetadata meta ;
2009-02-20 23:41:15 +00:00
2009-03-15 20:31:29 +00:00
Common : : Serializer ser ( fh , 0 ) ;
sync_SavegameMetadata ( ser , meta ) ;
2009-02-27 19:50:22 +00:00
2010-01-31 01:26:06 +00:00
if ( fh - > eos ( ) ) {
2010-08-24 09:00:53 +00:00
s - > r_acc = TRUE_REG ; // signal failure
2010-01-31 01:26:06 +00:00
return ;
}
2009-02-28 11:07:36 +00:00
2017-05-12 23:35:02 -05:00
// In SCI32 these checks are all in kCheckSaveGame32
if ( getSciVersion ( ) < SCI_VERSION_2 ) {
if ( ( meta . version < MINIMUM_SAVEGAME_VERSION ) | | ( meta . version > CURRENT_SAVEGAME_VERSION ) ) {
if ( meta . version < MINIMUM_SAVEGAME_VERSION ) {
showScummVMDialog ( _ ( " The format of this saved game is obsolete, unable to load it " ) ) ;
} else {
Common : : String msg = Common : : String : : format ( _ ( " Savegame version is %d, maximum supported is %0d " ) , meta . version , CURRENT_SAVEGAME_VERSION ) ;
showScummVMDialog ( msg ) ;
}
2010-06-15 08:25:51 +00:00
2010-08-24 09:00:53 +00:00
s - > r_acc = TRUE_REG ; // signal failure
2010-06-15 08:25:51 +00:00
return ;
}
2017-05-12 23:35:02 -05:00
if ( meta . gameObjectOffset > 0 & & meta . script0Size > 0 ) {
Resource * script0 = g_sci - > getResMan ( ) - > findResource ( ResourceId ( kResourceTypeScript , 0 ) , false ) ;
if ( script0 - > size ( ) ! = meta . script0Size | | g_sci - > getGameObject ( ) . getOffset ( ) ! = meta . gameObjectOffset ) {
showScummVMDialog ( _ ( " This saved game was created with a different version of the game, unable to load it " ) ) ;
s - > r_acc = TRUE_REG ; // signal failure
return ;
}
}
2010-06-15 08:25:51 +00:00
}
2010-07-07 20:12:41 +00:00
// We don't need the thumbnail here, so just read it and discard it
2010-08-02 22:28:30 +00:00
Graphics : : skipThumbnail ( * fh ) ;
2009-10-11 15:51:43 +00:00
2015-04-26 15:50:12 +02:00
// reset ports is one of the first things we do, because that may free() some hunk memory
2015-04-26 15:29:02 +02:00
// and we don't want to do that after we read in the saved game hunk memory
2015-04-27 20:50:37 +02:00
if ( g_sci - > _gfxPorts )
g_sci - > _gfxPorts - > reset ( ) ;
// clear screen
2016-02-22 21:38:01 +01:00
if ( getSciVersion ( ) < = SCI_VERSION_1_1 ) {
// Only do clearing the screen for SCI16
// Both SCI16 + SCI32 did not clear the screen.
// We basically do it for SCI16, because of KQ6.
// When hires portraits are shown and the user restores during that time, the portraits
// wouldn't get fully removed. In original SCI, the user wasn't able to restore during that time,
// so this is basically a workaround, so that ScummVM features work properly.
// For SCI32, behavior was verified in DOSBox, that SCI32 does not clear and also not redraw the screen.
// It only redraws elements that have changed in comparison to the state before the restore.
// If we cleared the screen for SCI32, we would have issues because of this behavior.
if ( g_sci - > _gfxScreen )
g_sci - > _gfxScreen - > clearForRestoreGame ( ) ;
}
2015-04-26 15:29:02 +02:00
2010-06-01 15:48:17 +00:00
s - > reset ( true ) ;
s - > saveLoadWithSerializer ( ser ) ; // FIXME: Error handling?
2009-02-15 06:10:59 +00:00
2009-02-20 19:08:38 +00:00
// Now copy all current state information
2009-02-15 06:10:59 +00:00
2010-06-08 13:16:30 +00:00
s - > _segMan - > reconstructStack ( s ) ;
2010-06-01 15:48:17 +00:00
s - > _segMan - > reconstructClones ( ) ;
2010-06-09 09:17:48 +00:00
s - > initGlobals ( ) ;
2010-06-10 11:43:20 +00:00
s - > gcCountDown = GC_INTERVAL - 1 ;
2009-02-15 06:10:59 +00:00
2009-02-20 19:08:38 +00:00
// Time state:
2010-06-10 11:18:10 +00:00
s - > lastWaitTime = g_system - > getMillis ( ) ;
2010-06-14 08:36:52 +00:00
s - > _screenUpdateTime = g_system - > getMillis ( ) ;
2016-01-14 10:54:27 -06:00
if ( meta . version > = 34 ) {
g_sci - > setTickCount ( meta . playTime ) ;
} else {
g_engine - > setTotalPlayTime ( meta . playTime * 1000 ) ;
}
2009-02-15 06:10:59 +00:00
2010-08-04 05:15:01 +00:00
if ( g_sci - > _gfxPorts )
2010-11-01 16:09:33 +00:00
g_sci - > _gfxPorts - > saveLoadWithSerializer ( ser ) ;
2010-12-24 14:56:04 +00:00
Vocabulary * voc = g_sci - > getVocabulary ( ) ;
if ( ser . getVersion ( ) > = 30 & & voc )
voc - > saveLoadWithSerializer ( ser ) ;
2010-12-24 14:47:47 +00:00
2010-10-31 20:57:50 +00:00
g_sci - > _soundCmd - > reconstructPlayList ( ) ;
2009-02-15 06:10:59 +00:00
2009-05-26 11:33:18 +00:00
// Message state:
2010-08-02 23:03:04 +00:00
delete s - > _msgState ;
2010-06-01 15:48:17 +00:00
s - > _msgState = new MessageState ( s - > _segMan ) ;
2009-05-26 11:33:18 +00:00
2010-12-07 00:47:05 +00:00
// System strings:
s - > _segMan - > initSysStrings ( ) ;
2010-06-08 21:05:46 +00:00
s - > abortScriptProcessing = kAbortLoadGame ;
2010-07-31 14:09:42 +00:00
// signal restored game to game scripts
s - > gameIsRestarting = GAMEISRESTARTING_RESTORE ;
2009-02-15 06:10:59 +00:00
}
2009-02-20 23:41:15 +00:00
2017-09-24 11:10:03 -05:00
void set_savegame_metadata ( Common : : Serializer & ser , Common : : WriteStream * fh , const Common : : String & savename , const Common : : String & version ) {
2017-09-23 01:40:25 -05:00
TimeDate curTime ;
g_system - > getTimeAndDate ( curTime ) ;
SavegameMetadata meta ;
meta . version = CURRENT_SAVEGAME_VERSION ;
meta . name = savename ;
meta . gameVersion = version ;
meta . saveDate = ( ( curTime . tm_mday & 0xFF ) < < 24 ) | ( ( ( curTime . tm_mon + 1 ) & 0xFF ) < < 16 ) | ( ( curTime . tm_year + 1900 ) & 0xFFFF ) ;
meta . saveTime = ( ( curTime . tm_hour & 0xFF ) < < 16 ) | ( ( ( curTime . tm_min ) & 0xFF ) < < 8 ) | ( ( curTime . tm_sec ) & 0xFF ) ;
Resource * script0 = g_sci - > getResMan ( ) - > findResource ( ResourceId ( kResourceTypeScript , 0 ) , false ) ;
assert ( script0 ) ;
meta . script0Size = script0 - > size ( ) ;
meta . gameObjectOffset = g_sci - > getGameObject ( ) . getOffset ( ) ;
sync_SavegameMetadata ( ser , meta ) ;
Graphics : : saveThumbnail ( * fh ) ;
}
2017-09-24 11:10:03 -05:00
void set_savegame_metadata ( Common : : WriteStream * fh , const Common : : String & savename , const Common : : String & version ) {
Common : : Serializer ser ( nullptr , fh ) ;
set_savegame_metadata ( ser , fh , savename , version ) ;
}
2017-09-23 01:39:16 -05:00
bool get_savegame_metadata ( Common : : SeekableReadStream * stream , SavegameMetadata & meta ) {
2009-03-15 20:31:29 +00:00
assert ( stream ) ;
2017-09-23 01:39:16 -05:00
Common : : Serializer ser ( stream , nullptr ) ;
sync_SavegameMetadata ( ser , meta ) ;
2009-02-20 23:41:15 +00:00
2009-03-15 20:31:29 +00:00
if ( stream - > eos ( ) )
2009-02-20 23:41:15 +00:00
return false ;
2017-09-23 01:39:16 -05:00
if ( ( meta . version < MINIMUM_SAVEGAME_VERSION ) | |
( meta . version > CURRENT_SAVEGAME_VERSION ) ) {
if ( meta . version < MINIMUM_SAVEGAME_VERSION )
2009-07-06 10:39:22 +00:00
warning ( " Old savegame version detected- can't load " ) ;
2009-02-20 23:41:15 +00:00
else
2017-09-23 01:39:16 -05:00
warning ( " Savegame version is %d- maximum supported is %0d " , meta . version , CURRENT_SAVEGAME_VERSION ) ;
2009-02-20 23:41:15 +00:00
return false ;
}
return true ;
}
2009-02-21 10:23:36 +00:00
} // End of namespace Sci