[Scummvm-cvs-logs] scummvm master -> 49210a803a53b84bcabe42fd339a1b205236c34d

sev- sev at scummvm.org
Thu Jul 4 13:59:40 CEST 2013


This automated email contains information about 5 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
f59512c47e RECORDER: Implement Events Recorder
fa61a35acd CONFIGURE: Remove superflous (and broken) command for eventrec.
93fc260885 SDL: Fix compilation by moving getMixerManager out of USE_OPENGL guard.
b286a6d033 RECORDER: Fix guard ifdef
49210a803a Merge pull request #331 from sev-/gsoc2012-eventsrecorder


Commit: f59512c47ea21c851535eeabf822aabdfde9167f
    https://github.com/scummvm/scummvm/commit/f59512c47ea21c851535eeabf822aabdfde9167f
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2013-05-16T14:18:09-07:00

Commit Message:
RECORDER: Implement Events Recorder

Changed paths:
  A backends/mixer/nullmixer/nullsdl-mixer.cpp
  A backends/mixer/nullmixer/nullsdl-mixer.h
  A backends/saves/recorder/recorder-saves.cpp
  A backends/saves/recorder/recorder-saves.h
  A common/recorderfile.cpp
  A common/recorderfile.h
  A gui/EventRecorder.cpp
  A gui/EventRecorder.h
  A gui/editrecorddialog.cpp
  A gui/editrecorddialog.h
  A gui/onscreendialog.cpp
  A gui/onscreendialog.h
  A gui/recorderdialog.cpp
  A gui/recorderdialog.h
  A gui/themes/scummmodern/editbtn.bmp
  A gui/themes/scummmodern/editbtn_small.bmp
  A gui/themes/scummmodern/fastreplay.bmp
  A gui/themes/scummmodern/fastreplay_small.bmp
  A gui/themes/scummmodern/stopbtn.bmp
  A gui/themes/scummmodern/stopbtn_small.bmp
  A gui/themes/scummmodern/switchbtn.bmp
  A gui/themes/scummmodern/switchbtn_small.bmp
  R common/EventRecorder.cpp
  R common/EventRecorder.h
    audio/mixer.cpp
    backends/events/default/default-events.cpp
    backends/events/sdl/sdl-events.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.h
    backends/modular-backend.cpp
    backends/module.mk
    backends/mutex/sdl/sdl-mutex.cpp
    backends/platform/android/android.cpp
    backends/platform/android/android.h
    backends/platform/bada/system.cpp
    backends/platform/bada/system.h
    backends/platform/dc/dc.h
    backends/platform/dc/time.cpp
    backends/platform/ds/arm9/source/dsmain.cpp
    backends/platform/ds/arm9/source/dsmain.h
    backends/platform/ds/arm9/source/osystem_ds.cpp
    backends/platform/ds/arm9/source/osystem_ds.h
    backends/platform/iphone/osys_main.cpp
    backends/platform/iphone/osys_main.h
    backends/platform/n64/osys_n64.h
    backends/platform/n64/osys_n64_base.cpp
    backends/platform/null/null.cpp
    backends/platform/ps2/systemps2.cpp
    backends/platform/ps2/systemps2.h
    backends/platform/psp/osys_psp.cpp
    backends/platform/psp/osys_psp.h
    backends/platform/psp/rtc.cpp
    backends/platform/psp/rtc.h
    backends/platform/sdl/sdl.cpp
    backends/platform/sdl/sdl.h
    backends/platform/wii/osystem.cpp
    backends/platform/wii/osystem.h
    backends/timer/default/default-timer.cpp
    base/commandLine.cpp
    base/main.cpp
    common/EventDispatcher.cpp
    common/debug.h
    common/events.h
    common/memstream.h
    common/module.mk
    common/random.cpp
    common/system.cpp
    common/system.h
    configure
    engines/advancedDetector.cpp
    engines/advancedDetector.h
    engines/cge/cge.cpp
    engines/dreamweb/dreamweb.cpp
    engines/sword25/gfx/image/renderedimage.cpp
    engines/sword25/gfx/image/renderedimage.h
    engines/wintermute/wintermute.cpp
    graphics/cursorman.cpp
    graphics/cursorman.h
    graphics/scaler.h
    graphics/scaler/thumbnail_intern.cpp
    graphics/thumbnail.cpp
    graphics/thumbnail.h
    gui/ThemeEngine.cpp
    gui/ThemeEngine.h
    gui/dialog.h
    gui/gui-manager.cpp
    gui/gui-manager.h
    gui/launcher.cpp
    gui/launcher.h
    gui/module.mk
    gui/themes/default.inc
    gui/themes/scummclassic.zip
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern.zip
    gui/themes/scummmodern/scummmodern_gfx.stx
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx



diff --git a/audio/mixer.cpp b/audio/mixer.cpp
index 8ff364b..ab3ed9e 100644
--- a/audio/mixer.cpp
+++ b/audio/mixer.cpp
@@ -20,6 +20,8 @@
  *
  */
 
+#include "gui/EventRecorder.h"
+
 #include "common/util.h"
 #include "common/system.h"
 #include "common/textconsole.h"
@@ -427,6 +429,7 @@ void MixerImpl::pauseHandle(SoundHandle handle, bool paused) {
 
 bool MixerImpl::isSoundIDActive(int id) {
 	Common::StackLock lock(_mutex);
+	g_eventRec.updateSubsystems();
 	for (int i = 0; i != NUM_CHANNELS; i++)
 		if (_channels[i] && _channels[i]->getId() == id)
 			return true;
@@ -443,6 +446,7 @@ int MixerImpl::getSoundID(SoundHandle handle) {
 
 bool MixerImpl::isSoundHandleActive(SoundHandle handle) {
 	Common::StackLock lock(_mutex);
+	g_eventRec.updateSubsystems();
 	const int index = handle._val % NUM_CHANNELS;
 	return _channels[index] && _channels[index]->getHandle()._val == handle._val;
 }
@@ -556,12 +560,12 @@ void Channel::pause(bool paused) {
 		_pauseLevel++;
 
 		if (_pauseLevel == 1)
-			_pauseStartTime = g_system->getMillis();
+			_pauseStartTime = g_system->getMillis(true);
 	} else if (_pauseLevel > 0) {
 		_pauseLevel--;
 
 		if (!_pauseLevel) {
-			_pauseTime = (g_system->getMillis() - _pauseStartTime);
+			_pauseTime = (g_system->getMillis(true) - _pauseStartTime);
 			_pauseStartTime = 0;
 		}
 	}
@@ -579,7 +583,7 @@ Timestamp Channel::getElapsedTime() {
 	if (isPaused())
 		delta = _pauseStartTime - _mixerTimeStamp;
 	else
-		delta = g_system->getMillis() - _mixerTimeStamp - _pauseTime;
+		delta = g_system->getMillis(true) - _mixerTimeStamp - _pauseTime;
 
 	// Convert the number of samples into a time duration.
 
@@ -599,13 +603,12 @@ int Channel::mix(int16 *data, uint len) {
 	assert(_stream);
 
 	int res = 0;
-
 	if (_stream->endOfData()) {
 		// TODO: call drain method
 	} else {
 		assert(_converter);
 		_samplesConsumed = _samplesDecoded;
-		_mixerTimeStamp = g_system->getMillis();
+		_mixerTimeStamp = g_system->getMillis(true);
 		_pauseTime = 0;
 		res = _converter->flow(*_stream, data, len, _volL, _volR);
 		_samplesDecoded += res;
diff --git a/backends/events/default/default-events.cpp b/backends/events/default/default-events.cpp
index 38a0c8d..bf76bbc 100644
--- a/backends/events/default/default-events.cpp
+++ b/backends/events/default/default-events.cpp
@@ -84,7 +84,8 @@ void DefaultEventManager::init() {
 }
 
 bool DefaultEventManager::pollEvent(Common::Event &event) {
-	uint32 time = g_system->getMillis();
+	// Skip recording of these events
+	uint32 time = g_system->getMillis(true);
 	bool result = false;
 
 	_dispatcher.dispatch();
diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp
index 0ca5bbb..e2ef7f6 100644
--- a/backends/events/sdl/sdl-events.cpp
+++ b/backends/events/sdl/sdl-events.cpp
@@ -106,7 +106,9 @@ void SdlEventSource::processMouseEvent(Common::Event &event, int x, int y) {
 }
 
 void SdlEventSource::handleKbdMouse() {
-	uint32 curTime = g_system->getMillis();
+	// Skip recording of these events
+	uint32 curTime = g_system->getMillis(true);
+
 	if (curTime >= _km.last_time + _km.delay_time) {
 		_km.last_time = curTime;
 		if (_km.x_down_count == 1) {
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index 02e58ab..e174a6e 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -40,6 +40,7 @@
 #include "graphics/scaler.h"
 #include "graphics/scaler/aspect.h"
 #include "graphics/surface.h"
+#include "gui/EventRecorder.h"
 
 static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
 	{"1x", _s("Normal (no scaling)"), GFX_NORMAL},
@@ -135,6 +136,7 @@ SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSou
 	_paletteDirtyStart(0), _paletteDirtyEnd(0),
 	_screenIsLocked(false),
 	_graphicsMutex(0),
+	_displayDisabled(false),
 #ifdef USE_SDL_DEBUG_FOCUSRECT
 	_enableFocusRectDebugCode(false), _enableFocusRect(false), _focusRect(),
 #endif
@@ -765,9 +767,20 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
 		fixupResolutionForAspectRatio(_videoMode.desiredAspectRatio, _videoMode.hardwareWidth, _videoMode.hardwareHeight);
 	}
 
-	_hwscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 16,
-		_videoMode.fullscreen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE
-	);
+
+#ifdef ENABLE_KEYMAPPER
+	_displayDisabled = ConfMan.getBool("disable_display");
+
+	if (_displayDisabled) {
+		_hwscreen = g_eventRec.getSurface(_videoMode.hardwareWidth, _videoMode.hardwareHeight);
+	} else 
+#endif
+		{
+		_hwscreen = SDL_SetVideoMode(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 16,
+			_videoMode.fullscreen ? (SDL_FULLSCREEN|SDL_SWSURFACE) : SDL_SWSURFACE
+			);
+	}
+
 #ifdef USE_RGB_COLOR
 	detectSupportedFormats();
 #endif
@@ -865,7 +878,12 @@ void SurfaceSdlGraphicsManager::unloadGFXMode() {
 	}
 
 	if (_hwscreen) {
-		SDL_FreeSurface(_hwscreen);
+		if (_displayDisabled) {
+			delete _hwscreen;
+		} else {
+			SDL_FreeSurface(_hwscreen);
+		}
+
 		_hwscreen = NULL;
 	}
 
@@ -1188,7 +1206,9 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
 #endif
 
 		// Finally, blit all our changes to the screen
-		SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList);
+		if (!_displayDisabled) {
+			SDL_UpdateRects(_hwscreen, _numDirtyRects, _dirtyRectList);
+		}
 	}
 
 	_numDirtyRects = 0;
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h
index 21444cc..97de0f9 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.h
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h
@@ -232,6 +232,9 @@ protected:
 	int _scalerType;
 	int _transactionMode;
 
+	// Indicates whether it is needed to free _hwsurface in destructor
+	bool _displayDisabled;
+
 	bool _screenIsLocked;
 	Graphics::Surface _framebuffer;
 
diff --git a/backends/mixer/nullmixer/nullsdl-mixer.cpp b/backends/mixer/nullmixer/nullsdl-mixer.cpp
new file mode 100644
index 0000000..2fd652e
--- /dev/null
+++ b/backends/mixer/nullmixer/nullsdl-mixer.cpp
@@ -0,0 +1,75 @@
+/* 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 "backends/mixer/nullmixer/nullsdl-mixer.h"
+#include "common/savefile.h"
+
+NullSdlMixerManager::NullSdlMixerManager() : SdlMixerManager() {
+	_outputRate = 22050;
+	_callsCounter = 0;
+	_callbackPeriod = 10;
+	_samples = 8192;
+	while (_samples * 16 > _outputRate * 2)
+		_samples >>= 1;
+	_samplesBuf = new uint8[_samples * 4];
+}
+
+NullSdlMixerManager::~NullSdlMixerManager() {
+	delete _samplesBuf;
+}
+
+void NullSdlMixerManager::init() {
+	_mixer = new Audio::MixerImpl(g_system, _outputRate);
+	assert(_mixer);
+	_mixer->setReady(true);
+}
+
+void NullSdlMixerManager::suspendAudio() {
+	_audioSuspended = true;
+}
+
+int NullSdlMixerManager::resumeAudio() {
+	if (!_audioSuspended) {
+		return -2;
+	}
+	_audioSuspended = false;
+	return 0;
+}
+
+
+void NullSdlMixerManager::startAudio() {
+}
+
+void NullSdlMixerManager::callbackHandler(byte *samples, int len) {
+	assert(_mixer);
+	_mixer->mixCallback(samples, len);
+}
+
+void NullSdlMixerManager::update() {
+	if (_audioSuspended) {
+		return;
+	}
+	_callsCounter++;
+	if ((_callsCounter % _callbackPeriod) == 0) {
+		callbackHandler(_samplesBuf, _samples);
+	}
+}
diff --git a/backends/mixer/nullmixer/nullsdl-mixer.h b/backends/mixer/nullmixer/nullsdl-mixer.h
new file mode 100644
index 0000000..94248ce
--- /dev/null
+++ b/backends/mixer/nullmixer/nullsdl-mixer.h
@@ -0,0 +1,62 @@
+/* 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.
+ *
+ */
+
+#ifndef BACKENDS_MIXER_NULLSDL_H
+#define BACKENDS_MIXER_NULLSDL_H
+
+#include "backends/mixer/sdl/sdl-mixer.h"
+#include "common/str.h"
+
+/** Audio mixer which in fact does not output audio.
+ *
+ *  It is used by events recorder since the recorder is intentionally
+ *  turning sound off to avoid stuttering.
+ *
+ *  It returns correct output and shoots callbacks, so all OSystem
+ *  users could work without modifications.
+ */
+
+class NullSdlMixerManager :	public SdlMixerManager {
+public:
+	NullSdlMixerManager();
+	virtual ~NullSdlMixerManager();
+
+	virtual void init();
+	void update();
+
+	virtual void suspendAudio();
+	virtual int resumeAudio();
+
+protected:
+
+	virtual void startAudio();
+	virtual void callbackHandler(byte *samples, int len);
+
+private:
+	uint32 _outputRate;
+	uint32 _callsCounter;
+	uint8  _callbackPeriod;
+	uint32 _samples;
+	uint8 *_samplesBuf;
+};
+
+#endif
diff --git a/backends/modular-backend.cpp b/backends/modular-backend.cpp
index b46f33a..94d0595 100644
--- a/backends/modular-backend.cpp
+++ b/backends/modular-backend.cpp
@@ -26,6 +26,7 @@
 
 #include "backends/graphics/graphics.h"
 #include "backends/mutex/mutex.h"
+#include "gui/EventRecorder.h"
 
 #include "audio/mixer.h"
 #include "graphics/pixelformat.h"
@@ -141,7 +142,9 @@ void ModularBackend::fillScreen(uint32 col) {
 }
 
 void ModularBackend::updateScreen() {
+	g_eventRec.preDrawOverlayGui();
 	_graphicsManager->updateScreen();
+	g_eventRec.postDrawOverlayGui();
 }
 
 void ModularBackend::setShakePos(int shakeOffset) {
diff --git a/backends/module.mk b/backends/module.mk
index a4f525d2..12cdc3d 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -70,7 +70,7 @@ MODULE_OBJS += \
 	mutex/sdl/sdl-mutex.o \
 	plugins/sdl/sdl-provider.o \
 	timer/sdl/sdl-timer.o
-	
+
 # SDL 1.3 removed audio CD support
 ifndef USE_SDL13
 MODULE_OBJS += \
@@ -214,5 +214,11 @@ MODULE_OBJS += \
 	plugins/wii/wii-provider.o
 endif
 
+ifdef ENABLE_EVENTRECORDER
+MODULE_OBJS += \
+	mixer/nullmixer/nullsdl-mixer.o \
+	saves/recorder/recorder-saves.o
+endif
+
 # Include common rules
 include $(srcdir)/rules.mk
diff --git a/backends/mutex/sdl/sdl-mutex.cpp b/backends/mutex/sdl/sdl-mutex.cpp
index 8491ae4..a51e6f0 100644
--- a/backends/mutex/sdl/sdl-mutex.cpp
+++ b/backends/mutex/sdl/sdl-mutex.cpp
@@ -33,15 +33,15 @@ OSystem::MutexRef SdlMutexManager::createMutex() {
 }
 
 void SdlMutexManager::lockMutex(OSystem::MutexRef mutex) {
-	SDL_mutexP((SDL_mutex *) mutex);
+	SDL_mutexP((SDL_mutex *)mutex);
 }
 
 void SdlMutexManager::unlockMutex(OSystem::MutexRef mutex) {
-	SDL_mutexV((SDL_mutex *) mutex);
+	SDL_mutexV((SDL_mutex *)mutex);
 }
 
 void SdlMutexManager::deleteMutex(OSystem::MutexRef mutex) {
-	SDL_DestroyMutex((SDL_mutex *) mutex);
+	SDL_DestroyMutex((SDL_mutex *)mutex);
 }
 
 #endif
diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp
index 0b31ee7..e066ea0 100644
--- a/backends/platform/android/android.cpp
+++ b/backends/platform/android/android.cpp
@@ -450,7 +450,7 @@ bool OSystem_Android::getFeatureState(Feature f) {
 	}
 }
 
-uint32 OSystem_Android::getMillis() {
+uint32 OSystem_Android::getMillis(bool skipRecord) {
 	timeval curTime;
 
 	gettimeofday(&curTime, 0);
diff --git a/backends/platform/android/android.h b/backends/platform/android/android.h
index 5f2f40b..b4813b3 100644
--- a/backends/platform/android/android.h
+++ b/backends/platform/android/android.h
@@ -274,7 +274,7 @@ public:
 	virtual void setCursorPalette(const byte *colors, uint start, uint num);
 
 	virtual bool pollEvent(Common::Event &event);
-	virtual uint32 getMillis();
+	virtual uint32 getMillis(bool skipRecord = false);
 	virtual void delayMillis(uint msecs);
 
 	virtual MutexRef createMutex(void);
diff --git a/backends/platform/bada/system.cpp b/backends/platform/bada/system.cpp
index 3f862c2..5c3c25c 100644
--- a/backends/platform/bada/system.cpp
+++ b/backends/platform/bada/system.cpp
@@ -373,7 +373,7 @@ bool BadaSystem::pollEvent(Common::Event &event) {
 	return _appForm->pollEvent(event);
 }
 
-uint32 BadaSystem::getMillis() {
+uint32 BadaSystem::getMillis(bool skipRecord) {
 	long long result, ticks = 0;
 	SystemTime::GetTicks(ticks);
 	result = ticks - _epoch;
diff --git a/backends/platform/bada/system.h b/backends/platform/bada/system.h
index c28686c..89394c5 100644
--- a/backends/platform/bada/system.h
+++ b/backends/platform/bada/system.h
@@ -83,7 +83,7 @@ private:
 
 	void updateScreen();
 	bool pollEvent(Common::Event &event);
-	uint32 getMillis();
+	uint32 getMillis(bool skipRecord = false);
 	void delayMillis(uint msecs);
 	void getTimeAndDate(TimeDate &t) const;
 	void fatalError();
diff --git a/backends/platform/dc/dc.h b/backends/platform/dc/dc.h
index d41839d..d62ced0 100644
--- a/backends/platform/dc/dc.h
+++ b/backends/platform/dc/dc.h
@@ -151,7 +151,7 @@ public:
   void setShakePos(int shake_pos);
 
   // Get the number of milliseconds since the program was started.
-  uint32 getMillis();
+  uint32 getMillis(bool skipRecord = false);
 
   // Delay for a specified amount of milliseconds
   void delayMillis(uint msecs);
diff --git a/backends/platform/dc/time.cpp b/backends/platform/dc/time.cpp
index 8cc3a71..1e5f44e 100644
--- a/backends/platform/dc/time.cpp
+++ b/backends/platform/dc/time.cpp
@@ -26,7 +26,7 @@
 #include "dc.h"
 
 
-uint32 OSystem_Dreamcast::getMillis()
+uint32 OSystem_Dreamcast::getMillis(bool skipRecord)
 {
   static uint32 msecs=0;
   static unsigned int t0=0;
diff --git a/backends/platform/ds/arm9/source/dsmain.cpp b/backends/platform/ds/arm9/source/dsmain.cpp
index 830c782..9dc66e8 100644
--- a/backends/platform/ds/arm9/source/dsmain.cpp
+++ b/backends/platform/ds/arm9/source/dsmain.cpp
@@ -2280,7 +2280,7 @@ void VBlankHandler(void) {
 	//REG_IF = IRQ_VBLANK;
 }
 
-int getMillis() {
+int getMillis(bool skipRecord) {
 	return currentTimeMillis;
 //	return frameCount * FRAME_TIME;
 }
diff --git a/backends/platform/ds/arm9/source/dsmain.h b/backends/platform/ds/arm9/source/dsmain.h
index ad49ae2..5e91fae 100644
--- a/backends/platform/ds/arm9/source/dsmain.h
+++ b/backends/platform/ds/arm9/source/dsmain.h
@@ -88,7 +88,7 @@ void	setGamma(int gamma);
 
 // Timers
 void 	setTimerCallback(OSystem_DS::TimerProc proc, int interval);		// Setup a callback function at a regular interval
-int 	getMillis();													// Return the current runtime in milliseconds
+int 	getMillis(bool skipRecord = false);								// Return the current runtime in milliseconds
 void 	doTimerCallback();												// Call callback function if required
 
 // Sound
diff --git a/backends/platform/ds/arm9/source/osystem_ds.cpp b/backends/platform/ds/arm9/source/osystem_ds.cpp
index a4b9c84..2f6358d 100644
--- a/backends/platform/ds/arm9/source/osystem_ds.cpp
+++ b/backends/platform/ds/arm9/source/osystem_ds.cpp
@@ -656,7 +656,7 @@ bool OSystem_DS::pollEvent(Common::Event &event) {
 	return false;
 }
 
-uint32 OSystem_DS::getMillis() {
+uint32 OSystem_DS::getMillis(bool skipRecord) {
 	return DS::getMillis();
 }
 
diff --git a/backends/platform/ds/arm9/source/osystem_ds.h b/backends/platform/ds/arm9/source/osystem_ds.h
index a6001da..4550e22 100644
--- a/backends/platform/ds/arm9/source/osystem_ds.h
+++ b/backends/platform/ds/arm9/source/osystem_ds.h
@@ -117,7 +117,7 @@ public:
 	virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, u32 keycolor, bool dontScale, const Graphics::PixelFormat *format);
 
 	virtual bool pollEvent(Common::Event &event);
-	virtual uint32 getMillis();
+	virtual uint32 getMillis(bool skipRecord = false);
 	virtual void delayMillis(uint msecs);
 	virtual void getTimeAndDate(TimeDate &t) const;
 
diff --git a/backends/platform/iphone/osys_main.cpp b/backends/platform/iphone/osys_main.cpp
index ed2c886..460d3fd 100644
--- a/backends/platform/iphone/osys_main.cpp
+++ b/backends/platform/iphone/osys_main.cpp
@@ -166,7 +166,7 @@ void OSystem_IPHONE::suspendLoop() {
 	_timeSuspended += getMillis() - startTime;
 }
 
-uint32 OSystem_IPHONE::getMillis() {
+uint32 OSystem_IPHONE::getMillis(bool skipRecord) {
 	//printf("getMillis()\n");
 
 	struct timeval currentTime;
diff --git a/backends/platform/iphone/osys_main.h b/backends/platform/iphone/osys_main.h
index 0371254..811a8dd 100644
--- a/backends/platform/iphone/osys_main.h
+++ b/backends/platform/iphone/osys_main.h
@@ -165,7 +165,7 @@ public:
 	virtual void setCursorPalette(const byte *colors, uint start, uint num);
 
 	virtual bool pollEvent(Common::Event &event);
-	virtual uint32 getMillis();
+	virtual uint32 getMillis(bool skipRecord = false);
 	virtual void delayMillis(uint msecs);
 
 	virtual MutexRef createMutex(void);
diff --git a/backends/platform/n64/osys_n64.h b/backends/platform/n64/osys_n64.h
index bc6b3cb..10138b2 100644
--- a/backends/platform/n64/osys_n64.h
+++ b/backends/platform/n64/osys_n64.h
@@ -184,7 +184,7 @@ public:
 	virtual void setCursorPalette(const byte *colors, uint start, uint num);
 
 	virtual bool pollEvent(Common::Event &event);
-	virtual uint32 getMillis();
+	virtual uint32 getMillis(bool skipRecord = false);
 	virtual void delayMillis(uint msecs);
 
 	virtual MutexRef createMutex(void);
diff --git a/backends/platform/n64/osys_n64_base.cpp b/backends/platform/n64/osys_n64_base.cpp
index 1e2aca9..afd93f5 100644
--- a/backends/platform/n64/osys_n64_base.cpp
+++ b/backends/platform/n64/osys_n64_base.cpp
@@ -810,7 +810,7 @@ void OSystem_N64::setMouseCursor(const void *buf, uint w, uint h, int hotspotX,
 	return;
 }
 
-uint32 OSystem_N64::getMillis() {
+uint32 OSystem_N64::getMillis(bool skipRecord) {
 	return getMilliTick();
 }
 
diff --git a/backends/platform/null/null.cpp b/backends/platform/null/null.cpp
index 4690a67..9e05539 100644
--- a/backends/platform/null/null.cpp
+++ b/backends/platform/null/null.cpp
@@ -49,7 +49,7 @@ public:
 
 	virtual bool pollEvent(Common::Event &event);
 
-	virtual uint32 getMillis();
+	virtual uint32 getMillis(bool skipRecord = false);
 	virtual void delayMillis(uint msecs);
 	virtual void getTimeAndDate(TimeDate &t) const {}
 
diff --git a/backends/platform/ps2/systemps2.cpp b/backends/platform/ps2/systemps2.cpp
index 5628658..a7d782b 100644
--- a/backends/platform/ps2/systemps2.cpp
+++ b/backends/platform/ps2/systemps2.cpp
@@ -571,7 +571,7 @@ void OSystem_PS2::displayMessageOnOSD(const char *msg) {
 	printf("displayMessageOnOSD: %s\n", msg);
 }
 
-uint32 OSystem_PS2::getMillis(void) {
+uint32 OSystem_PS2::getMillis(bool skipRecord) {
 	return msecCount;
 }
 
diff --git a/backends/platform/ps2/systemps2.h b/backends/platform/ps2/systemps2.h
index 99482d4..3ba40a7 100644
--- a/backends/platform/ps2/systemps2.h
+++ b/backends/platform/ps2/systemps2.h
@@ -82,7 +82,7 @@ public:
 	virtual void warpMouse(int x, int y);
 	virtual void setMouseCursor(const void *buf, uint w, uint h, int hotspot_x, int hotspot_y, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = 0);
 
-	virtual uint32 getMillis();
+	virtual uint32 getMillis(bool skipRecord = false);
 	virtual void delayMillis(uint msecs);
 	virtual bool pollEvent(Common::Event &event);
 
diff --git a/backends/platform/psp/osys_psp.cpp b/backends/platform/psp/osys_psp.cpp
index fb8c1c6..8559066 100644
--- a/backends/platform/psp/osys_psp.cpp
+++ b/backends/platform/psp/osys_psp.cpp
@@ -349,7 +349,7 @@ bool OSystem_PSP::pollEvent(Common::Event &event) {
 	return _inputHandler.getAllInputs(event);
 }
 
-uint32 OSystem_PSP::getMillis() {
+uint32 OSystem_PSP::getMillis(bool skipRecord) {
 	return PspRtc::instance().getMillis();
 }
 
diff --git a/backends/platform/psp/osys_psp.h b/backends/platform/psp/osys_psp.h
index 2afdabd..f4591e4 100644
--- a/backends/platform/psp/osys_psp.h
+++ b/backends/platform/psp/osys_psp.h
@@ -125,7 +125,7 @@ public:
 	bool processInput(Common::Event &event);
 
 	// Time
-	uint32 getMillis();
+	uint32 getMillis(bool skipRecord = false);
 	void delayMillis(uint msecs);
 
 	// Timer
diff --git a/backends/platform/psp/rtc.cpp b/backends/platform/psp/rtc.cpp
index cbbb7d3..4f15e45 100644
--- a/backends/platform/psp/rtc.cpp
+++ b/backends/platform/psp/rtc.cpp
@@ -52,7 +52,7 @@ void PspRtc::init() {						// init our starting ticks
 
 // Note that after we fill up 32 bits ie 50 days we'll loop back to 0, which may cause
 // unpredictable results
-uint32 PspRtc::getMillis() {
+uint32 PspRtc::getMillis(bool skipRecord) {
 	uint32 ticks[2];
 
 	sceRtcGetCurrentTick((u64 *)ticks);		// can introduce weird thread delays
diff --git a/backends/platform/psp/rtc.h b/backends/platform/psp/rtc.h
index 45885c3..d268968 100644
--- a/backends/platform/psp/rtc.h
+++ b/backends/platform/psp/rtc.h
@@ -40,7 +40,7 @@ public:
 		init();
 	}
 	void init();
-	uint32 getMillis();
+	uint32 getMillis(bool skipRecord = false);
 	uint32 getMicros();
 };
 
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index d548543..77e6f73 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -30,7 +30,7 @@
 
 #include "backends/platform/sdl/sdl.h"
 #include "common/config-manager.h"
-#include "common/EventRecorder.h"
+#include "gui/EventRecorder.h"
 #include "common/taskbar.h"
 #include "common/textconsole.h"
 
@@ -97,7 +97,9 @@ OSystem_SDL::~OSystem_SDL() {
 	_audiocdManager = 0;
 	delete _mixerManager;
 	_mixerManager = 0;
-	delete _timerManager;
+
+	delete g_eventRec.getTimerManager();
+
 	_timerManager = 0;
 	delete _mutexManager;
 	_mutexManager = 0;
@@ -131,9 +133,6 @@ void OSystem_SDL::init() {
 	if (_mutexManager == 0)
 		_mutexManager = new SdlMutexManager();
 
-	if (_timerManager == 0)
-		_timerManager = new SdlTimerManager();
-
 #if defined(USE_TASKBAR)
 	if (_taskbarManager == 0)
 		_taskbarManager = new Common::TaskbarManager();
@@ -191,10 +190,12 @@ void OSystem_SDL::initBackend() {
 
 	if (_mixerManager == 0) {
 		_mixerManager = new SdlMixerManager();
-
 		// Setup and start mixer
 		_mixerManager->init();
 	}
+	g_eventRec.registerMixerManager(_mixerManager);
+
+	g_eventRec.registerTimerManager(new SdlTimerManager());
 
 	if (_audiocdManager == 0) {
 		// Audio CD support was removed with SDL 1.3
@@ -466,14 +467,15 @@ void OSystem_SDL::setupIcon() {
 	free(icon);
 }
 
-uint32 OSystem_SDL::getMillis() {
+
+uint32 OSystem_SDL::getMillis(bool skipRecord) {
 	uint32 millis = SDL_GetTicks();
-	g_eventRec.processMillis(millis);
+	g_eventRec.processMillis(millis, skipRecord);
 	return millis;
 }
 
 void OSystem_SDL::delayMillis(uint msecs) {
-	if (!g_eventRec.processDelayMillis(msecs))
+	if (!g_eventRec.processDelayMillis())
 		SDL_Delay(msecs);
 }
 
@@ -491,12 +493,12 @@ void OSystem_SDL::getTimeAndDate(TimeDate &td) const {
 
 Audio::Mixer *OSystem_SDL::getMixer() {
 	assert(_mixerManager);
-	return _mixerManager->getMixer();
+	return getMixerManager()->getMixer();
 }
 
 SdlMixerManager *OSystem_SDL::getMixerManager() {
 	assert(_mixerManager);
-	return _mixerManager;
+	return g_eventRec.getMixerManager();
 }
 
 #ifdef USE_OPENGL
@@ -654,4 +656,8 @@ void OSystem_SDL::setupGraphicsModes() {
 	}
 }
 
+Common::TimerManager *OSystem_SDL::getTimerManager() {
+	return g_eventRec.getTimerManager();
+}
+
 #endif
diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h
index f05207b..840e73f 100644
--- a/backends/platform/sdl/sdl.h
+++ b/backends/platform/sdl/sdl.h
@@ -68,10 +68,11 @@ public:
 
 	virtual void setWindowCaption(const char *caption);
 	virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
-	virtual uint32 getMillis();
+	virtual uint32 getMillis(bool skipRecord = false);
 	virtual void delayMillis(uint msecs);
 	virtual void getTimeAndDate(TimeDate &td) const;
 	virtual Audio::Mixer *getMixer();
+	virtual Common::TimerManager *getTimerManager();
 
 protected:
 	bool _inited;
diff --git a/backends/platform/wii/osystem.cpp b/backends/platform/wii/osystem.cpp
index 22a6495..9d3a747 100644
--- a/backends/platform/wii/osystem.cpp
+++ b/backends/platform/wii/osystem.cpp
@@ -203,7 +203,7 @@ bool OSystem_Wii::getFeatureState(Feature f) {
 	}
 }
 
-uint32 OSystem_Wii::getMillis() {
+uint32 OSystem_Wii::getMillis(bool skipRecord) {
 	return ticks_to_millisecs(diff_ticks(_startup_time, gettime()));
 }
 
diff --git a/backends/platform/wii/osystem.h b/backends/platform/wii/osystem.h
index 5d6998d..287c70a 100644
--- a/backends/platform/wii/osystem.h
+++ b/backends/platform/wii/osystem.h
@@ -193,7 +193,7 @@ public:
 								const Graphics::PixelFormat *format);
 
 	virtual bool pollEvent(Common::Event &event);
-	virtual uint32 getMillis();
+	virtual uint32 getMillis(bool skipRecord = false);
 	virtual void delayMillis(uint msecs);
 
 	virtual MutexRef createMutex();
diff --git a/backends/saves/recorder/recorder-saves.cpp b/backends/saves/recorder/recorder-saves.cpp
new file mode 100644
index 0000000..49b4672
--- /dev/null
+++ b/backends/saves/recorder/recorder-saves.cpp
@@ -0,0 +1,35 @@
+/* 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 "backends/saves/recorder/recorder-saves.h"
+#include "gui/EventRecorder.h"
+#include "common/savefile.h"
+
+Common::InSaveFile *RecorderSaveFileManager::openForLoading(const Common::String &filename) {
+	Common::InSaveFile *result = g_eventRec.processSaveStream(filename);
+	return result;
+}
+
+Common::StringArray RecorderSaveFileManager::listSaveFiles(const Common::String &pattern) {
+	return g_eventRec.listSaveFiles(pattern);
+}
+
diff --git a/backends/saves/recorder/recorder-saves.h b/backends/saves/recorder/recorder-saves.h
new file mode 100644
index 0000000..692aeca
--- /dev/null
+++ b/backends/saves/recorder/recorder-saves.h
@@ -0,0 +1,36 @@
+/* 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.
+ *
+ */
+
+#ifndef BACKEND_SAVES_RECORDER_H
+#define BACKEND_SAVES_RECORDER_H
+
+#include "backends/saves/default/default-saves.h"
+
+/**
+ * Provides a savefile manager implementation for event recorder.
+ */
+class RecorderSaveFileManager : public DefaultSaveFileManager {
+	virtual Common::StringArray listSaveFiles(const Common::String &pattern);
+	virtual Common::InSaveFile *openForLoading(const Common::String &filename);
+};
+
+#endif
diff --git a/backends/timer/default/default-timer.cpp b/backends/timer/default/default-timer.cpp
index 9f56d58..ce93320 100644
--- a/backends/timer/default/default-timer.cpp
+++ b/backends/timer/default/default-timer.cpp
@@ -80,7 +80,7 @@ DefaultTimerManager::~DefaultTimerManager() {
 void DefaultTimerManager::handler() {
 	Common::StackLock lock(_mutex);
 
-	const uint32 curTime = g_system->getMillis();
+	uint32 curTime = g_system->getMillis(true);
 
 	// Repeat as long as there is a TimerSlot that is scheduled to fire.
 	TimerSlot *slot = _head->next;
diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 42a3a64..6f173a5 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -118,6 +118,13 @@ static const char HELP_STRING[] =
 	"  --aspect-ratio           Enable aspect ratio correction\n"
 	"  --render-mode=MODE       Enable additional render modes (cga, ega, hercGreen,\n"
 	"                           hercAmber, amiga)\n"
+#ifdef ENABLE_EVENTRECORDER
+	"  --record-mode=MODE       Specify record mode for event recorder (record, playback,\n"
+	"                           passthrough [default])\n"
+	"  --record-file=FILE       Specify record file name\n"
+	"  --disable-display        Disable any gfx output. Used for headless events\n"
+	"                           playback by Event Recorder\n"
+#endif
 	"\n"
 #if defined(ENABLE_SKY) || defined(ENABLE_QUEEN)
 	"  --alt-intro              Use alternative intro for CD versions of Beneath a\n"
@@ -232,10 +239,9 @@ void registerDefaults() {
 	ConfMan.registerDefault("confirm_exit", false);
 	ConfMan.registerDefault("disable_sdl_parachute", false);
 
+	ConfMan.registerDefault("disable_display", false);
 	ConfMan.registerDefault("record_mode", "none");
 	ConfMan.registerDefault("record_file_name", "record.bin");
-	ConfMan.registerDefault("record_temp_file_name", "record.tmp");
-	ConfMan.registerDefault("record_time_file_name", "record.time");
 
 	ConfMan.registerDefault("gui_saveload_chooser", "grid");
 	ConfMan.registerDefault("gui_saveload_last_pos", "0");
@@ -424,6 +430,17 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha
 			DO_OPTION_BOOL('f', "fullscreen")
 			END_OPTION
 
+#ifdef ENABLE_EVENTRECORDER
+			DO_LONG_OPTION_INT("disable-display")
+			END_OPTION
+
+			DO_LONG_OPTION("record-mode")
+			END_OPTION
+
+			DO_LONG_OPTION("record-file-name")
+			END_OPTION
+#endif
+
 			DO_LONG_OPTION("opl-driver")
 			END_OPTION
 
@@ -569,18 +586,6 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha
 			END_OPTION
 #endif
 
-			DO_LONG_OPTION("record-mode")
-			END_OPTION
-
-			DO_LONG_OPTION("record-file-name")
-			END_OPTION
-
-			DO_LONG_OPTION("record-temp-file-name")
-			END_OPTION
-
-			DO_LONG_OPTION("record-time-file-name")
-			END_OPTION
-
 #ifdef IPHONE
 			// This is automatically set when launched from the Springboard.
 			DO_LONG_OPTION_OPT("launchedFromSB", 0)
diff --git a/base/main.cpp b/base/main.cpp
index 355a65f..3f51c97 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -42,8 +42,11 @@
 #include "common/debug.h"
 #include "common/debug-channels.h" /* for debug manager */
 #include "common/events.h"
-#include "common/EventRecorder.h"
+#include "gui/EventRecorder.h"
 #include "common/fs.h"
+#ifdef ENABLE_EVENTRECORDER
+#include "common/recorderfile.h"
+#endif
 #include "common/system.h"
 #include "common/textconsole.h"
 #include "common/tokenizer.h"
@@ -409,7 +412,9 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 			settings["gfx-mode"] = "default";
 		}
 	}
-
+	if (settings.contains("disable-display")) {
+		ConfMan.setInt("disable-display", 1, Common::ConfigManager::kTransientDomain);
+	}
 	setupGraphics(system);
 
 	// Init the different managers that are used by the engines.
@@ -428,7 +433,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 	// TODO: This is just to match the current behavior, when we further extend
 	// our event recorder, we might do this at another place. Or even change
 	// the whole API for that ;-).
-	g_eventRec.init();
+	g_eventRec.RegisterEventSource();
 
 	// Now as the event manager is created, setup the keymapper
 	setupKeymapper(system);
@@ -448,6 +453,21 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 			// to save memory
 			PluginManager::instance().unloadPluginsExcept(PLUGIN_TYPE_ENGINE, plugin);
 
+#ifdef ENABLE_EVENTRECORDER
+			Common::String recordMode = ConfMan.get("record_mode");
+			Common::String recordFileName = ConfMan.get("record_file_name");
+
+			if (recordMode == "record") {
+				g_eventRec.init(g_eventRec.generateRecordFileName(ConfMan.getActiveDomainName()), GUI::EventRecorder::kRecorderRecord);
+			} else if (recordMode == "playback") {
+				g_eventRec.init(recordFileName, GUI::EventRecorder::kRecorderPlayback);
+			} else if ((recordMode == "info") && (!recordFileName.empty())) {
+				Common::PlaybackFile record;
+				record.openRead(recordFileName);
+				debug("info:author=%s name=%s description=%s", record.getHeader().author.c_str(), record.getHeader().name.c_str(), record.getHeader().description.c_str());
+				break;
+			}
+#endif
 			// Try to run the game
 			Common::Error result = runGame(plugin, system, specialDebug);
 
@@ -478,6 +498,11 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 			#ifdef FORCE_RTL
 			g_system->getEventManager()->resetQuit();
 			#endif
+			#ifdef ENABLE_EVENTRECORDER
+			if (g_eventRec.checkForContinueGame()) {
+				continue;
+			}
+			#endif
 
 			// Discard any command line options. It's unlikely that the user
 			// wanted to apply them to *all* games ever launched.
@@ -501,7 +526,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 	GUI::GuiManager::destroy();
 	Common::ConfigManager::destroy();
 	Common::DebugManager::destroy();
-	Common::EventRecorder::destroy();
+	GUI::EventRecorder::destroy();
 	Common::SearchManager::destroy();
 #ifdef USE_TRANSLATION
 	Common::TranslationManager::destroy();
diff --git a/common/EventDispatcher.cpp b/common/EventDispatcher.cpp
index 012a2df..dc123e8 100644
--- a/common/EventDispatcher.cpp
+++ b/common/EventDispatcher.cpp
@@ -38,7 +38,9 @@ EventDispatcher::~EventDispatcher() {
 			delete i->observer;
 	}
 
-	delete _mapper;
+	if (_autoFreeMapper) {
+		delete _mapper;
+	}
 	_mapper = 0;
 }
 
@@ -68,11 +70,15 @@ void EventDispatcher::dispatch() {
 	}
 }
 
-void EventDispatcher::registerMapper(EventMapper *mapper) {
-	delete _mapper;
+void EventDispatcher::registerMapper(EventMapper *mapper, bool autoFree) {
+	if (_autoFreeMapper) {
+		delete _mapper;
+	}
 	_mapper = mapper;
+	_autoFreeMapper = autoFree;
 }
 
+
 void EventDispatcher::registerSource(EventSource *source, bool autoFree) {
 	SourceEntry newEntry;
 
diff --git a/common/EventRecorder.cpp b/common/EventRecorder.cpp
deleted file mode 100644
index 5e24f12..0000000
--- a/common/EventRecorder.cpp
+++ /dev/null
@@ -1,428 +0,0 @@
-/* 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 "common/EventRecorder.h"
-
-#include "common/bufferedstream.h"
-#include "common/config-manager.h"
-#include "common/random.h"
-#include "common/savefile.h"
-#include "common/textconsole.h"
-
-namespace Common {
-
-DECLARE_SINGLETON(EventRecorder);
-
-#define RECORD_SIGNATURE 0x54455354
-#define RECORD_VERSION 1
-
-uint32 readTime(ReadStream *inFile) {
-	uint32 d = inFile->readByte();
-	if (d == 0xff) {
-		d = inFile->readUint32LE();
-	}
-
-	return d;
-}
-
-void writeTime(WriteStream *outFile, uint32 d) {
-		//Simple RLE compression
-	if (d >= 0xff) {
-		outFile->writeByte(0xff);
-		outFile->writeUint32LE(d);
-	} else {
-		outFile->writeByte(d);
-	}
-}
-
-void readRecord(SeekableReadStream *inFile, uint32 &diff, Event &event, uint32 &millis) {
-	millis = readTime(inFile);
-
-	diff = inFile->readUint32LE();
-
-	event.type = (EventType)inFile->readUint32LE();
-
-	switch (event.type) {
-	case EVENT_KEYDOWN:
-	case EVENT_KEYUP:
-		event.kbd.keycode = (KeyCode)inFile->readSint32LE();
-		event.kbd.ascii = inFile->readUint16LE();
-		event.kbd.flags = inFile->readByte();
-		break;
-	case EVENT_MOUSEMOVE:
-	case EVENT_LBUTTONDOWN:
-	case EVENT_LBUTTONUP:
-	case EVENT_RBUTTONDOWN:
-	case EVENT_RBUTTONUP:
-	case EVENT_WHEELUP:
-	case EVENT_WHEELDOWN:
-	case EVENT_MBUTTONDOWN:
-	case EVENT_MBUTTONUP:
-		event.mouse.x = inFile->readSint16LE();
-		event.mouse.y = inFile->readSint16LE();
-		break;
-	default:
-		break;
-	}
-}
-
-void writeRecord(WriteStream *outFile, uint32 diff, const Event &event, uint32 millis) {
-	writeTime(outFile, millis);
-
-	outFile->writeUint32LE(diff);
-
-	outFile->writeUint32LE((uint32)event.type);
-
-	switch (event.type) {
-	case EVENT_KEYDOWN:
-	case EVENT_KEYUP:
-		outFile->writeSint32LE(event.kbd.keycode);
-		outFile->writeUint16LE(event.kbd.ascii);
-		outFile->writeByte(event.kbd.flags);
-		break;
-	case EVENT_MOUSEMOVE:
-	case EVENT_LBUTTONDOWN:
-	case EVENT_LBUTTONUP:
-	case EVENT_RBUTTONDOWN:
-	case EVENT_RBUTTONUP:
-	case EVENT_WHEELUP:
-	case EVENT_WHEELDOWN:
-	case EVENT_MBUTTONDOWN:
-	case EVENT_MBUTTONUP:
-		outFile->writeSint16LE(event.mouse.x);
-		outFile->writeSint16LE(event.mouse.y);
-		break;
-	default:
-		break;
-	}
-}
-
-EventRecorder::EventRecorder() {
-	_recordFile = NULL;
-	_recordTimeFile = NULL;
-	_playbackFile = NULL;
-	_playbackTimeFile = NULL;
-	_timeMutex = g_system->createMutex();
-	_recorderMutex = g_system->createMutex();
-
-	_eventCount = 0;
-	_lastEventCount = 0;
-	_lastMillis = 0;
-	_lastEventMillis = 0;
-
-	_recordMode = kPassthrough;
-}
-
-EventRecorder::~EventRecorder() {
-	deinit();
-
-	g_system->deleteMutex(_timeMutex);
-	g_system->deleteMutex(_recorderMutex);
-}
-
-void EventRecorder::init() {
-	String recordModeString = ConfMan.get("record_mode");
-	if (recordModeString.compareToIgnoreCase("record") == 0) {
-		_recordMode = kRecorderRecord;
-
-		debug(3, "EventRecorder: record");
-	} else {
-		if (recordModeString.compareToIgnoreCase("playback") == 0) {
-			_recordMode = kRecorderPlayback;
-			debug(3, "EventRecorder: playback");
-		} else {
-			_recordMode = kPassthrough;
-			debug(3, "EventRecorder: passthrough");
-		}
-	}
-
-	_recordFileName = ConfMan.get("record_file_name");
-	if (_recordFileName.empty()) {
-		_recordFileName = "record.bin";
-	}
-	_recordTempFileName = ConfMan.get("record_temp_file_name");
-	if (_recordTempFileName.empty()) {
-		_recordTempFileName = "record.tmp";
-	}
-	_recordTimeFileName = ConfMan.get("record_time_file_name");
-	if (_recordTimeFileName.empty()) {
-		_recordTimeFileName = "record.time";
-	}
-
-	// recorder stuff
-	if (_recordMode == kRecorderRecord) {
-		_recordCount = 0;
-		_recordTimeCount = 0;
-		_recordFile = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving(_recordTempFileName), 128 * 1024);
-		_recordTimeFile = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving(_recordTimeFileName), 128 * 1024);
-		_recordSubtitles = ConfMan.getBool("subtitles");
-	}
-
-	uint32 sign;
-	uint32 randomSourceCount;
-	if (_recordMode == kRecorderPlayback) {
-		_playbackCount = 0;
-		_playbackTimeCount = 0;
-		_playbackFile = wrapBufferedSeekableReadStream(g_system->getSavefileManager()->openForLoading(_recordFileName), 128 * 1024, DisposeAfterUse::YES);
-		_playbackTimeFile = wrapBufferedSeekableReadStream(g_system->getSavefileManager()->openForLoading(_recordTimeFileName), 128 * 1024, DisposeAfterUse::YES);
-
-		if (!_playbackFile) {
-			warning("Cannot open playback file %s. Playback was switched off", _recordFileName.c_str());
-			_recordMode = kPassthrough;
-		}
-
-		if (!_playbackTimeFile) {
-			warning("Cannot open playback time file %s. Playback was switched off", _recordTimeFileName.c_str());
-			_recordMode = kPassthrough;
-		}
-	}
-
-	if (_recordMode == kRecorderPlayback) {
-		sign = _playbackFile->readUint32LE();
-		if (sign != RECORD_SIGNATURE) {
-			error("Unknown record file signature");
-		}
-
-		_playbackFile->readUint32LE(); // version
-
-		// conf vars
-		ConfMan.setBool("subtitles", _playbackFile->readByte() != 0);
-
-		_recordCount = _playbackFile->readUint32LE();
-		_recordTimeCount = _playbackFile->readUint32LE();
-
-		randomSourceCount = _playbackFile->readUint32LE();
-		for (uint i = 0; i < randomSourceCount; ++i) {
-			RandomSourceRecord rec;
-			rec.name = "";
-			uint32 sLen = _playbackFile->readUint32LE();
-			for (uint j = 0; j < sLen; ++j) {
-				char c = _playbackFile->readSByte();
-				rec.name += c;
-			}
-			rec.seed = _playbackFile->readUint32LE();
-			_randomSourceRecords.push_back(rec);
-		}
-
-		_hasPlaybackEvent = false;
-	}
-
-	g_system->getEventManager()->getEventDispatcher()->registerSource(this, false);
-	g_system->getEventManager()->getEventDispatcher()->registerObserver(this, EventManager::kEventRecorderPriority, false, true);
-}
-
-void EventRecorder::deinit() {
-	debug(3, "EventRecorder: deinit");
-
-	g_system->getEventManager()->getEventDispatcher()->unregisterSource(this);
-	g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
-
-	g_system->lockMutex(_timeMutex);
-	g_system->lockMutex(_recorderMutex);
-	_recordMode = kPassthrough;
-	g_system->unlockMutex(_timeMutex);
-	g_system->unlockMutex(_recorderMutex);
-
-	delete _playbackFile;
-	delete _playbackTimeFile;
-
-	if (_recordFile != NULL) {
-		_recordFile->finalize();
-		delete _recordFile;
-		_recordTimeFile->finalize();
-		delete _recordTimeFile;
-
-		_playbackFile = g_system->getSavefileManager()->openForLoading(_recordTempFileName);
-
-		assert(_playbackFile);
-
-		_recordFile = g_system->getSavefileManager()->openForSaving(_recordFileName);
-		_recordFile->writeUint32LE(RECORD_SIGNATURE);
-		_recordFile->writeUint32LE(RECORD_VERSION);
-
-		// conf vars
-		_recordFile->writeByte(_recordSubtitles ? 1 : 0);
-
-		_recordFile->writeUint32LE(_recordCount);
-		_recordFile->writeUint32LE(_recordTimeCount);
-
-		_recordFile->writeUint32LE(_randomSourceRecords.size());
-		for (uint i = 0; i < _randomSourceRecords.size(); ++i) {
-			_recordFile->writeUint32LE(_randomSourceRecords[i].name.size());
-			_recordFile->writeString(_randomSourceRecords[i].name);
-			_recordFile->writeUint32LE(_randomSourceRecords[i].seed);
-		}
-
-		for (uint i = 0; i < _recordCount; ++i) {
-			uint32 tempDiff;
-			Event tempEvent;
-			uint32 millis;
-			readRecord(_playbackFile, tempDiff, tempEvent, millis);
-			writeRecord(_recordFile, tempDiff, tempEvent, millis);
-		}
-
-		_recordFile->finalize();
-		delete _recordFile;
-		delete _playbackFile;
-
-		//TODO: remove recordTempFileName'ed file
-	}
-}
-
-void EventRecorder::registerRandomSource(RandomSource &rnd, const String &name) {
-	if (_recordMode == kRecorderRecord) {
-		RandomSourceRecord rec;
-		rec.name = name;
-		rec.seed = rnd.getSeed();
-		_randomSourceRecords.push_back(rec);
-	}
-
-	if (_recordMode == kRecorderPlayback) {
-		for (uint i = 0; i < _randomSourceRecords.size(); ++i) {
-			if (_randomSourceRecords[i].name == name) {
-				rnd.setSeed(_randomSourceRecords[i].seed);
-				_randomSourceRecords.remove_at(i);
-				break;
-			}
-		}
-	}
-}
-
-void EventRecorder::processMillis(uint32 &millis) {
-	uint32 d;
-	if (_recordMode == kPassthrough) {
-		return;
-	}
-
-	g_system->lockMutex(_timeMutex);
-	if (_recordMode == kRecorderRecord) {
-		d = millis - _lastMillis;
-		writeTime(_recordTimeFile, d);
-
-		_recordTimeCount++;
-	}
-
-	if (_recordMode == kRecorderPlayback) {
-		if (_recordTimeCount > _playbackTimeCount) {
-			d = readTime(_playbackTimeFile);
-
-			while ((_lastMillis + d > millis) && (_lastMillis + d - millis > 50)) {
-				_recordMode = kPassthrough;
-				g_system->delayMillis(50);
-				millis = g_system->getMillis();
-				_recordMode = kRecorderPlayback;
-			}
-
-			millis = _lastMillis + d;
-			_playbackTimeCount++;
-		}
-	}
-
-	_lastMillis = millis;
-	g_system->unlockMutex(_timeMutex);
-}
-
-bool EventRecorder::processDelayMillis(uint &msecs) {
-	if (_recordMode == kRecorderPlayback) {
-		_recordMode = kPassthrough;
-
-		uint32 millis = g_system->getMillis();
-
-		_recordMode = kRecorderPlayback;
-
-		if (_lastMillis > millis) {
-			// Skip delay if we're getting late
-			return true;
-		}
-	}
-
-	return false;
-}
-
-bool EventRecorder::notifyEvent(const Event &ev) {
-	if (_recordMode != kRecorderRecord)
-		return false;
-
-	StackLock lock(_recorderMutex);
-	++_eventCount;
-
-	writeRecord(_recordFile, _eventCount - _lastEventCount, ev, _lastMillis - _lastEventMillis);
-
-	_recordCount++;
-	_lastEventCount = _eventCount;
-	_lastEventMillis = _lastMillis;
-
-	return false;
-}
-
-bool EventRecorder::notifyPoll() {
-	if (_recordMode != kRecorderRecord)
-		return false;
-
-	++_eventCount;
-
-	return false;
-}
-
-bool EventRecorder::pollEvent(Event &ev) {
-	uint32 millis;
-
-	if (_recordMode != kRecorderPlayback)
-		return false;
-
-	StackLock lock(_recorderMutex);
-	++_eventCount;
-
-	if (!_hasPlaybackEvent) {
-		if (_recordCount > _playbackCount) {
-			readRecord(_playbackFile, const_cast<uint32&>(_playbackDiff), _playbackEvent, millis);
-			_playbackCount++;
-			_hasPlaybackEvent = true;
-		}
-	}
-
-	if (_hasPlaybackEvent) {
-		if (_playbackDiff <= (_eventCount - _lastEventCount)) {
-			switch (_playbackEvent.type) {
-			case EVENT_MOUSEMOVE:
-			case EVENT_LBUTTONDOWN:
-			case EVENT_LBUTTONUP:
-			case EVENT_RBUTTONDOWN:
-			case EVENT_RBUTTONUP:
-			case EVENT_WHEELUP:
-			case EVENT_WHEELDOWN:
-				g_system->warpMouse(_playbackEvent.mouse.x, _playbackEvent.mouse.y);
-				break;
-			default:
-				break;
-			}
-			ev = _playbackEvent;
-			_hasPlaybackEvent = false;
-			_lastEventCount = _eventCount;
-			return true;
-		}
-	}
-
-	return false;
-}
-
-} // End of namespace Common
diff --git a/common/EventRecorder.h b/common/EventRecorder.h
deleted file mode 100644
index 43a08b0..0000000
--- a/common/EventRecorder.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/* 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.
- *
- */
-
-#ifndef COMMON_EVENTRECORDER_H
-#define COMMON_EVENTRECORDER_H
-
-#include "common/scummsys.h"
-#include "common/events.h"
-#include "common/singleton.h"
-#include "common/mutex.h"
-#include "common/array.h"
-
-#define g_eventRec (Common::EventRecorder::instance())
-
-namespace Common {
-
-class RandomSource;
-class SeekableReadStream;
-class WriteStream;
-
-/**
- * Our generic event recorder.
- *
- * TODO: Add more documentation.
- */
-class EventRecorder : private EventSource, private EventObserver, public Singleton<EventRecorder> {
-	friend class Singleton<SingletonBaseType>;
-	EventRecorder();
-	~EventRecorder();
-public:
-	void init();
-	void deinit();
-
-	/** Register random source so it can be serialized in game test purposes */
-	void registerRandomSource(RandomSource &rnd, const String &name);
-
-	/** TODO: Add documentation, this is only used by the backend */
-	void processMillis(uint32 &millis);
-
-	/** TODO: Add documentation, this is only used by the backend */
-	bool processDelayMillis(uint &msecs);
-
-private:
-	bool notifyEvent(const Event &ev);
-	bool notifyPoll();
-	bool pollEvent(Event &ev);
-	bool allowMapping() const { return false; }
-
-	class RandomSourceRecord {
-	public:
-		String name;
-		uint32 seed;
-	};
-	Array<RandomSourceRecord> _randomSourceRecords;
-
-	bool _recordSubtitles;
-	volatile uint32 _recordCount;
-	volatile uint32 _lastRecordEvent;
-	volatile uint32 _recordTimeCount;
-	volatile uint32 _lastEventMillis;
-	WriteStream *_recordFile;
-	WriteStream *_recordTimeFile;
-	MutexRef _timeMutex;
-	MutexRef _recorderMutex;
-	volatile uint32 _lastMillis;
-
-	volatile uint32 _playbackCount;
-	volatile uint32 _playbackDiff;
-	volatile bool _hasPlaybackEvent;
-	volatile uint32 _playbackTimeCount;
-	Event _playbackEvent;
-	SeekableReadStream *_playbackFile;
-	SeekableReadStream *_playbackTimeFile;
-
-	volatile uint32 _eventCount;
-	volatile uint32 _lastEventCount;
-
-	enum RecordMode {
-		kPassthrough = 0,
-		kRecorderRecord = 1,
-		kRecorderPlayback = 2
-	};
-	volatile RecordMode _recordMode;
-	String _recordFileName;
-	String _recordTempFileName;
-	String _recordTimeFileName;
-};
-
-} // End of namespace Common
-
-#endif
diff --git a/common/debug.h b/common/debug.h
index dc94839..859f3c4 100644
--- a/common/debug.h
+++ b/common/debug.h
@@ -117,5 +117,9 @@ void debugCN(uint32 debugChannels, const char *s, ...) GCC_PRINTF(2, 3);
  */
 extern int gDebugLevel;
 
+//Global constant for EventRecorder debug channel
+enum GlobalDebugLevels {
+	kDebugLevelEventRec = 1 << 30
+};
 
 #endif
diff --git a/common/events.h b/common/events.h
index 7366c51..9029a40 100644
--- a/common/events.h
+++ b/common/events.h
@@ -288,11 +288,14 @@ public:
 	 * to the EventDispatcher, thus it will be deleted
 	 * with "delete", when EventDispatcher is destroyed.
 	 *
-	 * Note there is only one mapper per EventDispatcher
-	 * possible, thus when this method is called twice,
-	 * the former mapper will be destroied.
+	 * @param autoFree	Destroy previous mapper [default]
+	 *         		Normally we allow only one event mapper to exists,
+	 *			However Event Recorder must intervent into normal
+	 *			event flow without altering its semantics. Thus during
+	 *			Event Recorder playback and recording we allow
+	 *			two mappers.
 	 */
-	void registerMapper(EventMapper *mapper);
+	void registerMapper(EventMapper *mapper, bool autoFree = true);
 
 	/**
 	 * Queries the setup event mapper.
@@ -326,6 +329,7 @@ public:
 	 */
 	void unregisterObserver(EventObserver *obs);
 private:
+	bool _autoFreeMapper;
 	EventMapper *_mapper;
 
 	struct Entry {
diff --git a/common/memstream.h b/common/memstream.h
index 260fb64..7fa6500 100644
--- a/common/memstream.h
+++ b/common/memstream.h
@@ -89,8 +89,9 @@ public:
  */
 class MemoryWriteStream : public WriteStream {
 private:
-	byte *_ptr;
 	const uint32 _bufSize;
+protected:
+	byte *_ptr;
 	uint32 _pos;
 	bool _err;
 public:
@@ -117,6 +118,40 @@ public:
 };
 
 /**
+ * MemoryWriteStream subclass with ability to set stream position indicator.
+ */
+class SeekableMemoryWriteStream : public MemoryWriteStream {
+private:
+	byte *_ptrOrig;
+public:
+	SeekableMemoryWriteStream(byte *buf, uint32 len) : MemoryWriteStream(buf, len), _ptrOrig(buf) {}
+	uint32 seek(uint32 offset, int whence = SEEK_SET) {
+		switch (whence) {
+		case SEEK_END:
+			// SEEK_END works just like SEEK_SET, only 'reversed',
+			// i.e. from the end.
+			offset = size() + offset;
+			// Fall through
+		case SEEK_SET:
+			_ptr = _ptrOrig + offset;
+			_pos = offset;
+			break;
+		case SEEK_CUR:
+			_ptr += offset;
+			_pos += offset;
+			break;
+		}
+		// Post-Condition
+		if (_pos > size()) {
+			_pos = size();
+			_ptr = _ptrOrig + _pos;
+		}
+		return _pos;
+	}
+};
+
+
+/**
  * A sort of hybrid between MemoryWriteStream and Array classes. A stream
  * that grows as it's written to.
  */
diff --git a/common/module.mk b/common/module.mk
index d96b11e..9f9126c 100644
--- a/common/module.mk
+++ b/common/module.mk
@@ -10,7 +10,6 @@ MODULE_OBJS := \
 	error.o \
 	EventDispatcher.o \
 	EventMapper.o \
-	EventRecorder.o \
 	file.o \
 	fs.o \
 	gui_options.o \
@@ -51,5 +50,10 @@ MODULE_OBJS += \
 	rdft.o \
 	sinetables.o
 
+ifdef ENABLE_EVENTRECORDER
+MODULE_OBJS += \
+	recorderfile.o
+endif
+
 # Include common rules
 include $(srcdir)/rules.mk
diff --git a/common/random.cpp b/common/random.cpp
index fd75534..a74ab83 100644
--- a/common/random.cpp
+++ b/common/random.cpp
@@ -21,7 +21,7 @@
 
 #include "common/random.h"
 #include "common/system.h"
-#include "common/EventRecorder.h"
+#include "gui/EventRecorder.h"
 
 
 namespace Common {
@@ -30,13 +30,8 @@ RandomSource::RandomSource(const String &name) {
 	// Use system time as RNG seed. Normally not a good idea, if you are using
 	// a RNG for security purposes, but good enough for our purposes.
 	assert(g_system);
-	uint32 seed = g_system->getMillis();
+	uint32 seed = g_eventRec.getRandomSeed(name);
 	setSeed(seed);
-
-	// Register this random source with the event recorder. This may end
-	// up querying or resetting the current seed, so we must call it
-	// *after* the initial seed has been set.
-	g_eventRec.registerRandomSource(*this, name);
 }
 
 void RandomSource::setSeed(uint32 seed) {
diff --git a/common/recorderfile.cpp b/common/recorderfile.cpp
new file mode 100644
index 0000000..60c47e1
--- /dev/null
+++ b/common/recorderfile.cpp
@@ -0,0 +1,708 @@
+/* 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 "common/system.h"
+#include "gui/EventRecorder.h"
+#include "common/md5.h"
+#include "common/recorderfile.h"
+#include "common/savefile.h"
+#include "common/bufferedstream.h"
+#include "graphics/thumbnail.h"
+#include "graphics/surface.h"
+#include "graphics/scaler.h"
+
+#define RECORD_VERSION 1
+
+namespace Common {
+
+PlaybackFile::PlaybackFile() : _tmpRecordFile(_tmpBuffer, kRecordBuffSize), _tmpPlaybackFile(_tmpBuffer, kRecordBuffSize) {
+	_readStream = NULL;
+	_writeStream = NULL;
+	_screenshotsFile = NULL;
+	_mode = kClosed;
+}
+
+PlaybackFile::~PlaybackFile() {
+	close();
+}
+
+bool PlaybackFile::openWrite(const String &fileName) {
+	close();
+	_header.fileName = fileName;
+	_writeStream = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving(fileName), 128 * 1024);
+	_headerDumped = false;
+	_recordCount = 0;
+	if (_writeStream == NULL) {
+		return false;
+	}
+	_mode = kWrite;
+	return true;
+}
+
+bool PlaybackFile::openRead(const String &fileName) {
+	close();
+	_header.fileName = fileName;
+	_eventsSize = 0;
+	_tmpPlaybackFile.seek(0);
+	_readStream = wrapBufferedSeekableReadStream(g_system->getSavefileManager()->openForLoading(fileName), 128 * 1024, DisposeAfterUse::YES);
+	if (_readStream == NULL) {
+		debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=fail reason=\"file %s not found\"", fileName.c_str());
+		return false;
+	}
+	if (!parseHeader()) {
+		debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=fail reason=\"header parsing failed\"");
+		return false;
+	}
+	_screenshotsFile = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving("screenshots.bin"), 128 * 1024);
+	debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=success");
+	_mode = kRead;
+	return true;
+}
+
+void PlaybackFile::close() {
+	delete _readStream;
+	_readStream = NULL;
+	if (_writeStream != NULL) {
+		dumpRecordsToFile();
+		_writeStream->finalize();
+		delete _writeStream;
+		_writeStream = NULL;
+		updateHeader();
+	}
+	if (_screenshotsFile != NULL) {
+		_screenshotsFile->finalize();
+		delete _screenshotsFile;
+		_screenshotsFile = NULL;
+	}
+	for (HashMap<String, SaveFileBuffer>::iterator  i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) {
+		free(i->_value.buffer);
+	}
+	_header.saveFiles.clear();
+	_mode = kClosed;
+}
+
+bool PlaybackFile::parseHeader() {
+	PlaybackFileHeader result;
+	ChunkHeader nextChunk;
+	_playbackParseState = kFileStateCheckFormat;
+	if (!readChunkHeader(nextChunk)) {
+		_playbackParseState = kFileStateError;
+		return false;
+	}
+	while ((_playbackParseState != kFileStateDone) && (_playbackParseState != kFileStateError)) {
+		if (processChunk(nextChunk)) {
+			if (!readChunkHeader(nextChunk)) {
+				warning("Error in header parsing");
+				_playbackParseState = kFileStateError;
+			}
+		}
+	}
+	return _playbackParseState == kFileStateDone;
+}
+
+bool PlaybackFile::checkPlaybackFileVersion() {
+	uint32 version;
+	version = _readStream->readUint32LE();
+	if (version != RECORD_VERSION) {
+		warning("Incorrect playback file version. Expected version %d, but got %d.", RECORD_VERSION, version);
+		return false;
+	}
+	return true;
+}
+
+
+String PlaybackFile::readString(int len) {
+	String result;
+	char buf[50];
+	int readSize = 49;
+	while (len > 0)	{
+		if (len <= 49) {
+			readSize = len;
+		}
+		_readStream->read(buf, readSize);
+		buf[readSize] = 0;
+		result += buf;
+		len -= readSize;
+	}
+	return result;
+}
+
+bool PlaybackFile::readChunkHeader(PlaybackFile::ChunkHeader &nextChunk) {
+	nextChunk.id = (FileTag)_readStream->readUint32LE();
+	nextChunk.len = _readStream->readUint32LE();
+	return !_readStream->err() && !_readStream->eos();
+}
+
+bool PlaybackFile::processChunk(ChunkHeader &nextChunk) {
+	switch (_playbackParseState) {
+	case kFileStateCheckFormat:
+		if (nextChunk.id == kFormatIdTag) {
+			_playbackParseState = kFileStateCheckVersion;
+		} else {
+			warning("Unknown playback file signature");
+			_playbackParseState = kFileStateError;
+		}
+		break;
+	case kFileStateCheckVersion:
+		if ((nextChunk.id == kVersionTag) && checkPlaybackFileVersion()) {
+			_playbackParseState = kFileStateSelectSection;
+		} else {
+			_playbackParseState = kFileStateError;
+		}
+		break;
+	case kFileStateSelectSection:
+		switch (nextChunk.id) {
+		case kHeaderSectionTag:
+			_playbackParseState = kFileStateProcessHeader;
+			break;
+		case kHashSectionTag:
+			_playbackParseState = kFileStateProcessHash;
+			break;
+		case kRandomSectionTag:
+			_playbackParseState = kFileStateProcessRandom;
+			break;
+		case kEventTag:
+		case kScreenShotTag:
+			_readStream->seek(-8, SEEK_CUR);
+			_playbackParseState = kFileStateDone;
+			return false;
+		case kSaveTag:
+			_playbackParseState = kFileStateProcessSave;
+			break;
+		case kSettingsSectionTag:
+			_playbackParseState = kFileStateProcessSettings;
+			warning("Loading record header");
+			break;
+		default:
+			_readStream->skip(nextChunk.len);
+			break;
+		}
+		break;
+	case kFileStateProcessSave:
+		if (nextChunk.id == kSaveRecordTag) {
+			readSaveRecord();
+		} else {
+			_playbackParseState = kFileStateSelectSection;
+			return false;
+		}
+		break;
+	case kFileStateProcessHeader:
+		switch (nextChunk.id) {
+		case kAuthorTag:
+			_header.author = readString(nextChunk.len);
+			break;
+		case kCommentsTag:
+			_header.notes = readString(nextChunk.len);
+			break;
+		case kNameTag:
+			_header.name = readString(nextChunk.len);
+			break;
+		default:
+			_playbackParseState = kFileStateSelectSection;
+			return false;
+		}
+		break;
+	case kFileStateProcessHash:
+		if (nextChunk.id == kHashRecordTag) {
+			readHashMap(nextChunk);
+		} else {
+			_playbackParseState = kFileStateSelectSection;
+			return false;
+		}
+		break;
+	case kFileStateProcessRandom:
+		if (nextChunk.id == kRandomRecordTag) {
+			processRndSeedRecord(nextChunk);
+		} else {
+			_playbackParseState = kFileStateSelectSection;
+			return false;
+		}
+		break;
+	case kFileStateProcessSettings:
+		if (nextChunk.id == kSettingsRecordTag) {
+			if (!processSettingsRecord()) {
+				_playbackParseState = kFileStateError;
+				return false;
+			}
+		} else {
+			_playbackParseState = kFileStateSelectSection;
+			return false;
+		}
+		break;
+	default:
+			return false;
+	}
+	return true;
+}
+
+void PlaybackFile::returnToChunkHeader() {
+	_readStream->seek(-8, SEEK_CUR);
+}
+
+void PlaybackFile::readHashMap(ChunkHeader chunk) {
+	String hashName = readString(chunk.len - 32);
+	String hashMd5 = readString(32);
+	_header.hashRecords[hashName] = hashMd5;
+}
+
+void PlaybackFile::processRndSeedRecord(ChunkHeader chunk) {
+	String randomSourceName = readString(chunk.len - 4);
+	uint32 randomSourceSeed = _readStream->readUint32LE();
+	_header.randomSourceRecords[randomSourceName] = randomSourceSeed;
+}
+
+bool PlaybackFile::processSettingsRecord() {
+	ChunkHeader keyChunk;
+	if (!readChunkHeader(keyChunk) || (keyChunk.id != kSettingsRecordKeyTag)) {
+		warning("Invalid format of settings section");
+		return false;
+	}
+	String key = readString(keyChunk.len);
+	ChunkHeader valueChunk;
+	if (!readChunkHeader(valueChunk) || (valueChunk.id != kSettingsRecordValueTag)) {
+		warning("Invalid format of settings section");
+		return false;
+	}
+	String value = readString(valueChunk.len);
+	_header.settingsRecords[key] = value;
+	return true;
+}
+
+
+bool PlaybackFile::readSaveRecord() {
+	ChunkHeader fileNameChunk;
+	if (!readChunkHeader(fileNameChunk) || (fileNameChunk.id != kSaveRecordNameTag)) {
+		warning("Invalid format of save section");
+		return false;
+	}
+	String fileName = readString(fileNameChunk.len);
+	ChunkHeader saveBufferChunk;
+	if (!readChunkHeader(saveBufferChunk) || (saveBufferChunk.id != kSaveRecordBufferTag)) {
+		warning("Invalid format of save section");
+		return false;
+	}
+	SaveFileBuffer buf;
+	buf.size = saveBufferChunk.len;
+	buf.buffer = (byte *)malloc(saveBufferChunk.len);
+	_readStream->read(buf.buffer, buf.size);
+	_header.saveFiles[fileName] = buf;
+	debugC(1, kDebugLevelEventRec, "playback:action=\"Load save file\" filename=%s len=%d", fileName.c_str(), buf.size);
+	return true;
+}
+
+
+
+RecorderEvent PlaybackFile::getNextEvent() {
+	assert(_mode == kRead);
+	if (isEventsBufferEmpty()) {
+		PlaybackFile::ChunkHeader header;
+		header.id = kFormatIdTag;
+		while (header.id != kEventTag) {
+			if (!readChunkHeader(header) || _readStream->eos()) {
+				break;
+			}
+			switch (header.id) {
+			case kEventTag:
+				readEventsToBuffer(header.len);
+				break;
+			case kScreenShotTag:
+				_readStream->seek(-4, SEEK_CUR);
+				header.len = _readStream->readUint32BE();
+				_readStream->skip(header.len-8);
+				break;
+			case kMD5Tag:
+				checkRecordedMD5();
+				break;
+			default:
+				_readStream->skip(header.len);
+				break;
+			}
+		}
+	}
+	RecorderEvent result;
+	readEvent(result);
+	return result;
+}
+
+bool PlaybackFile::isEventsBufferEmpty() {
+	return (uint32)_tmpPlaybackFile.pos() == _eventsSize;
+}
+
+void PlaybackFile::readEvent(RecorderEvent& event) {
+	event.recordedtype = (RecorderEventType)_tmpPlaybackFile.readByte();
+	switch (event.recordedtype) {
+	case kRecorderEventTypeTimer:
+		event.time = _tmpPlaybackFile.readUint32LE();
+		break;
+	case kRecorderEventTypeNormal:
+		event.type = (EventType)_tmpPlaybackFile.readUint32LE();
+		switch (event.type) {
+		case EVENT_KEYDOWN:
+		case EVENT_KEYUP:
+			event.time = _tmpPlaybackFile.readUint32LE();
+			event.kbd.keycode = (KeyCode)_tmpPlaybackFile.readSint32LE();
+			event.kbd.ascii = _tmpPlaybackFile.readUint16LE();
+			event.kbd.flags = _tmpPlaybackFile.readByte();
+			break;
+		case EVENT_MOUSEMOVE:
+		case EVENT_LBUTTONDOWN:
+		case EVENT_LBUTTONUP:
+		case EVENT_RBUTTONDOWN:
+		case EVENT_RBUTTONUP:
+		case EVENT_WHEELUP:
+		case EVENT_WHEELDOWN:
+		case EVENT_MBUTTONDOWN:
+		case EVENT_MBUTTONUP:
+			event.time = _tmpPlaybackFile.readUint32LE();
+			event.mouse.x = _tmpPlaybackFile.readSint16LE();
+			event.mouse.y = _tmpPlaybackFile.readSint16LE();
+			break;
+		default:
+			event.time = _tmpPlaybackFile.readUint32LE();
+			break;
+		}
+		break;
+	}
+	event.synthetic = true;
+}
+
+void PlaybackFile::readEventsToBuffer(uint32 size) {
+	_readStream->read(_tmpBuffer, size);
+	_tmpPlaybackFile.seek(0);
+	_eventsSize = size;
+}
+
+void PlaybackFile::saveScreenShot(Graphics::Surface &screen, byte md5[16]) {
+	dumpRecordsToFile();
+	_writeStream->writeUint32LE(kMD5Tag);
+	_writeStream->writeUint32LE(16);
+	_writeStream->write(md5, 16);
+	Graphics::saveThumbnail(*_writeStream, screen);
+}
+
+void PlaybackFile::dumpRecordsToFile() {
+	if (!_headerDumped) {
+		dumpHeaderToFile();
+		_headerDumped = true;
+	}
+	if (_recordCount == 0) {
+		return;
+	}
+	_writeStream->writeUint32LE(kEventTag);
+	_writeStream->writeUint32LE(_tmpRecordFile.pos());
+	_writeStream->write(_tmpBuffer, _tmpRecordFile.pos());
+	_tmpRecordFile.seek(0);
+	_recordCount = 0;
+}
+
+void PlaybackFile::dumpHeaderToFile() {
+	_writeStream->writeUint32LE(kFormatIdTag);
+	// Specify size for first tag as NULL since we cannot calculate
+	// size of the file at time of the header dumping
+	_writeStream->writeUint32LE(0);
+	_writeStream->writeUint32LE(kVersionTag);
+	_writeStream->writeUint32LE(4);
+	_writeStream->writeUint32LE(RECORD_VERSION);
+	writeHeaderSection();
+	writeGameHash();
+	writeRandomRecords();
+	writeGameSettings();
+	writeSaveFilesSection();
+}
+
+void PlaybackFile::writeHeaderSection() {
+	uint32 headerSize = 0;
+	if (!_header.author.empty()) {
+		headerSize = _header.author.size() + 8;
+	}
+	if (!_header.notes.empty()) {
+		headerSize += _header.notes.size() + 8;
+	}
+	if (!_header.name.empty()) {
+		headerSize += _header.name.size() + 8;
+	}
+	if (headerSize == 0) {
+		return;
+	}
+	_writeStream->writeUint32LE(kHeaderSectionTag);
+	_writeStream->writeUint32LE(headerSize);
+	if (!_header.author.empty()) {
+		_writeStream->writeUint32LE(kAuthorTag);
+		_writeStream->writeUint32LE(_header.author.size());
+		_writeStream->writeString(_header.author);
+	}
+	if (!_header.notes.empty()) {
+		_writeStream->writeUint32LE(kCommentsTag);
+		_writeStream->writeUint32LE(_header.notes.size());
+		_writeStream->writeString(_header.notes);
+	}
+	if (!_header.name.empty()) {
+		_writeStream->writeUint32LE(kNameTag);
+		_writeStream->writeUint32LE(_header.name.size());
+		_writeStream->writeString(_header.name);
+	}
+}
+
+void PlaybackFile::writeGameHash() {
+	uint32 hashSectionSize = 0;
+	for (StringMap::iterator i = _header.hashRecords.begin(); i != _header.hashRecords.end(); ++i) {
+		hashSectionSize = hashSectionSize + i->_key.size() + i->_value.size() + 8;
+	}
+	if (_header.hashRecords.size() == 0) {
+		return;
+	}
+	_writeStream->writeUint32LE(kHashSectionTag);
+	_writeStream->writeUint32LE(hashSectionSize);
+	for (StringMap::iterator i = _header.hashRecords.begin(); i != _header.hashRecords.end(); ++i) {
+		_writeStream->writeUint32LE(kHashRecordTag);
+		_writeStream->writeUint32LE(i->_key.size() + i->_value.size());
+		_writeStream->writeString(i->_key);
+		_writeStream->writeString(i->_value);
+	}
+}
+
+void PlaybackFile::writeRandomRecords() {
+	uint32 randomSectionSize = 0;
+	for (RandomSeedsDictionary::iterator i = _header.randomSourceRecords.begin(); i != _header.randomSourceRecords.end(); ++i) {
+		randomSectionSize = randomSectionSize + i->_key.size() + 12;
+	}
+	if (_header.randomSourceRecords.size() == 0) {
+		return;
+	}
+	_writeStream->writeUint32LE(kRandomSectionTag);
+	_writeStream->writeUint32LE(randomSectionSize);
+	for (RandomSeedsDictionary::iterator i = _header.randomSourceRecords.begin(); i != _header.randomSourceRecords.end(); ++i) {
+		_writeStream->writeUint32LE(kRandomRecordTag);
+		_writeStream->writeUint32LE(i->_key.size() + 4);
+		_writeStream->writeString(i->_key);
+		_writeStream->writeUint32LE(i->_value);
+	}
+}
+
+void PlaybackFile::writeEvent(const RecorderEvent &event) {
+	assert(_mode == kWrite);
+	_recordCount++;
+	_tmpRecordFile.writeByte(event.recordedtype);
+	switch (event.recordedtype) {
+	case kRecorderEventTypeTimer:
+		_tmpRecordFile.writeUint32LE(event.time);
+		break;
+	case kRecorderEventTypeNormal:
+		_tmpRecordFile.writeUint32LE((uint32)event.type);
+		switch(event.type) {
+		case EVENT_KEYDOWN:
+		case EVENT_KEYUP:
+			_tmpRecordFile.writeUint32LE(event.time);
+			_tmpRecordFile.writeSint32LE(event.kbd.keycode);
+			_tmpRecordFile.writeUint16LE(event.kbd.ascii);
+			_tmpRecordFile.writeByte(event.kbd.flags);
+			break;
+		case EVENT_MOUSEMOVE:
+		case EVENT_LBUTTONDOWN:
+		case EVENT_LBUTTONUP:
+		case EVENT_RBUTTONDOWN:
+		case EVENT_RBUTTONUP:
+		case EVENT_WHEELUP:
+		case EVENT_WHEELDOWN:
+		case EVENT_MBUTTONDOWN:
+		case EVENT_MBUTTONUP:
+			_tmpRecordFile.writeUint32LE(event.time);
+			_tmpRecordFile.writeSint16LE(event.mouse.x);
+			_tmpRecordFile.writeSint16LE(event.mouse.y);
+			break;
+		default:
+			_tmpRecordFile.writeUint32LE(event.time);
+			break;
+		}
+		break;
+	}
+	if (_recordCount == kMaxBufferedRecords) {
+		dumpRecordsToFile();
+	}
+}
+
+void PlaybackFile::writeGameSettings() {
+	_writeStream->writeUint32LE(kSettingsSectionTag);
+	uint32 settingsSectionSize = 0;
+	for (StringMap::iterator i = _header.settingsRecords.begin(); i != _header.settingsRecords.end(); ++i) {
+		settingsSectionSize += i->_key.size() + i->_value.size() + 24;
+	}
+	_writeStream->writeUint32LE(settingsSectionSize);
+	for (StringMap::iterator i = _header.settingsRecords.begin(); i != _header.settingsRecords.end(); ++i) {
+		_writeStream->writeUint32LE(kSettingsRecordTag);
+		_writeStream->writeUint32LE(i->_key.size() + i->_value.size() + 16);
+		_writeStream->writeUint32LE(kSettingsRecordKeyTag);
+		_writeStream->writeUint32LE(i->_key.size());
+		_writeStream->writeString(i->_key);
+		_writeStream->writeUint32LE(kSettingsRecordValueTag);
+		_writeStream->writeUint32LE(i->_value.size());
+		_writeStream->writeString(i->_value);
+	}
+}
+
+int PlaybackFile::getScreensCount() {
+	if (_mode != kRead) {
+		return 0;
+	}
+	_readStream->seek(0);
+	int result = 0;
+	while (skipToNextScreenshot()) {
+		uint32 size = _readStream->readUint32BE();
+		_readStream->skip(size-8);
+		++result;
+	}
+	return result;
+}
+
+bool PlaybackFile::skipToNextScreenshot() {
+	while (true) {
+		FileTag id = (FileTag)_readStream->readUint32LE();
+		if (_readStream->eos()) {
+			break;
+		}
+		if (id == kScreenShotTag) {
+			return true;
+		}
+		else {
+			uint32 size = _readStream->readUint32LE();
+			_readStream->skip(size);
+		}
+	}
+	return false;
+}
+
+Graphics::Surface *PlaybackFile::getScreenShot(int number) {
+	if (_mode != kRead) {
+		return NULL;
+	}
+	_readStream->seek(0);
+	int screenCount = 1;
+	while (skipToNextScreenshot()) {
+		if (screenCount == number) {
+			screenCount++;
+			_readStream->seek(-4, SEEK_CUR);
+			return Graphics::loadThumbnail(*_readStream);
+		} else {
+			uint32 size = _readStream->readUint32BE();
+			_readStream->skip(size-8);
+			screenCount++;
+		}
+	}
+	return NULL;
+}
+
+void PlaybackFile::updateHeader() {
+	if (_mode == kWrite) {
+		_readStream = g_system->getSavefileManager()->openForLoading(_header.fileName);
+	}
+	_readStream->seek(0);
+	skipHeader();
+	String tmpFilename = "_" + _header.fileName;
+	_writeStream = g_system->getSavefileManager()->openForSaving(tmpFilename);
+	dumpHeaderToFile();
+	uint32 readedSize = 0;
+	do {
+		readedSize = _readStream->read(_tmpBuffer, kRecordBuffSize);
+		_writeStream->write(_tmpBuffer, readedSize);
+	} while (readedSize != 0);
+	delete _writeStream;
+	_writeStream = NULL;
+	delete _readStream;
+	_readStream = NULL;
+	g_system->getSavefileManager()->removeSavefile(_header.fileName);
+	g_system->getSavefileManager()->renameSavefile(tmpFilename, _header.fileName);
+	if (_mode == kRead) {
+		openRead(_header.fileName);
+	}
+}
+
+void PlaybackFile::skipHeader() {
+	while (true) {
+		uint32 id = _readStream->readUint32LE();
+		if (_readStream->eos()) {
+			break;
+		}
+		if ((id == kScreenShotTag) || (id == kEventTag) || (id == kMD5Tag)) {
+			_readStream->seek(-4, SEEK_CUR);
+			return;
+		}
+		else {
+			uint32 size = _readStream->readUint32LE();
+			_readStream->skip(size);
+		}
+	}
+}
+
+void PlaybackFile::addSaveFile(const String &fileName, InSaveFile *saveStream) {
+	uint oldPos = saveStream->pos();
+	saveStream->seek(0);
+	_header.saveFiles[fileName].buffer = (byte *)malloc(saveStream->size());
+	_header.saveFiles[fileName].size = saveStream->size();
+	saveStream->read(_header.saveFiles[fileName].buffer, saveStream->size());
+	saveStream->seek(oldPos);
+}
+
+void PlaybackFile::writeSaveFilesSection() {
+	uint size = 0;
+	for (HashMap<String, SaveFileBuffer>::iterator  i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) {
+		size += i->_value.size + i->_key.size() + 24;
+	}
+	if (size == 0) {
+		return;
+	}
+	_writeStream->writeSint32LE(kSaveTag);
+	_writeStream->writeSint32LE(size);
+	for (HashMap<String, SaveFileBuffer>::iterator  i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) {
+		_writeStream->writeSint32LE(kSaveRecordTag);
+		_writeStream->writeSint32LE(i->_key.size() + i->_value.size + 16);
+		_writeStream->writeSint32LE(kSaveRecordNameTag);
+		_writeStream->writeSint32LE(i->_key.size());
+		_writeStream->writeString(i->_key);
+		_writeStream->writeSint32LE(kSaveRecordBufferTag);
+		_writeStream->writeSint32LE(i->_value.size);
+		_writeStream->write(i->_value.buffer, i->_value.size);
+	}
+}
+
+
+void PlaybackFile::checkRecordedMD5() {
+	uint8 currentMD5[16];
+	uint8 savedMD5[16];
+	Graphics::Surface screen;
+	_readStream->read(savedMD5, 16);
+	if (!g_eventRec.grabScreenAndComputeMD5(screen, currentMD5)) {
+		return;
+	}
+	uint32 seconds = g_system->getMillis(true) / 1000;
+	String screenTime = String::format("%.2d:%.2d:%.2d", seconds / 3600 % 24, seconds / 60 % 60, seconds % 60);
+	if (memcmp(savedMD5, currentMD5, 16) != 0) {
+		debugC(1, kDebugLevelEventRec, "playback:action=\"Check screenshot\" time=%s result = fail", screenTime.c_str());
+		warning("Recorded and current screenshots are different");
+	} else {
+		debugC(1, kDebugLevelEventRec, "playback:action=\"Check screenshot\" time=%s result = success", screenTime.c_str());
+	}
+	Graphics::saveThumbnail(*_screenshotsFile, screen);
+	screen.free();
+}
+
+
+}
diff --git a/common/recorderfile.h b/common/recorderfile.h
new file mode 100644
index 0000000..1c95e5a
--- /dev/null
+++ b/common/recorderfile.h
@@ -0,0 +1,180 @@
+/* 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.
+ *
+ */
+
+#ifndef COMMON_RECORDERFILE_H
+#define COMMON_RECORDERFILE_H
+
+#include "common/scummsys.h"
+#include "common/events.h"
+#include "common/mutex.h"
+#include "common/memstream.h"
+#include "common/config-manager.h"
+#include "common/savefile.h"
+
+//capacity of records buffer
+#define kMaxBufferedRecords 10000
+#define kRecordBuffSize sizeof(RecorderEvent) * kMaxBufferedRecords
+
+namespace Common {
+
+enum RecorderEventType {
+	kRecorderEventTypeNormal = 0,
+	kRecorderEventTypeTimer = 1
+};
+
+struct RecorderEvent : Event {
+	RecorderEventType recordedtype;
+	uint32 time;
+};
+
+
+
+class PlaybackFile {
+	typedef HashMap<String, uint32, IgnoreCase_Hash, IgnoreCase_EqualTo> RandomSeedsDictionary;
+	enum fileMode {
+		kRead = 0,
+		kWrite = 1,
+		kClosed = 2
+	};
+	enum PlaybackFileState {
+		kFileStateCheckFormat,
+		kFileStateCheckVersion,
+		kFileStateProcessHash,
+		kFileStateProcessHeader,
+		kFileStateProcessRandom,
+		kFileStateSelectSection,
+		kFileStateProcessSettings,
+		kFileStateProcessSave,
+		kFileStateDone,
+		kFileStateError
+	};
+	enum FileTag {
+		kFormatIdTag = MKTAG('P','B','C','K'),
+		kVersionTag = MKTAG('V','E','R','S'),
+		kHeaderSectionTag = MKTAG('H','E','A','D'),
+		kHashSectionTag = MKTAG('H','A','S','H'),
+		kRandomSectionTag = MKTAG('R','A','N','D'),
+		kEventTag = MKTAG('E','V','N','T'),
+		kScreenShotTag = MKTAG('B','M','H','T'),
+		kSettingsSectionTag = MKTAG('S','E','T','T'),
+		kAuthorTag = MKTAG('H','A','U','T'),
+		kCommentsTag = MKTAG('H','C','M','T'),
+		kNameTag = MKTAG('H','N','A','M'),
+		kHashRecordTag = MKTAG('H','R','C','D'),
+		kRandomRecordTag = MKTAG('R','R','C','D'),
+		kSettingsRecordTag = MKTAG('S','R','E','C'),
+		kSettingsRecordKeyTag = MKTAG('S','K','E','Y'),
+		kSettingsRecordValueTag = MKTAG('S','V','A','L'),
+		kSaveTag = MKTAG('S','A','V','E'),
+		kSaveRecordTag = MKTAG('R','S','A','V'),
+		kSaveRecordNameTag = MKTAG('S','N','A','M'),
+		kSaveRecordBufferTag = MKTAG('S','B','U','F'),
+		kMD5Tag = MKTAG('M','D','5',' ')
+	};
+	struct ChunkHeader {
+		FileTag id;
+		uint32 len;
+	};
+public:
+	struct SaveFileBuffer {
+		byte *buffer;
+		uint32 size;
+	};
+	struct PlaybackFileHeader {
+		String fileName;
+		String author;
+		String name;
+		String notes;
+		String description;
+		StringMap hashRecords;
+		StringMap settingsRecords;
+		HashMap<String, SaveFileBuffer> saveFiles;
+		RandomSeedsDictionary randomSourceRecords;
+	};
+	PlaybackFile();
+	~PlaybackFile();
+
+	bool openWrite(const String &fileName);
+	bool openRead(const String &fileName);
+	void close();
+
+	RecorderEvent getNextEvent();
+	void writeEvent(const RecorderEvent &event);
+
+	void saveScreenShot(Graphics::Surface &screen, byte md5[16]);
+	Graphics::Surface *getScreenShot(int number);
+	int getScreensCount();
+
+	bool isEventsBufferEmpty();
+	PlaybackFileHeader &getHeader() {return _header;}
+	void updateHeader();
+	void addSaveFile(const String &fileName, InSaveFile *saveStream);
+private:
+	WriteStream *_recordFile;
+	WriteStream *_writeStream;
+	WriteStream *_screenshotsFile;
+	MemoryReadStream _tmpPlaybackFile;
+	SeekableReadStream *_readStream;
+	SeekableMemoryWriteStream _tmpRecordFile;
+
+	fileMode _mode;
+	bool _headerDumped;
+	int _recordCount;
+	uint32 _eventsSize;
+	byte _tmpBuffer[kRecordBuffSize];
+	PlaybackFileHeader _header;
+	PlaybackFileState _playbackParseState;
+
+	void skipHeader();
+	bool parseHeader();
+	bool processChunk(ChunkHeader &nextChunk);
+	void returnToChunkHeader();
+
+	bool readSaveRecord();
+	void checkRecordedMD5();
+	bool readChunkHeader(ChunkHeader &nextChunk);
+	void processRndSeedRecord(ChunkHeader chunk);
+	bool processSettingsRecord();
+
+	bool checkPlaybackFileVersion();
+
+	void dumpHeaderToFile();
+	void writeSaveFilesSection();
+	void writeGameSettings();
+	void writeHeaderSection();
+	void writeGameHash();
+	void writeRandomRecords();
+
+	void dumpRecordsToFile();
+
+	String readString(int len);
+	void readHashMap(ChunkHeader chunk);
+
+	bool skipToNextScreenshot();
+	void readEvent(RecorderEvent& event);
+	void readEventsToBuffer(uint32 size);
+	bool grabScreenAndComputeMD5(Graphics::Surface &screen, uint8 md5[16]);
+};
+
+} // End of namespace Common
+
+#endif
diff --git a/common/system.cpp b/common/system.cpp
index 5921054..b40072a 100644
--- a/common/system.cpp
+++ b/common/system.cpp
@@ -30,6 +30,7 @@
 #include "common/taskbar.h"
 #include "common/updates.h"
 #include "common/textconsole.h"
+#include "gui/EventRecorder.h"
 
 #include "backends/audiocd/default/default-audiocd.h"
 #include "backends/fs/fs-factory.h"
@@ -84,7 +85,7 @@ void OSystem::initBackend() {
 		error("Backend failed to instantiate audio CD manager");
 	if (!_eventManager)
 		error("Backend failed to instantiate event manager");
-	if (!_timerManager)
+	if (!getTimerManager())
 		error("Backend failed to instantiate timer manager");
 
 	// TODO: We currently don't check _savefileManager, because at least
@@ -152,3 +153,11 @@ Common::String OSystem::getDefaultConfigFileName() {
 Common::String OSystem::getSystemLanguage() const {
 	return "en_US";
 }
+
+Common::TimerManager *OSystem::getTimerManager() {
+	return _timerManager;
+}
+
+Common::SaveFileManager *OSystem::getSavefileManager() {
+	return g_eventRec.getSaveManager(_savefileManager);
+}
diff --git a/common/system.h b/common/system.h
index 99b947d..81c4bdf 100644
--- a/common/system.h
+++ b/common/system.h
@@ -890,8 +890,14 @@ public:
 	/** @name Events and Time */
 	//@{
 
-	/** Get the number of milliseconds since the program was started. */
-	virtual uint32 getMillis() = 0;
+	/** Get the number of milliseconds since the program was started.
+
+	    @param skipRecord   Skip recording of this value by event recorder.
+	    			This could be needed particularly when we are in
+				an on-screen GUI loop where player can pause
+				the recording.
+	*/
+	virtual uint32 getMillis(bool skipRecord = false) = 0;
 
 	/** Delay/sleep for the specified amount of milliseconds. */
 	virtual void delayMillis(uint msecs) = 0;
@@ -907,9 +913,7 @@ public:
 	 * Return the timer manager singleton. For more information, refer
 	 * to the TimerManager documentation.
 	 */
-	inline Common::TimerManager *getTimerManager() {
-		return _timerManager;
-	}
+	virtual Common::TimerManager *getTimerManager();
 
 	/**
 	 * Return the event manager singleton. For more information, refer
@@ -1086,9 +1090,7 @@ public:
 	 * and other modifiable persistent game data. For more information,
 	 * refer to the SaveFileManager documentation.
 	 */
-	inline Common::SaveFileManager *getSavefileManager() {
-		return _savefileManager;
-	}
+	Common::SaveFileManager *getSavefileManager();
 
 #if defined(USE_TASKBAR)
 	/**
diff --git a/configure b/configure
index db347c1..08eba65 100755
--- a/configure
+++ b/configure
@@ -138,9 +138,10 @@ _build_hq_scalers=yes
 _enable_prof=no
 _global_constructors=no
 _bink=yes
-# Default vkeybd/keymapper options
+# Default vkeybd/keymapper/eventrec options
 _vkeybd=no
 _keymapper=no
+_eventrec=auto
 # GUI translation options
 _translation=yes
 # Default platform settings
@@ -1036,6 +1037,8 @@ for ac_option in $@; do
 	--disable-vkeybd)         _vkeybd=no      ;;
 	--enable-keymapper)       _keymapper=yes  ;;
 	--disable-keymapper)      _keymapper=no   ;;
+	--enable-eventrecorder)   _eventrec=yes  ;;
+	--disable-eventrecorder)  _eventrec=no   ;;
 	--enable-text-console)    _text_console=yes ;;
 	--disable-text-console)   _text_console=no ;;
 	--with-fluidsynth-prefix=*)
@@ -2882,6 +2885,20 @@ case $_backend in
 esac
 
 #
+# Enable Event Recorder only for backends that support it
+#
+case $_backend in
+	sdl)
+		if test "$_eventrec" = auto ; then
+			_eventrec=yes
+		fi
+		;;
+	*)
+		_eventrec=no
+		;;
+esac
+
+#
 # Disable savegame timestamp support for backends which don't have a reliable real time clock
 #
 case $_backend in
@@ -3797,10 +3814,12 @@ fi
 define_in_config_if_yes $_nasm 'USE_NASM'
 
 #
-# Enable vkeybd / keymapper
+# Enable vkeybd / keymapper / event recorder
 #
 define_in_config_if_yes $_vkeybd 'ENABLE_VKEYBD'
 define_in_config_if_yes $_keymapper 'ENABLE_KEYMAPPER'
+define_in_config_if_yes $_eventrec 'ENABLE_EVENTRECORDER'
+add_line_to_config_mk_if_yes $_eventrec "ENABLE_EVENTRECORDER = 1"
 
 # Check whether to build translation support
 #
@@ -3946,7 +3965,11 @@ if test "$_vkeybd" = yes ; then
 fi
 
 if test "$_keymapper" = yes ; then
-	echo ", keymapper"
+	echo_n ", keymapper"
+fi
+
+if test "$_eventrec" = yes ; then
+	echo ", event recorder"
 else
 	echo
 fi
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index b1d1008..9023548 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -29,7 +29,7 @@
 #include "common/system.h"
 #include "common/textconsole.h"
 #include "common/translation.h"
-
+#include "gui/EventRecorder.h"
 #include "engines/advancedDetector.h"
 #include "engines/obsolete.h"
 
@@ -301,6 +301,7 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
 		return Common::kUserCanceled;
 
 	debug(2, "Running %s", gameDescriptor.description().c_str());
+	initSubSystems(agdDesc);
 	if (!createInstance(syst, engine, agdDesc))
 		return Common::kNoGameDataFoundError;
 	else
@@ -606,3 +607,9 @@ AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, con
 	_maxScanDepth = 1;
 	_directoryGlobs = NULL;
 }
+
+void AdvancedMetaEngine::initSubSystems(const ADGameDescription *gameDesc) const {
+	if (gameDesc) {
+		g_eventRec.processGameDescription(gameDesc);
+	}
+}
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index 3eec33a..71d2c4a 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -280,6 +280,9 @@ protected:
 		return 0;
 	}
 
+private:
+	void initSubSystems(const ADGameDescription *gameDesc) const;
+
 protected:
 	/**
 	 * Detect games in specified directory.
diff --git a/engines/cge/cge.cpp b/engines/cge/cge.cpp
index 6cc0c45..af7e91f 100644
--- a/engines/cge/cge.cpp
+++ b/engines/cge/cge.cpp
@@ -25,7 +25,6 @@
 #include "common/debug.h"
 #include "common/debug-channels.h"
 #include "common/error.h"
-#include "common/EventRecorder.h"
 #include "common/file.h"
 #include "common/fs.h"
 #include "engines/advancedDetector.h"
diff --git a/engines/dreamweb/dreamweb.cpp b/engines/dreamweb/dreamweb.cpp
index c3ede46..08838a7 100644
--- a/engines/dreamweb/dreamweb.cpp
+++ b/engines/dreamweb/dreamweb.cpp
@@ -23,7 +23,6 @@
 #include "common/config-manager.h"
 #include "common/debug-channels.h"
 #include "common/events.h"
-#include "common/EventRecorder.h"
 #include "common/file.h"
 #include "common/func.h"
 #include "common/system.h"
diff --git a/engines/sword25/gfx/image/renderedimage.cpp b/engines/sword25/gfx/image/renderedimage.cpp
index c8a6666..81a29c7 100644
--- a/engines/sword25/gfx/image/renderedimage.cpp
+++ b/engines/sword25/gfx/image/renderedimage.cpp
@@ -41,6 +41,7 @@
 #include "sword25/gfx/renderobjectmanager.h"
 
 #include "common/system.h"
+#include "graphics/thumbnail.h"
 
 namespace Sword25 {
 
@@ -509,60 +510,4 @@ void RenderedImage::checkForTransparency() {
 	}
 }
 
-/**
- * Scales a passed surface, creating a new surface with the result
- * @param srcImage		Source image to scale
- * @param scaleFactor	Scale amount. Must be between 0 and 1.0 (but not zero)
- * @remarks Caller is responsible for freeing the returned surface
- */
-Graphics::Surface *RenderedImage::scale(const Graphics::Surface &srcImage, int xSize, int ySize) {
-	Graphics::Surface *s = new Graphics::Surface();
-	s->create(xSize, ySize, srcImage.format);
-
-	int *horizUsage = scaleLine(xSize, srcImage.w);
-	int *vertUsage = scaleLine(ySize, srcImage.h);
-
-	// Loop to create scaled version
-	for (int yp = 0; yp < ySize; ++yp) {
-		const byte *srcP = (const byte *)srcImage.getBasePtr(0, vertUsage[yp]);
-		byte *destP = (byte *)s->getBasePtr(0, yp);
-
-		for (int xp = 0; xp < xSize; ++xp) {
-			const byte *tempSrcP = srcP + (horizUsage[xp] * srcImage.format.bytesPerPixel);
-			for (int byteCtr = 0; byteCtr < srcImage.format.bytesPerPixel; ++byteCtr) {
-				*destP++ = *tempSrcP++;
-			}
-		}
-	}
-
-	// Delete arrays and return surface
-	delete[] horizUsage;
-	delete[] vertUsage;
-	return s;
-}
-
-/**
- * Returns an array indicating which pixels of a source image horizontally or vertically get
- * included in a scaled image
- */
-int *RenderedImage::scaleLine(int size, int srcSize) {
-	int scale = 100 * size / srcSize;
-	assert(scale > 0);
-	int *v = new int[size];
-	Common::fill(v, &v[size], 0);
-
-	int distCtr = 0;
-	int *destP = v;
-	for (int distIndex = 0; distIndex < srcSize; ++distIndex) {
-		distCtr += scale;
-		while (distCtr >= 100) {
-			assert(destP < &v[size]);
-			*destP++ = distIndex;
-			distCtr -= 100;
-		}
-	}
-
-	return v;
-}
-
 } // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/renderedimage.h b/engines/sword25/gfx/image/renderedimage.h
index a25b258..116f97d 100644
--- a/engines/sword25/gfx/image/renderedimage.h
+++ b/engines/sword25/gfx/image/renderedimage.h
@@ -104,8 +104,6 @@ public:
 		return true;
 	}
 
-	static Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize);
-
 	void setIsTransparent(bool isTransparent) { _isTransparent = isTransparent; }
 	virtual bool isSolid() const { return !_isTransparent; }
 
@@ -119,7 +117,6 @@ private:
 	Graphics::Surface *_backSurface;
 
 	void checkForTransparency();
-	static int *scaleLine(int size, int srcSize);
 };
 
 } // End of namespace Sword25
diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp
index 89a6f1b..19848b0 100644
--- a/engines/wintermute/wintermute.cpp
+++ b/engines/wintermute/wintermute.cpp
@@ -26,7 +26,6 @@
 #include "common/debug.h"
 #include "common/debug-channels.h"
 #include "common/error.h"
-#include "common/EventRecorder.h"
 #include "common/file.h"
 #include "common/fs.h"
 #include "common/tokenizer.h"
diff --git a/graphics/cursorman.cpp b/graphics/cursorman.cpp
index c818101..6825767 100644
--- a/graphics/cursorman.cpp
+++ b/graphics/cursorman.cpp
@@ -48,6 +48,9 @@ bool CursorManager::isVisible() {
 bool CursorManager::showMouse(bool visible) {
 	if (_cursorStack.empty())
 		return false;
+	if (_locked) {
+		return false;
+	}
 
 	_cursorStack.top()->_visible = visible;
 
@@ -225,6 +228,10 @@ void CursorManager::replaceCursorPalette(const byte *colors, uint start, uint nu
 	}
 }
 
+void CursorManager::lock(bool locked) {
+	_locked = locked;
+}
+
 CursorManager::Cursor::Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) {
 #ifdef USE_RGB_COLOR
 	if (!format)
diff --git a/graphics/cursorman.h b/graphics/cursorman.h
index 66e8d1b..b4d8ad9 100644
--- a/graphics/cursorman.h
+++ b/graphics/cursorman.h
@@ -160,12 +160,15 @@ public:
 	 */
 	void replaceCursorPalette(const byte *colors, uint start, uint num);
 
+	void lock(bool locked);
 private:
 	friend class Common::Singleton<SingletonBaseType>;
 	// Even though this is basically the default constructor we implement it
 	// ourselves, so it is private and thus there is no way to create this class
 	// except from the Singleton code.
-	CursorManager() {}
+	CursorManager() {
+		_locked = false;
+	}
 	~CursorManager();
 
 	struct Cursor {
@@ -198,6 +201,7 @@ private:
 	};
 	Common::Stack<Cursor *> _cursorStack;
 	Common::Stack<Palette *> _cursorPaletteStack;
+	bool _locked;
 };
 
 } // End of namespace Graphics
diff --git a/graphics/scaler.h b/graphics/scaler.h
index 1e5b796..54d022d 100644
--- a/graphics/scaler.h
+++ b/graphics/scaler.h
@@ -89,4 +89,10 @@ extern bool createThumbnailFromScreen(Graphics::Surface *surf);
  */
 extern bool createThumbnail(Graphics::Surface *surf, const uint8 *pixels, int w, int h, const uint8 *palette);
 
+/**
+ * Downscale screenshot to thumbnale size.
+ *
+ */
+extern bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in);
+
 #endif
diff --git a/graphics/scaler/thumbnail_intern.cpp b/graphics/scaler/thumbnail_intern.cpp
index 88f3cc2..8a98263 100644
--- a/graphics/scaler/thumbnail_intern.cpp
+++ b/graphics/scaler/thumbnail_intern.cpp
@@ -134,7 +134,7 @@ static bool grabScreen565(Graphics::Surface *surf) {
 	return true;
 }
 
-static bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in) {
+bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in) {
 	uint16 width = in.w;
 	uint16 inHeight = in.h;
 
@@ -206,7 +206,7 @@ static bool createThumbnail(Graphics::Surface &out, Graphics::Surface &in) {
 	return true;
 }
 
-bool createThumbnailFromScreen(Graphics::Surface* surf) {
+bool createThumbnailFromScreen(Graphics::Surface *surf) {
 	assert(surf);
 
 	Graphics::Surface screen;
@@ -236,3 +236,31 @@ bool createThumbnail(Graphics::Surface *surf, const uint8 *pixels, int w, int h,
 
 	return createThumbnail(*surf, screen);
 }
+
+// this is somewhat awkward, but createScreenShot should logically be in graphics,
+// but moving other functions in this file into that namespace breaks several engines
+namespace Graphics {
+bool createScreenShot(Graphics::Surface &surf) {
+	Graphics::PixelFormat screenFormat = g_system->getScreenFormat();
+	//convert surface to 2 bytes pixel format to avoid problems with palette saving and loading
+	if ((screenFormat.bytesPerPixel == 1) || (screenFormat.bytesPerPixel == 2)) {
+		return grabScreen565(&surf);
+	} else {
+		Graphics::Surface *screen = g_system->lockScreen();
+		if (!screen) {
+			return false;
+		}
+		surf.create(screen->w, screen->h, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+		for (uint y = 0; y < screen->h; ++y) {
+			for (uint x = 0; x < screen->w; ++x) {
+				byte r = 0, g = 0, b = 0, a = 0;
+				uint32 col = READ_UINT32(screen->getBasePtr(x, y));
+				screenFormat.colorToARGB(col, a, r, g, b);
+				((uint32 *)surf.pixels)[y * surf.w + x] = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(a, r, g, b);
+			}
+		}
+		g_system->unlockScreen();
+		return true;
+	}
+}
+} // End of namespace Graphics
diff --git a/graphics/thumbnail.cpp b/graphics/thumbnail.cpp
index ddb3773..d04c218 100644
--- a/graphics/thumbnail.cpp
+++ b/graphics/thumbnail.cpp
@@ -23,6 +23,7 @@
 #include "graphics/scaler.h"
 #include "graphics/colormasks.h"
 #include "common/endian.h"
+#include "common/algorithm.h"
 #include "common/system.h"
 #include "common/stream.h"
 #include "common/textconsole.h"
@@ -143,7 +144,6 @@ Graphics::Surface *loadThumbnail(Common::SeekableReadStream &in) {
 			assert(0);
 		}
 	}
-
 	return to;
 }
 
@@ -216,4 +216,55 @@ bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb) {
 	return true;
 }
 
+
+/**
+ * Returns an array indicating which pixels of a source image horizontally or vertically get
+ * included in a scaled image
+ */
+int *scaleLine(int size, int srcSize) {
+	int scale = 100 * size / srcSize;
+	assert(scale > 0);
+	int *v = new int[size];
+	Common::fill(v, &v[size], 0);
+
+	int distCtr = 0;
+	int *destP = v;
+	for (int distIndex = 0; distIndex < srcSize; ++distIndex) {
+		distCtr += scale;
+		while (distCtr >= 100) {
+			assert(destP < &v[size]);
+			*destP++ = distIndex;
+			distCtr -= 100;
+		}
+	}
+
+	return v;
+}
+
+Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize) {
+	Graphics::Surface *s = new Graphics::Surface();
+	s->create(xSize, ySize, srcImage.format);
+
+	int *horizUsage = scaleLine(xSize, srcImage.w);
+	int *vertUsage = scaleLine(ySize, srcImage.h);
+
+	// Loop to create scaled version
+	for (int yp = 0; yp < ySize; ++yp) {
+		const byte *srcP = (const byte *)srcImage.getBasePtr(0, vertUsage[yp]);
+		byte *destP = (byte *)s->getBasePtr(0, yp);
+
+		for (int xp = 0; xp < xSize; ++xp) {
+			const byte *tempSrcP = srcP + (horizUsage[xp] * srcImage.format.bytesPerPixel);
+			for (int byteCtr = 0; byteCtr < srcImage.format.bytesPerPixel; ++byteCtr) {
+				*destP++ = *tempSrcP++;
+			}
+		}
+	}
+
+	// Delete arrays and return surface
+	delete[] horizUsage;
+	delete[] vertUsage;
+	return s;
+}
+
 } // End of namespace Graphics
diff --git a/graphics/thumbnail.h b/graphics/thumbnail.h
index 45a0fdb..c857809 100644
--- a/graphics/thumbnail.h
+++ b/graphics/thumbnail.h
@@ -64,6 +64,24 @@ bool saveThumbnail(Common::WriteStream &out);
  */
 bool saveThumbnail(Common::WriteStream &out, const Graphics::Surface &thumb);
 
+/**
+ * Grabs framebuffer into surface
+ *
+ * @param surf	a surface
+ * @return		false if a error occurred
+ */
+bool createScreenShot(Graphics::Surface &surf);
+
+/**
+ * Scales a passed surface, creating a new surface with the result
+ * @param srcImage		Source image to scale
+ * @param xSize			New surface width
+ * @param ySize			New surface height
+ * @remarks Caller is responsible for freeing the returned surface
+ */
+Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize);
+
+
 } // End of namespace Graphics
 
 #endif
diff --git a/gui/EventRecorder.cpp b/gui/EventRecorder.cpp
new file mode 100644
index 0000000..94b955c
--- /dev/null
+++ b/gui/EventRecorder.cpp
@@ -0,0 +1,699 @@
+/* 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"
+
+namespace Common {
+DECLARE_SINGLETON(GUI::EventRecorder);
+}
+
+#ifdef ENABLE_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;
+	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 ((gameDesc == NULL) && (_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(Common::ConfigManager::Domain *domain) {
+	for (Common::ConfigManager::Domain::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::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::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);
+	}
+}
+
+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.pixels, 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) {
+	SDL_Surface *surface = new SDL_Surface();
+	surface->format = new SDL_PixelFormat();
+	surface->flags = 0;
+	surface->format->palette = NULL;
+	surface->format->BitsPerPixel = 16;
+	surface->format->BytesPerPixel = 2;
+	surface->format->Rloss = 3;
+	surface->format->Gloss = 2;
+	surface->format->Bloss = 3;
+	surface->format->Aloss = 8;
+	surface->format->Rshift = 11;
+	surface->format->Gshift = 5;
+	surface->format->Bshift = 0;
+	surface->format->Ashift = 0;
+	surface->format->Rmask = 63488;
+	surface->format->Gmask = 2016;
+	surface->format->Bmask = 31;
+	surface->format->Amask = 0;
+	surface->format->colorkey = 0;
+	surface->format->alpha = 255;
+	surface->w = width;
+	surface->h = height;
+	surface->pitch = width * 2;
+	surface->pixels = (char *)malloc(surface->pitch * surface->h);
+	surface->offset = 0;
+	surface->hwdata = NULL;
+	surface->clip_rect.x = 0;
+	surface->clip_rect.y = 0;
+	surface->clip_rect.w = width;
+	surface->clip_rect.h = height;
+	surface->unused1 = 0;
+	surface->locked = 0;
+	surface->map = NULL;
+	surface->format_version = 4;
+	surface->refcount = 1;
+	return surface;
+}
+
+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
+
diff --git a/gui/EventRecorder.h b/gui/EventRecorder.h
new file mode 100644
index 0000000..3e32d89
--- /dev/null
+++ b/gui/EventRecorder.h
@@ -0,0 +1,293 @@
+/* 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.
+ *
+ */
+
+#ifndef GUI_EVENTRECORDER_H
+#define GUI_EVENTRECORDER_H
+
+#include "common/system.h"
+
+#include "common/events.h"
+#include "common/savefile.h"
+#include "common/singleton.h"
+
+#include "engines/advancedDetector.h"
+
+#ifdef ENABLE_EVENTRECORDER
+
+#include "common/mutex.h"
+#include "common/array.h"
+#include "common/memstream.h"
+#include "backends/keymapper/keymapper.h"
+#include "backends/mixer/sdl/sdl-mixer.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "backends/timer/sdl/sdl-timer.h"
+#include "common/config-manager.h"
+#include "common/recorderfile.h"
+#include "backends/saves/recorder/recorder-saves.h"
+#include "backends/mixer/nullmixer/nullsdl-mixer.h"
+#include "backends/saves/default/default-saves.h"
+
+
+#define g_eventRec (GUI::EventRecorder::instance())
+
+namespace GUI {
+	class OnScreenDialog;
+}
+
+namespace GUI {
+class RandomSource;
+class SeekableReadStream;
+class WriteStream;
+
+
+/**
+ * Our generic event recorder.
+ *
+ * TODO: Add more documentation.
+ */
+class EventRecorder : private Common::EventSource, public Common::Singleton<EventRecorder>, private Common::DefaultEventMapper {
+	friend class Common::Singleton<SingletonBaseType>;
+	EventRecorder();
+	~EventRecorder();
+public:
+	/** Specify operation mode of Event Recorder */
+	enum RecordMode {
+		kPassthrough = 0,		/**< kPassthrough, do nothing */
+		kRecorderRecord = 1,		/**< kRecorderRecord, do the recording */
+		kRecorderPlayback = 2,		/**< kRecorderPlayback, playback existing recording */
+		kRecorderPlaybackPause = 3	/**< kRecordetPlaybackPause, interal state when user pauses the playback */
+	};
+
+	void init(Common::String recordFileName, RecordMode mode);
+	void deinit();
+	bool processDelayMillis();
+	uint32 getRandomSeed(const Common::String &name);
+	void processMillis(uint32 &millis, bool skipRecord);
+	bool processAudio(uint32 &samples, bool paused);
+	void processGameDescription(const ADGameDescription *desc);
+	Common::SeekableReadStream *processSaveStream(const Common::String & fileName);
+
+	/** Hooks for intercepting into GUI processing, so required events could be shoot
+	 *  or filtered out */
+	void preDrawOverlayGui();
+	void postDrawOverlayGui();
+
+	/** Set recording author
+	 *
+	 *  @see getAuthor
+	 */
+	void setAuthor(const Common::String &author) {
+		_author = author;
+	}
+
+	/** Set recording notes
+	 *
+	 *  @see getNotes
+	 */
+	void setNotes(const Common::String &desc){
+		_desc = desc;
+	}
+
+	/** Set descriptive name of the recording
+	 *
+	 *  @see getName
+	 */
+	void setName(const Common::String &name) {
+		_name = name;
+	}
+
+	/** Get recording author
+	 *
+	 *  @see getAuthor
+	 */
+	const Common::String getAuthor() {
+		return _author;
+	}
+
+	/** Get recording notes
+	 *
+	 *  @see setNotes
+	 */
+	const Common::String getNotes() {
+		return _desc;
+	}
+
+	/** Get recording name
+	 *
+	 *  @see setName
+	 */
+	const Common::String getName() {
+		return _name;
+	}
+	void setRedraw(bool redraw) {
+		_needRedraw = redraw;
+	}
+
+	void registerMixerManager(SdlMixerManager *mixerManager);
+	void registerTimerManager(DefaultTimerManager *timerManager);
+
+	SdlMixerManager *getMixerManager();
+	DefaultTimerManager *getTimerManager();
+
+	void deleteRecord(const Common::String& fileName);
+	bool checkForContinueGame();
+
+	void suspendRecording() {
+		_savedState = _initialized;
+		_initialized = false;
+	}
+
+	void resumeRecording() {
+		_initialized = _savedState;
+	}
+
+	Common::StringArray listSaveFiles(const Common::String &pattern);
+	Common::String generateRecordFileName(const Common::String &target);
+
+	Common::SaveFileManager *getSaveManager(Common::SaveFileManager *realSaveManager);
+	SDL_Surface *getSurface(int width, int height);
+	void RegisterEventSource();
+
+	/** Retrieve game screenshot and compute its checksum for comparison */
+	bool grabScreenAndComputeMD5(Graphics::Surface &screen, uint8 md5[16]);
+
+	void updateSubsystems();
+	bool switchMode();
+	void switchFastMode();
+
+private:
+	virtual Common::List<Common::Event> mapEvent(const Common::Event &ev, Common::EventSource *source);
+	bool notifyPoll();
+	bool pollEvent(Common::Event &ev);
+	bool _initialized;
+	volatile uint32 _fakeTimer;
+	bool _savedState;
+	bool _needcontinueGame;
+	int _temporarySlot;
+	Common::String _author;
+	Common::String _desc;
+	Common::String _name;
+
+	Common::SaveFileManager *_realSaveManager;
+	SdlMixerManager *_realMixerManager;
+	DefaultTimerManager *_timerManager;
+	RecorderSaveFileManager _fakeSaveManager;
+	NullSdlMixerManager *_fakeMixerManager;
+	GUI::OnScreenDialog *controlPanel;
+	Common::RecorderEvent _nextEvent;
+
+	void setFileHeader();
+	void setGameMd5(const ADGameDescription *gameDesc);
+	void getConfig();
+	void getConfigFromDomain(Common::ConfigManager::Domain *domain);
+	void removeDifferentEntriesInDomain(Common::ConfigManager::Domain *domain);
+	void applyPlaybackSettings();
+
+	void switchMixer();
+	void switchTimerManagers();
+
+	void togglePause();
+
+	void takeScreenshot();
+
+	bool openRecordFile(const Common::String &fileName);
+
+	bool checkGameHash(const ADGameDescription *desc);
+
+	void checkForKeyCode(const Common::Event &event);
+	bool allowMapping() const { return false; }
+
+	volatile uint32 _lastMillis;
+	uint32 _lastScreenshotTime;
+	uint32 _screenshotPeriod;
+	Common::PlaybackFile *_playbackFile;
+
+	void saveScreenShot();
+	void checkRecordedMD5();
+	void deleteTemporarySave();
+	volatile RecordMode _recordMode;
+	Common::String _recordFileName;
+	bool _fastPlayback;
+	bool _needRedraw;
+};
+
+} // End of namespace GUI
+
+#else
+
+#ifdef SDL_BACKEND
+#include "backends/timer/default/default-timer.h"
+#include "backends/mixer/sdl/sdl-mixer.h"
+#endif
+
+#define g_eventRec (GUI::EventRecorder::instance())
+
+namespace GUI {
+
+class EventRecorder : private Common::EventSource, public Common::Singleton<EventRecorder>, private Common::DefaultEventMapper {
+	friend class Common::Singleton<SingletonBaseType>;
+
+  public:
+	EventRecorder() {
+#ifdef SDL_BACKEND
+	  _timerManager = NULL;
+	  _realMixerManager = NULL;
+#endif
+	}
+	~EventRecorder() {}
+
+	bool pollEvent(Common::Event &ev) { return false; }
+	void RegisterEventSource() {}
+	void deinit() {}
+	void suspendRecording() {}
+	void resumeRecording() {}
+	void preDrawOverlayGui() {}
+	void postDrawOverlayGui() {}
+	void processGameDescription(const ADGameDescription *desc) {}
+	void updateSubsystems() {}
+	uint32 getRandomSeed(const Common::String &name) { return g_system->getMillis(); }
+	Common::SaveFileManager *getSaveManager(Common::SaveFileManager *realSaveManager) { return realSaveManager; }
+
+#ifdef SDL_BACKEND
+  private:
+	DefaultTimerManager *_timerManager;
+	SdlMixerManager *_realMixerManager;
+
+  public:
+	DefaultTimerManager *getTimerManager() { return _timerManager; }
+	void registerTimerManager(DefaultTimerManager *timerManager) { _timerManager = timerManager; }
+
+	SdlMixerManager *getMixerManager() { return _realMixerManager; }
+	void registerMixerManager(SdlMixerManager *mixerManager) { _realMixerManager = mixerManager; }
+
+	void processMillis(uint32 &millis, bool skipRecord) {}
+	bool processDelayMillis() { return false; }
+#endif
+
+};
+
+} // namespace GUI
+
+#endif // ENABLE_EVENTRECORDER
+
+#endif
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index e2fa258..3ce043c 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -50,6 +50,14 @@ const char * const ThemeEngine::kImageEraser = "eraser.bmp";
 const char * const ThemeEngine::kImageDelbtn = "delbtn.bmp";
 const char * const ThemeEngine::kImageList = "list.bmp";
 const char * const ThemeEngine::kImageGrid = "grid.bmp";
+const char * const ThemeEngine::kImageStopbtn = "stopbtn.bmp";
+const char * const ThemeEngine::kImageEditbtn = "editbtn.bmp";
+const char * const ThemeEngine::kImageSwitchModebtn = "switchbtn.bmp";
+const char * const ThemeEngine::kImageFastReplaybtn = "fastreplay.bmp";
+const char * const ThemeEngine::kImageStopSmallbtn = "stopbtn_small.bmp";
+const char * const ThemeEngine::kImageEditSmallbtn = "editbtn_small.bmp";
+const char * const ThemeEngine::kImageSwitchModeSmallbtn = "switchbtn_small.bmp";
+const char * const ThemeEngine::kImageFastReplaySmallbtn = "fastreplay_small.bmp";
 
 struct TextDrawData {
 	const Graphics::Font *_fontPtr;
@@ -465,11 +473,7 @@ void ThemeEngine::enable() {
 	if (_enabled)
 		return;
 
-	if (_useCursor) {
-		CursorMan.pushCursorPalette(_cursorPal, 0, _cursorPalSize);
-		CursorMan.pushCursor(_cursor, _cursorWidth, _cursorHeight, _cursorHotspotX, _cursorHotspotY, 255, true);
-		CursorMan.showMouse(true);
-	}
+	showCursor();
 
 	_system->showOverlay();
 	clearAll();
@@ -482,10 +486,8 @@ void ThemeEngine::disable() {
 
 	_system->hideOverlay();
 
-	if (_useCursor) {
-		CursorMan.popCursorPalette();
-		CursorMan.popCursor();
-	}
+	hideCursor();
+
 
 	_enabled = false;
 }
@@ -1787,5 +1789,20 @@ Common::String ThemeEngine::getThemeId(const Common::String &filename) {
 	}
 }
 
+void ThemeEngine::showCursor() {
+	if (_useCursor) {
+		CursorMan.pushCursorPalette(_cursorPal, 0, _cursorPalSize);
+		CursorMan.pushCursor(_cursor, _cursorWidth, _cursorHeight, _cursorHotspotX, _cursorHotspotY, 255, true);
+		CursorMan.showMouse(true);
+	}
+}
+
+void ThemeEngine::hideCursor() {
+	if (_useCursor) {
+		CursorMan.popCursorPalette();
+		CursorMan.popCursor();
+	}
+}
+
 
 } // End of namespace GUI.
diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h
index 6e5fd29..160ceb3 100644
--- a/gui/ThemeEngine.h
+++ b/gui/ThemeEngine.h
@@ -234,6 +234,14 @@ public:
 	static const char *const kImageDelbtn; ///< Delete characters in the predictive dialog
 	static const char *const kImageList;      ///< List image used in save/load chooser selection
 	static const char *const kImageGrid;      ///< Grid image used in save/load chooser selection
+	static const char *const kImageStopbtn; ///< Stop recording button in recorder onscreen dialog
+	static const char *const kImageEditbtn; ///< Edit recording metadata in recorder onscreen dialog
+	static const char *const kImageSwitchModebtn; ///< Switch mode button in recorder onscreen dialog
+	static const char *const kImageFastReplaybtn; ///< Fast playback mode button in recorder onscreen dialog
+	static const char *const kImageStopSmallbtn; ///< Stop recording button in recorder onscreen dialog (for 320xY)
+	static const char *const kImageEditSmallbtn; ///< Edit recording metadata in recorder onscreen dialog (for 320xY)
+	static const char *const kImageSwitchModeSmallbtn; ///< Switch mode button in recorder onscreen dialog (for 320xY)
+	static const char *const kImageFastReplaySmallbtn; ///< Fast playback mode button in recorder onscreen dialog (for 320xY)
 
 	/**
 	 * Graphics mode enumeration.
@@ -275,8 +283,13 @@ public:
 
 	void refresh();
 	void enable();
+
+	void showCursor();
+	void hideCursor();
+
 	void disable();
 
+
 	/**
 	 * Query the set up pixel format.
 	 */
diff --git a/gui/dialog.h b/gui/dialog.h
index 1773c66..d269a2f 100644
--- a/gui/dialog.h
+++ b/gui/dialog.h
@@ -37,6 +37,8 @@ struct Event;
 
 namespace GUI {
 
+class EventRecorder;
+
 class Widget;
 
 // Some "common" commands sent to handleCommand()
@@ -47,6 +49,7 @@ enum {
 
 class Dialog : public GuiObject {
 	friend class GuiManager;
+	friend class EventRecorder;
 	friend class Tooltip;
 protected:
 	Widget	*_mouseWidget;
diff --git a/gui/editrecorddialog.cpp b/gui/editrecorddialog.cpp
new file mode 100644
index 0000000..a6a7a25
--- /dev/null
+++ b/gui/editrecorddialog.cpp
@@ -0,0 +1,87 @@
+/* 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 "editrecorddialog.h"
+#include "gui/widgets/edittext.h"
+#include "common/translation.h"
+
+
+namespace GUI {
+
+const Common::String EditRecordDialog::getAuthor() {
+	return _authorEdit->getEditString();
+}
+
+void EditRecordDialog::setAuthor(const Common::String &author) {
+	_authorEdit->setEditString(author);
+}
+
+const Common::String EditRecordDialog::getNotes() {
+	return _notesEdit->getEditString();
+} 
+
+void EditRecordDialog::setNotes(const Common::String &desc) {
+	_notesEdit->setEditString(desc);
+}
+
+const Common::String EditRecordDialog::getName() {
+	return _nameEdit->getEditString();
+}
+
+void EditRecordDialog::setName(const Common::String &name) {
+	_nameEdit->setEditString(name);
+}
+
+EditRecordDialog::~EditRecordDialog() {
+}
+
+EditRecordDialog::EditRecordDialog(const Common::String author, const Common::String name, const Common::String notes) : Dialog("EditRecordDialog") {
+	new StaticTextWidget(this,"EditRecordDialog.AuthorLabel",_("Author:"));
+	new StaticTextWidget(this,"EditRecordDialog.NameLabel",_("Name:"));
+	new StaticTextWidget(this,"EditRecordDialog.NotesLabel",_("Notes:"));
+	_authorEdit = new EditTextWidget(this, "EditRecordDialog.AuthorEdit","");
+	_notesEdit = new EditTextWidget(this, "EditRecordDialog.NotesEdit","");
+	_nameEdit = new EditTextWidget(this, "EditRecordDialog.NameEdit","");
+	_authorEdit->setEditString(author);
+	_notesEdit->setEditString(notes);
+	_nameEdit->setEditString(name);
+	new GUI::ButtonWidget(this, "EditRecordDialog.Cancel", _("Cancel"), 0, kCloseCmd);
+	new GUI::ButtonWidget(this, "EditRecordDialog.OK", _("Ok"), 0, kOKCmd);
+}
+
+void EditRecordDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+	switch(cmd) {
+	case kCloseCmd:
+		setResult(kCloseCmd);
+		close();
+		break;
+	case kOKCmd:
+		setResult(kOKCmd);
+		close();
+		break;
+	default:
+		Dialog::handleCommand(sender, cmd, data);
+		break;
+	}
+}
+
+}
diff --git a/gui/editrecorddialog.h b/gui/editrecorddialog.h
new file mode 100644
index 0000000..c8da452
--- /dev/null
+++ b/gui/editrecorddialog.h
@@ -0,0 +1,56 @@
+/* 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.
+ *
+ */
+
+#ifndef GUI_EDITRECORDDIALOG_H
+#define GUI_EDITRECORDDIALOG_H
+
+#include "gui/dialog.h"
+
+namespace GUI {
+
+class EditTextWidget;
+class StaticTextWidget;
+
+class EditRecordDialog : public Dialog {
+private:
+	EditTextWidget *_notesEdit;
+	EditTextWidget *_nameEdit;
+	EditTextWidget *_authorEdit;
+	EditRecordDialog() : Dialog("EditRecordDialog") {};
+public:
+	EditRecordDialog(const Common::String author, const Common::String name, const Common::String notes);
+	~EditRecordDialog();
+
+	const Common::String getAuthor();
+	const Common::String getNotes();
+	const Common::String getName();
+
+	void setAuthor(const Common::String &author);
+	void setNotes(const Common::String &desc);
+	void setName(const Common::String &name);
+
+	virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+};
+
+}// End of namespace GUI
+
+#endif
diff --git a/gui/gui-manager.cpp b/gui/gui-manager.cpp
index a0ef421..78b40a4 100644
--- a/gui/gui-manager.cpp
+++ b/gui/gui-manager.cpp
@@ -27,6 +27,7 @@
 #include "common/rect.h"
 #include "common/textconsole.h"
 #include "common/translation.h"
+#include "gui/EventRecorder.h"
 
 #include "backends/keymapper/keymapper.h"
 
@@ -253,12 +254,13 @@ Dialog *GuiManager::getTopDialog() const {
 void GuiManager::runLoop() {
 	Dialog * const activeDialog = getTopDialog();
 	bool didSaveState = false;
-	int button;
-	uint32 time;
 
 	if (activeDialog == 0)
 		return;
 
+	// Suspend recording while GUI is shown
+	g_eventRec.suspendRecording();
+
 	if (!_stateIsSaved) {
 		saveState();
 		_theme->enable();
@@ -296,10 +298,10 @@ void GuiManager::runLoop() {
 //		_theme->updateScreen();
 //		_system->updateScreen();
 
-		if (lastRedraw + waitTime < _system->getMillis()) {
+		if (lastRedraw + waitTime < _system->getMillis(true)) {
 			_theme->updateScreen();
 			_system->updateScreen();
-			lastRedraw = _system->getMillis();
+			lastRedraw = _system->getMillis(true);
 		}
 
 		Common::Event event;
@@ -314,72 +316,21 @@ void GuiManager::runLoop() {
 			if (activeDialog != getTopDialog() && event.type != Common::EVENT_SCREEN_CHANGED)
 				continue;
 
-			Common::Point mouse(event.mouse.x - activeDialog->_x, event.mouse.y - activeDialog->_y);
-
-			switch (event.type) {
-			case Common::EVENT_KEYDOWN:
-				activeDialog->handleKeyDown(event.kbd);
-				break;
-			case Common::EVENT_KEYUP:
-				activeDialog->handleKeyUp(event.kbd);
-				break;
-			case Common::EVENT_MOUSEMOVE:
-				activeDialog->handleMouseMoved(mouse.x, mouse.y, 0);
-
-				if (mouse.x != _lastMousePosition.x || mouse.y != _lastMousePosition.y) {
-					_lastMousePosition.x = mouse.x;
-					_lastMousePosition.y = mouse.y;
-					_lastMousePosition.time = _system->getMillis();
-				}
+			processEvent(event, activeDialog);
 
+			if (event.type == Common::EVENT_MOUSEMOVE) {
 				tooltipCheck = true;
-				break;
-			// We don't distinguish between mousebuttons (for now at least)
-			case Common::EVENT_LBUTTONDOWN:
-			case Common::EVENT_RBUTTONDOWN:
-				button = (event.type == Common::EVENT_LBUTTONDOWN ? 1 : 2);
-				time = _system->getMillis();
-				if (_lastClick.count && (time < _lastClick.time + kDoubleClickDelay)
-							&& ABS(_lastClick.x - event.mouse.x) < 3
-							&& ABS(_lastClick.y - event.mouse.y) < 3) {
-					_lastClick.count++;
-				} else {
-					_lastClick.x = event.mouse.x;
-					_lastClick.y = event.mouse.y;
-					_lastClick.count = 1;
-				}
-				_lastClick.time = time;
-				activeDialog->handleMouseDown(mouse.x, mouse.y, button, _lastClick.count);
-				break;
-			case Common::EVENT_LBUTTONUP:
-			case Common::EVENT_RBUTTONUP:
-				button = (event.type == Common::EVENT_LBUTTONUP ? 1 : 2);
-				activeDialog->handleMouseUp(mouse.x, mouse.y, button, _lastClick.count);
-				break;
-			case Common::EVENT_WHEELUP:
-				activeDialog->handleMouseWheel(mouse.x, mouse.y, -1);
-				break;
-			case Common::EVENT_WHEELDOWN:
-				activeDialog->handleMouseWheel(mouse.x, mouse.y, 1);
-				break;
-			case Common::EVENT_SCREEN_CHANGED:
-				screenChange();
-				break;
-			default:
-#ifdef ENABLE_KEYMAPPER
-				activeDialog->handleOtherEvent(event);
-#endif
-				break;
 			}
 
-			if (lastRedraw + waitTime < _system->getMillis()) {
+
+			if (lastRedraw + waitTime < _system->getMillis(true)) {
 				_theme->updateScreen();
 				_system->updateScreen();
-				lastRedraw = _system->getMillis();
+				lastRedraw = _system->getMillis(true);
 			}
 		}
 
-		if (tooltipCheck && _lastMousePosition.time + kTooltipDelay < _system->getMillis()) {
+		if (tooltipCheck && _lastMousePosition.time + kTooltipDelay < _system->getMillis(true)) {
 			Widget *wdg = activeDialog->findWidget(_lastMousePosition.x, _lastMousePosition.y);
 			if (wdg && wdg->hasTooltip() && !(wdg->getFlags() & WIDGET_PRESSED)) {
 				Tooltip *tooltip = new Tooltip();
@@ -409,6 +360,9 @@ void GuiManager::runLoop() {
 		restoreState();
 		_useStdCursor = false;
 	}
+
+	// Resume recording once GUI is shown
+	g_eventRec.resumeRecording();
 }
 
 #pragma mark -
@@ -492,7 +446,7 @@ void GuiManager::setupCursor() {
 // very much. We could plug in a different cursor here if we like to.
 
 void GuiManager::animateCursor() {
-	int time = _system->getMillis();
+	int time = _system->getMillis(true);
 	if (time > _cursorAnimateTimer + kCursorAnimateDelay) {
 		for (int i = 0; i < 15; i++) {
 			if ((i < 6) || (i > 8)) {
@@ -537,4 +491,64 @@ void GuiManager::screenChange() {
 	_system->updateScreen();
 }
 
+void GuiManager::processEvent(const Common::Event &event, Dialog *const activeDialog) {
+	int button;
+	uint32 time;
+	Common::Point mouse(event.mouse.x - activeDialog->_x, event.mouse.y - activeDialog->_y);
+	switch (event.type) {
+	case Common::EVENT_KEYDOWN:
+		activeDialog->handleKeyDown(event.kbd);
+		break;
+	case Common::EVENT_KEYUP:
+		activeDialog->handleKeyUp(event.kbd);
+		break;
+	case Common::EVENT_MOUSEMOVE:
+		activeDialog->handleMouseMoved(mouse.x, mouse.y, 0);
+
+		if (mouse.x != _lastMousePosition.x || mouse.y != _lastMousePosition.y) {
+			_lastMousePosition.x = mouse.x;
+			_lastMousePosition.y = mouse.y;
+			_lastMousePosition.time = _system->getMillis(true);
+		}
+
+		break;
+		// We don't distinguish between mousebuttons (for now at least)
+	case Common::EVENT_LBUTTONDOWN:
+	case Common::EVENT_RBUTTONDOWN:
+		button = (event.type == Common::EVENT_LBUTTONDOWN ? 1 : 2);
+		time = _system->getMillis(true);
+		if (_lastClick.count && (time < _lastClick.time + kDoubleClickDelay)
+			&& ABS(_lastClick.x - event.mouse.x) < 3
+			&& ABS(_lastClick.y - event.mouse.y) < 3) {
+				_lastClick.count++;
+		} else {
+			_lastClick.x = event.mouse.x;
+			_lastClick.y = event.mouse.y;
+			_lastClick.count = 1;
+		}
+		_lastClick.time = time;
+		activeDialog->handleMouseDown(mouse.x, mouse.y, button, _lastClick.count);
+		break;
+	case Common::EVENT_LBUTTONUP:
+	case Common::EVENT_RBUTTONUP:
+		button = (event.type == Common::EVENT_LBUTTONUP ? 1 : 2);
+		activeDialog->handleMouseUp(mouse.x, mouse.y, button, _lastClick.count);
+		break;
+	case Common::EVENT_WHEELUP:
+		activeDialog->handleMouseWheel(mouse.x, mouse.y, -1);
+		break;
+	case Common::EVENT_WHEELDOWN:
+		activeDialog->handleMouseWheel(mouse.x, mouse.y, 1);
+		break;
+	case Common::EVENT_SCREEN_CHANGED:
+		screenChange();
+		break;
+	default:
+	#ifdef ENABLE_KEYMAPPER
+		activeDialog->handleOtherEvent(event);
+	#endif
+		break;
+	}
+}
+
 } // End of namespace GUI
diff --git a/gui/gui-manager.h b/gui/gui-manager.h
index 49542fd..b52d91b 100644
--- a/gui/gui-manager.h
+++ b/gui/gui-manager.h
@@ -35,6 +35,10 @@ namespace Graphics {
 class Font;
 }
 
+namespace Common {
+	struct Event;
+}
+
 namespace GUI {
 
 class Dialog;
@@ -67,6 +71,8 @@ public:
 	// until no dialogs are active anymore.
 	void runLoop();
 
+	void processEvent(const Common::Event &event, Dialog *const activeDialog);
+
 	bool isActive() const	{ return ! _dialogStack.empty(); }
 
 	bool loadNewTheme(Common::String id, ThemeEngine::GraphicsMode gfx = ThemeEngine::kGfxDisabled, bool force = false);
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 4e35b54..77d4cce 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -37,6 +37,11 @@
 #include "gui/message.h"
 #include "gui/gui-manager.h"
 #include "gui/options.h"
+#ifdef ENABLE_EVENTRECORDER
+#include "gui/onscreendialog.h"
+#include "gui/recorderdialog.h"
+#include "gui/EventRecorder.h"
+#endif
 #include "gui/saveload.h"
 #include "gui/widgets/edittext.h"
 #include "gui/widgets/list.h"
@@ -596,7 +601,6 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 LauncherDialog::LauncherDialog()
 	: Dialog(0, 0, 320, 200) {
 	_backgroundType = GUI::ThemeEngine::kDialogBackgroundMain;
-
 	const int screenW = g_system->getOverlayWidth();
 	const int screenH = g_system->getOverlayHeight();
 
@@ -779,10 +783,9 @@ void LauncherDialog::updateListing() {
 }
 
 void LauncherDialog::addGame() {
-	int modifiers = g_system->getEventManager()->getModifierState();
 
 #ifndef DISABLE_MASS_ADD
-	const bool massAdd = (modifiers & Common::KBD_SHIFT) != 0;
+	const bool massAdd = checkModifier(Common::KBD_SHIFT);
 
 	if (massAdd) {
 		MessageDialog alert(_("Do you really want to run the mass game detector? "
@@ -975,6 +978,49 @@ void LauncherDialog::editGame(int item) {
 	}
 }
 
+void LauncherDialog::loadGameButtonPressed(int item) {
+#ifdef ENABLE_EVENTRECORDER
+	const bool shiftPressed = checkModifier(Common::KBD_SHIFT);
+	if (shiftPressed) {
+		recordGame(item);
+	} else {
+		loadGame(item);
+	}
+	updateButtons();
+#else
+	loadGame(item);
+#endif
+}
+
+#ifdef ENABLE_EVENTRECORDER
+void LauncherDialog::recordGame(int item) {
+	RecorderDialog recorderDialog;
+	MessageDialog alert(_("Do you want to load savegame?"),
+		_("Yes"), _("No"));
+	switch(recorderDialog.runModal(_domains[item])) {
+	case RecorderDialog::kRecordDialogClose:
+		break;
+	case RecorderDialog::kRecordDialogPlayback:
+		ConfMan.setActiveDomain(_domains[item]);
+		close();
+		ConfMan.set("record_mode", "playback", ConfigManager::kTransientDomain);
+		ConfMan.set("record_file_name", recorderDialog.getFileName(), ConfigManager::kTransientDomain);
+		break;
+	case RecorderDialog::kRecordDialogRecord:
+		ConfMan.setActiveDomain(_domains[item]);
+		if (alert.runModal() == GUI::kMessageOK) {
+			loadGame(item);
+		}
+		close();
+		g_eventRec.setAuthor(recorderDialog._author);
+		g_eventRec.setName(recorderDialog._name);
+		g_eventRec.setNotes(recorderDialog._notes);
+		ConfMan.set("record_mode", "record", ConfigManager::kTransientDomain);
+		break;
+	}
+}
+#endif
+
 void LauncherDialog::loadGame(int item) {
 	String gameId = ConfMan.get("gameid", _domains[item]);
 	if (gameId.empty())
@@ -1039,7 +1085,7 @@ void LauncherDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		editGame(item);
 		break;
 	case kLoadGameCmd:
-		loadGame(item);
+		loadGameButtonPressed(item);
 		break;
 	case kOptionsCmd: {
 		GlobalOptionsDialog options;
@@ -1109,20 +1155,28 @@ void LauncherDialog::updateButtons() {
 		_loadButton->setEnabled(en);
 		_loadButton->draw();
 	}
+	switchButtonsText(_addButton, "~A~dd Game...", "Mass Add...");
+#ifdef ENABLE_EVENTRECORDER
+	switchButtonsText(_loadButton, "~L~oad...", "Record...");
+#endif
+}
 
-	// Update the label of the "Add" button depending on whether shift is pressed or not
-	int modifiers = g_system->getEventManager()->getModifierState();
-	const bool massAdd = (modifiers & Common::KBD_SHIFT) != 0;
+// Update the label of the button depending on whether shift is pressed or not
+void LauncherDialog::switchButtonsText(ButtonWidget *button, const char *normalText, const char *shiftedText) {
+	const bool shiftPressed = checkModifier(Common::KBD_SHIFT);
 	const bool lowRes = g_system->getOverlayWidth() <= 320;
 
-	const char *newAddButtonLabel = massAdd
-		? (lowRes ? _c("Mass Add...", "lowres") : _("Mass Add..."))
-		: (lowRes ? _c("~A~dd Game...", "lowres") : _("~A~dd Game..."));
+	const char *newAddButtonLabel = shiftPressed
+		? (lowRes ? _c(shiftedText, "lowres") : _(shiftedText))
+		: (lowRes ? _c(normalText, "lowres") : _(normalText));
 
-	if (_addButton->getLabel() != newAddButtonLabel)
-		_addButton->setLabel(newAddButtonLabel);
+	if (button->getLabel() != newAddButtonLabel)
+		button->setLabel(newAddButtonLabel);
 }
 
+
+
+
 void LauncherDialog::reflowLayout() {
 #ifndef DISABLE_FANCY_THEMES
 	if (g_gui.xmlEval()->getVar("Globals.ShowLauncherLogo") == 1 && g_gui.theme()->supportsImages()) {
@@ -1186,4 +1240,9 @@ void LauncherDialog::reflowLayout() {
 	Dialog::reflowLayout();
 }
 
+bool LauncherDialog::checkModifier(int checkedModifier) {
+	int modifiers = g_system->getEventManager()->getModifierState();
+	return (modifiers & checkedModifier) != 0;
+}
+
 } // End of namespace GUI
diff --git a/gui/launcher.h b/gui/launcher.h
index fc04843..2ab47be 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -56,7 +56,7 @@ protected:
 	ListWidget		*_list;
 	ButtonWidget	*_addButton;
 	Widget			*_startButton;
-	Widget			*_loadButton;
+	ButtonWidget	*_loadButton;
 	Widget			*_editButton;
 	Widget			*_removeButton;
 #ifndef DISABLE_FANCY_THEMES
@@ -80,6 +80,7 @@ protected:
 	void updateListing();
 
 	void updateButtons();
+	void switchButtonsText(ButtonWidget *button, const char *normalText, const char *shiftedText);
 
 	void open();
 	void close();
@@ -100,6 +101,16 @@ protected:
 	void editGame(int item);
 
 	/**
+	 * Facade for "Load..."/"Record..." buttons.
+	 */
+	void loadGameButtonPressed(int item);
+
+	/**
+	 * Handle "Record..." button.
+	 */
+	void recordGame(int item);
+
+	/**
 	 * Handle "Load..." button.
 	 */
 	void loadGame(int item);
@@ -111,6 +122,8 @@ protected:
 	 * @target	name of target to select
 	 */
 	void selectTarget(const String &target);
+private:
+	bool checkModifier(int modifier);
 };
 
 } // End of namespace GUI
diff --git a/gui/module.mk b/gui/module.mk
index bda3c88..338e43c 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -7,6 +7,7 @@ MODULE_OBJS := \
 	debugger.o \
 	dialog.o \
 	error.o \
+	EventRecorder.o \
 	gui-manager.o \
 	launcher.o \
 	massadd.o \
@@ -38,6 +39,13 @@ MODULE_OBJS += \
 	browser.o
 endif
 
+ifdef ENABLE_EVENTRECORDER
+MODULE_OBJS += \
+	editrecorddialog.o \
+	onscreendialog.o \
+	recorderdialog.o
+endif
+
 ifdef USE_FLUIDSYNTH
 MODULE_OBJS += \
 	fluidsynth-dialog.o
diff --git a/gui/onscreendialog.cpp b/gui/onscreendialog.cpp
new file mode 100644
index 0000000..efe8038
--- /dev/null
+++ b/gui/onscreendialog.cpp
@@ -0,0 +1,231 @@
+/* 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 "common/system.h"
+
+#include "gui/gui-manager.h"
+
+#include "gui/EventRecorder.h"
+
+#include "common/events.h"
+#include "common/rect.h"
+#include "common/translation.h"
+
+#include "graphics/cursorman.h"
+
+#include "gui/editrecorddialog.h"
+#include "gui/ThemeEval.h"
+
+#include "gui/onscreendialog.h"
+
+namespace GUI {
+
+bool OnScreenDialog::isVisible() const {
+	return true;
+}
+
+enum {
+	kStopCmd = 'STOP',
+	kEditCmd = 'EDIT',
+	kSwitchModeCmd = 'MODE',
+	kFastModeCmd = 'FAST'
+};
+
+void OnScreenDialog::reflowLayout() {
+	GuiObject::reflowLayout();
+}
+
+void OnScreenDialog::releaseFocus() {
+}
+
+OnScreenDialog::OnScreenDialog(bool isRecord) : Dialog("OnScreenDialog") {
+	_x = _y = 0;
+
+#ifndef DISABLE_FANCY_THEMES
+	if (g_gui.xmlEval()->getVar("Globals.OnScreenDialog.ShowPics") == 1 && g_gui.theme()->supportsImages()) {
+		GUI::PicButtonWidget *btn;
+		btn = new PicButtonWidget(this, "OnScreenDialog.StopButton", 0, kStopCmd, 0);
+		btn->useThemeTransparency(true);
+
+		if (g_system->getOverlayWidth() > 320)
+			btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageStopbtn));
+		else
+			btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageStopSmallbtn));
+
+		if (isRecord) {
+			btn = new PicButtonWidget(this, "OnScreenDialog.EditButton", 0, kEditCmd, 0);
+			btn->useThemeTransparency(true);
+
+			if (g_system->getOverlayWidth() > 320)
+				btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageEditbtn));
+			else
+				btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageEditSmallbtn));
+		} else {
+			btn = new PicButtonWidget(this, "OnScreenDialog.SwitchModeButton", 0, kSwitchModeCmd, 0);
+			btn->useThemeTransparency(true);
+			if (g_system->getOverlayWidth() > 320)
+				btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageSwitchModebtn));
+			else
+				btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageSwitchModeSmallbtn));
+
+			btn = new PicButtonWidget(this, "OnScreenDialog.FastReplayButton", 0, kFastModeCmd, 0);
+			btn->useThemeTransparency(true);
+			if (g_system->getOverlayWidth() > 320)
+				btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageFastReplaybtn));
+			else
+				btn->setGfx(g_gui.theme()->getImageSurface(ThemeEngine::kImageFastReplaySmallbtn));
+		}
+	} else
+#endif
+	{
+		GUI::ButtonWidget *btn;
+		if (g_system->getOverlayWidth() > 320)
+			btn = new ButtonWidget(this, "OnScreenDialog.StopButton", "[ ]", _("Stop"), kStopCmd);
+		else
+			btn = new ButtonWidget(this, "OnScreenDialog.StopButton", "[]", _("Stop"), kStopCmd);
+
+		if (isRecord) {
+			btn = new ButtonWidget(this, "OnScreenDialog.EditButton", "E", _("Edit record description"), kEditCmd);
+		} else {
+			btn = new ButtonWidget(this, "OnScreenDialog.SwitchModeButton", "G", _("Switch to Game"), kSwitchModeCmd);
+
+			btn = new ButtonWidget(this, "OnScreenDialog.FastReplayButton", ">>", _("Fast replay"), kFastModeCmd);
+		}
+	}
+
+
+	text = new GUI::StaticTextWidget(this, "OnScreenDialog.TimeLabel", "00:00:00");
+	_enableDrag = false;
+	_mouseOver = false;
+	_editDlgShown = false;
+}
+
+void OnScreenDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+	Common::Event eventRTL;
+	switch (cmd) {
+	case kStopCmd:
+		eventRTL.type = Common::EVENT_RTL;
+		g_system->getEventManager()->pushEvent(eventRTL);
+		close();
+		break;
+	case kEditCmd:
+		dlg = new EditRecordDialog(g_eventRec.getAuthor(), g_eventRec.getName(), g_eventRec.getNotes());
+		CursorMan.lock(false);
+		g_eventRec.setRedraw(false);
+		g_system->showOverlay();
+		_editDlgShown = true;
+		dlg->runModal();
+		_editDlgShown = false;
+		g_system->hideOverlay();
+		g_eventRec.setRedraw(true);
+		CursorMan.lock(true);
+		g_eventRec.setAuthor(((EditRecordDialog *)dlg)->getAuthor());
+		g_eventRec.setName(((EditRecordDialog *)dlg)->getName());
+		g_eventRec.setNotes(((EditRecordDialog *)dlg)->getNotes());
+		delete dlg;
+		break;
+	case kSwitchModeCmd:
+		if (g_eventRec.switchMode()) {
+			close();
+		}
+		break;
+	case kFastModeCmd:
+		g_eventRec.switchFastMode();
+		break;
+	}
+}
+
+void OnScreenDialog::setReplayedTime(uint32 newTime) {
+	if (newTime - lastTime > 1000) {
+		uint32 seconds = newTime / 1000;
+		text->setLabel(Common::String::format("%.2d:%.2d:%.2d", seconds / 3600 % 24, seconds / 60 % 60, seconds % 60));
+		lastTime = newTime;
+	}
+}
+
+OnScreenDialog::~OnScreenDialog() {
+}
+
+void OnScreenDialog::handleMouseMoved(int x, int y, int button) {
+	if (_enableDrag) {
+		_x = _x + x - _dragPoint.x;
+		_y = _y + y - _dragPoint.y;
+	}
+	Dialog::handleMouseMoved(x, y, button);
+	if (isMouseOver(x, y)) {
+		if (_mouseOver == false) {
+			g_gui.theme()->showCursor();
+			CursorMan.lock(true);
+		}
+		_mouseOver = true;
+	} else {
+		if (_mouseOver == true) {
+			CursorMan.lock(false);
+			g_gui.theme()->hideCursor();
+		}
+		_mouseOver = false;
+	}
+}
+
+void OnScreenDialog::handleMouseDown(int x, int y, int button, int clickCount) {
+	if (isMouseOver(x, y)) {
+		_dragPoint.x = x;
+		_dragPoint.y = y;
+		_enableDrag = true;
+	}
+	Dialog::handleMouseDown(x, y, button, clickCount);
+}
+
+void OnScreenDialog::handleMouseUp(int x, int y, int button, int clickCount) {
+	if (isMouseOver(x, y)) {
+
+	}
+	_enableDrag = false;
+	Dialog::handleMouseUp(x, y, button, clickCount);
+}
+
+bool OnScreenDialog::isMouseOver(int x, int y) {
+	return (x >= 0 && x < _w && y >= 0 && y < _h);
+}
+
+bool OnScreenDialog::isMouseOver() {
+	return _mouseOver;
+}
+
+void OnScreenDialog::close() {
+	CursorMan.lock(false);
+	Dialog::close();
+}
+
+Dialog *OnScreenDialog::getActiveDlg() {
+	if (_editDlgShown) {
+		return dlg;
+	} else {
+		return this;
+	}
+}
+
+bool OnScreenDialog::isEditDlgVisible() {
+	return _editDlgShown;
+}
+
+}
diff --git a/gui/onscreendialog.h b/gui/onscreendialog.h
new file mode 100644
index 0000000..4f3839a
--- /dev/null
+++ b/gui/onscreendialog.h
@@ -0,0 +1,64 @@
+/* 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.
+ *
+ */
+
+#ifndef GUI_ONSCREENDIALOG_H
+#define GUI_ONSCREENDIALOG_H
+
+#include "gui/dialog.h"
+#include "gui/widget.h"
+
+namespace GUI {
+
+class OnScreenDialog : public Dialog {
+private:
+	uint32 lastTime;
+	bool _enableDrag;
+	bool _mouseOver;
+	bool _editDlgShown;
+	Common::Point _dragPoint;
+	GUI::StaticTextWidget *text;
+	Dialog *dlg;
+	bool isMouseOver(int x, int y);
+public:
+	OnScreenDialog(bool recordingMode);
+	~OnScreenDialog();
+	virtual void close();
+	virtual bool isVisible() const;
+	virtual void reflowLayout();
+
+	void setReplayedTime(uint32 newTime);
+
+	virtual void handleMouseMoved(int x, int y, int button);
+	virtual void handleMouseDown(int x, int y, int button, int clickCount);
+	virtual void handleMouseUp(int x, int y, int button, int clickCount);
+	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+
+	bool isMouseOver();
+	bool isEditDlgVisible();
+	Dialog *getActiveDlg();
+protected:
+	virtual void	releaseFocus();
+};
+
+} // End of namespace GUI
+
+#endif
diff --git a/gui/recorderdialog.cpp b/gui/recorderdialog.cpp
new file mode 100644
index 0000000..55f342d
--- /dev/null
+++ b/gui/recorderdialog.cpp
@@ -0,0 +1,291 @@
+/* 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 "common/algorithm.h"
+#include "common/bufferedstream.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "graphics/colormasks.h"
+#include "graphics/palette.h"
+#include "graphics/scaler.h"
+#include "graphics/thumbnail.h"
+#include "common/translation.h"
+#include "gui/widgets/list.h"
+#include "gui/editrecorddialog.h"
+#include "gui/EventRecorder.h"
+#include "gui/message.h"
+#include "gui/saveload.h"
+#include "common/system.h"
+#include "gui/ThemeEval.h"
+#include "gui/gui-manager.h"
+#include "recorderdialog.h"
+
+#define MAX_RECORDS_NAMES 0xFF
+
+namespace GUI {
+
+enum {
+	kRecordCmd = 'RCRD',
+	kPlaybackCmd = 'PBCK',
+	kDeleteCmd = 'DEL ',
+	kNextScreenshotCmd = 'NEXT',
+	kPrevScreenshotCmd = 'PREV',
+	kEditRecordCmd = 'EDIT'
+};
+
+RecorderDialog::RecorderDialog() : Dialog("RecorderDialog"), _list(0), _currentScreenshot(0) {
+	_backgroundType = ThemeEngine::kDialogBackgroundSpecial;
+
+	new StaticTextWidget(this, "SaveLoadChooser.Title", _("Recorder or Playback Gameplay"));
+
+	_list = new GUI::ListWidget(this, "RecorderDialog.List");
+	_list->setNumberingMode(GUI::kListNumberingOff);
+
+	_deleteButton = new GUI::ButtonWidget(this, "RecorderDialog.Delete", _("Delete"), 0, kDeleteCmd);
+	new GUI::ButtonWidget(this, "RecorderDialog.Cancel", _("Cancel"), 0, kCloseCmd);
+	new GUI::ButtonWidget(this, "RecorderDialog.Record", _("Record"), 0, kRecordCmd);
+	_playbackButton = new GUI::ButtonWidget(this, "RecorderDialog.Playback", _("Playback"), 0, kPlaybackCmd);
+	
+	_editButton = new GUI::ButtonWidget(this, "RecorderDialog.Edit", _("Edit"), 0, kEditRecordCmd);
+
+	_editButton->setEnabled(false);
+	_deleteButton->setEnabled(false);
+	_playbackButton->setEnabled(false);
+
+	_gfxWidget = new GUI::GraphicsWidget(this, 0, 0, 10, 10);
+	_container = new GUI::ContainerWidget(this, 0, 0, 10, 10);
+	if (g_gui.xmlEval()->getVar("Globals.RecorderDialog.ExtInfo.Visible") == 1) {
+		new GUI::ButtonWidget(this,"RecorderDialog.NextScreenShotButton", "<", 0, kPrevScreenshotCmd);
+		new GUI::ButtonWidget(this, "RecorderDialog.PreviousScreenShotButton", ">", 0, kNextScreenshotCmd);	
+		_currentScreenshotText = new StaticTextWidget(this, "RecorderDialog.currentScreenshot", "0/0");
+		_authorText = new StaticTextWidget(this, "RecorderDialog.Author", _("Author: "));
+		_notesText = new StaticTextWidget(this, "RecorderDialog.Notes", _("Notes: "));
+	}
+	if (_gfxWidget)
+		_gfxWidget->setGfx(0);
+}
+
+
+void RecorderDialog::reflowLayout() {
+	if (g_gui.xmlEval()->getVar("Globals.RecorderDialog.ExtInfo.Visible") == 1) {
+		int16 x, y;
+		uint16 w, h;
+
+		if (!g_gui.xmlEval()->getWidgetData("RecorderDialog.Thumbnail", x, y, w, h)) {
+			error("Error when loading position data for Recorder Thumbnails");
+		}
+
+		int thumbW = kThumbnailWidth;
+		int thumbH = kThumbnailHeight2;
+		int thumbX = x + (w >> 1) - (thumbW >> 1);
+		int thumbY = y + kLineHeight;
+
+		_container->resize(x, y, w, h);
+		_gfxWidget->resize(thumbX, thumbY, thumbW, thumbH);
+
+		_container->setVisible(true);
+		_gfxWidget->setVisible(true);
+		updateSelection(false);
+	} else {
+		_container->setVisible(false);
+		_gfxWidget->setVisible(false);
+	}
+	Dialog::reflowLayout();
+}
+
+
+
+void RecorderDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+	switch(cmd) {
+	case kEditRecordCmd: {
+		if (_list->getSelected() >= 0) {
+			EditRecordDialog editDlg(_fileHeaders[_list->getSelected()].author, _fileHeaders[_list->getSelected()].name, _fileHeaders[_list->getSelected()].notes);
+			if (editDlg.runModal() != kOKCmd) {
+				return;
+			}
+			_playbackFile.openRead(_fileHeaders[_list->getSelected()].fileName);
+			_playbackFile.getHeader().author = editDlg.getAuthor();
+			_playbackFile.getHeader().name = editDlg.getName();
+			_playbackFile.getHeader().notes = editDlg.getNotes();
+			_playbackFile.updateHeader();
+			_fileHeaders[_list->getSelected()] = _playbackFile.getHeader();
+			int oldselection = _list->getSelected();
+			updateList();
+			_list->setSelected(oldselection);
+			updateSelection(true);
+			_playbackFile.close();
+		}
+	}
+		break;
+	case kNextScreenshotCmd:
+		++_currentScreenshot;
+		updateScreenshot();
+		break;
+	case kPrevScreenshotCmd:
+		--_currentScreenshot;
+		updateScreenshot();
+		break;
+	case kDeleteCmd:
+		if (_list->getSelected() >= 0) {
+			MessageDialog alert(_("Do you really want to delete this record?"),
+				_("Delete"), _("Cancel"));
+			if (alert.runModal() == GUI::kMessageOK) {
+				_playbackFile.close();
+				g_eventRec.deleteRecord(_fileHeaders[_list->getSelected()].fileName);
+				_list->setSelected(-1);
+				updateList();
+			}
+		}
+		break;
+	case GUI::kListSelectionChangedCmd:
+		updateSelection(true);
+		break;
+	case kRecordCmd: {
+		TimeDate t;
+		Common::String gameId = ConfMan.get("gameid", _target);
+		const EnginePlugin *plugin = 0;
+		GameDescriptor desc = EngineMan.findGame(gameId, &plugin);
+		g_system->getTimeAndDate(t);
+		EditRecordDialog editDlg("Unknown Author", Common::String::format("%.2d.%.2d.%.4d ", t.tm_mday, t.tm_mon, 1900 + t.tm_year) + desc.description(), "");
+		if (editDlg.runModal() != kOKCmd) {
+			return;
+		}
+		_author = editDlg.getAuthor();
+		_name = editDlg.getName();
+		_notes = editDlg.getNotes();
+		_filename = g_eventRec.generateRecordFileName(_target);
+		setResult(kRecordDialogRecord);
+		close();
+		}
+		break;
+	case kPlaybackCmd:
+		if (_list->getSelected() >= 0) {
+			_filename = _fileHeaders[_list->getSelected()].fileName;
+			setResult(kRecordDialogPlayback);
+			close();
+		}
+		break;
+	case kCloseCmd:
+		setResult(kRecordDialogClose);
+	default:
+		Dialog::handleCommand(sender, cmd, data);
+		}
+	}
+
+void RecorderDialog::updateList() {
+	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+	Common::String pattern(_target+".r??");
+	Common::StringArray files = saveFileMan->listSavefiles(pattern);
+	Common::PlaybackFile file;
+	Common::StringArray namesList;
+	_fileHeaders.clear();
+	for (Common::StringArray::iterator i = files.begin(); i != files.end(); ++i) {
+		if (file.openRead(*i)) {
+			namesList.push_back(file.getHeader().name);
+			_fileHeaders.push_back(file.getHeader());
+		}
+		file.close();
+	}
+	_list->setList(namesList);
+	_list->draw();
+}
+
+int RecorderDialog::runModal(Common::String &target) {
+	_target = target;
+	updateList();
+	return Dialog::runModal();
+}
+
+RecorderDialog::~RecorderDialog() {
+}
+
+void RecorderDialog::updateSelection(bool redraw) {
+	if (_list->getSelected() >= 0) {
+		_editButton->setEnabled(true);
+		_deleteButton->setEnabled(true);
+		_playbackButton->setEnabled(true);
+	}
+
+	if (g_gui.xmlEval()->getVar("Globals.RecorderDialog.ExtInfo.Visible") != 1)
+		return;
+
+	_gfxWidget->setGfx(-1, -1, 0, 0, 0);
+	_screenShotsCount = 0;
+	_currentScreenshot = 0;
+	updateScreenShotsText();
+	if (_list->getSelected() >= 0) {
+		_authorText->setLabel(_("Author: ") + _fileHeaders[_list->getSelected()].author);
+		_notesText->setLabel(_("Notes: ") + _fileHeaders[_list->getSelected()].notes);
+
+		_firstScreenshotUpdate = true;
+		updateScreenshot();
+		if ((_screenShotsCount) > 0) {
+			_currentScreenshot = 1;
+		}
+		updateScreenshot();
+	} else {
+		_authorText->setLabel(_("Author: "));
+		_notesText->setLabel(_("Notes: "));
+		_screenShotsCount = -1;
+		_currentScreenshot = 0;
+		_gfxWidget->setGfx(-1, -1, 0, 0, 0);
+		_gfxWidget->draw();
+		updateScreenShotsText();
+	}
+}
+
+void RecorderDialog::updateScreenshot() {
+	if (_list->getSelected() == -1) {
+		return;
+	}
+	if (_currentScreenshot < 1) {
+		_currentScreenshot = _screenShotsCount;
+	}
+	if (_currentScreenshot > _screenShotsCount) {
+		_currentScreenshot = 1;
+	}
+	if (_firstScreenshotUpdate) {
+		_playbackFile.openRead(_fileHeaders[_list->getSelected()].fileName);
+		_screenShotsCount = _playbackFile.getScreensCount();
+		_firstScreenshotUpdate = false;
+	}
+	Graphics::Surface *srcsf = _playbackFile.getScreenShot(_currentScreenshot);
+	if (srcsf != NULL) {
+		Graphics::Surface *destsf = Graphics::scale(*srcsf, _gfxWidget->getWidth(), _gfxWidget->getHeight());
+		_gfxWidget->setGfx(destsf);
+		updateScreenShotsText();
+		delete destsf;
+		delete srcsf;
+	} else {
+		_gfxWidget->setGfx(-1, -1, 0, 0, 0);
+	}
+	_gfxWidget->draw();
+}
+
+void RecorderDialog::updateScreenShotsText() {
+	if (_screenShotsCount == -1) {
+		_currentScreenshotText->setLabel(Common::String::format("%d / ?", _currentScreenshot));
+	} else {
+		_currentScreenshotText->setLabel(Common::String::format("%d / %d", _currentScreenshot, _screenShotsCount));
+	}
+}
+
+} // End of namespace GUI
diff --git a/gui/recorderdialog.h b/gui/recorderdialog.h
new file mode 100644
index 0000000..eb690a4
--- /dev/null
+++ b/gui/recorderdialog.h
@@ -0,0 +1,81 @@
+/* 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.
+ */
+
+#ifndef GUI_RECORDER_DIALOG_H
+#define GUI_RECORDER_DIALOG_H
+#include "common/stream.h"
+#include "common/recorderfile.h"
+#include "gui/dialog.h"
+namespace GUI {
+
+class ListWidget;
+class GraphicsWidget;
+class ButtonWidget;
+class CommandSender;
+class ContainerWidget;
+class StaticTextWidget;
+
+class RecorderDialog : public GUI::Dialog {
+private:
+	bool _firstScreenshotUpdate;
+	Common::PlaybackFile _playbackFile;
+	Common::String _target;
+	Common::String _filename;
+	int _currentScreenshot;
+	int _screenShotsCount;
+	Common::Array<Common::PlaybackFile::PlaybackFileHeader> _fileHeaders;
+	GUI::ListWidget *_list;
+	GUI::ContainerWidget *_container;
+	GUI::GraphicsWidget *_gfxWidget;
+	GUI::StaticTextWidget *_currentScreenshotText;
+	GUI::StaticTextWidget *_authorText;
+	GUI::StaticTextWidget *_notesText;
+	GUI::ButtonWidget *_editButton;
+	GUI::ButtonWidget *_deleteButton;
+	GUI::ButtonWidget *_playbackButton;
+
+	void updateList();
+	void updateScreenShotsText();
+	void updateSelection(bool redraw);
+	void updateScreenshot();
+public:
+	Common::String _author;
+	Common::String _name;
+	Common::String _notes;
+	enum DialogResult {
+		kRecordDialogClose,
+		kRecordDialogRecord,
+		kRecordDialogPlayback
+	};
+	RecorderDialog();
+	~RecorderDialog();
+
+	virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+	virtual void reflowLayout();
+
+	int runModal(Common::String &target);
+	const Common::String getFileName() {return _filename;}
+};
+
+}  // End of namespace GUI
+
+
+#endif
diff --git a/gui/themes/default.inc b/gui/themes/default.inc
index 6d8e6ba..1b6ae3e 100644
--- a/gui/themes/default.inc
+++ b/gui/themes/default.inc
@@ -610,50 +610,54 @@
 "/> "
 "</drawdata> "
 "</render_info> "
-"<layout_info resolution='y<400'> "
+"<layout_info resolution='y>399'> "
 "<globals> "
-"<def var='Line.Height' value='12' /> "
-"<def var='Font.Height' value='10' /> "
-"<def var='About.OuterBorder' value='10'/> "
-"<def var='Layout.Spacing' value='8'/> "
+"<def var='Line.Height' value='16' /> "
+"<def var='Font.Height' value='16' /> "
+"<def var='About.OuterBorder' value='80'/> "
+"<def var='Layout.Spacing' value='8' /> "
 "<def var='ShowLauncherLogo' value='0'/> "
 "<def var='ShowGlobalMenuLogo' value='0'/> "
 "<def var='ShowSearchPic' value='0'/> "
 "<def var='ShowChooserPics' value='0'/> "
-"<def var='ShowChooserPageDisplay' value='0'/> "
-"<def var='SaveLoadChooser.ExtInfo.Visible' value='0'/> "
-"<def var='KeyMapper.Spacing' value='5'/> "
-"<def var='KeyMapper.LabelWidth' value='80'/> "
-"<def var='KeyMapper.ButtonWidth' value='60'/> "
-"<def var='Tooltip.MaxWidth' value='70'/> "
-"<def var='Tooltip.XDelta' value='8'/> "
-"<def var='Tooltip.YDelta' value='8'/> "
-"<def var='Predictive.Button.Width' value='45' /> "
-"<def var='Predictive.Button.Height' value='15' /> "
-"<widget name='Button' "
-"size='72,16' "
-"/> "
-"<widget name='Slider' "
-"size='85,12' "
-"/> "
+"<def var='ShowChooserPageDisplay' value='1'/> "
+"<def var='SaveLoadChooser.ExtInfo.Visible' value='1'/> "
+"<def var='RecorderDialog.ExtInfo.Visible' value='1'/> "
+"<def var='OnScreenDialog.ShowPics' value='0'/> "
+"<def var='KeyMapper.Spacing' value='10'/> "
+"<def var='KeyMapper.LabelWidth' value='100'/> "
+"<def var='KeyMapper.ButtonWidth' value='80'/> "
+"<def var='Tooltip.MaxWidth' value='200'/> "
+"<def var='Tooltip.XDelta' value='16'/> "
+"<def var='Tooltip.YDelta' value='16'/> "
+"<def var='Predictive.Button.Width' value='60' /> "
 "<widget name='OptionsLabel' "
 "size='110,Globals.Line.Height' "
 "textalign='right' "
 "/> "
 "<widget name='SmallLabel' "
-"size='18,Globals.Line.Height' "
+"size='24,Globals.Line.Height' "
+"/> "
+"<widget name='ShortOptionsLabel' "
+"size='60,Globals.Line.Height' "
+"/> "
+"<widget name='Button' "
+"size='108,24' "
+"/> "
+"<widget name='Slider' "
+"size='128,18' "
 "/> "
 "<widget name='PopUp' "
-"size='-1,15' "
+"size='-1,19' "
 "/> "
 "<widget name='Checkbox' "
-"size='-1,Globals.Line.Height' "
+"size='-1,14' "
 "/> "
 "<widget name='Radiobutton' "
 "size='-1,Globals.Line.Height' "
 "/> "
 "<widget name='ListWidget' "
-"padding='5,0,0,0' "
+"padding='5,0,8,0' "
 "/> "
 "<widget name='PopUpWidget' "
 "padding='7,5,0,0' "
@@ -665,29 +669,35 @@
 "padding='7,5,5,5' "
 "/> "
 "<widget name='Scrollbar' "
-"size='9,0' "
+"size='15,0' "
 "/> "
 "<widget name='TabWidget.Tab' "
-"size='45,16' "
-"padding='0,0,2,0' "
+"size='75,27' "
+"padding='0,0,8,0' "
 "/> "
 "<widget name='TabWidget.Body' "
-"padding='0,0,0,-8' "
+"padding='0,0,0,0' "
 "/> "
 "<widget name='TabWidget.NavButton' "
-"size='32,18' "
-"padding='0,0,1,0' "
+"size='15,18' "
+"padding='0,3,4,0' "
+"/> "
+"<widget name='EditRecordLabel' "
+"size='60,25' "
+"/> "
+"<widget name='EditRecord' "
+"size='240,25' "
 "/> "
 "</globals> "
 "<dialog name='Launcher' overlays='screen'> "
-"<layout type='vertical' center='true' padding='6,6,2,2'> "
+"<layout type='vertical' center='true' padding='16,16,8,8'> "
 "<widget name='Version' "
 "height='Globals.Line.Height' "
 "textalign='center' "
 "/> "
-"<layout type='horizontal' spacing='5' padding='0,0,0,0'> "
+"<layout type='horizontal' spacing='5' padding='10,0,0,0'> "
 "<widget name='SearchDesc' "
-"width='50' "
+"width='60' "
 "height='Globals.Line.Height' "
 "textalign='right' "
 "/> "
@@ -702,38 +712,39 @@
 "<space /> "
 "</layout> "
 "<widget name='GameList'/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
 "<widget name='LoadGameButton' "
-"height='12' "
+"height='20' "
 "/> "
 "<widget name='AddGameButton' "
-"height='12' "
+"height='20' "
 "/> "
 "<widget name='EditGameButton' "
-"height='12' "
+"height='20' "
 "/> "
 "<widget name='RemoveGameButton' "
-"height='12' "
+"height='20' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
+"<space size='4'/> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
 "<widget name='QuitButton' "
-"height='12' "
+"height='20' "
 "/> "
 "<widget name='AboutButton' "
-"height='12' "
+"height='20' "
 "/> "
 "<widget name='OptionsButton' "
-"height='12' "
+"height='20' "
 "/> "
 "<widget name='StartButton' "
-"height='12' "
+"height='20' "
 "/> "
 "</layout> "
 "</layout> "
 "</dialog> "
-"<dialog name='Browser' overlays='screen' inset='8' shading='dim'> "
-"<layout type='vertical' padding='8,8,0,4'> "
+"<dialog name='Browser' overlays='Dialog.Launcher.GameList' shading='dim'> "
+"<layout type='vertical' padding='8,8,8,8'> "
 "<widget name='Headline' "
 "height='Globals.Line.Height' "
 "/> "
@@ -741,7 +752,7 @@
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='List'/> "
-"<layout type='vertical' padding='0,0,8,0'> "
+"<layout type='vertical' padding='0,0,16,0'> "
 "<widget name='Hidden' "
 "type='Checkbox' "
 "/> "
@@ -760,10 +771,10 @@
 "</layout> "
 "</layout> "
 "</dialog> "
-"<dialog name='GlobalOptions' overlays='screen' inset='16' shading='dim'> "
+"<dialog name='GlobalOptions' overlays='Dialog.Launcher.GameList' shading='dim'> "
 "<layout type='vertical' padding='0,0,0,0'> "
 "<widget name='TabWidget'/> "
-"<layout type='horizontal' padding='8,8,8,8'> "
+"<layout type='horizontal' padding='16,16,16,16'> "
 "<space/> "
 "<widget name='Cancel' "
 "type='Button' "
@@ -776,7 +787,7 @@
 "</dialog> "
 "<dialog name='GlobalOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> "
 "<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='grModePopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -784,7 +795,7 @@
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='grRenderPopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -802,7 +813,7 @@
 "</dialog> "
 "<dialog name='GlobalOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> "
 "<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='auMidiPopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -810,7 +821,7 @@
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='auOPLPopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -818,7 +829,7 @@
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='auSampleRatePopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -826,7 +837,7 @@
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='3' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
 "<widget name='subToggleDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -840,7 +851,7 @@
 "type='Radiobutton' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
 "<widget name='subSubtitleSpeedDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -854,8 +865,9 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GlobalOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='0,0,0,0' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
 "<widget name='vcMusicText' "
 "type='OptionsLabel' "
 "/> "
@@ -866,7 +878,7 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
 "<widget name='vcSfxText' "
 "type='OptionsLabel' "
 "/> "
@@ -877,7 +889,7 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
 "<widget name='vcSpeechText' "
 "type='OptionsLabel' "
 "/> "
@@ -888,8 +900,8 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
-"<space size='110' /> "
+"</layout> "
+"<layout type='vertical' padding='24,0,24,0' center='true'> "
 "<widget name='vcMuteCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -897,8 +909,8 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GlobalOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='6'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='auPrefGmPopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -906,7 +918,7 @@
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='mcFontButton' "
 "type='Button' "
 "/> "
@@ -921,7 +933,7 @@
 "<widget name='mcMixedCheckbox' "
 "type='Checkbox' "
 "/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
 "<widget name='mcMidiGainText' "
 "type='OptionsLabel' "
 "/> "
@@ -934,14 +946,14 @@
 "/> "
 "</layout> "
 "<widget name='mcFluidSynthSettings' "
-"width='150' "
+"width='200' "
 "height='Globals.Button.Height' "
 "/> "
 "</layout> "
 "</dialog> "
 "<dialog name='GlobalOptions_MT32' overlays='Dialog.GlobalOptions.TabWidget'> "
 "<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='auPrefMt32PopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -959,7 +971,7 @@
 "</dialog> "
 "<dialog name='GlobalOptions_Paths' overlays='Dialog.GlobalOptions.TabWidget'> "
 "<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='SaveButton' "
 "type='Button' "
 "/> "
@@ -971,7 +983,7 @@
 "width='Globals.Line.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='ThemeButton' "
 "type='Button' "
 "/> "
@@ -983,7 +995,7 @@
 "width='Globals.Line.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='ExtraButton' "
 "type='Button' "
 "/> "
@@ -1007,7 +1019,7 @@
 "</dialog> "
 "<dialog name='GlobalOptions_Misc' overlays='Dialog.GlobalOptions.TabWidget'> "
 "<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='ThemeButton' "
 "type='Button' "
 "/> "
@@ -1015,31 +1027,25 @@
 "height='Globals.Line.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='RendererPopupDesc' "
-"width='80' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
 "/> "
 "<widget name='RendererPopup' "
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='AutosavePeriodPopupDesc' "
-"width='80' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
 "/> "
 "<widget name='AutosavePeriodPopup' "
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='GuiLanguagePopupDesc' "
-"width='80' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
 "/> "
 "<widget name='GuiLanguagePopup' "
 "type='PopUp' "
@@ -1074,10 +1080,10 @@
 "</layout> "
 "</layout> "
 "</dialog> "
-"<dialog name='GameOptions' overlays='screen' inset='16' shading='dim'> "
+"<dialog name='GameOptions' overlays='Dialog.Launcher.GameList' shading='dim'> "
 "<layout type='vertical' padding='0,0,0,0' spacing='16'> "
 "<widget name='TabWidget'/> "
-"<layout type='horizontal' padding='8,8,8,8'> "
+"<layout type='horizontal' padding='16,16,16,4'> "
 "<space/> "
 "<widget name='Cancel' "
 "type='Button' "
@@ -1089,7 +1095,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -1097,7 +1103,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -1105,7 +1111,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -1113,7 +1119,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_MT32' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -1121,7 +1127,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -1129,43 +1135,34 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Game' overlays='Dialog.GameOptions.TabWidget' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='vertical' padding='16,16,16,16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='Id' "
-"width='35' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
 "/> "
 "<widget name='Domain' "
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='Name' "
-"width='35' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
 "/> "
 "<widget name='Desc' "
 "type='PopUp' "
 "/> "
 "</layout> "
-"<space size='8'/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='LangPopupDesc' "
-"width='60' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
 "/> "
 "<widget name='LangPopup' "
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='PlatformPopupDesc' "
-"width='60' "
-"height='Globals.Line.Height' "
-"textalign='right' "
+"type='OptionsLabel' "
 "/> "
 "<widget name='PlatformPopup' "
 "type='PopUp' "
@@ -1174,8 +1171,8 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Paths' overlays='Dialog.GameOptions.TabWidget' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
+"<layout type='vertical' padding='16,16,16,16'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='Savepath' "
 "type='Button' "
 "/> "
@@ -1187,7 +1184,7 @@
 "width='Globals.Line.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='Extrapath' "
 "type='Button' "
 "/> "
@@ -1199,7 +1196,7 @@
 "width='Globals.Line.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='Gamepath' "
 "type='Button' "
 "/> "
@@ -1210,7 +1207,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Engine' overlays='Dialog.GameOptions.TabWidget' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,8'> "
+"<layout type='vertical' padding='16,16,16,16'> "
 "<widget name='customOption1Checkbox' "
 "type='Checkbox' "
 "/> "
@@ -1235,55 +1232,57 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GlobalMenu' overlays='screen_center'> "
-"<layout type='vertical' padding='2,2,4,6' center='true' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' center='true'> "
 "<widget name='Title' "
-"width='160' "
-"height='4' "
+"width='210' "
+"height='Globals.Line.Height' "
 "/> "
 "<widget name='Version' "
-"width='160' "
-"height='4' "
+"width='210' "
+"height='Globals.Line.Height' "
 "/> "
-"<space size='1'/> "
+"<widget name='Resume' "
+"width='150' "
+"height='Globals.Button.Height' "
+"/> "
+"<space size='10'/> "
 "<widget name='Load' "
-"width='120' "
-"height='12' "
+"width='150' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='Save' "
-"width='120' "
-"height='12' "
+"width='150' "
+"height='Globals.Button.Height' "
 "/> "
-"<space size='1'/> "
+"<space size='10'/> "
 "<widget name='Options' "
-"width='120' "
-"height='12' "
+"width='150' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='Help' "
-"width='120' "
-"height='12' "
+"width='150' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='About' "
-"width='120' "
-"height='12' "
-"/> "
-"<space size='1'/> "
-"<widget name='Resume' "
-"width='120' "
-"height='12' "
+"width='150' "
+"height='Globals.Button.Height' "
 "/> "
+"<space size='10'/> "
 "<widget name='RTL' "
-"width='120' "
-"height='12' "
+"width='150' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='Quit' "
-"width='120' "
-"height='12' "
+"width='150' "
+"height='Globals.Button.Height' "
 "/> "
 "</layout> "
 "</dialog> "
 "<dialog name='GlobalConfig' overlays='screen_center'> "
 "<layout type='vertical' padding='8,8,8,8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='vertical' padding='0,0,0,0' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
 "<widget name='vcMusicText' "
 "type='OptionsLabel' "
 "/> "
@@ -1294,7 +1293,7 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
 "<widget name='vcSfxText' "
 "type='OptionsLabel' "
 "/> "
@@ -1305,7 +1304,7 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
 "<widget name='vcSpeechText' "
 "type='OptionsLabel' "
 "/> "
@@ -1316,34 +1315,33 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
-"<space size='110' /> "
+"</layout> "
+"<layout type='vertical' padding='24,24,24,24' center='true'> "
 "<widget name='vcMuteCheckbox' "
 "type='Checkbox' "
-"width='80' "
+"width='80'  "
 "/> "
 "</layout> "
-"<layout type='vertical' padding='0,0,0,0' spacing='1' center='true'> "
+"</layout> "
+"<space size='8' /> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
 "<widget name='subToggleDesc' "
 "type='OptionsLabel' "
 "/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='subToggleSpeechOnly' "
 "type='Radiobutton' "
-"width='90' "
+"width='100' "
 "/> "
 "<widget name='subToggleSubOnly' "
 "type='Radiobutton' "
-"width='90' "
+"width='100' "
 "/> "
 "<widget name='subToggleSubBoth' "
 "type='Radiobutton' "
-"width='90' "
+"width='100' "
 "/> "
 "</layout> "
-"</layout> "
-"<space size='2' /> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
 "<widget name='subSubtitleSpeedDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -1354,8 +1352,8 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<space size='16'/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='4'> "
+"<space size='60'/> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
 "<widget name='Keys' "
 "type='Button' "
 "/> "
@@ -1372,7 +1370,7 @@
 "<dialog name='FluidSynthSettings' overlays='GlobalOptions' shading='dim'> "
 "<layout type='vertical' padding='0,0,0,0'> "
 "<widget name='TabWidget'/> "
-"<layout type='horizontal' padding='8,8,8,8'> "
+"<layout type='horizontal' padding='16,16,16,16'> "
 "<space/> "
 "<widget name='ResetSettings' "
 "type='Button' "
@@ -1387,7 +1385,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='FluidSynthSettings_Chorus' overlays='Dialog.FluidSynthSettings.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -1450,7 +1448,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='FluidSynthSettings_Reverb' overlays='Dialog.FluidSynthSettings.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -1505,7 +1503,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='FluidSynthSettings_Misc' overlays='Dialog.FluidSynthSettings.TabWidget'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
 "<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='InterpolationText' "
 "type='OptionsLabel' "
@@ -1517,10 +1515,25 @@
 "</layout> "
 "</dialog> "
 "<dialog name='SaveLoadChooser' overlays='screen' inset='8' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,8' center='true'> "
-"<widget name='Title' height='Globals.Line.Height'/> "
+"<layout type='vertical' padding='8,8,8,32' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
+"<widget name='Title' "
+"height='Globals.Line.Height' "
+"/> "
+"<space/> "
+"<widget name='PageDisplay' "
+"width='200' "
+"height='Globals.Line.Height' "
+"/> "
+"</layout> "
+"<layout type='horizontal' padding='0,0,0,16' spacing='16'> "
 "<widget name='List' /> "
-"<layout type='horizontal' padding='0,0,16,0'> "
+"<widget name='Thumbnail' "
+"width='180' "
+"height='200' "
+"/> "
+"</layout> "
+"<layout type='horizontal' padding='0,0,0,0'> "
 "<widget name='ListSwitch' "
 "height='Globals.Line.Height' "
 "width='Globals.Line.Height' "
@@ -1533,7 +1546,7 @@
 "<widget name='Delete' "
 "type='Button' "
 "/> "
-"<space size='16'/> "
+"<space size='32'/> "
 "<widget name='Cancel' "
 "type='Button' "
 "/> "
@@ -1546,7 +1559,7 @@
 "<dialog name='SavenameDialog' overlays='screen_center'> "
 "<layout type='vertical' padding='8,8,8,8'> "
 "<widget name='DescriptionText' "
-"width='180' "
+"width='320' "
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='Description' "
@@ -1556,22 +1569,139 @@
 "<widget name='Cancel' "
 "type='Button' "
 "/> "
+"<space size='96'/> "
 "<widget name='Ok' "
 "type='Button' "
 "/> "
 "</layout> "
 "</layout> "
 "</dialog> "
-"<dialog name='ScummHelp' overlays='screen'> "
-"<layout type='vertical' padding='8,8,8,8'> "
+"<dialog name='RecorderDialog' overlays='screen' inset='8' shading='dim'> "
+"<layout type='vertical' padding='8,8,8,32' center='true'> "
 "<widget name='Title' "
+"height='Globals.Line.Height' "
+"/> "
+"<layout type='horizontal' padding='0,0,0,16' spacing='16'> "
+"<widget name='List' /> "
+"<layout type='vertical' padding='0,0,0,0'> "
+"<widget name='Thumbnail' "
 "width='180' "
+"height='170' "
+"/> "
+"<layout type='horizontal' padding='0,0,0,0'> "
+"<widget name='NextScreenShotButton' "
+"width='25' "
+"height='25' "
+"/> "
+"<widget name='currentScreenshot' "
+"width='125' "
+"height='25' "
+"textalign='center' "
+"/> "
+"<widget name='PreviousScreenShotButton' "
+"width='25' "
+"height='25' "
+"/> "
+"</layout> "
+"<widget name='Author' height='Globals.Line.Height' /> "
+"<widget name='Notes' height='Globals.Line.Height' /> "
+"</layout> "
+"</layout> "
+"<layout type='horizontal' padding='0,0,0,0'> "
+"<widget name='Delete' "
+"type='Button' "
+"/> "
+"<space size='16'/> "
+"<widget name='Cancel' "
+"type='Button' "
+"/> "
+"<space size='16'/> "
+"<widget name='Edit' "
+"type='Button' "
+"/> "
+"<widget name='Record' "
+"type='Button' "
+"/> "
+"<widget name='Playback' "
+"type='Button' "
+"/> "
+"</layout> "
+"</layout> "
+"</dialog> "
+"<dialog name='OnScreenDialog' overlays='screen_center'> "
+"<layout type='horizontal' spacing='5' padding='5,3,5,3' center='true'> "
+"<widget name='StopButton' "
+"width='32' "
+"height='32' "
+"/> "
+"<widget name='EditButton' "
+"width='32' "
+"height='32' "
+"/> "
+"<widget name='SwitchModeButton' "
+"width='32' "
+"height='32' "
+"/> "
+"<widget name='FastReplayButton' "
+"width='32' "
+"height='32' "
+"/> "
+"<widget name='TimeLabel' "
+"width='50' "
+"height='30' "
+"/> "
+"</layout> "
+"</dialog> "
+"<dialog name='EditRecordDialog' overlays='screen_center'> "
+"<layout type='vertical' padding='8,8,8,8' center='true'> "
+"<widget name='Title' "
+"width='320' "
+"height='Globals.Line.Height' "
+"/> "
+"<layout type='horizontal' spacing='5' padding='0,0,0,10'> "
+"<widget name='AuthorLabel' "
+"type='EditRecordLabel' "
+"/> "
+"<widget name='AuthorEdit' "
+"type='EditRecord' "
+"/> "
+"</layout> "
+"<layout type='horizontal' spacing='5' padding='0,0,0,10'> "
+"<widget name='NameLabel' "
+"type='EditRecordLabel' "
+"/> "
+"<widget name='NameEdit' "
+"type='EditRecord' "
+"/> "
+"</layout> "
+"<layout type='horizontal' spacing='5' padding='0,0,0,10'> "
+"<widget name='NotesLabel' "
+"type='EditRecordLabel' "
+"/> "
+"<widget name='NotesEdit' "
+"type='EditRecord' "
+"/> "
+"</layout> "
+"<layout type='horizontal' spacing='5' padding='0,0,0,10'> "
+"<widget name='Cancel' "
+"type='Button' "
+"/> "
+"<widget name='OK' "
+"type='Button' "
+"/> "
+"</layout> "
+"</layout> "
+"</dialog> "
+"<dialog name='ScummHelp' overlays='screen_center'> "
+"<layout type='vertical' padding='8,8,8,8' center='true'> "
+"<widget name='Title' "
+"width='320' "
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='HelpText' "
-"height='140' "
+"height='200' "
 "/> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='horizontal' padding='0,0,16,0'> "
 "<widget name='Prev' "
 "type='Button' "
 "/> "
@@ -1588,7 +1718,7 @@
 "<dialog name='LoomTownsDifficultyDialog' overlays='screen_center'> "
 "<layout type='vertical' padding='8,8,8,8' center='true'> "
 "<widget name='Description1' "
-"width='280' "
+"width='320' "
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='Description2' "
@@ -1606,20 +1736,20 @@
 "</layout> "
 "</dialog> "
 "<dialog name='MassAdd' overlays='screen_center' shading='dim'> "
-"<layout type='vertical' padding='4,4,16,4' center='true'> "
+"<layout type='vertical' padding='8,8,32,8' center='true'> "
 "<widget name='DirProgressText' "
-"width='280' "
+"width='480' "
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='GameProgressText' "
-"width='280' "
+"width='480' "
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='GameList' "
-"width='280' "
-"height='100' "
+"width='480' "
+"height='250' "
 "/> "
-"<layout type='horizontal' padding='4,4,4,4'> "
+"<layout type='horizontal' padding='8,8,8,8'> "
 "<widget name='Ok' "
 "type='Button' "
 "/> "
@@ -1630,20 +1760,20 @@
 "</layout> "
 "</dialog> "
 "<dialog name='KeyMapper' overlays='screen_center' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,8' spacing='10' center='true'> "
+"<layout type='vertical' padding='8,8,32,8' spacing='10' center='true'> "
 "<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='PopupDesc' "
 "type='OptionsLabel' "
 "/> "
 "<widget name='Popup' "
 "type='PopUp' "
-"width='150' "
+"width='400' "
 "height='Globals.Line.Height' "
 "/> "
 "</layout> "
 "<widget name='KeymapArea' "
-"width='300' "
-"height='120' "
+"width='600' "
+"height='280' "
 "/> "
 "<widget name='Close' "
 "type='Button' "
@@ -1651,142 +1781,144 @@
 "</layout> "
 "</dialog> "
 "<dialog name='Predictive' overlays='screen_center'> "
-"<layout type='vertical' padding='1,1,1,1' center='true'> "
+"<layout type='vertical' padding='5,5,5,5' center='true'> "
 "<widget name='Headline' "
 "height='Globals.Line.Height' "
-"width='150' "
+"width='210' "
 "textalign='center' "
 "/> "
-"<layout type='horizontal' padding='3,3,3,3'> "
+"<layout type='horizontal' padding='5,5,5,5'> "
 "<widget name='Word' "
-"width='120' "
+"width='190' "
 "height='Globals.Button.Height' "
 "/> "
 "<widget name='Delete' "
 "width='20' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "</layout> "
+"<space size='5' /> "
 "<layout type='horizontal' padding='3,3,3,3'> "
 "<widget name='Button1' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='Button2' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='Button3' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "</layout> "
 "<layout type='horizontal' padding='3,3,3,3'> "
 "<widget name='Button4' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='Button5' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='Button6' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "</layout> "
 "<layout type='horizontal' padding='3,3,3,3'> "
 "<widget name='Button7' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='Button8' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='Button9' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='3,3,3,0'> "
+"<layout type='horizontal' padding='3,3,3,3'> "
 "<widget name='Pre' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='Button0' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='Next' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "</layout> "
-"<space size='3' /> "
-"<layout type='horizontal' padding='3,3,0,3'> "
+"<space size='5' /> "
+"<layout type='horizontal' padding='3,3,3,3'> "
 "<widget name='Add' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
+"<space size='22'/> "
 "<widget name='Cancel' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "<widget name='OK' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Predictive.Button.Height' "
+"height='Globals.Button.Height' "
 "/> "
 "</layout> "
 "</layout> "
 "</dialog> "
 "</layout_info> "
-"<layout_info resolution='y>399'> "
+"<layout_info resolution='y<400'> "
 "<globals> "
-"<def var='Line.Height' value='16' /> "
-"<def var='Font.Height' value='16' /> "
-"<def var='About.OuterBorder' value='80'/> "
-"<def var='Layout.Spacing' value='8' /> "
+"<def var='Line.Height' value='12' /> "
+"<def var='Font.Height' value='10' /> "
+"<def var='About.OuterBorder' value='10'/> "
+"<def var='Layout.Spacing' value='8'/> "
 "<def var='ShowLauncherLogo' value='0'/> "
 "<def var='ShowGlobalMenuLogo' value='0'/> "
 "<def var='ShowSearchPic' value='0'/> "
 "<def var='ShowChooserPics' value='0'/> "
-"<def var='ShowChooserPageDisplay' value='1'/> "
-"<def var='SaveLoadChooser.ExtInfo.Visible' value='1'/> "
-"<def var='KeyMapper.Spacing' value='10'/> "
-"<def var='KeyMapper.LabelWidth' value='100'/> "
-"<def var='KeyMapper.ButtonWidth' value='80'/> "
-"<def var='Tooltip.MaxWidth' value='200'/> "
-"<def var='Tooltip.XDelta' value='16'/> "
-"<def var='Tooltip.YDelta' value='16'/> "
-"<def var='Predictive.Button.Width' value='60' /> "
+"<def var='ShowChooserPageDisplay' value='0'/> "
+"<def var='SaveLoadChooser.ExtInfo.Visible' value='0'/> "
+"<def var='RecorderDialog.ExtInfo.Visible' value='0'/> "
+"<def var='OnScreenDialog.ShowPics' value='0'/> "
+"<def var='KeyMapper.Spacing' value='5'/> "
+"<def var='KeyMapper.LabelWidth' value='80'/> "
+"<def var='KeyMapper.ButtonWidth' value='60'/> "
+"<def var='Tooltip.MaxWidth' value='70'/> "
+"<def var='Tooltip.XDelta' value='8'/> "
+"<def var='Tooltip.YDelta' value='8'/> "
+"<def var='Predictive.Button.Width' value='45' /> "
+"<def var='Predictive.Button.Height' value='15' /> "
+"<widget name='Button' "
+"size='72,16' "
+"/> "
+"<widget name='Slider' "
+"size='85,12' "
+"/> "
 "<widget name='OptionsLabel' "
 "size='110,Globals.Line.Height' "
 "textalign='right' "
 "/> "
 "<widget name='SmallLabel' "
-"size='24,Globals.Line.Height' "
-"/> "
-"<widget name='ShortOptionsLabel' "
-"size='60,Globals.Line.Height' "
-"/> "
-"<widget name='Button' "
-"size='108,24' "
-"/> "
-"<widget name='Slider' "
-"size='128,18' "
+"size='18,Globals.Line.Height' "
 "/> "
 "<widget name='PopUp' "
-"size='-1,19' "
+"size='-1,15' "
 "/> "
 "<widget name='Checkbox' "
-"size='-1,14' "
+"size='-1,Globals.Line.Height' "
 "/> "
 "<widget name='Radiobutton' "
 "size='-1,Globals.Line.Height' "
 "/> "
 "<widget name='ListWidget' "
-"padding='5,0,8,0' "
+"padding='5,0,0,0' "
 "/> "
 "<widget name='PopUpWidget' "
 "padding='7,5,0,0' "
@@ -1798,29 +1930,35 @@
 "padding='7,5,5,5' "
 "/> "
 "<widget name='Scrollbar' "
-"size='15,0' "
+"size='9,0' "
 "/> "
 "<widget name='TabWidget.Tab' "
-"size='75,27' "
-"padding='0,0,8,0' "
+"size='45,16' "
+"padding='0,0,2,0' "
 "/> "
 "<widget name='TabWidget.Body' "
-"padding='0,0,0,0' "
+"padding='0,0,0,-8' "
 "/> "
 "<widget name='TabWidget.NavButton' "
-"size='15,18' "
-"padding='0,3,4,0' "
+"size='32,18' "
+"padding='0,0,1,0' "
+"/> "
+"<widget name='EditRecordLabel' "
+"size='60,Globals.Line.Height' "
+"/> "
+"<widget name='EditRecord' "
+"size='120,15' "
 "/> "
 "</globals> "
 "<dialog name='Launcher' overlays='screen'> "
-"<layout type='vertical' center='true' padding='16,16,8,8'> "
+"<layout type='vertical' center='true' padding='6,6,2,2'> "
 "<widget name='Version' "
 "height='Globals.Line.Height' "
 "textalign='center' "
 "/> "
-"<layout type='horizontal' spacing='5' padding='10,0,0,0'> "
+"<layout type='horizontal' spacing='5' padding='0,0,0,0'> "
 "<widget name='SearchDesc' "
-"width='60' "
+"width='50' "
 "height='Globals.Line.Height' "
 "textalign='right' "
 "/> "
@@ -1835,39 +1973,38 @@
 "<space /> "
 "</layout> "
 "<widget name='GameList'/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
 "<widget name='LoadGameButton' "
-"height='20' "
+"height='12' "
 "/> "
 "<widget name='AddGameButton' "
-"height='20' "
+"height='12' "
 "/> "
 "<widget name='EditGameButton' "
-"height='20' "
+"height='12' "
 "/> "
 "<widget name='RemoveGameButton' "
-"height='20' "
+"height='12' "
 "/> "
 "</layout> "
-"<space size='4'/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
 "<widget name='QuitButton' "
-"height='20' "
+"height='12' "
 "/> "
 "<widget name='AboutButton' "
-"height='20' "
+"height='12' "
 "/> "
 "<widget name='OptionsButton' "
-"height='20' "
+"height='12' "
 "/> "
 "<widget name='StartButton' "
-"height='20' "
+"height='12' "
 "/> "
 "</layout> "
 "</layout> "
 "</dialog> "
-"<dialog name='Browser' overlays='Dialog.Launcher.GameList' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,8'> "
+"<dialog name='Browser' overlays='screen' inset='8' shading='dim'> "
+"<layout type='vertical' padding='8,8,0,4'> "
 "<widget name='Headline' "
 "height='Globals.Line.Height' "
 "/> "
@@ -1875,7 +2012,7 @@
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='List'/> "
-"<layout type='vertical' padding='0,0,16,0'> "
+"<layout type='vertical' padding='0,0,8,0'> "
 "<widget name='Hidden' "
 "type='Checkbox' "
 "/> "
@@ -1894,10 +2031,10 @@
 "</layout> "
 "</layout> "
 "</dialog> "
-"<dialog name='GlobalOptions' overlays='Dialog.Launcher.GameList' shading='dim'> "
+"<dialog name='GlobalOptions' overlays='screen' inset='16' shading='dim'> "
 "<layout type='vertical' padding='0,0,0,0'> "
 "<widget name='TabWidget'/> "
-"<layout type='horizontal' padding='16,16,16,16'> "
+"<layout type='horizontal' padding='8,8,8,8'> "
 "<space/> "
 "<widget name='Cancel' "
 "type='Button' "
@@ -1910,7 +2047,7 @@
 "</dialog> "
 "<dialog name='GlobalOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> "
 "<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='grModePopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -1918,7 +2055,7 @@
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='grRenderPopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -1936,7 +2073,7 @@
 "</dialog> "
 "<dialog name='GlobalOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> "
 "<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='auMidiPopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -1944,7 +2081,7 @@
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='auOPLPopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -1952,7 +2089,7 @@
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='auSampleRatePopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -1960,7 +2097,7 @@
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='3' center='true'> "
 "<widget name='subToggleDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -1974,7 +2111,7 @@
 "type='Radiobutton' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='subSubtitleSpeedDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -1988,9 +2125,8 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GlobalOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='horizontal' padding='16,16,16,16' spacing='8'> "
-"<layout type='vertical' padding='0,0,0,0' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='vcMusicText' "
 "type='OptionsLabel' "
 "/> "
@@ -2001,7 +2137,7 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='vcSfxText' "
 "type='OptionsLabel' "
 "/> "
@@ -2012,7 +2148,7 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='vcSpeechText' "
 "type='OptionsLabel' "
 "/> "
@@ -2023,8 +2159,8 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"</layout> "
-"<layout type='vertical' padding='24,0,24,0' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<space size='110' /> "
 "<widget name='vcMuteCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -2032,8 +2168,8 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GlobalOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='vertical' padding='16,16,16,16' spacing='6'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='auPrefGmPopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -2041,7 +2177,7 @@
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
 "<widget name='mcFontButton' "
 "type='Button' "
 "/> "
@@ -2056,7 +2192,7 @@
 "<widget name='mcMixedCheckbox' "
 "type='Checkbox' "
 "/> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='mcMidiGainText' "
 "type='OptionsLabel' "
 "/> "
@@ -2069,14 +2205,14 @@
 "/> "
 "</layout> "
 "<widget name='mcFluidSynthSettings' "
-"width='200' "
+"width='150' "
 "height='Globals.Button.Height' "
 "/> "
 "</layout> "
 "</dialog> "
 "<dialog name='GlobalOptions_MT32' overlays='Dialog.GlobalOptions.TabWidget'> "
 "<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='auPrefMt32PopupDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -2094,7 +2230,7 @@
 "</dialog> "
 "<dialog name='GlobalOptions_Paths' overlays='Dialog.GlobalOptions.TabWidget'> "
 "<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
 "<widget name='SaveButton' "
 "type='Button' "
 "/> "
@@ -2106,7 +2242,7 @@
 "width='Globals.Line.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
 "<widget name='ThemeButton' "
 "type='Button' "
 "/> "
@@ -2118,7 +2254,7 @@
 "width='Globals.Line.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
 "<widget name='ExtraButton' "
 "type='Button' "
 "/> "
@@ -2142,7 +2278,7 @@
 "</dialog> "
 "<dialog name='GlobalOptions_Misc' overlays='Dialog.GlobalOptions.TabWidget'> "
 "<layout type='vertical' padding='16,16,16,16' spacing='8'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16'> "
 "<widget name='ThemeButton' "
 "type='Button' "
 "/> "
@@ -2150,25 +2286,31 @@
 "height='Globals.Line.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='RendererPopupDesc' "
-"type='OptionsLabel' "
+"width='80' "
+"height='Globals.Line.Height' "
+"textalign='right' "
 "/> "
 "<widget name='RendererPopup' "
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='AutosavePeriodPopupDesc' "
-"type='OptionsLabel' "
+"width='80' "
+"height='Globals.Line.Height' "
+"textalign='right' "
 "/> "
 "<widget name='AutosavePeriodPopup' "
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='GuiLanguagePopupDesc' "
-"type='OptionsLabel' "
+"width='80' "
+"height='Globals.Line.Height' "
+"textalign='right' "
 "/> "
 "<widget name='GuiLanguagePopup' "
 "type='PopUp' "
@@ -2203,10 +2345,10 @@
 "</layout> "
 "</layout> "
 "</dialog> "
-"<dialog name='GameOptions' overlays='Dialog.Launcher.GameList' shading='dim'> "
+"<dialog name='GameOptions' overlays='screen' inset='16' shading='dim'> "
 "<layout type='vertical' padding='0,0,0,0' spacing='16'> "
 "<widget name='TabWidget'/> "
-"<layout type='horizontal' padding='16,16,16,4'> "
+"<layout type='horizontal' padding='8,8,8,8'> "
 "<space/> "
 "<widget name='Cancel' "
 "type='Button' "
@@ -2218,7 +2360,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -2226,7 +2368,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -2234,7 +2376,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -2242,7 +2384,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_MT32' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -2250,7 +2392,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -2258,34 +2400,43 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Game' overlays='Dialog.GameOptions.TabWidget' shading='dim'> "
-"<layout type='vertical' padding='16,16,16,16'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='vertical' padding='8,8,8,8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='Id' "
-"type='OptionsLabel' "
+"width='35' "
+"height='Globals.Line.Height' "
+"textalign='right' "
 "/> "
 "<widget name='Domain' "
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='Name' "
-"type='OptionsLabel' "
+"width='35' "
+"height='Globals.Line.Height' "
+"textalign='right' "
 "/> "
 "<widget name='Desc' "
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<space size='8'/> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='LangPopupDesc' "
-"type='OptionsLabel' "
+"width='60' "
+"height='Globals.Line.Height' "
+"textalign='right' "
 "/> "
 "<widget name='LangPopup' "
 "type='PopUp' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='PlatformPopupDesc' "
-"type='OptionsLabel' "
+"width='60' "
+"height='Globals.Line.Height' "
+"textalign='right' "
 "/> "
 "<widget name='PlatformPopup' "
 "type='PopUp' "
@@ -2294,8 +2445,8 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Paths' overlays='Dialog.GameOptions.TabWidget' shading='dim'> "
-"<layout type='vertical' padding='16,16,16,16'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='vertical' padding='8,8,8,8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
 "<widget name='Savepath' "
 "type='Button' "
 "/> "
@@ -2307,7 +2458,7 @@
 "width='Globals.Line.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
 "<widget name='Extrapath' "
 "type='Button' "
 "/> "
@@ -2319,7 +2470,7 @@
 "width='Globals.Line.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> "
 "<widget name='Gamepath' "
 "type='Button' "
 "/> "
@@ -2330,7 +2481,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GameOptions_Engine' overlays='Dialog.GameOptions.TabWidget' shading='dim'> "
-"<layout type='vertical' padding='16,16,16,16'> "
+"<layout type='vertical' padding='8,8,8,8'> "
 "<widget name='customOption1Checkbox' "
 "type='Checkbox' "
 "/> "
@@ -2355,57 +2506,55 @@
 "</layout> "
 "</dialog> "
 "<dialog name='GlobalMenu' overlays='screen_center'> "
-"<layout type='vertical' padding='16,16,16,16' center='true'> "
+"<layout type='vertical' padding='2,2,4,6' center='true' spacing='6'> "
 "<widget name='Title' "
-"width='210' "
-"height='Globals.Line.Height' "
+"width='160' "
+"height='4' "
 "/> "
 "<widget name='Version' "
-"width='210' "
-"height='Globals.Line.Height' "
-"/> "
-"<widget name='Resume' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='160' "
+"height='4' "
 "/> "
-"<space size='10'/> "
+"<space size='1'/> "
 "<widget name='Load' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='120' "
+"height='12' "
 "/> "
 "<widget name='Save' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='120' "
+"height='12' "
 "/> "
-"<space size='10'/> "
+"<space size='1'/> "
 "<widget name='Options' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='120' "
+"height='12' "
 "/> "
 "<widget name='Help' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='120' "
+"height='12' "
 "/> "
 "<widget name='About' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='120' "
+"height='12' "
+"/> "
+"<space size='1'/> "
+"<widget name='Resume' "
+"width='120' "
+"height='12' "
 "/> "
-"<space size='10'/> "
 "<widget name='RTL' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='120' "
+"height='12' "
 "/> "
 "<widget name='Quit' "
-"width='150' "
-"height='Globals.Button.Height' "
+"width='120' "
+"height='12' "
 "/> "
 "</layout> "
 "</dialog> "
 "<dialog name='GlobalConfig' overlays='screen_center'> "
 "<layout type='vertical' padding='8,8,8,8'> "
-"<layout type='horizontal' padding='0,0,0,0'> "
-"<layout type='vertical' padding='0,0,0,0' center='true'> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='vcMusicText' "
 "type='OptionsLabel' "
 "/> "
@@ -2416,7 +2565,7 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='vcSfxText' "
 "type='OptionsLabel' "
 "/> "
@@ -2427,7 +2576,7 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='8'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='vcSpeechText' "
 "type='OptionsLabel' "
 "/> "
@@ -2438,33 +2587,34 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"</layout> "
-"<layout type='vertical' padding='24,24,24,24' center='true'> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
+"<space size='110' /> "
 "<widget name='vcMuteCheckbox' "
 "type='Checkbox' "
-"width='80'  "
+"width='80' "
 "/> "
 "</layout> "
-"</layout> "
-"<space size='8' /> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"<layout type='vertical' padding='0,0,0,0' spacing='1' center='true'> "
 "<widget name='subToggleDesc' "
 "type='OptionsLabel' "
 "/> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='subToggleSpeechOnly' "
 "type='Radiobutton' "
-"width='100' "
+"width='90' "
 "/> "
 "<widget name='subToggleSubOnly' "
 "type='Radiobutton' "
-"width='100' "
+"width='90' "
 "/> "
 "<widget name='subToggleSubBoth' "
 "type='Radiobutton' "
-"width='100' "
+"width='90' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"</layout> "
+"<space size='2' /> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> "
 "<widget name='subSubtitleSpeedDesc' "
 "type='OptionsLabel' "
 "/> "
@@ -2475,8 +2625,8 @@
 "type='SmallLabel' "
 "/> "
 "</layout> "
-"<space size='60'/> "
-"<layout type='horizontal' padding='0,0,0,0' spacing='10'> "
+"<space size='16'/> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='4'> "
 "<widget name='Keys' "
 "type='Button' "
 "/> "
@@ -2493,7 +2643,7 @@
 "<dialog name='FluidSynthSettings' overlays='GlobalOptions' shading='dim'> "
 "<layout type='vertical' padding='0,0,0,0'> "
 "<widget name='TabWidget'/> "
-"<layout type='horizontal' padding='16,16,16,16'> "
+"<layout type='horizontal' padding='8,8,8,8'> "
 "<space/> "
 "<widget name='ResetSettings' "
 "type='Button' "
@@ -2508,7 +2658,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='FluidSynthSettings_Chorus' overlays='Dialog.FluidSynthSettings.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -2571,7 +2721,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='FluidSynthSettings_Reverb' overlays='Dialog.FluidSynthSettings.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
 "<widget name='EnableTabCheckbox' "
 "type='Checkbox' "
 "/> "
@@ -2626,7 +2776,7 @@
 "</layout> "
 "</dialog> "
 "<dialog name='FluidSynthSettings_Misc' overlays='Dialog.FluidSynthSettings.TabWidget'> "
-"<layout type='vertical' padding='16,16,16,16' spacing='8'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='6'> "
 "<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='InterpolationText' "
 "type='OptionsLabel' "
@@ -2638,25 +2788,10 @@
 "</layout> "
 "</dialog> "
 "<dialog name='SaveLoadChooser' overlays='screen' inset='8' shading='dim'> "
-"<layout type='vertical' padding='8,8,8,32' center='true'> "
-"<layout type='horizontal' padding='0,0,0,0'> "
-"<widget name='Title' "
-"height='Globals.Line.Height' "
-"/> "
-"<space/> "
-"<widget name='PageDisplay' "
-"width='200' "
-"height='Globals.Line.Height' "
-"/> "
-"</layout> "
-"<layout type='horizontal' padding='0,0,0,16' spacing='16'> "
+"<layout type='vertical' padding='8,8,8,8' center='true'> "
+"<widget name='Title' height='Globals.Line.Height'/> "
 "<widget name='List' /> "
-"<widget name='Thumbnail' "
-"width='180' "
-"height='200' "
-"/> "
-"</layout> "
-"<layout type='horizontal' padding='0,0,0,0'> "
+"<layout type='horizontal' padding='0,0,16,0'> "
 "<widget name='ListSwitch' "
 "height='Globals.Line.Height' "
 "width='Globals.Line.Height' "
@@ -2669,7 +2804,7 @@
 "<widget name='Delete' "
 "type='Button' "
 "/> "
-"<space size='32'/> "
+"<space size='16'/> "
 "<widget name='Cancel' "
 "type='Button' "
 "/> "
@@ -2682,7 +2817,7 @@
 "<dialog name='SavenameDialog' overlays='screen_center'> "
 "<layout type='vertical' padding='8,8,8,8'> "
 "<widget name='DescriptionText' "
-"width='320' "
+"width='180' "
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='Description' "
@@ -2692,23 +2827,114 @@
 "<widget name='Cancel' "
 "type='Button' "
 "/> "
-"<space size='96'/> "
 "<widget name='Ok' "
 "type='Button' "
 "/> "
 "</layout> "
 "</layout> "
 "</dialog> "
-"<dialog name='ScummHelp' overlays='screen_center'> "
+"<dialog name='RecorderDialog' overlays='screen' inset='8' shading='dim'> "
+"<layout type='vertical' padding='8,8,8,4' center='true'> "
+"<widget name='Title' "
+"height='Globals.Line.Height' "
+"/> "
+"<widget name='List' /> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='2'> "
+"<widget name='Edit' "
+"type='Button' "
+"/> "
+"<space /> "
+"<widget name='Record' "
+"type='Button' "
+"/> "
+"</layout> "
+"<layout type='horizontal' padding='0,0,0,0' spacing='2'> "
+"<widget name='Delete' "
+"type='Button' "
+"/> "
+"<space /> "
+"<widget name='Cancel' "
+"type='Button' "
+"/> "
+"<widget name='Playback' "
+"type='Button' "
+"/> "
+"</layout> "
+"</layout> "
+"</dialog> "
+"<dialog name='OnScreenDialog' overlays='screen_center'> "
+"<layout type='horizontal' spacing='5' padding='3,2,3,2' center='true'> "
+"<widget name='StopButton' "
+"width='16' "
+"height='16' "
+"/> "
+"<widget name='EditButton' "
+"width='16' "
+"height='16' "
+"/> "
+"<widget name='SwitchModeButton' "
+"width='16' "
+"height='16' "
+"/> "
+"<widget name='FastReplayButton' "
+"width='16' "
+"height='16' "
+"/> "
+"<widget name='TimeLabel' "
+"width='50' "
+"height='16' "
+"/> "
+"</layout> "
+"</dialog> "
+"<dialog name='EditRecordDialog' overlays='screen_center'> "
 "<layout type='vertical' padding='8,8,8,8' center='true'> "
 "<widget name='Title' "
-"width='320' "
+"height='Globals.Line.Height' "
+"/> "
+"<layout type='horizontal' spacing='5' padding='0,0,0,10'> "
+"<widget name='AuthorLabel' "
+"type='EditRecordLabel' "
+"/> "
+"<widget name='AuthorEdit' "
+"type='EditRecord' "
+"/> "
+"</layout> "
+"<layout type='horizontal' spacing='5' padding='0,0,0,10'> "
+"<widget name='NameLabel' "
+"type='EditRecordLabel' "
+"/> "
+"<widget name='NameEdit' "
+"type='EditRecord' "
+"/> "
+"</layout> "
+"<layout type='horizontal' spacing='5' padding='0,0,0,10'> "
+"<widget name='NotesLabel' "
+"type='EditRecordLabel' "
+"/> "
+"<widget name='NotesEdit' "
+"type='EditRecord' "
+"/> "
+"</layout> "
+"<layout type='horizontal' spacing='5' padding='0,0,0,0'> "
+"<widget name='Cancel' "
+"type='Button' "
+"/> "
+"<widget name='OK' "
+"type='Button' "
+"/> "
+"</layout> "
+"</layout> "
+"</dialog> "
+"<dialog name='ScummHelp' overlays='screen'> "
+"<layout type='vertical' padding='8,8,8,8'> "
+"<widget name='Title' "
+"width='180' "
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='HelpText' "
-"height='200' "
+"height='140' "
 "/> "
-"<layout type='horizontal' padding='0,0,16,0'> "
+"<layout type='horizontal' padding='0,0,0,0'> "
 "<widget name='Prev' "
 "type='Button' "
 "/> "
@@ -2725,7 +2951,7 @@
 "<dialog name='LoomTownsDifficultyDialog' overlays='screen_center'> "
 "<layout type='vertical' padding='8,8,8,8' center='true'> "
 "<widget name='Description1' "
-"width='320' "
+"width='280' "
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='Description2' "
@@ -2743,20 +2969,20 @@
 "</layout> "
 "</dialog> "
 "<dialog name='MassAdd' overlays='screen_center' shading='dim'> "
-"<layout type='vertical' padding='8,8,32,8' center='true'> "
+"<layout type='vertical' padding='4,4,16,4' center='true'> "
 "<widget name='DirProgressText' "
-"width='480' "
+"width='280' "
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='GameProgressText' "
-"width='480' "
+"width='280' "
 "height='Globals.Line.Height' "
 "/> "
 "<widget name='GameList' "
-"width='480' "
-"height='250' "
+"width='280' "
+"height='100' "
 "/> "
-"<layout type='horizontal' padding='8,8,8,8'> "
+"<layout type='horizontal' padding='4,4,4,4'> "
 "<widget name='Ok' "
 "type='Button' "
 "/> "
@@ -2767,20 +2993,20 @@
 "</layout> "
 "</dialog> "
 "<dialog name='KeyMapper' overlays='screen_center' shading='dim'> "
-"<layout type='vertical' padding='8,8,32,8' spacing='10' center='true'> "
+"<layout type='vertical' padding='8,8,8,8' spacing='10' center='true'> "
 "<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> "
 "<widget name='PopupDesc' "
 "type='OptionsLabel' "
 "/> "
 "<widget name='Popup' "
 "type='PopUp' "
-"width='400' "
+"width='150' "
 "height='Globals.Line.Height' "
 "/> "
 "</layout> "
 "<widget name='KeymapArea' "
-"width='600' "
-"height='280' "
+"width='300' "
+"height='120' "
 "/> "
 "<widget name='Close' "
 "type='Button' "
@@ -2788,93 +3014,91 @@
 "</layout> "
 "</dialog> "
 "<dialog name='Predictive' overlays='screen_center'> "
-"<layout type='vertical' padding='5,5,5,5' center='true'> "
+"<layout type='vertical' padding='1,1,1,1' center='true'> "
 "<widget name='Headline' "
 "height='Globals.Line.Height' "
-"width='210' "
+"width='150' "
 "textalign='center' "
 "/> "
-"<layout type='horizontal' padding='5,5,5,5'> "
+"<layout type='horizontal' padding='3,3,3,3'> "
 "<widget name='Word' "
-"width='190' "
+"width='120' "
 "height='Globals.Button.Height' "
 "/> "
 "<widget name='Delete' "
 "width='20' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "</layout> "
-"<space size='5' /> "
 "<layout type='horizontal' padding='3,3,3,3'> "
 "<widget name='Button1' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "<widget name='Button2' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "<widget name='Button3' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "</layout> "
 "<layout type='horizontal' padding='3,3,3,3'> "
 "<widget name='Button4' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "<widget name='Button5' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "<widget name='Button6' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "</layout> "
 "<layout type='horizontal' padding='3,3,3,3'> "
 "<widget name='Button7' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "<widget name='Button8' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "<widget name='Button9' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "</layout> "
-"<layout type='horizontal' padding='3,3,3,3'> "
+"<layout type='horizontal' padding='3,3,3,0'> "
 "<widget name='Pre' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "<widget name='Button0' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "<widget name='Next' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "</layout> "
-"<space size='5' /> "
-"<layout type='horizontal' padding='3,3,3,3'> "
+"<space size='3' /> "
+"<layout type='horizontal' padding='3,3,0,3'> "
 "<widget name='Add' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
-"<space size='22'/> "
 "<widget name='Cancel' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "<widget name='OK' "
 "width='Globals.Predictive.Button.Width' "
-"height='Globals.Button.Height' "
+"height='Globals.Predictive.Button.Height' "
 "/> "
 "</layout> "
 "</layout> "
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index 297ff20..4154c6c 100644
Binary files a/gui/themes/scummclassic.zip and b/gui/themes/scummclassic.zip differ
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 180e8fb..5fd2d6f 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -36,6 +36,9 @@
 		<def var = 'ShowChooserPageDisplay' value = '1'/>
 
 		<def var = 'SaveLoadChooser.ExtInfo.Visible' value = '1'/>
+		<def var = 'RecorderDialog.ExtInfo.Visible' value = '1'/>
+
+		<def var = 'OnScreenDialog.ShowPics' value = '0'/>
 
 		<def var = 'KeyMapper.Spacing' value = '10'/>
 		<def var = 'KeyMapper.LabelWidth' value = '100'/>
@@ -101,6 +104,12 @@
 				size = '15, 18'
 				padding = '0, 3, 4, 0'
 		/>
+		<widget name = 'EditRecordLabel'
+				size = '60, 25'
+		/>
+		<widget name = 'EditRecord'
+				size = '240, 25'
+		/>
 	</globals>
 
 	<dialog name = 'Launcher' overlays = 'screen'>
@@ -1019,6 +1028,125 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'RecorderDialog' overlays = 'screen' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 32' center = 'true'>
+			<widget name = 'Title'
+					height = 'Globals.Line.Height'
+			/>
+			<layout type = 'horizontal' padding = '0, 0, 0, 16' spacing = '16'>
+				<widget name = 'List' />
+				<layout type = 'vertical' padding = '0, 0, 0, 0'>
+					<widget name = 'Thumbnail'
+							width = '180'
+							height = '170'
+					/>
+					<layout type = 'horizontal' padding = '0, 0, 0, 0'>	
+						<widget name = 'NextScreenShotButton'
+								width = '25'
+								height = '25'
+						/>
+						<widget name = 'currentScreenshot'
+								width = '125'
+								height = '25'
+								textalign = 'center'
+						/>
+						<widget name = 'PreviousScreenShotButton'
+								width = '25'
+								height = '25'
+						/>
+					</layout>
+					<widget name = 'Author' height = 'Globals.Line.Height' />
+					<widget name = 'Notes' height = 'Globals.Line.Height' />
+				</layout>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0'>
+				<widget name = 'Delete'
+						type = 'Button'
+				/>
+				<space size = '16'/>
+				<widget name = 'Cancel'
+						type = 'Button'
+				/>
+				<space size = '16'/>
+				<widget name = 'Edit'
+						type = 'Button'
+				/>
+				<widget name = 'Record'
+						type = 'Button'
+				/>
+				<widget name = 'Playback'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'OnScreenDialog' overlays = 'screen_center'>
+		<layout type = 'horizontal'  spacing = '5' padding = '5, 3, 5, 3' center = 'true'>
+			<widget name = 'StopButton'
+				width = '32'
+				height = '32'
+			/>
+			<widget name = 'EditButton'
+				width = '32'
+				height = '32'
+			/>
+			<widget name = 'SwitchModeButton'
+				width = '32'
+				height = '32'
+			/>
+			<widget name = 'FastReplayButton'
+				width = '32'
+				height = '32'
+			/>
+			<widget name = 'TimeLabel'
+				width = '50'
+				height = '30'
+			/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'EditRecordDialog' overlays = 'screen_center'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'>
+			<widget name = 'Title'
+					width = '320'
+					height = 'Globals.Line.Height'
+			/>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'AuthorLabel'
+						type = 'EditRecordLabel'
+				/>		
+				<widget name = 'AuthorEdit'
+						type = 'EditRecord'
+				/>		
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'NameLabel'
+						type = 'EditRecordLabel'
+				/>		
+				<widget name = 'NameEdit'
+						type = 'EditRecord'
+				/>
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'NotesLabel'
+						type = 'EditRecordLabel'
+				/>
+				<widget name = 'NotesEdit'
+						type = 'EditRecord'
+				/>
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'Cancel'
+						type = 'Button'
+				/>
+				<widget name = 'OK'
+						type = 'Button'
+				/>			
+			</layout>
+		</layout>
+	</dialog>
+	
 	<dialog name = 'ScummHelp' overlays = 'screen_center'>
 		<layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'>
 			<widget name = 'Title'
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index 8bb03de..802998d 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -37,6 +37,9 @@
 		<def var = 'ShowChooserPageDisplay' value = '0'/>
 
 		<def var = 'SaveLoadChooser.ExtInfo.Visible' value = '0'/>
+		<def var = 'RecorderDialog.ExtInfo.Visible' value = '0'/>
+
+		<def var = 'OnScreenDialog.ShowPics' value = '0'/>
 
 		<def var = 'KeyMapper.Spacing' value = '5'/>
 		<def var = 'KeyMapper.LabelWidth' value = '80'/>
@@ -99,6 +102,12 @@
 				size = '32, 18'
 				padding = '0, 0, 1, 0'
 		/>
+		<widget name = 'EditRecordLabel'
+				size = '60, Globals.Line.Height'
+		/>
+		<widget name = 'EditRecord'
+				size = '120, 15'
+		/>
 	</globals>
 
 	<dialog name = 'Launcher' overlays = 'screen'>
@@ -1013,6 +1022,101 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'RecorderDialog' overlays = 'screen' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 4' center = 'true'>
+			<widget name = 'Title'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'List' />
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '2'>
+			  <widget name = 'Edit'
+					  type = 'Button'
+					  />
+			  <space />
+			  <widget name = 'Record'
+					  type = 'Button'
+					  />
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '2'>
+			  <widget name = 'Delete'
+					  type = 'Button'
+					  />
+			  <space />
+			  <widget name = 'Cancel'
+					  type = 'Button'
+					  />
+			  <widget name = 'Playback'
+					  type = 'Button'
+					  />
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'OnScreenDialog' overlays = 'screen_center'>
+		<layout type = 'horizontal'  spacing = '5' padding = '3, 2, 3, 2' center = 'true'>
+			<widget name = 'StopButton'
+				width = '16'
+				height = '16'
+			/>
+			<widget name = 'EditButton'
+				width = '16'
+				height = '16'
+			/>
+			<widget name = 'SwitchModeButton'
+				width = '16'
+				height = '16'
+			/>
+			<widget name = 'FastReplayButton'
+				width = '16'
+				height = '16'
+			/>
+			<widget name = 'TimeLabel'
+				width = '50'
+				height = '16'
+			/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'EditRecordDialog' overlays = 'screen_center'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'>
+			<widget name = 'Title'
+					height = 'Globals.Line.Height'
+			/>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'AuthorLabel'
+						type = 'EditRecordLabel'
+				/>		
+				<widget name = 'AuthorEdit'
+						type = 'EditRecord'
+				/>		
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'NameLabel'
+						type = 'EditRecordLabel'
+				/>		
+				<widget name = 'NameEdit'
+							type = 'EditRecord'
+				/>
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'NotesLabel'
+						type = 'EditRecordLabel'
+				/>
+				<widget name = 'NotesEdit'
+							type = 'EditRecord'
+				/>
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 0'>
+				<widget name = 'Cancel'
+						type = 'Button'
+				/>
+				<widget name = 'OK'
+						type = 'Button'
+				/>			
+			</layout>
+		</layout>
+	</dialog>
+
 	<dialog name = 'ScummHelp' overlays = 'screen'>
 		<layout type = 'vertical' padding = '8, 8, 8, 8'>
 			<widget name = 'Title'
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index dbd8499..3925c67 100644
Binary files a/gui/themes/scummmodern.zip and b/gui/themes/scummmodern.zip differ
diff --git a/gui/themes/scummmodern/editbtn.bmp b/gui/themes/scummmodern/editbtn.bmp
new file mode 100644
index 0000000..49eb403
Binary files /dev/null and b/gui/themes/scummmodern/editbtn.bmp differ
diff --git a/gui/themes/scummmodern/editbtn_small.bmp b/gui/themes/scummmodern/editbtn_small.bmp
new file mode 100644
index 0000000..8a0357f
Binary files /dev/null and b/gui/themes/scummmodern/editbtn_small.bmp differ
diff --git a/gui/themes/scummmodern/fastreplay.bmp b/gui/themes/scummmodern/fastreplay.bmp
new file mode 100644
index 0000000..35ad2b4
Binary files /dev/null and b/gui/themes/scummmodern/fastreplay.bmp differ
diff --git a/gui/themes/scummmodern/fastreplay_small.bmp b/gui/themes/scummmodern/fastreplay_small.bmp
new file mode 100644
index 0000000..8ef004c
Binary files /dev/null and b/gui/themes/scummmodern/fastreplay_small.bmp differ
diff --git a/gui/themes/scummmodern/scummmodern_gfx.stx b/gui/themes/scummmodern/scummmodern_gfx.stx
index 4d449f5..1b3bcea 100644
--- a/gui/themes/scummmodern/scummmodern_gfx.stx
+++ b/gui/themes/scummmodern/scummmodern_gfx.stx
@@ -103,6 +103,14 @@
 		<bitmap filename = 'delbtn.bmp'/>
 		<bitmap filename = 'list.bmp'/>
 		<bitmap filename = 'grid.bmp'/>
+		<bitmap filename = 'stopbtn.bmp'/>
+		<bitmap filename = 'editbtn.bmp'/>
+		<bitmap filename = 'switchbtn.bmp'/>
+		<bitmap filename = 'fastreplay.bmp'/>
+		<bitmap filename = 'stopbtn_small.bmp'/>
+		<bitmap filename = 'editbtn_small.bmp'/>
+		<bitmap filename = 'switchbtn_small.bmp'/>
+		<bitmap filename = 'fastreplay_small.bmp'/>
 	</bitmaps>
 
 	<fonts>
diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx
index 49c13cf..4fd6ae6e 100644
--- a/gui/themes/scummmodern/scummmodern_layout.stx
+++ b/gui/themes/scummmodern/scummmodern_layout.stx
@@ -43,6 +43,9 @@
 		<def var = 'ShowChooserPageDisplay' value = '1'/>
 
 		<def var = 'SaveLoadChooser.ExtInfo.Visible' value = '1'/>
+		<def var = 'RecorderDialog.ExtInfo.Visible' value = '1'/>
+
+		<def var = 'OnScreenDialog.ShowPics' value = '1'/>
 
 		<def var = 'KeyMapper.Spacing' value = '10'/>
 		<def var = 'KeyMapper.LabelWidth' value = '100'/>
@@ -106,6 +109,13 @@
 				size = '15, 18'
 				padding = '0, 3, 4, 0'
 		/>
+
+		<widget name = 'EditRecordLabel'
+				size = '60, 25'
+		/>
+		<widget name = 'EditRecord'
+				size = '240, 25'
+		/>
 	</globals>
 
 	<dialog name = 'Launcher' overlays = 'screen'>
@@ -1032,6 +1042,126 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'RecorderDialog' overlays = 'screen' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 32' center = 'true'>
+			<widget name = 'Title'
+					height = 'Globals.Line.Height'
+			/>
+			<layout type = 'horizontal' padding = '0, 0, 0, 16' spacing = '16'>
+				<widget name = 'List' />
+				<layout type = 'vertical' padding = '0, 0, 0, 0'>
+					<widget name = 'Thumbnail'
+							width = '180'
+							height = '170'
+					/>
+					<layout type = 'horizontal' padding = '0, 0, 0, 0'>	
+						<widget name = 'NextScreenShotButton'
+								width = '25'
+								height = '25'
+						/>
+						<widget name = 'currentScreenshot'
+								width = '125'
+								height = '25'
+								textalign = 'center'
+						/>
+						<widget name = 'PreviousScreenShotButton'
+								width = '25'
+								height = '25'
+						/>
+					</layout>
+					<widget name = 'Author' height = 'Globals.Line.Height' />
+					<widget name = 'Notes' height = 'Globals.Line.Height' />
+				</layout>
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0'>
+				<space/>
+				<widget name = 'Delete'
+						type = 'Button'
+				/>
+				<space size = '16'/>
+				<widget name = 'Cancel'
+						type = 'Button'
+				/>
+				<space size = '16'/>
+				<widget name = 'Edit'
+						type = 'Button'
+				/>
+				<widget name = 'Record'
+						type = 'Button'
+				/>
+				<widget name = 'Playback'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'OnScreenDialog' overlays = 'screen_center'>
+		<layout type = 'horizontal'  spacing = '5' padding = '5, 3, 5, 3' center = 'true'>
+			<widget name = 'StopButton'
+				width = '32'
+				height = '32'
+			/>
+			<widget name = 'EditButton'
+				width = '32'
+				height = '32'
+			/>
+			<widget name = 'SwitchModeButton'
+				width = '32'
+				height = '32'
+			/>
+			<widget name = 'FastReplayButton'
+				width = '32'
+				height = '32'
+			/>
+			<widget name = 'TimeLabel'
+				width = '50'
+				height = '30'
+			/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'EditRecordDialog' overlays = 'screen_center'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'>
+			<widget name = 'Title'
+					width = '320'
+					height = 'Globals.Line.Height'
+			/>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'AuthorLabel'
+						type = 'EditRecordLabel'
+				/>		
+				<widget name = 'AuthorEdit'
+						type = 'EditRecord'
+				/>		
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'NameLabel'
+						type = 'EditRecordLabel'
+				/>		
+				<widget name = 'NameEdit'
+						type = 'EditRecord'
+				/>
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'NotesLabel'
+						type = 'EditRecordLabel'
+				/>
+				<widget name = 'NotesEdit'
+						type = 'EditRecord'
+				/>
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'Cancel'
+						type = 'Button'
+				/>
+				<widget name = 'OK'
+						type = 'Button'
+				/>			
+			</layout>
+		</layout>
+	</dialog>
+	
 	<dialog name = 'ScummHelp' overlays = 'screen_center'>
 		<layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'>
 			<widget name = 'Title'
diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
index 9658402..cee1e4a 100644
--- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx
+++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx
@@ -35,6 +35,9 @@
 		<def var = 'ShowChooserPageDisplay' value = '0'/>
 
 		<def var = 'SaveLoadChooser.ExtInfo.Visible' value = '0'/>
+		<def var = 'RecorderDialog.ExtInfo.Visible' value = '0'/>
+
+		<def var = 'OnScreenDialog.ShowPics' value = '1'/>
 
 		<def var = 'Predictive.Button.Width' value = '45' />
 		<def var = 'Predictive.Button.Height' value = '15' />
@@ -97,6 +100,12 @@
 				size = '32, 18'
 				padding = '0, 0, 2, 0'
 		/>
+		<widget name = 'EditRecordLabel'
+				size = '60, Globals.Line.Height'
+		/>
+		<widget name = 'EditRecord'
+				size = '120, 15'
+		/>
 	</globals>
 
 	<dialog name = 'Launcher' overlays = 'screen'>
@@ -1012,6 +1021,122 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'SavenameDialog' overlays = 'screen_center'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8'>
+			<widget name = 'DescriptionText'
+					width = '320'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'Description'
+					height = '19'
+			/>
+			<layout type = 'horizontal' padding = '0, 0, 16, 0'>
+				<widget name = 'Cancel'
+						type = 'Button'
+				/>
+				<space size = '96'/>
+				<widget name = 'Ok'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'RecorderDialog' overlays = 'screen' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 4' center = 'true'>
+			<widget name = 'Title'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'List' />
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '2'>
+			  <widget name = 'Edit'
+					  type = 'Button'
+					  />
+			  <space />
+			  <widget name = 'Record'
+					  type = 'Button'
+					  />
+			</layout>
+			<layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '2'>
+			  <widget name = 'Delete'
+					  type = 'Button'
+					  />
+			  <space />
+			  <widget name = 'Cancel'
+					  type = 'Button'
+					  />
+			  <widget name = 'Playback'
+					  type = 'Button'
+					  />
+			</layout>
+		</layout>
+	</dialog>
+
+	<dialog name = 'OnScreenDialog' overlays = 'screen_center'>
+		<layout type = 'horizontal'  spacing = '5' padding = '3, 2, 3, 2' center = 'true'>
+			<widget name = 'StopButton'
+				width = '16'
+				height = '16'
+			/>
+			<widget name = 'EditButton'
+				width = '16'
+				height = '16'
+			/>
+			<widget name = 'SwitchModeButton'
+				width = '16'
+				height = '16'
+			/>
+			<widget name = 'FastReplayButton'
+				width = '16'
+				height = '16'
+			/>
+			<widget name = 'TimeLabel'
+				width = '50'
+				height = '16'
+			/>
+		</layout>
+	</dialog>
+
+	<dialog name = 'EditRecordDialog' overlays = 'screen_center'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' center = 'true'>
+			<widget name = 'Title'
+					height = 'Globals.Line.Height'
+			/>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'AuthorLabel'
+						type = 'EditRecordLabel'
+				/>		
+				<widget name = 'AuthorEdit'
+						type = 'EditRecord'
+				/>		
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'NameLabel'
+						type = 'EditRecordLabel'
+				/>		
+				<widget name = 'NameEdit'
+							type = 'EditRecord'
+				/>
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 10'>
+				<widget name = 'NotesLabel'
+						type = 'EditRecordLabel'
+				/>
+				<widget name = 'NotesEdit'
+							type = 'EditRecord'
+				/>
+			</layout>
+			<layout type = 'horizontal'  spacing = '5' padding = '0, 0, 0, 0'>
+				<widget name = 'Cancel'
+						type = 'Button'
+				/>
+				<widget name = 'OK'
+						type = 'Button'
+				/>			
+			</layout>
+		</layout>
+	</dialog>
+	
 	<dialog name = 'ScummHelp' overlays = 'screen' inset = '8'>
 		<layout type = 'vertical' padding = '8, 8, 8, 8'>
 			<widget name = 'Title'
diff --git a/gui/themes/scummmodern/stopbtn.bmp b/gui/themes/scummmodern/stopbtn.bmp
new file mode 100644
index 0000000..3575956
Binary files /dev/null and b/gui/themes/scummmodern/stopbtn.bmp differ
diff --git a/gui/themes/scummmodern/stopbtn_small.bmp b/gui/themes/scummmodern/stopbtn_small.bmp
new file mode 100644
index 0000000..ffd5025
Binary files /dev/null and b/gui/themes/scummmodern/stopbtn_small.bmp differ
diff --git a/gui/themes/scummmodern/switchbtn.bmp b/gui/themes/scummmodern/switchbtn.bmp
new file mode 100644
index 0000000..6bafa4a
Binary files /dev/null and b/gui/themes/scummmodern/switchbtn.bmp differ
diff --git a/gui/themes/scummmodern/switchbtn_small.bmp b/gui/themes/scummmodern/switchbtn_small.bmp
new file mode 100644
index 0000000..929b128
Binary files /dev/null and b/gui/themes/scummmodern/switchbtn_small.bmp differ


Commit: fa61a35acd2cafbf2a0e2752dd15bbcbb9b55463
    https://github.com/scummvm/scummvm/commit/fa61a35acd2cafbf2a0e2752dd15bbcbb9b55463
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2013-07-04T04:47:25-07:00

Commit Message:
CONFIGURE: Remove superflous (and broken) command for eventrec.

Changed paths:
    configure



diff --git a/configure b/configure
index 08eba65..c93f06e 100755
--- a/configure
+++ b/configure
@@ -3819,7 +3819,6 @@ define_in_config_if_yes $_nasm 'USE_NASM'
 define_in_config_if_yes $_vkeybd 'ENABLE_VKEYBD'
 define_in_config_if_yes $_keymapper 'ENABLE_KEYMAPPER'
 define_in_config_if_yes $_eventrec 'ENABLE_EVENTRECORDER'
-add_line_to_config_mk_if_yes $_eventrec "ENABLE_EVENTRECORDER = 1"
 
 # Check whether to build translation support
 #


Commit: 93fc2608859c9cb64f291eeacb766c53d4b55323
    https://github.com/scummvm/scummvm/commit/93fc2608859c9cb64f291eeacb766c53d4b55323
Author: Johannes Schickel (lordhoto at scummvm.org)
Date: 2013-07-04T04:47:25-07:00

Commit Message:
SDL: Fix compilation by moving getMixerManager out of USE_OPENGL guard.

Changed paths:
    backends/platform/sdl/sdl.cpp



diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index 77e6f73..f55dd27 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -501,6 +501,10 @@ SdlMixerManager *OSystem_SDL::getMixerManager() {
 	return g_eventRec.getMixerManager();
 }
 
+Common::TimerManager *OSystem_SDL::getTimerManager() {
+	return g_eventRec.getTimerManager();
+}
+
 #ifdef USE_OPENGL
 
 const OSystem::GraphicsMode *OSystem_SDL::getSupportedGraphicsModes() const {
@@ -656,8 +660,4 @@ void OSystem_SDL::setupGraphicsModes() {
 	}
 }
 
-Common::TimerManager *OSystem_SDL::getTimerManager() {
-	return g_eventRec.getTimerManager();
-}
-
 #endif


Commit: b286a6d033287dce11dfa4216ad11728b892667d
    https://github.com/scummvm/scummvm/commit/b286a6d033287dce11dfa4216ad11728b892667d
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2013-07-04T04:54:56-07:00

Commit Message:
RECORDER: Fix guard ifdef

Changed paths:
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp



diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index e174a6e..f66f43e 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -768,7 +768,7 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
 	}
 
 
-#ifdef ENABLE_KEYMAPPER
+#ifdef ENABLE_EVENTRECORDER
 	_displayDisabled = ConfMan.getBool("disable_display");
 
 	if (_displayDisabled) {


Commit: 49210a803a53b84bcabe42fd339a1b205236c34d
    https://github.com/scummvm/scummvm/commit/49210a803a53b84bcabe42fd339a1b205236c34d
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2013-07-04T04:58:54-07:00

Commit Message:
Merge pull request #331 from sev-/gsoc2012-eventsrecorder

GSoC2012: Event Recorder (reworked)

Changed paths:
  A backends/mixer/nullmixer/nullsdl-mixer.cpp
  A backends/mixer/nullmixer/nullsdl-mixer.h
  A backends/saves/recorder/recorder-saves.cpp
  A backends/saves/recorder/recorder-saves.h
  A common/recorderfile.cpp
  A common/recorderfile.h
  A gui/EventRecorder.cpp
  A gui/EventRecorder.h
  A gui/editrecorddialog.cpp
  A gui/editrecorddialog.h
  A gui/onscreendialog.cpp
  A gui/onscreendialog.h
  A gui/recorderdialog.cpp
  A gui/recorderdialog.h
  A gui/themes/scummmodern/editbtn.bmp
  A gui/themes/scummmodern/editbtn_small.bmp
  A gui/themes/scummmodern/fastreplay.bmp
  A gui/themes/scummmodern/fastreplay_small.bmp
  A gui/themes/scummmodern/stopbtn.bmp
  A gui/themes/scummmodern/stopbtn_small.bmp
  A gui/themes/scummmodern/switchbtn.bmp
  A gui/themes/scummmodern/switchbtn_small.bmp
  R common/EventRecorder.cpp
  R common/EventRecorder.h
    audio/mixer.cpp
    backends/events/default/default-events.cpp
    backends/events/sdl/sdl-events.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.h
    backends/modular-backend.cpp
    backends/module.mk
    backends/mutex/sdl/sdl-mutex.cpp
    backends/platform/android/android.cpp
    backends/platform/android/android.h
    backends/platform/bada/system.cpp
    backends/platform/bada/system.h
    backends/platform/dc/dc.h
    backends/platform/dc/time.cpp
    backends/platform/ds/arm9/source/dsmain.cpp
    backends/platform/ds/arm9/source/dsmain.h
    backends/platform/ds/arm9/source/osystem_ds.cpp
    backends/platform/ds/arm9/source/osystem_ds.h
    backends/platform/iphone/osys_main.cpp
    backends/platform/iphone/osys_main.h
    backends/platform/n64/osys_n64.h
    backends/platform/n64/osys_n64_base.cpp
    backends/platform/null/null.cpp
    backends/platform/ps2/systemps2.cpp
    backends/platform/ps2/systemps2.h
    backends/platform/psp/osys_psp.cpp
    backends/platform/psp/osys_psp.h
    backends/platform/psp/rtc.cpp
    backends/platform/psp/rtc.h
    backends/platform/sdl/sdl.cpp
    backends/platform/sdl/sdl.h
    backends/platform/wii/osystem.cpp
    backends/platform/wii/osystem.h
    backends/timer/default/default-timer.cpp
    base/commandLine.cpp
    base/main.cpp
    common/EventDispatcher.cpp
    common/debug.h
    common/events.h
    common/memstream.h
    common/module.mk
    common/random.cpp
    common/system.cpp
    common/system.h
    configure
    engines/advancedDetector.cpp
    engines/advancedDetector.h
    engines/cge/cge.cpp
    engines/dreamweb/dreamweb.cpp
    engines/sword25/gfx/image/renderedimage.cpp
    engines/sword25/gfx/image/renderedimage.h
    engines/wintermute/wintermute.cpp
    graphics/cursorman.cpp
    graphics/cursorman.h
    graphics/scaler.h
    graphics/scaler/thumbnail_intern.cpp
    graphics/thumbnail.cpp
    graphics/thumbnail.h
    gui/ThemeEngine.cpp
    gui/ThemeEngine.h
    gui/dialog.h
    gui/gui-manager.cpp
    gui/gui-manager.h
    gui/launcher.cpp
    gui/launcher.h
    gui/module.mk
    gui/themes/default.inc
    gui/themes/scummclassic.zip
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern.zip
    gui/themes/scummmodern/scummmodern_gfx.stx
    gui/themes/scummmodern/scummmodern_layout.stx
    gui/themes/scummmodern/scummmodern_layout_lowres.stx









More information about the Scummvm-git-logs mailing list