aboutsummaryrefslogtreecommitdiff
path: root/gui/EventRecorder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gui/EventRecorder.cpp')
-rw-r--r--gui/EventRecorder.cpp680
1 files changed, 680 insertions, 0 deletions
diff --git a/gui/EventRecorder.cpp b/gui/EventRecorder.cpp
new file mode 100644
index 0000000000..71f66911e9
--- /dev/null
+++ b/gui/EventRecorder.cpp
@@ -0,0 +1,680 @@
+/* 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.
+
+ * 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.
+
+ * 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"
+
+#ifdef ENABLE_EVENTRECORDER
+
+namespace Common {
+DECLARE_SINGLETON(GUI::EventRecorder);
+}
+
+#include "common/debug-channels.h"
+#include "backends/timer/sdl/sdl-timer.h"
+#include "backends/mixer/sdl/sdl-mixer.h"
+#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;
+const int kDefaultBPP = 2;
+
+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() {
+ _timerManager = NULL;
+ _recordMode = kPassthrough;
+ _fakeMixerManager = NULL;
+ _initialized = false;
+ _needRedraw = false;
+ _fastPlayback = false;
+
+ _fakeTimer = 0;
+ _savedState = false;
+ _needcontinueGame = false;
+ _temporarySlot = 0;
+ _realSaveManager = 0;
+ _realMixerManager = 0;
+ _controlPanel = 0;
+ _lastMillis = 0;
+ _lastScreenshotTime = 0;
+ _screenshotPeriod = 0;
+ _playbackFile = 0;
+
+ DebugMan.addDebugChannel(kDebugLevelEventRec, "EventRec", "Event recorder debug level");
+}
+
+EventRecorder::~EventRecorder() {
+ if (_timerManager != NULL) {
+ delete _timerManager;
+ }
+}
+
+void EventRecorder::deinit() {
+ if (!_initialized) {
+ return;
+ }
+ setFileHeader();
+ _needRedraw = false;
+ _initialized = false;
+ _recordMode = kPassthrough;
+ delete _fakeMixerManager;
+ _fakeMixerManager = NULL;
+ _controlPanel->close();
+ delete _controlPanel;
+ debugC(1, kDebugLevelEventRec, "playback:action=stopplayback");
+ g_system->getEventManager()->getEventDispatcher()->unregisterSource(this);
+ _recordMode = kPassthrough;
+ _playbackFile->close();
+ delete _playbackFile;
+ switchMixer();
+ switchTimerManagers();
+ DebugMan.disableDebugChannel("EventRec");
+}
+
+void EventRecorder::processMillis(uint32 &millis, bool skipRecord) {
+ if (!_initialized) {
+ return;
+ }
+ if (skipRecord) {
+ millis = _fakeTimer;
+ return;
+ }
+ if (_recordMode == kRecorderPlaybackPause) {
+ millis = _fakeTimer;
+ }
+ uint32 millisDelay;
+ Common::RecorderEvent timerEvent;
+ switch (_recordMode) {
+ case kRecorderRecord:
+ updateSubsystems();
+ millisDelay = millis - _lastMillis;
+ _lastMillis = millis;
+ _fakeTimer += millisDelay;
+ _controlPanel->setReplayedTime(_fakeTimer);
+ timerEvent.recordedtype = Common::kRecorderEventTypeTimer;
+ timerEvent.time = _fakeTimer;
+ _playbackFile->writeEvent(timerEvent);
+ takeScreenshot();
+ _timerManager->handler();
+ break;
+ case kRecorderPlayback:
+ updateSubsystems();
+ if (_nextEvent.recordedtype == Common::kRecorderEventTypeTimer) {
+ _fakeTimer = _nextEvent.time;
+ _nextEvent = _playbackFile->getNextEvent();
+ _timerManager->handler();
+ } else {
+ if (_nextEvent.type == Common::EVENT_RTL) {
+ error("playback:action=stopplayback");
+ } else {
+ uint32 seconds = _fakeTimer / 1000;
+ Common::String screenTime = Common::String::format("%.2d:%.2d:%.2d", seconds / 3600 % 24, seconds / 60 % 60, seconds % 60);
+ error("playback:action=error reason=\"synchronization error\" time = %s", screenTime.c_str());
+ }
+ }
+ millis = _fakeTimer;
+ _controlPanel->setReplayedTime(_fakeTimer);
+ break;
+ case kRecorderPlaybackPause:
+ millis = _fakeTimer;
+ break;
+ default:
+ break;
+ }
+}
+
+bool EventRecorder::processDelayMillis() {
+ return _fastPlayback;
+}
+
+void EventRecorder::checkForKeyCode(const Common::Event &event) {
+ if ((event.type == Common::EVENT_KEYDOWN) && (event.kbd.flags & Common::KBD_CTRL) && (event.kbd.keycode == Common::KEYCODE_p) && (!event.synthetic)) {
+ togglePause();
+ }
+}
+
+bool EventRecorder::pollEvent(Common::Event &ev) {
+ if ((_recordMode != kRecorderPlayback) || !_initialized)
+ return false;
+
+ if ((_nextEvent.recordedtype == Common::kRecorderEventTypeTimer) || (_nextEvent.type == Common::EVENT_INVALID)) {
+ return false;
+ }
+
+ switch (_nextEvent.type) {
+ 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:
+ g_system->warpMouse(_nextEvent.mouse.x, _nextEvent.mouse.y);
+ break;
+ default:
+ break;
+ }
+ ev = _nextEvent;
+ _nextEvent = _playbackFile->getNextEvent();
+ 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;
+ _controlPanel->runModal();
+ _recordMode = oldState;
+ _initialized = true;
+ break;
+ case kRecorderPlaybackPause:
+ _controlPanel->close();
+ break;
+ default:
+ break;
+ }
+}
+
+void EventRecorder::RegisterEventSource() {
+ g_system->getEventManager()->getEventDispatcher()->registerMapper(this, false);
+}
+
+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) {
+ Common::String pattern(target+".r??");
+ 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 "";
+}
+
+
+void EventRecorder::init(Common::String recordFileName, RecordMode mode) {
+ _fakeMixerManager = new NullSdlMixerManager();
+ _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());
+ }
+ g_system->getEventManager()->getEventDispatcher()->registerSource(this, false);
+ _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) {
+ _controlPanel = new GUI::OnScreenDialog(_recordMode == kRecorderRecord);
+ }
+ 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.
+ *
+ *@param id of recording or playing back game
+ *@return true in case of success, false in case of error
+ *
+ */
+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) {
+ if (_playbackFile->getHeader().hashRecords.size() != 0) {
+ warning("Engine doesn't contain description table");
+ return false;
+ }
+ for (const ADGameFileDescription *fileDesc = gameDesc->filesDescriptions; fileDesc->fileName; fileDesc++) {
+ 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;
+}
+
+void EventRecorder::registerMixerManager(SdlMixerManager *mixerManager) {
+ _realMixerManager = mixerManager;
+}
+
+void EventRecorder::switchMixer() {
+ if (_recordMode == kPassthrough) {
+ _realMixerManager->resumeAudio();
+ } else {
+ _realMixerManager->suspendAudio();
+ _fakeMixerManager->resumeAudio();
+ }
+}
+
+SdlMixerManager *EventRecorder::getMixerManager() {
+ if (_recordMode == kPassthrough) {
+ return _realMixerManager;
+ } else {
+ return _fakeMixerManager;
+ }
+}
+
+void EventRecorder::getConfigFromDomain(const Common::ConfigManager::Domain *domain) {
+ for (Common::ConfigManager::Domain::const_iterator entry = domain->begin(); entry!= domain->end(); ++entry) {
+ _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() {
+ for (Common::StringMap::const_iterator i = _playbackFile->getHeader().settingsRecords.begin(); i != _playbackFile->getHeader().settingsRecords.end(); ++i) {
+ 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) {
+ for (Common::ConfigManager::Domain::const_iterator entry = domain->begin(); entry!= domain->end(); ++entry) {
+ 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;
+}
+
+Common::List<Common::Event> EventRecorder::mapEvent(const Common::Event &ev, Common::EventSource *source) {
+ if ((!_initialized) && (_recordMode != kRecorderPlaybackPause)) {
+ return DefaultEventMapper::mapEvent(ev, source);
+ }
+
+ 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:
+ if (ev.synthetic != true) {
+ return Common::List<Common::Event>();
+ }
+ return Common::DefaultEventMapper::mapEvent(ev, source);
+ break;
+ case kRecorderRecord:
+ g_gui.processEvent(evt, _controlPanel);
+ if (((evt.type == Common::EVENT_LBUTTONDOWN) || (evt.type == Common::EVENT_LBUTTONUP) || (evt.type == Common::EVENT_MOUSEMOVE)) && _controlPanel->isMouseOver()) {
+ return Common::List<Common::Event>();
+ } else {
+ Common::RecorderEvent e;
+ memcpy(&e, &ev, sizeof(ev));
+ e.recordedtype = Common::kRecorderEventTypeNormal;
+ e.time = _fakeTimer;
+ _playbackFile->writeEvent(e);
+ return DefaultEventMapper::mapEvent(ev, source);
+ }
+ break;
+ case kRecorderPlaybackPause: {
+ Common::Event dialogEvent;
+ if (_controlPanel->isEditDlgVisible()) {
+ dialogEvent = ev;
+ } else {
+ dialogEvent = evt;
+ }
+ g_gui.processEvent(dialogEvent, _controlPanel->getActiveDlg());
+ if (((dialogEvent.type == Common::EVENT_LBUTTONDOWN) || (dialogEvent.type == Common::EVENT_LBUTTONUP) || (dialogEvent.type == Common::EVENT_MOUSEMOVE)) && _controlPanel->isMouseOver()) {
+ return Common::List<Common::Event>();
+ }
+ return Common::DefaultEventMapper::mapEvent(dialogEvent, source);
+ }
+ break;
+ default:
+ return Common::DefaultEventMapper::mapEvent(ev, source);
+ }
+
+ return Common::DefaultEventMapper::mapEvent(ev, source);
+}
+
+void EventRecorder::setGameMd5(const ADGameDescription *gameDesc) {
+ for (const ADGameFileDescription *fileDesc = gameDesc->filesDescriptions; fileDesc->fileName; fileDesc++) {
+ if (fileDesc->md5 != NULL) {
+ _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;
+ }
+ Common::MemoryReadStream bitmapStream((const byte*)screen.getPixels(), screen.w * screen.h * screen.format.bytesPerPixel);
+ 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);
+ if (saveFile != NULL) {
+ _playbackFile->addSaveFile(fileName, saveFile);
+ saveFile->seek(0);
+ }
+ return saveFile;
+ default:
+ return NULL;
+ break;
+ }
+}
+
+Common::SaveFileManager *EventRecorder::getSaveManager(Common::SaveFileManager *realSaveManager) {
+ _realSaveManager = realSaveManager;
+ if (_recordMode != kPassthrough) {
+ return &_fakeSaveManager;
+ } else {
+ return realSaveManager;
+ }
+}
+
+void EventRecorder::preDrawOverlayGui() {
+ if ((_initialized) || (_needRedraw)) {
+ RecordMode oldMode = _recordMode;
+ _recordMode = kPassthrough;
+ g_system->showOverlay();
+ g_gui.theme()->clearAll();
+ g_gui.theme()->openDialog(true, GUI::ThemeEngine::kShadingNone);
+ _controlPanel->drawDialog();
+ g_gui.theme()->finishBuffering();
+ g_gui.theme()->updateScreen();
+ _recordMode = oldMode;
+ }
+}
+
+void EventRecorder::postDrawOverlayGui() {
+ if ((_initialized) || (_needRedraw)) {
+ 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;
+ const EnginePlugin *plugin = 0;
+ GameDescriptor desc = EngineMan.findGame(ConfMan.getActiveDomainName(), &plugin);
+ g_system->getTimeAndDate(t);
+ if (_author.empty()) {
+ setAuthor("Unknown Author");
+ }
+ if (_name.empty()) {
+ g_eventRec.setName(Common::String::format("%.2d.%.2d.%.4d ", t.tm_mday, t.tm_mon, 1900 + t.tm_year) + desc.description());
+ }
+ _playbackFile->getHeader().author = _author;
+ _playbackFile->getHeader().notes = _desc;
+ _playbackFile->getHeader().name = _name;
+}
+
+SDL_Surface *EventRecorder::getSurface(int width, int height) {
+ // Create a RGB565 surface of the requested dimensions.
+ return SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 16, 0xF800, 0x07E0, 0x001F, 0x0000);
+}
+
+bool EventRecorder::switchMode() {
+ const Common::String gameId = ConfMan.get("gameid");
+ const EnginePlugin *plugin = 0;
+ EngineMan.findGame(gameId, &plugin);
+ bool metaInfoSupport = (*plugin)->hasFeature(MetaEngine::kSavesSupportMetaInfo);
+ bool featuresSupport = metaInfoSupport &&
+ g_engine->canSaveGameStateCurrently() &&
+ (*plugin)->hasFeature(MetaEngine::kSupportsListSaves) &&
+ (*plugin)->hasFeature(MetaEngine::kSupportsDeleteSave);
+ if (!featuresSupport) {
+ return false;
+ }
+
+ int emptySlot = 1;
+ SaveStateList saveList = (*plugin)->listSaves(gameId.c_str());
+ 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) {
+ Common::Event eventRTL;
+ eventRTL.type = Common::EVENT_RTL;
+ g_system->getEventManager()->pushEvent(eventRTL);
+ }
+ }
+ ConfMan.set("record_mode", "", Common::ConfigManager::kTransientDomain);
+ 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;
+ const Common::String gameId = ConfMan.get("gameid");
+ const EnginePlugin *plugin = 0;
+ EngineMan.findGame(gameId, &plugin);
+ (*plugin)->removeSaveState(gameId.c_str(), _temporarySlot);
+ _temporarySlot = -1;
+}
+
+} // End of namespace GUI
+
+#endif // ENABLE_EVENTRECORDER
+