2013-05-16 21:18:09 +00:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
2014-02-18 01:34:20 +00:00
*
2013-05-16 21:18:09 +00:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2014-02-18 01:34:20 +00:00
*
2013-05-16 21:18:09 +00:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
*/
# include "gui/EventRecorder.h"
2013-07-07 03:54:45 +00:00
# ifdef ENABLE_EVENTRECORDER
2017-07-10 19:17:41 +00:00
namespace Common {
DECLARE_SINGLETON ( GUI : : EventRecorder ) ;
}
2013-05-16 21:18:09 +00:00
# include "common/debug-channels.h"
# include "backends/timer/sdl/sdl-timer.h"
2019-11-30 15:10:52 +00:00
# include "backends/mixer/mixer.h"
2013-05-16 21:18:09 +00:00
# include "common/config-manager.h"
# include "common/md5.h"
# include "gui/gui-manager.h"
# include "gui/widget.h"
# include "gui/onscreendialog.h"
# include "common/random.h"
# include "common/savefile.h"
# include "common/textconsole.h"
# include "graphics/thumbnail.h"
# include "graphics/surface.h"
# include "graphics/scaler.h"
namespace GUI {
const int kMaxRecordsNames = 0x64 ;
const int kDefaultScreenshotPeriod = 60000 ;
uint32 readTime ( Common : : ReadStream * inFile ) {
uint32 d = inFile - > readByte ( ) ;
if ( d = = 0xff ) {
d = inFile - > readUint32LE ( ) ;
}
return d ;
}
void writeTime ( Common : : WriteStream * outFile , uint32 d ) {
//Simple RLE compression
if ( d > = 0xff ) {
outFile - > writeByte ( 0xff ) ;
outFile - > writeUint32LE ( d ) ;
} else {
outFile - > writeByte ( d ) ;
}
}
EventRecorder : : EventRecorder ( ) {
2020-01-05 16:48:04 +00:00
_timerManager = nullptr ;
2013-05-16 21:18:09 +00:00
_recordMode = kPassthrough ;
2020-01-05 16:48:04 +00:00
_fakeMixerManager = nullptr ;
2013-05-16 21:18:09 +00:00
_initialized = false ;
_needRedraw = false ;
2021-06-24 17:07:01 +00:00
_processingMillis = false ;
2013-05-16 21:18:09 +00:00
_fastPlayback = false ;
2013-07-14 16:14:00 +00:00
_fakeTimer = 0 ;
_savedState = false ;
_needcontinueGame = false ;
_temporarySlot = 0 ;
2020-01-05 16:48:04 +00:00
_realSaveManager = nullptr ;
_realMixerManager = nullptr ;
_controlPanel = nullptr ;
2013-07-14 16:14:00 +00:00
_lastMillis = 0 ;
_lastScreenshotTime = 0 ;
_screenshotPeriod = 0 ;
2020-01-05 16:48:04 +00:00
_playbackFile = nullptr ;
2013-05-16 21:18:09 +00:00
}
EventRecorder : : ~ EventRecorder ( ) {
2020-01-05 16:48:04 +00:00
delete _timerManager ;
2013-05-16 21:18:09 +00:00
}
void EventRecorder : : deinit ( ) {
if ( ! _initialized ) {
return ;
}
setFileHeader ( ) ;
_needRedraw = false ;
_initialized = false ;
_recordMode = kPassthrough ;
delete _fakeMixerManager ;
2020-01-05 16:48:04 +00:00
_fakeMixerManager = nullptr ;
2013-07-14 16:16:01 +00:00
_controlPanel - > close ( ) ;
delete _controlPanel ;
2013-05-16 21:18:09 +00:00
debugC ( 1 , kDebugLevelEventRec , " playback:action=stopplayback " ) ;
2021-06-23 17:52:44 +00:00
Common : : EventDispatcher * eventDispater = g_system - > getEventManager ( ) - > getEventDispatcher ( ) ;
eventDispater - > unregisterSource ( this ) ;
eventDispater - > ignoreSources ( false ) ;
2013-05-16 21:18:09 +00:00
_recordMode = kPassthrough ;
_playbackFile - > close ( ) ;
delete _playbackFile ;
switchMixer ( ) ;
switchTimerManagers ( ) ;
DebugMan . disableDebugChannel ( " EventRec " ) ;
}
void EventRecorder : : processMillis ( uint32 & millis , bool skipRecord ) {
if ( ! _initialized ) {
return ;
}
2021-06-24 17:07:01 +00:00
if ( skipRecord | | _processingMillis ) {
2013-05-16 21:18:09 +00:00
millis = _fakeTimer ;
return ;
}
2021-06-24 17:07:01 +00:00
// to prevent calling this recursively
2013-05-16 21:18:09 +00:00
if ( _recordMode = = kRecorderPlaybackPause ) {
millis = _fakeTimer ;
}
uint32 millisDelay ;
Common : : RecorderEvent timerEvent ;
switch ( _recordMode ) {
case kRecorderRecord :
updateSubsystems ( ) ;
millisDelay = millis - _lastMillis ;
_lastMillis = millis ;
_fakeTimer + = millisDelay ;
2013-07-14 16:16:01 +00:00
_controlPanel - > setReplayedTime ( _fakeTimer ) ;
2013-05-16 21:18:09 +00:00
timerEvent . recordedtype = Common : : kRecorderEventTypeTimer ;
timerEvent . time = _fakeTimer ;
_playbackFile - > writeEvent ( timerEvent ) ;
takeScreenshot ( ) ;
_timerManager - > handler ( ) ;
break ;
case kRecorderPlayback :
2021-06-16 20:20:20 +00:00
if ( _nextEvent . recordedtype ! = Common : : kRecorderEventTypeTimer ) {
// just re-use any previous millis value
// this might happen if you have EventSource instances registered, that
// are querying the millis by themselves, too. If the EventRecorder::poll
// is registered and thus dispatched after those EventSource instances, it
// might look like it ran out-of-sync.
2021-06-24 17:07:01 +00:00
millis = _fakeTimer ;
2021-06-16 20:20:20 +00:00
return ;
2013-05-16 21:18:09 +00:00
}
2021-06-24 17:07:01 +00:00
_processingMillis = true ;
2021-06-16 20:20:20 +00:00
_fakeTimer = _nextEvent . time ;
2021-06-24 17:07:01 +00:00
millis = _fakeTimer ;
debug ( 3 , " millis event: %u " , millis ) ;
updateSubsystems ( ) ;
2021-06-16 20:20:20 +00:00
_nextEvent = _playbackFile - > getNextEvent ( ) ;
_timerManager - > handler ( ) ;
2013-07-14 16:16:01 +00:00
_controlPanel - > setReplayedTime ( _fakeTimer ) ;
2021-06-24 17:07:01 +00:00
_processingMillis = false ;
2013-05-16 21:18:09 +00:00
break ;
case kRecorderPlaybackPause :
millis = _fakeTimer ;
break ;
default :
break ;
}
}
bool EventRecorder : : processDelayMillis ( ) {
return _fastPlayback ;
}
void EventRecorder : : checkForKeyCode ( const Common : : Event & event ) {
2017-09-09 14:43:55 +00:00
if ( ( event . type = = Common : : EVENT_KEYDOWN ) & & ( event . kbd . flags & Common : : KBD_CTRL ) & & ( event . kbd . keycode = = Common : : KEYCODE_p ) & & ( ! event . kbdRepeat ) ) {
2013-05-16 21:18:09 +00:00
togglePause ( ) ;
}
}
bool EventRecorder : : pollEvent ( Common : : Event & ev ) {
if ( ( _recordMode ! = kRecorderPlayback ) | | ! _initialized )
return false ;
2013-07-14 17:01:47 +00:00
2021-06-16 20:20:20 +00:00
if ( ( _nextEvent . recordedtype = = Common : : kRecorderEventTypeTimer ) | | ( _nextEvent . type = = Common : : EVENT_INVALID ) ) {
2013-05-16 21:18:09 +00:00
return false ;
}
2021-06-21 18:56:01 +00:00
ev = _nextEvent ;
_nextEvent = _playbackFile - > getNextEvent ( ) ;
switch ( ev . type ) {
2013-05-16 21:18:09 +00:00
case Common : : EVENT_MOUSEMOVE :
case Common : : EVENT_LBUTTONDOWN :
case Common : : EVENT_LBUTTONUP :
case Common : : EVENT_RBUTTONDOWN :
case Common : : EVENT_RBUTTONUP :
case Common : : EVENT_WHEELUP :
case Common : : EVENT_WHEELDOWN :
2021-06-21 18:56:01 +00:00
g_system - > warpMouse ( ev . mouse . x , ev . mouse . y ) ;
2013-05-16 21:18:09 +00:00
break ;
default :
break ;
}
return true ;
}
void EventRecorder : : switchFastMode ( ) {
if ( _recordMode = = kRecorderPlaybackPause ) {
_fastPlayback = ! _fastPlayback ;
}
}
void EventRecorder : : togglePause ( ) {
RecordMode oldState ;
switch ( _recordMode ) {
case kRecorderPlayback :
case kRecorderRecord :
oldState = _recordMode ;
_recordMode = kRecorderPlaybackPause ;
2013-07-14 16:16:01 +00:00
_controlPanel - > runModal ( ) ;
2013-05-16 21:18:09 +00:00
_recordMode = oldState ;
_initialized = true ;
break ;
case kRecorderPlaybackPause :
2013-07-14 16:16:01 +00:00
_controlPanel - > close ( ) ;
2013-05-16 21:18:09 +00:00
break ;
default :
break ;
}
}
void EventRecorder : : RegisterEventSource ( ) {
2017-08-15 08:16:01 +00:00
g_system - > getEventManager ( ) - > getEventDispatcher ( ) - > registerObserver ( this , Common : : EventManager : : kEventRecorderPriority , false ) ;
2013-05-16 21:18:09 +00:00
}
uint32 EventRecorder : : getRandomSeed ( const Common : : String & name ) {
uint32 result = g_system - > getMillis ( ) ;
if ( _recordMode = = kRecorderRecord ) {
_playbackFile - > getHeader ( ) . randomSourceRecords [ name ] = result ;
} else if ( _recordMode = = kRecorderPlayback ) {
result = _playbackFile - > getHeader ( ) . randomSourceRecords [ name ] ;
}
return result ;
}
Common : : String EventRecorder : : generateRecordFileName ( const Common : : String & target ) {
2021-06-16 18:56:30 +00:00
Common : : String pattern ( target + " .r?? " ) ;
2013-05-16 21:18:09 +00:00
Common : : StringArray files = g_system - > getSavefileManager ( ) - > listSavefiles ( pattern ) ;
for ( int i = 0 ; i < kMaxRecordsNames ; + + i ) {
Common : : String recordName = Common : : String : : format ( " %s.r%02d " , target . c_str ( ) , i ) ;
if ( find ( files . begin ( ) , files . end ( ) , recordName ) ! = files . end ( ) ) {
continue ;
}
return recordName ;
}
return " " ;
}
2021-02-20 18:17:14 +00:00
void EventRecorder : : init ( const Common : : String & recordFileName , RecordMode mode ) {
2019-11-30 16:03:46 +00:00
_fakeMixerManager = new NullMixerManager ( ) ;
2013-05-16 21:18:09 +00:00
_fakeMixerManager - > init ( ) ;
_fakeMixerManager - > suspendAudio ( ) ;
_fakeTimer = 0 ;
_lastMillis = g_system - > getMillis ( ) ;
_playbackFile = new Common : : PlaybackFile ( ) ;
_lastScreenshotTime = 0 ;
_recordMode = mode ;
_needcontinueGame = false ;
if ( ConfMan . hasKey ( " disable_display " ) ) {
DebugMan . enableDebugChannel ( " EventRec " ) ;
gDebugLevel = 1 ;
}
if ( _recordMode = = kRecorderPlayback ) {
debugC ( 1 , kDebugLevelEventRec , " playback:action= \" Load file \" filename=%s " , recordFileName . c_str ( ) ) ;
2021-06-23 17:52:44 +00:00
Common : : EventDispatcher * eventDispater = g_system - > getEventManager ( ) - > getEventDispatcher ( ) ;
eventDispater - > clearEvents ( ) ;
eventDispater - > ignoreSources ( true ) ;
eventDispater - > registerSource ( this , false ) ;
2013-05-16 21:18:09 +00:00
}
_screenshotPeriod = ConfMan . getInt ( " screenshot_period " ) ;
if ( _screenshotPeriod = = 0 ) {
_screenshotPeriod = kDefaultScreenshotPeriod ;
}
if ( ! openRecordFile ( recordFileName ) ) {
deinit ( ) ;
error ( " playback:action=error reason= \" Record file loading error \" " ) ;
return ;
}
if ( _recordMode ! = kPassthrough ) {
2013-07-14 16:16:01 +00:00
_controlPanel = new GUI : : OnScreenDialog ( _recordMode = = kRecorderRecord ) ;
2019-12-28 09:43:58 +00:00
_controlPanel - > reflowLayout ( ) ;
2013-05-16 21:18:09 +00:00
}
if ( _recordMode = = kRecorderPlayback ) {
applyPlaybackSettings ( ) ;
_nextEvent = _playbackFile - > getNextEvent ( ) ;
}
if ( _recordMode = = kRecorderRecord ) {
getConfig ( ) ;
}
switchMixer ( ) ;
switchTimerManagers ( ) ;
_needRedraw = true ;
_initialized = true ;
}
/**
* Opens or creates file depend of recording mode .
*
2021-06-23 17:52:44 +00:00
* @ param id of recording or playing back game
* @ return true in case of success , false in case of error
2013-05-16 21:18:09 +00:00
*/
bool EventRecorder : : openRecordFile ( const Common : : String & fileName ) {
bool result ;
switch ( _recordMode ) {
case kRecorderRecord :
return _playbackFile - > openWrite ( fileName ) ;
case kRecorderPlayback :
_recordMode = kPassthrough ;
result = _playbackFile - > openRead ( fileName ) ;
_recordMode = kRecorderPlayback ;
return result ;
default :
return false ;
}
return true ;
}
bool EventRecorder : : checkGameHash ( const ADGameDescription * gameDesc ) {
2016-03-25 15:30:12 +00:00
if ( _playbackFile - > getHeader ( ) . hashRecords . size ( ) = = 0 ) {
2013-05-16 21:18:09 +00:00
warning ( " Engine doesn't contain description table " ) ;
return false ;
}
for ( const ADGameFileDescription * fileDesc = gameDesc - > filesDescriptions ; fileDesc - > fileName ; fileDesc + + ) {
2020-08-29 15:20:48 +00:00
if ( fileDesc - > md5 = = nullptr )
continue ;
2013-05-16 21:18:09 +00:00
if ( _playbackFile - > getHeader ( ) . hashRecords . find ( fileDesc - > fileName ) = = _playbackFile - > getHeader ( ) . hashRecords . end ( ) ) {
warning ( " MD5 hash for file %s not found in record file " , fileDesc - > fileName ) ;
debugC ( 1 , kDebugLevelEventRec , " playback:action= \" Check game hash \" filename=%s filehash=%s storedhash= \" \" result=different " , fileDesc - > fileName , fileDesc - > md5 ) ;
return false ;
}
if ( _playbackFile - > getHeader ( ) . hashRecords [ fileDesc - > fileName ] ! = fileDesc - > md5 ) {
warning ( " Incorrect version of game file %s. Stored MD5 is %s. MD5 of loaded game is %s " , fileDesc - > fileName , _playbackFile - > getHeader ( ) . hashRecords [ fileDesc - > fileName ] . c_str ( ) , fileDesc - > md5 ) ;
debugC ( 1 , kDebugLevelEventRec , " playback:action= \" Check game hash \" filename=%s filehash=%s storedhash=%s result=different " , fileDesc - > fileName , fileDesc - > md5 , _playbackFile - > getHeader ( ) . hashRecords [ fileDesc - > fileName ] . c_str ( ) ) ;
return false ;
}
debugC ( 1 , kDebugLevelEventRec , " playback:action= \" Check game hash \" filename=%s filehash=%s storedhash=%s result=equal " , fileDesc - > fileName , fileDesc - > md5 , _playbackFile - > getHeader ( ) . hashRecords [ fileDesc - > fileName ] . c_str ( ) ) ;
}
return true ;
}
2019-11-30 15:10:52 +00:00
void EventRecorder : : registerMixerManager ( MixerManager * mixerManager ) {
2013-05-16 21:18:09 +00:00
_realMixerManager = mixerManager ;
}
void EventRecorder : : switchMixer ( ) {
if ( _recordMode = = kPassthrough ) {
_realMixerManager - > resumeAudio ( ) ;
} else {
_realMixerManager - > suspendAudio ( ) ;
_fakeMixerManager - > resumeAudio ( ) ;
}
}
2019-11-30 15:10:52 +00:00
MixerManager * EventRecorder : : getMixerManager ( ) {
2013-05-16 21:18:09 +00:00
if ( _recordMode = = kPassthrough ) {
return _realMixerManager ;
} else {
return _fakeMixerManager ;
}
}
2013-08-08 01:12:02 +00:00
void EventRecorder : : getConfigFromDomain ( const Common : : ConfigManager : : Domain * domain ) {
for ( Common : : ConfigManager : : Domain : : const_iterator entry = domain - > begin ( ) ; entry ! = domain - > end ( ) ; + + entry ) {
2013-05-16 21:18:09 +00:00
_playbackFile - > getHeader ( ) . settingsRecords [ entry - > _key ] = entry - > _value ;
}
}
void EventRecorder : : getConfig ( ) {
getConfigFromDomain ( ConfMan . getDomain ( ConfMan . kApplicationDomain ) ) ;
getConfigFromDomain ( ConfMan . getActiveDomain ( ) ) ;
_playbackFile - > getHeader ( ) . settingsRecords [ " save_slot " ] = ConfMan . get ( " save_slot " ) ;
}
void EventRecorder : : applyPlaybackSettings ( ) {
2013-08-08 01:12:02 +00:00
for ( Common : : StringMap : : const_iterator i = _playbackFile - > getHeader ( ) . settingsRecords . begin ( ) ; i ! = _playbackFile - > getHeader ( ) . settingsRecords . end ( ) ; + + i ) {
2013-05-16 21:18:09 +00:00
Common : : String currentValue = ConfMan . get ( i - > _key ) ;
if ( currentValue ! = i - > _value ) {
ConfMan . set ( i - > _key , i - > _value , ConfMan . kTransientDomain ) ;
debugC ( 1 , kDebugLevelEventRec , " playback:action= \" Apply settings \" key=%s storedvalue=%s currentvalue=%s result=different " , i - > _key . c_str ( ) , i - > _value . c_str ( ) , currentValue . c_str ( ) ) ;
} else {
debugC ( 1 , kDebugLevelEventRec , " playback:action= \" Apply settings \" key=%s storedvalue=%s currentvalue=%s result=equal " , i - > _key . c_str ( ) , i - > _value . c_str ( ) , currentValue . c_str ( ) ) ;
}
}
removeDifferentEntriesInDomain ( ConfMan . getDomain ( ConfMan . kApplicationDomain ) ) ;
removeDifferentEntriesInDomain ( ConfMan . getActiveDomain ( ) ) ;
}
void EventRecorder : : removeDifferentEntriesInDomain ( Common : : ConfigManager : : Domain * domain ) {
2013-08-08 01:12:02 +00:00
for ( Common : : ConfigManager : : Domain : : const_iterator entry = domain - > begin ( ) ; entry ! = domain - > end ( ) ; + + entry ) {
2013-05-16 21:18:09 +00:00
if ( _playbackFile - > getHeader ( ) . settingsRecords . find ( entry - > _key ) = = _playbackFile - > getHeader ( ) . settingsRecords . end ( ) ) {
debugC ( 1 , kDebugLevelEventRec , " playback:action= \" Apply settings \" checksettings:key=%s storedvalue=%s currentvalue= " " result=different " , entry - > _key . c_str ( ) , entry - > _value . c_str ( ) ) ;
domain - > erase ( entry - > _key ) ;
}
}
}
DefaultTimerManager * EventRecorder : : getTimerManager ( ) {
return _timerManager ;
}
void EventRecorder : : registerTimerManager ( DefaultTimerManager * timerManager ) {
_timerManager = timerManager ;
}
void EventRecorder : : switchTimerManagers ( ) {
delete _timerManager ;
if ( _recordMode = = kPassthrough ) {
_timerManager = new SdlTimerManager ( ) ;
} else {
_timerManager = new DefaultTimerManager ( ) ;
}
}
void EventRecorder : : updateSubsystems ( ) {
if ( _recordMode = = kPassthrough ) {
return ;
}
RecordMode oldRecordMode = _recordMode ;
_recordMode = kPassthrough ;
_fakeMixerManager - > update ( ) ;
_recordMode = oldRecordMode ;
}
2017-08-15 08:16:01 +00:00
bool EventRecorder : : notifyEvent ( const Common : : Event & ev ) {
2013-05-16 21:18:09 +00:00
if ( ( ! _initialized ) & & ( _recordMode ! = kRecorderPlaybackPause ) ) {
2017-08-15 08:16:01 +00:00
return false ;
2013-05-16 21:18:09 +00:00
}
checkForKeyCode ( ev ) ;
Common : : Event evt = ev ;
evt . mouse . x = evt . mouse . x * ( g_system - > getOverlayWidth ( ) / g_system - > getWidth ( ) ) ;
evt . mouse . y = evt . mouse . y * ( g_system - > getOverlayHeight ( ) / g_system - > getHeight ( ) ) ;
switch ( _recordMode ) {
case kRecorderPlayback :
2017-08-15 08:16:01 +00:00
return false ;
2013-05-16 21:18:09 +00:00
case kRecorderRecord :
2013-07-14 16:16:01 +00:00
g_gui . processEvent ( evt , _controlPanel ) ;
if ( ( ( evt . type = = Common : : EVENT_LBUTTONDOWN ) | | ( evt . type = = Common : : EVENT_LBUTTONUP ) | | ( evt . type = = Common : : EVENT_MOUSEMOVE ) ) & & _controlPanel - > isMouseOver ( ) ) {
2017-08-15 08:16:01 +00:00
return true ;
2013-05-16 21:18:09 +00:00
} else {
2019-09-13 23:54:10 +00:00
Common : : RecorderEvent e ( ev ) ;
2013-05-16 21:18:09 +00:00
e . recordedtype = Common : : kRecorderEventTypeNormal ;
e . time = _fakeTimer ;
_playbackFile - > writeEvent ( e ) ;
2017-08-15 08:16:01 +00:00
return false ;
2013-05-16 21:18:09 +00:00
}
case kRecorderPlaybackPause : {
Common : : Event dialogEvent ;
2013-07-14 16:16:01 +00:00
if ( _controlPanel - > isEditDlgVisible ( ) ) {
2013-05-16 21:18:09 +00:00
dialogEvent = ev ;
} else {
dialogEvent = evt ;
}
2013-07-14 16:16:01 +00:00
g_gui . processEvent ( dialogEvent , _controlPanel - > getActiveDlg ( ) ) ;
if ( ( ( dialogEvent . type = = Common : : EVENT_LBUTTONDOWN ) | | ( dialogEvent . type = = Common : : EVENT_LBUTTONUP ) | | ( dialogEvent . type = = Common : : EVENT_MOUSEMOVE ) ) & & _controlPanel - > isMouseOver ( ) ) {
2017-08-15 08:16:01 +00:00
return true ;
2013-05-16 21:18:09 +00:00
}
2017-08-15 08:16:01 +00:00
return false ;
2013-05-16 21:18:09 +00:00
}
default :
2017-08-15 08:16:01 +00:00
return false ;
2013-05-16 21:18:09 +00:00
}
}
void EventRecorder : : setGameMd5 ( const ADGameDescription * gameDesc ) {
for ( const ADGameFileDescription * fileDesc = gameDesc - > filesDescriptions ; fileDesc - > fileName ; fileDesc + + ) {
2020-01-05 16:48:04 +00:00
if ( fileDesc - > md5 ! = nullptr ) {
2013-05-16 21:18:09 +00:00
_playbackFile - > getHeader ( ) . hashRecords [ fileDesc - > fileName ] = fileDesc - > md5 ;
}
}
}
void EventRecorder : : processGameDescription ( const ADGameDescription * desc ) {
if ( _recordMode = = kRecorderRecord ) {
setGameMd5 ( desc ) ;
}
if ( ( _recordMode = = kRecorderPlayback ) & & ! checkGameHash ( desc ) ) {
deinit ( ) ;
error ( " playback:action=error reason= \" \" " ) ;
}
}
void EventRecorder : : deleteRecord ( const Common : : String & fileName ) {
g_system - > getSavefileManager ( ) - > removeSavefile ( fileName ) ;
}
void EventRecorder : : takeScreenshot ( ) {
if ( ( _fakeTimer - _lastScreenshotTime ) > _screenshotPeriod ) {
Graphics : : Surface screen ;
uint8 md5 [ 16 ] ;
if ( grabScreenAndComputeMD5 ( screen , md5 ) ) {
_lastScreenshotTime = _fakeTimer ;
_playbackFile - > saveScreenShot ( screen , md5 ) ;
screen . free ( ) ;
}
}
}
bool EventRecorder : : grabScreenAndComputeMD5 ( Graphics : : Surface & screen , uint8 md5 [ 16 ] ) {
if ( ! createScreenShot ( screen ) ) {
warning ( " Can't save screenshot " ) ;
return false ;
}
2013-08-03 00:36:28 +00:00
Common : : MemoryReadStream bitmapStream ( ( const byte * ) screen . getPixels ( ) , screen . w * screen . h * screen . format . bytesPerPixel ) ;
2013-05-16 21:18:09 +00:00
computeStreamMD5 ( bitmapStream , md5 ) ;
return true ;
}
Common : : SeekableReadStream * EventRecorder : : processSaveStream ( const Common : : String & fileName ) {
Common : : InSaveFile * saveFile ;
switch ( _recordMode ) {
case kRecorderPlayback :
debugC ( 1 , kDebugLevelEventRec , " playback:action= \" Process save file \" filename=%s len=%d " , fileName . c_str ( ) , _playbackFile - > getHeader ( ) . saveFiles [ fileName ] . size ) ;
return new Common : : MemoryReadStream ( _playbackFile - > getHeader ( ) . saveFiles [ fileName ] . buffer , _playbackFile - > getHeader ( ) . saveFiles [ fileName ] . size ) ;
case kRecorderRecord :
saveFile = _realSaveManager - > openForLoading ( fileName ) ;
2020-01-05 16:48:04 +00:00
if ( saveFile ! = nullptr ) {
2013-05-16 21:18:09 +00:00
_playbackFile - > addSaveFile ( fileName , saveFile ) ;
saveFile - > seek ( 0 ) ;
}
return saveFile ;
default :
2020-01-05 16:48:04 +00:00
return nullptr ;
2013-05-16 21:18:09 +00:00
}
}
Common : : SaveFileManager * EventRecorder : : getSaveManager ( Common : : SaveFileManager * realSaveManager ) {
_realSaveManager = realSaveManager ;
if ( _recordMode ! = kPassthrough ) {
return & _fakeSaveManager ;
} else {
return realSaveManager ;
}
}
void EventRecorder : : preDrawOverlayGui ( ) {
2018-01-07 09:39:22 +00:00
if ( ( _initialized ) | | ( _needRedraw ) ) {
2013-05-16 21:18:09 +00:00
RecordMode oldMode = _recordMode ;
_recordMode = kPassthrough ;
g_system - > showOverlay ( ) ;
g_gui . theme ( ) - > clearAll ( ) ;
2018-01-07 09:39:22 +00:00
g_gui . theme ( ) - > drawToBackbuffer ( ) ;
_controlPanel - > drawDialog ( kDrawLayerBackground ) ;
g_gui . theme ( ) - > drawToScreen ( ) ;
g_gui . theme ( ) - > copyBackBufferToScreen ( ) ;
_controlPanel - > drawDialog ( kDrawLayerForeground ) ;
2013-05-16 21:18:09 +00:00
g_gui . theme ( ) - > updateScreen ( ) ;
_recordMode = oldMode ;
2018-01-07 09:39:22 +00:00
}
2013-05-16 21:18:09 +00:00
}
void EventRecorder : : postDrawOverlayGui ( ) {
2021-04-15 19:20:04 +00:00
if ( ( _initialized ) | | ( _needRedraw ) ) {
2013-05-16 21:18:09 +00:00
RecordMode oldMode = _recordMode ;
_recordMode = kPassthrough ;
g_system - > hideOverlay ( ) ;
_recordMode = oldMode ;
}
}
Common : : StringArray EventRecorder : : listSaveFiles ( const Common : : String & pattern ) {
if ( _recordMode = = kRecorderPlayback ) {
Common : : StringArray result ;
for ( Common : : HashMap < Common : : String , Common : : PlaybackFile : : SaveFileBuffer > : : iterator i = _playbackFile - > getHeader ( ) . saveFiles . begin ( ) ; i ! = _playbackFile - > getHeader ( ) . saveFiles . end ( ) ; + + i ) {
if ( i - > _key . matchString ( pattern , false , true ) ) {
result . push_back ( i - > _key ) ;
}
}
return result ;
} else {
return _realSaveManager - > listSavefiles ( pattern ) ;
}
}
void EventRecorder : : setFileHeader ( ) {
if ( _recordMode ! = kRecorderRecord ) {
return ;
}
TimeDate t ;
2016-09-25 06:45:42 +00:00
QualifiedGameDescriptor desc = EngineMan . findTarget ( ConfMan . getActiveDomainName ( ) ) ;
2013-05-16 21:18:09 +00:00
g_system - > getTimeAndDate ( t ) ;
if ( _author . empty ( ) ) {
setAuthor ( " Unknown Author " ) ;
}
if ( _name . empty ( ) ) {
2021-07-08 21:05:50 +00:00
g_eventRec . setName ( Common : : String : : format ( " %.2d.%.2d.%.4d " , t . tm_mday , t . tm_mon + 1 , 1900 + t . tm_year ) + desc . description ) ;
2013-05-16 21:18:09 +00:00
}
_playbackFile - > getHeader ( ) . author = _author ;
_playbackFile - > getHeader ( ) . notes = _desc ;
_playbackFile - > getHeader ( ) . name = _name ;
}
SDL_Surface * EventRecorder : : getSurface ( int width , int height ) {
2013-07-04 13:41:01 +00:00
// Create a RGB565 surface of the requested dimensions.
return SDL_CreateRGBSurface ( SDL_SWSURFACE , width , height , 16 , 0xF800 , 0x07E0 , 0x001F , 0x0000 ) ;
2013-05-16 21:18:09 +00:00
}
bool EventRecorder : : switchMode ( ) {
2016-09-15 16:39:45 +00:00
const Plugin * plugin = EngineMan . findPlugin ( ConfMan . get ( " engineid " ) ) ;
2017-11-11 05:06:42 +00:00
bool metaInfoSupport = plugin - > get < MetaEngine > ( ) . hasFeature ( MetaEngine : : kSavesSupportMetaInfo ) ;
2013-05-16 21:18:09 +00:00
bool featuresSupport = metaInfoSupport & &
g_engine - > canSaveGameStateCurrently ( ) & &
2017-11-11 05:06:42 +00:00
plugin - > get < MetaEngine > ( ) . hasFeature ( MetaEngine : : kSupportsListSaves ) & &
plugin - > get < MetaEngine > ( ) . hasFeature ( MetaEngine : : kSupportsDeleteSave ) ;
2013-05-16 21:18:09 +00:00
if ( ! featuresSupport ) {
return false ;
}
2016-09-15 16:39:45 +00:00
const Common : : String target = ConfMan . getActiveDomainName ( ) ;
SaveStateList saveList = plugin - > get < MetaEngine > ( ) . listSaves ( target . c_str ( ) ) ;
2013-05-16 21:18:09 +00:00
int emptySlot = 1 ;
for ( SaveStateList : : const_iterator x = saveList . begin ( ) ; x ! = saveList . end ( ) ; + + x ) {
int saveSlot = x - > getSaveSlot ( ) ;
if ( saveSlot = = 0 ) {
continue ;
}
if ( emptySlot ! = saveSlot ) {
break ;
}
emptySlot + + ;
}
Common : : String saveName ;
if ( emptySlot > = 0 ) {
saveName = Common : : String : : format ( " Save %d " , emptySlot + 1 ) ;
Common : : Error status = g_engine - > saveGameState ( emptySlot , saveName ) ;
if ( status . getCode ( ) = = Common : : kNoError ) {
2020-05-12 02:27:27 +00:00
Common : : Event eventReturnToLauncher ;
eventReturnToLauncher . type = Common : : EVENT_RETURN_TO_LAUNCHER ;
g_system - > getEventManager ( ) - > pushEvent ( eventReturnToLauncher ) ;
2013-05-16 21:18:09 +00:00
}
}
2020-07-01 18:01:48 +00:00
ConfMan . set ( " record_mode " , " " , Common : : ConfigManager : : kTransientDomain ) ;
2013-05-16 21:18:09 +00:00
ConfMan . setInt ( " save_slot " , emptySlot , Common : : ConfigManager : : kTransientDomain ) ;
_needcontinueGame = true ;
return true ;
}
bool EventRecorder : : checkForContinueGame ( ) {
bool result = _needcontinueGame ;
_needcontinueGame = false ;
return result ;
}
void EventRecorder : : deleteTemporarySave ( ) {
if ( _temporarySlot = = - 1 ) return ;
2016-09-15 16:39:45 +00:00
const Plugin * plugin = EngineMan . findPlugin ( ConfMan . get ( " engineid " ) ) ;
const Common : : String target = ConfMan . getActiveDomainName ( ) ;
plugin - > get < MetaEngine > ( ) . removeSaveState ( target . c_str ( ) , _temporarySlot ) ;
2013-05-16 21:18:09 +00:00
_temporarySlot = - 1 ;
}
} // End of namespace GUI
# endif // ENABLE_EVENTRECORDER