/* 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;

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
