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

/*
 * This file is based on WME Lite.
 * http://dead-code.org/redir.php?target=wmelite
 * Copyright (c) 2011 Jan Nedoma
 */

#include "engines/wintermute/dcgf.h"
#include "engines/wintermute/base/base_engine.h"
#include "engines/wintermute/base/base_game.h"
#include "engines/wintermute/base/base_game_music.h"
#include "engines/wintermute/base/base_game_settings.h"
#include "engines/wintermute/base/base_fader.h"
#include "engines/wintermute/base/base_file_manager.h"
#include "engines/wintermute/base/font/base_font.h"
#include "engines/wintermute/base/font/base_font_storage.h"
#include "engines/wintermute/base/gfx/base_renderer.h"
#include "engines/wintermute/base/base_keyboard_state.h"
#include "engines/wintermute/base/base_parser.h"
#include "engines/wintermute/base/base_quick_msg.h"
#include "engines/wintermute/base/sound/base_sound_manager.h"
#include "engines/wintermute/base/base_sprite.h"
#include "engines/wintermute/base/base_sub_frame.h"
#include "engines/wintermute/base/base_transition_manager.h"
#include "engines/wintermute/base/base_viewport.h"
#include "engines/wintermute/base/base_region.h"
#include "engines/wintermute/base/base_surface_storage.h"
#include "engines/wintermute/base/saveload.h"
#include "engines/wintermute/base/save_thumb_helper.h"
#include "engines/wintermute/base/scriptables/script_value.h"
#include "engines/wintermute/base/scriptables/script_engine.h"
#include "engines/wintermute/base/scriptables/script_stack.h"
#include "engines/wintermute/base/scriptables/script.h"
#include "engines/wintermute/base/sound/base_sound.h"
#include "engines/wintermute/video/video_player.h"
#include "engines/wintermute/video/video_theora_player.h"
#include "engines/wintermute/utils/utils.h"
#include "engines/wintermute/utils/crc.h"
#include "engines/wintermute/utils/path_util.h"
#include "engines/wintermute/utils/string_util.h"
#include "engines/wintermute/ui/ui_window.h"
#include "engines/wintermute/wintermute.h"
#include "engines/wintermute/platform_osystem.h"
#include "base/version.h"
#include "common/config-manager.h"
#include "common/savefile.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "common/keyboard.h"
#include "common/system.h"
#include "common/file.h"
#include "graphics/scaler.h"

#if EXTENDED_DEBUGGER_ENABLED
#include "engines/wintermute/base/scriptables/debuggable/debuggable_script_engine.h"
#endif

namespace Wintermute {

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

IMPLEMENT_PERSISTENT(BaseGame, true)


//////////////////////////////////////////////////////////////////////
BaseGame::BaseGame(const Common::String &targetName) : BaseObject(this), _targetName(targetName), _timerNormal(), _timerLive() {
	_shuttingDown = false;

	_state = GAME_RUNNING;
	_origState = GAME_RUNNING;
	_freezeLevel = 0;

	_interactive = true;
	_origInteractive = false;

	_surfaceStorage = nullptr;
	_fontStorage = nullptr;
	_renderer = nullptr;
	_soundMgr = nullptr;
	_transMgr = nullptr;
	_scEngine = nullptr;
	_keyboardState = nullptr;

	_mathClass = nullptr;

	_debugLogFile = nullptr;
	_debugDebugMode = false;
	_debugShowFPS = false;

	_systemFont = nullptr;
	_videoFont = nullptr;

	_videoPlayer = nullptr;
	_theoraPlayer = nullptr;

	_mainObject = nullptr;
	_activeObject = nullptr;

	_fader = nullptr;

	_offsetX = _offsetY = 0;
	_offsetPercentX = _offsetPercentY = 0.0f;

	_subtitles = true;
	_videoSubtitles = true;

	_sequence = 0;

	_mousePos.x = _mousePos.y = 0;
	_mouseLeftDown = _mouseRightDown = _mouseMidlleDown = false;
	_capturedObject = nullptr;

	// FPS counters
	_lastTime = _fpsTime = _deltaTime = _framesRendered = _fps = 0;

	_cursorNoninteractive = nullptr;

	_useD3D = false;

	_musicSystem = new BaseGameMusic(this);

	_editorForceScripts = false;
	_editorAlwaysRegister = false;

	_focusedWindow = nullptr;

	_loadInProgress = false;

	_quitting = false;
	_loading = false;
	_scheduledLoadSlot = -1;

	_personalizedSave = false;

	_editorMode = false;
	//_doNotExpandStrings = false;

	_engineLogCallback = nullptr;
	_engineLogCallbackData = nullptr;

	_smartCache = false;
	_surfaceGCCycleTime = 10000;

	_reportTextureFormat = false;

	_viewportSP = -1;

	_subtitlesSpeed = 70;

	_forceNonStreamedSounds = false;

	// These are NOT the actual engine defaults (they are 0, 0),
	// but we have a use for thumbnails even for games that don't
	// use them in-game, hence we set a default that is suitably
	// sized for the GMM (expecting 4:3 ratio)
	_thumbnailWidth = kThumbnailWidth;
	_thumbnailHeight = kThumbnailHeight2;

	_localSaveDir = "saves";

	_saveDirChecked = false;

	_loadingIcon = nullptr;
	_loadingIconX = _loadingIconY = 0;
	_loadingIconPersistent = false;

	_textEncoding = TEXT_ANSI;
	_textRTL = false;

	_soundBufferSizeSec = 3;
	_suspendedRendering = false;

	_lastCursor = nullptr;

	_mouseLockRect.setEmpty();

	_suppressScriptErrors = false;
	_lastMiniUpdate = 0;
	_miniUpdateEnabled = false;

	_cachedThumbnail = nullptr;

	_autorunDisabled = false;

	// compatibility bits
	_compatKillMethodThreads = false;

	_usedMem = 0;


	_autoSaveOnExit = true;
	_autoSaveSlot = 999;
	_cursorHidden = false;

	// Block kept as a reminder that the engine CAN run in constrained/touch-mode
	/*#ifdef __IPHONEOS__
	    _touchInterface = true;
	    _constrainedMemory = true; // TODO differentiate old and new iOS devices
	#else*/
	_touchInterface = false;
	_constrainedMemory = false;

	_settings = new BaseGameSettings(this);
//#endif

}


//////////////////////////////////////////////////////////////////////
BaseGame::~BaseGame() {
	_shuttingDown = true;

	LOG(0, "");
	LOG(0, "Shutting down...");

	ConfMan.setBool("last_run", true);

	cleanup();

	delete _cachedThumbnail;

	delete _mathClass;

	delete _transMgr;
	delete _scEngine;
	delete _fontStorage;
	delete _surfaceStorage;
	delete _videoPlayer;
	delete _theoraPlayer;
	delete _soundMgr;
	//SAFE_DELETE(_keyboardState);

	delete _renderer;
	delete _musicSystem;
	delete _settings;

	_cachedThumbnail = nullptr;

	_mathClass = nullptr;

	_transMgr = nullptr;
	_scEngine = nullptr;
	_fontStorage = nullptr;
	_surfaceStorage = nullptr;
	_videoPlayer = nullptr;
	_theoraPlayer = nullptr;
	_soundMgr = nullptr;

	_renderer = nullptr;
	_musicSystem = nullptr;
	_settings = nullptr;

	DEBUG_DebugDisable();
	debugC(kWintermuteDebugLog, "--- shutting down normally ---\n");
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::cleanup() {
	delete _loadingIcon;
	_loadingIcon = nullptr;

	_engineLogCallback = nullptr;
	_engineLogCallbackData = nullptr;

	_musicSystem->cleanup();

	unregisterObject(_fader);
	_fader = nullptr;

	for (uint32 i = 0; i < _regObjects.size(); i++) {
		delete _regObjects[i];
		_regObjects[i] = nullptr;
	}
	_regObjects.clear();

	_windows.clear(); // refs only
	_focusedWindow = nullptr; // ref only

	delete _cursorNoninteractive;
	delete _cursor;
	delete _activeCursor;
	_cursorNoninteractive = nullptr;
	_cursor = nullptr;
	_activeCursor = nullptr;

	delete _scValue;
	delete _sFX;
	_scValue = nullptr;
	_sFX = nullptr;

	for (uint32 i = 0; i < _scripts.size(); i++) {
		_scripts[i]->_owner = nullptr;
		_scripts[i]->finish();
	}
	_scripts.clear();

	_fontStorage->removeFont(_systemFont);
	_systemFont = nullptr;

	_fontStorage->removeFont(_videoFont);
	_videoFont = nullptr;

	for (uint32 i = 0; i < _quickMessages.size(); i++) {
		delete _quickMessages[i];
	}
	_quickMessages.clear();

	_viewportStack.clear();
	_viewportSP = -1;

	setName(nullptr);
	setFilename(nullptr);
	for (int i = 0; i < 7; i++) {
		delete[] _caption[i];
		_caption[i] = nullptr;
	}

	_lastCursor = nullptr;

	delete _keyboardState;
	_keyboardState = nullptr;

	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////
bool BaseGame::initConfManSettings() {
	if (ConfMan.hasKey("debug_mode")) {
		if (ConfMan.getBool("debug_mode")) {
			DEBUG_DebugEnable("./wme.log");
		}
	}

	if (ConfMan.hasKey("show_fps")) {
		_debugShowFPS = ConfMan.getBool("show_fps");
	} else {
		_debugShowFPS = false;
	}

	if (ConfMan.hasKey("bilinear_filtering")) {
		_bilinearFiltering = ConfMan.getBool("bilinear_filtering");
	} else {
		_bilinearFiltering = false;
	}

	if (ConfMan.hasKey("disable_smartcache")) {
		_smartCache = ConfMan.getBool("disable_smartcache");
	} else {
		_smartCache = true;
	}

	if (!_smartCache) {
		LOG(0, "Smart cache is DISABLED");
	}
	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////
bool BaseGame::initRenderer() {
	bool windowedMode = !ConfMan.getBool("fullscreen");
	return _renderer->initRenderer(_settings->getResWidth(), _settings->getResHeight(), windowedMode);
}

//////////////////////////////////////////////////////////////////////
bool BaseGame::loadGameSettingsFile() {
	return loadFile(_settings->getGameFile());
}

//////////////////////////////////////////////////////////////////////
bool BaseGame::initialize1() {
	bool loaded = false; // Not really a loop, but a goto-replacement.
	while (!loaded) {
		_surfaceStorage = new BaseSurfaceStorage(this);
		if (_surfaceStorage == nullptr) {
			break;
		}

		_fontStorage = new BaseFontStorage(this);
		if (_fontStorage == nullptr) {
			break;
		}

		_soundMgr = new BaseSoundMgr(this);
		if (_soundMgr == nullptr) {
			break;
		}

		_mathClass = makeSXMath(this);
		if (_mathClass == nullptr) {
			break;
		}

#if EXTENDED_DEBUGGER_ENABLED
		_scEngine = new DebuggableScEngine(this);
#else
		_scEngine = new ScEngine(this);
#endif
		if (_scEngine == nullptr) {
			break;
		}

		_videoPlayer = new VideoPlayer(this);
		if (_videoPlayer == nullptr) {
			break;
		}

		_transMgr = new BaseTransitionMgr(this);
		if (_transMgr == nullptr) {
			break;
		}

		_keyboardState = new BaseKeyboardState(this);
		if (_keyboardState == nullptr) {
			break;
		}

		_fader = new BaseFader(this);
		if (_fader == nullptr) {
			break;
		}
		registerObject(_fader);

		loaded = true;
	}
	if (loaded == true) {
		return STATUS_OK;
	} else {
		delete _mathClass;
		delete _keyboardState;
		delete _transMgr;
		delete _surfaceStorage;
		delete _fontStorage;
		delete _soundMgr;
		delete _scEngine;
		delete _videoPlayer;
		return STATUS_FAILED;
	}
}


//////////////////////////////////////////////////////////////////////
bool BaseGame::initialize2() { // we know whether we are going to be accelerated
	_renderer = makeOSystemRenderer(this);
	if (_renderer == nullptr) {
		return STATUS_FAILED;
	}

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////
bool BaseGame::initialize3() { // renderer is initialized
	_posX = _renderer->getWidth() / 2;
	_posY = _renderer->getHeight() / 2;
	_renderer->initIndicator();
	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////
void BaseGame::DEBUG_DebugEnable(const char *filename) {
	_debugDebugMode = true;

	int secs = g_system->getMillis() / 1000;
	int hours = secs / 3600;
	secs = secs % 3600;
	int mins = secs / 60;
	secs = secs % 60;

#ifdef _DEBUG
	LOG(0, "********** DEBUG LOG OPENED %02d-%02d-%02d (Debug Build) *******************", hours, mins, secs);
#else
	LOG(0, "********** DEBUG LOG OPENED %02d-%02d-%02d (Release Build) *****************", hours, mins, secs);
#endif

	LOG(0, "%s - %s ver %d.%d.%d%s ", gScummVMFullVersion, DCGF_NAME, DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, DCGF_VER_SUFFIX);

	AnsiString platform = BasePlatform::getPlatformName();
	LOG(0, "Platform: %s", platform.c_str());
	LOG(0, "");
}


//////////////////////////////////////////////////////////////////////
void BaseGame::DEBUG_DebugDisable() {
	if (_debugLogFile != nullptr) {
		LOG(0, "********** DEBUG LOG CLOSED ********************************************");
		//fclose((FILE *)_debugLogFile);
		_debugLogFile = nullptr;
	}
	_debugDebugMode = false;
}


//////////////////////////////////////////////////////////////////////
void BaseGame::LOG(bool res, const char *fmt, ...) {
	uint32 secs = g_system->getMillis() / 1000;
	uint32 hours = secs / 3600;
	secs = secs % 3600;
	uint32 mins = secs / 60;
	secs = secs % 60;

	char buff[512];
	va_list va;

	va_start(va, fmt);
	vsprintf(buff, fmt, va);
	va_end(va);

	// redirect to an engine's own callback
	if (_engineLogCallback) {
		_engineLogCallback(buff, res, _engineLogCallbackData);
	}

	debugCN(kWintermuteDebugLog, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff);

	//fprintf((FILE *)_debugLogFile, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff);
	//fflush((FILE *)_debugLogFile);

	//QuickMessage(buff);
}


//////////////////////////////////////////////////////////////////////////
void BaseGame::setEngineLogCallback(ENGINE_LOG_CALLBACK callback, void *data) {
	_engineLogCallback = callback;
	_engineLogCallbackData = data;
}


//////////////////////////////////////////////////////////////////////
bool BaseGame::initLoop() {
	_viewportSP = -1;

	_currentTime = g_system->getMillis();

	_renderer->initLoop();
	_musicSystem->updateMusicCrossfade();

	_surfaceStorage->initLoop();
	_fontStorage->initLoop();


	//_activeObject = nullptr;

	// count FPS
	_deltaTime = _currentTime - _lastTime;
	_lastTime  = _currentTime;
	_fpsTime += _deltaTime;

	_timerLive.updateTime(_deltaTime, 1000);

	if (_state != GAME_FROZEN) {
		_timerNormal.updateTime(_deltaTime, 1000);
	} else {
		_timerNormal.setTimeDelta(0);
	}

	_framesRendered++;
	if (_fpsTime > 1000) {
		_fps = _framesRendered;
		_framesRendered  = 0;
		_fpsTime = 0;
	}
	//_gameRef->LOG(0, "%d", _fps);

	getMousePos(&_mousePos);

	_focusedWindow = nullptr;
	for (int i = _windows.size() - 1; i >= 0; i--) {
		if (_windows[i]->isVisible()) {
			_focusedWindow = _windows[i];
			break;
		}
	}

	updateSounds();

	if (_fader) {
		_fader->update();
	}

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////
bool BaseGame::initInput() {
	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
int BaseGame::getSequence() {
	return ++_sequence;
}


//////////////////////////////////////////////////////////////////////////
void BaseGame::setOffset(int32 offsetX, int32 offsetY) {
	_offsetX = offsetX;
	_offsetY = offsetY;
}

//////////////////////////////////////////////////////////////////////////
void BaseGame::getOffset(int *offsetX, int *offsetY) const {
	if (offsetX != nullptr) {
		*offsetX = _offsetX;
	}
	if (offsetY != nullptr) {
		*offsetY = _offsetY;
	}
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::loadFile(const char *filename) {
	char *buffer = (char *)BaseFileManager::getEngineInstance()->readWholeFile(filename);
	if (buffer == nullptr) {
		_gameRef->LOG(0, "BaseGame::LoadFile failed for file '%s'", filename);
		return STATUS_FAILED;
	}

	bool ret;

	setFilename(filename);

	if (DID_FAIL(ret = loadBuffer(buffer, true))) {
		_gameRef->LOG(0, "Error parsing GAME file '%s'", filename);
	}

	delete[] buffer;

	return ret;
}


TOKEN_DEF_START
TOKEN_DEF(GAME)
TOKEN_DEF(TEMPLATE)
TOKEN_DEF(NAME)
TOKEN_DEF(SYSTEM_FONT)
TOKEN_DEF(VIDEO_FONT)
TOKEN_DEF(EVENTS)
TOKEN_DEF(CURSOR)
TOKEN_DEF(ACTIVE_CURSOR)
TOKEN_DEF(NONINTERACTIVE_CURSOR)
TOKEN_DEF(STRING_TABLE)
TOKEN_DEF(RESOLUTION)
TOKEN_DEF(SETTINGS)
TOKEN_DEF(REQUIRE_3D_ACCELERATION)
TOKEN_DEF(REQUIRE_SOUND)
TOKEN_DEF(HWTL_MODE)
TOKEN_DEF(ALLOW_WINDOWED_MODE)
TOKEN_DEF(ALLOW_ACCESSIBILITY_TAB)
TOKEN_DEF(ALLOW_ABOUT_TAB)
TOKEN_DEF(ALLOW_ADVANCED)
TOKEN_DEF(ALLOW_DESKTOP_RES)
TOKEN_DEF(REGISTRY_PATH)
TOKEN_DEF(PERSONAL_SAVEGAMES)
TOKEN_DEF(SCRIPT)
TOKEN_DEF(CAPTION)
TOKEN_DEF(PROPERTY)
TOKEN_DEF(SUBTITLES_SPEED)
TOKEN_DEF(SUBTITLES)
TOKEN_DEF(VIDEO_SUBTITLES)
TOKEN_DEF(EDITOR_PROPERTY)
TOKEN_DEF(THUMBNAIL_WIDTH)
TOKEN_DEF(THUMBNAIL_HEIGHT)
TOKEN_DEF(INDICATOR_X)
TOKEN_DEF(INDICATOR_Y)
TOKEN_DEF(INDICATOR_WIDTH)
TOKEN_DEF(INDICATOR_HEIGHT)
TOKEN_DEF(INDICATOR_COLOR)
TOKEN_DEF(SAVE_IMAGE_X)
TOKEN_DEF(SAVE_IMAGE_Y)
TOKEN_DEF(SAVE_IMAGE)
TOKEN_DEF(LOAD_IMAGE_X)
TOKEN_DEF(LOAD_IMAGE_Y)
TOKEN_DEF(LOAD_IMAGE)
TOKEN_DEF(LOCAL_SAVE_DIR)
TOKEN_DEF(RICH_SAVED_GAMES)
TOKEN_DEF(SAVED_GAME_EXT)
TOKEN_DEF(GUID)
TOKEN_DEF(COMPAT_KILL_METHOD_THREADS)
TOKEN_DEF_END
//////////////////////////////////////////////////////////////////////////
bool BaseGame::loadBuffer(char *buffer, bool complete) {
	TOKEN_TABLE_START(commands)
	TOKEN_TABLE(GAME)
	TOKEN_TABLE(TEMPLATE)
	TOKEN_TABLE(NAME)
	TOKEN_TABLE(SYSTEM_FONT)
	TOKEN_TABLE(VIDEO_FONT)
	TOKEN_TABLE(EVENTS)
	TOKEN_TABLE(CURSOR)
	TOKEN_TABLE(ACTIVE_CURSOR)
	TOKEN_TABLE(NONINTERACTIVE_CURSOR)
	TOKEN_TABLE(PERSONAL_SAVEGAMES)
	TOKEN_TABLE(SCRIPT)
	TOKEN_TABLE(CAPTION)
	TOKEN_TABLE(PROPERTY)
	TOKEN_TABLE(SUBTITLES_SPEED)
	TOKEN_TABLE(SUBTITLES)
	TOKEN_TABLE(VIDEO_SUBTITLES)
	TOKEN_TABLE(EDITOR_PROPERTY)
	TOKEN_TABLE(THUMBNAIL_WIDTH)
	TOKEN_TABLE(THUMBNAIL_HEIGHT)
	TOKEN_TABLE(INDICATOR_X)
	TOKEN_TABLE(INDICATOR_Y)
	TOKEN_TABLE(INDICATOR_WIDTH)
	TOKEN_TABLE(INDICATOR_HEIGHT)
	TOKEN_TABLE(INDICATOR_COLOR)
	TOKEN_TABLE(SAVE_IMAGE_X)
	TOKEN_TABLE(SAVE_IMAGE_Y)
	TOKEN_TABLE(SAVE_IMAGE)
	TOKEN_TABLE(LOAD_IMAGE_X)
	TOKEN_TABLE(LOAD_IMAGE_Y)
	TOKEN_TABLE(LOAD_IMAGE)
	TOKEN_TABLE(LOCAL_SAVE_DIR)
	TOKEN_TABLE(COMPAT_KILL_METHOD_THREADS)
	TOKEN_TABLE_END

	// Declare a few variables necessary for moving data from these settings over to the renderer:
	// The values are the same as the defaults set in BaseRenderer.
	int loadImageX = 0;
	int loadImageY = 0;
	int saveImageX = 0;
	int saveImageY = 0;
	int indicatorX = -1;
	int indicatorY = -1;
	int indicatorWidth = -1;
	int indicatorHeight = 8;
	uint32 indicatorColor = BYTETORGBA(255, 0, 0, 128);
	Common::String loadImageName = "";
	Common::String saveImageName = "";

	char *params;
	int cmd;
	BaseParser parser;

	if (complete) {
		if (parser.getCommand(&buffer, commands, &params) != TOKEN_GAME) {
			_gameRef->LOG(0, "'GAME' keyword expected.");
			return STATUS_FAILED;
		}
		buffer = params;
	}

	while ((cmd = parser.getCommand(&buffer, commands, &params)) > 0) {
		switch (cmd) {
		case TOKEN_TEMPLATE:
			if (DID_FAIL(loadFile(params))) {
				cmd = PARSERR_GENERIC;
			}
			break;

		case TOKEN_NAME:
			setName(params);
			break;

		case TOKEN_CAPTION:
			setCaption(params);
			break;

		case TOKEN_SYSTEM_FONT:
			if (_systemFont) {
				_fontStorage->removeFont(_systemFont);
			}
			_systemFont = nullptr;

			_systemFont = _gameRef->_fontStorage->addFont(params);
			break;

		case TOKEN_VIDEO_FONT:
			if (_videoFont) {
				_fontStorage->removeFont(_videoFont);
			}
			_videoFont = nullptr;

			_videoFont = _gameRef->_fontStorage->addFont(params);
			break;


		case TOKEN_CURSOR:
			delete _cursor;
			_cursor = new BaseSprite(_gameRef);
			if (!_cursor || DID_FAIL(_cursor->loadFile(params))) {
				delete _cursor;
				_cursor = nullptr;
				cmd = PARSERR_GENERIC;
			}
			break;

		case TOKEN_ACTIVE_CURSOR:
			delete _activeCursor;
			_activeCursor = nullptr;
			_activeCursor = new BaseSprite(_gameRef);
			if (!_activeCursor || DID_FAIL(_activeCursor->loadFile(params))) {
				delete _activeCursor;
				_activeCursor = nullptr;
				cmd = PARSERR_GENERIC;
			}
			break;

		case TOKEN_NONINTERACTIVE_CURSOR:
			delete _cursorNoninteractive;
			_cursorNoninteractive = new BaseSprite(_gameRef);
			if (!_cursorNoninteractive || DID_FAIL(_cursorNoninteractive->loadFile(params))) {
				delete _cursorNoninteractive;
				_cursorNoninteractive = nullptr;
				cmd = PARSERR_GENERIC;
			}
			break;

		case TOKEN_SCRIPT:
			addScript(params);
			break;

		case TOKEN_PERSONAL_SAVEGAMES:
			parser.scanStr(params, "%b", &_personalizedSave);
			break;

		case TOKEN_SUBTITLES:
			parser.scanStr(params, "%b", &_subtitles);
			break;

		case TOKEN_SUBTITLES_SPEED:
			parser.scanStr(params, "%d", &_subtitlesSpeed);
			break;

		case TOKEN_VIDEO_SUBTITLES:
			parser.scanStr(params, "%b", &_videoSubtitles);
			break;

		case TOKEN_PROPERTY:
			parseProperty(params, false);
			break;

		case TOKEN_EDITOR_PROPERTY:
			parseEditorProperty(params, false);
			break;

		case TOKEN_THUMBNAIL_WIDTH:
			parser.scanStr(params, "%d", &_thumbnailWidth);
			break;

		case TOKEN_THUMBNAIL_HEIGHT:
			parser.scanStr(params, "%d", &_thumbnailHeight);
			break;

		case TOKEN_INDICATOR_X:
			parser.scanStr(params, "%d", &indicatorX);
			break;

		case TOKEN_INDICATOR_Y:
			parser.scanStr(params, "%d", &indicatorY);
			break;

		case TOKEN_INDICATOR_COLOR: {
			int r, g, b, a;
			parser.scanStr(params, "%d,%d,%d,%d", &r, &g, &b, &a);
			indicatorColor = BYTETORGBA(r, g, b, a);
		}
		break;

		case TOKEN_INDICATOR_WIDTH:
			parser.scanStr(params, "%d", &indicatorWidth);
			break;

		case TOKEN_INDICATOR_HEIGHT:
			parser.scanStr(params, "%d", &indicatorHeight);
			break;

		case TOKEN_SAVE_IMAGE:
			saveImageName = params;
			break;

		case TOKEN_SAVE_IMAGE_X:
			parser.scanStr(params, "%d", &saveImageX);
			break;

		case TOKEN_SAVE_IMAGE_Y:
			parser.scanStr(params, "%d", &saveImageY);
			break;

		case TOKEN_LOAD_IMAGE:
			loadImageName = params;
			break;

		case TOKEN_LOAD_IMAGE_X:
			parser.scanStr(params, "%d", &loadImageX);
			break;

		case TOKEN_LOAD_IMAGE_Y:
			parser.scanStr(params, "%d", &loadImageY);
			break;

		case TOKEN_LOCAL_SAVE_DIR:
			_localSaveDir = params;
			break;

		case TOKEN_COMPAT_KILL_METHOD_THREADS:
			parser.scanStr(params, "%b", &_compatKillMethodThreads);
			break;
		}
	}

	_renderer->setIndicator(indicatorWidth, indicatorHeight, indicatorX, indicatorY, indicatorColor);
	_renderer->initIndicator(); // In case we just reset the values.
	_renderer->setSaveImage(saveImageName.c_str(), saveImageX, saveImageY);
	_renderer->setLoadingScreen(loadImageName.c_str(), loadImageX, loadImageY);

	if (!_systemFont) {
		_systemFont = _gameRef->_fontStorage->addFont("system_font.fnt");
	}


	if (cmd == PARSERR_TOKENNOTFOUND) {
		_gameRef->LOG(0, "Syntax error in GAME definition");
		return STATUS_FAILED;
	}
	if (cmd == PARSERR_GENERIC) {
		_gameRef->LOG(0, "Error loading GAME definition");
		return STATUS_FAILED;
	}

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
// high level scripting interface
//////////////////////////////////////////////////////////////////////////
bool BaseGame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
	//////////////////////////////////////////////////////////////////////////
	// LOG
	//////////////////////////////////////////////////////////////////////////
	if (strcmp(name, "LOG") == 0) {
		stack->correctParams(1);
		LOG(0, stack->pop()->getString());
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// Caption
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Caption") == 0) {
		bool res = BaseObject::scCallMethod(script, stack, thisStack, name);
		setWindowTitle();
		return res;
	}

	//////////////////////////////////////////////////////////////////////////
	// Msg
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Msg") == 0) {
		stack->correctParams(1);
		quickMessage(stack->pop()->getString());
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// RunScript
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "RunScript") == 0) {
		_gameRef->LOG(0, "**Warning** The 'RunScript' method is now obsolete. Use 'AttachScript' instead (same syntax)");
		stack->correctParams(1);
		if (DID_FAIL(addScript(stack->pop()->getString()))) {
			stack->pushBool(false);
		} else {
			stack->pushBool(true);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// LoadStringTable
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "LoadStringTable") == 0) {
		stack->correctParams(2);
		const char *filename = stack->pop()->getString();
		ScValue *val = stack->pop();

		bool clearOld;
		if (val->isNULL()) {
			clearOld = true;
		} else {
			clearOld = val->getBool();
		}

		if (DID_FAIL(_settings->loadStringTable(filename, clearOld))) {
			stack->pushBool(false);
		} else {
			stack->pushBool(true);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// ValidObject
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ValidObject") == 0) {
		stack->correctParams(1);
		BaseScriptable *obj = stack->pop()->getNative();
		if (validObject((BaseObject *) obj)) {
			stack->pushBool(true);
		} else {
			stack->pushBool(false);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// Reset
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Reset") == 0) {
		stack->correctParams(0);
		resetContent();
		stack->pushNULL();

		return STATUS_OK;
	}


	//////////////////////////////////////////////////////////////////////////
	// UnloadObject
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "UnloadObject") == 0) {
		stack->correctParams(1);
		ScValue *val = stack->pop();
		BaseObject *obj = (BaseObject *)val->getNative();
		unregisterObject(obj);
		if (val->getType() == VAL_VARIABLE_REF) {
			val->setNULL();
		}

		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// LoadWindow
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "LoadWindow") == 0) {
		stack->correctParams(1);
		UIWindow *win = new UIWindow(_gameRef);
		if (win && DID_SUCCEED(win->loadFile(stack->pop()->getString()))) {
			_windows.add(win);
			registerObject(win);
			stack->pushNative(win, true);
		} else {
			delete win;
			win = nullptr;
			stack->pushNULL();
		}
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// ExpandString
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ExpandString") == 0) {
		stack->correctParams(1);
		ScValue *val = stack->pop();
		char *str = new char[strlen(val->getString()) + 1];
		strcpy(str, val->getString());
		expandStringByStringTable(&str);
		stack->pushString(str);
		delete[] str;
		return STATUS_OK;
	}

	else if (_musicSystem->scCallMethod(script, stack, thisStack, name) == STATUS_OK) {
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SetMousePos
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SetMousePos") == 0) {
		stack->correctParams(2);
		int32 x = stack->pop()->getInt();
		int32 y = stack->pop()->getInt();
		x = MAX<int32>(x, 0);
		x = MIN(x, _renderer->getWidth());
		y = MAX<int32>(y, 0);
		y = MIN(y, _renderer->getHeight());
		Point32 p;
		p.x = x + _renderer->_drawOffsetX;
		p.y = y + _renderer->_drawOffsetY;

		BasePlatform::setCursorPos(p.x, p.y);

		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// LockMouseRect
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "LockMouseRect") == 0) {
		stack->correctParams(4);
		int left = stack->pop()->getInt();
		int top = stack->pop()->getInt();
		int right = stack->pop()->getInt();
		int bottom = stack->pop()->getInt();

		if (right < left) {
			BaseUtils::swap(&left, &right);
		}
		if (bottom < top) {
			BaseUtils::swap(&top, &bottom);
		}

		_mouseLockRect.setRect(left, top, right, bottom);

		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// PlayVideo
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "PlayVideo") == 0) {
		_gameRef->LOG(0, "Warning: Game.PlayVideo() is now deprecated. Use Game.PlayTheora() instead.");

		stack->correctParams(6);
		const char *filename = stack->pop()->getString();
		warning("PlayVideo: %s - not implemented yet", filename);
		ScValue *valType = stack->pop();
		int type;
		if (valType->isNULL()) {
			type = (int)VID_PLAY_STRETCH;
		} else {
			type = valType->getInt();
		}

		int xVal = stack->pop()->getInt();
		int yVal = stack->pop()->getInt();
		bool freezeMusic = stack->pop()->getBool(true);

		ScValue *valSub = stack->pop();
		const char *subtitleFile = valSub->isNULL() ? nullptr : valSub->getString();

		if (type < (int)VID_PLAY_POS || type > (int)VID_PLAY_CENTER) {
			type = (int)VID_PLAY_STRETCH;
		}

		if (DID_SUCCEED(_gameRef->_videoPlayer->initialize(filename, subtitleFile))) {
			if (DID_SUCCEED(_gameRef->_videoPlayer->play((TVideoPlayback)type, xVal, yVal, freezeMusic))) {
				stack->pushBool(true);
				script->sleep(0);
			} else {
				stack->pushBool(false);
			}
		} else {
			stack->pushBool(false);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// PlayTheora
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "PlayTheora") == 0) {
		stack->correctParams(7);
		const char *filename = stack->pop()->getString();
		ScValue *valType = stack->pop();
		int type;
		if (valType->isNULL()) {
			type = (int)VID_PLAY_STRETCH;
		} else {
			type = valType->getInt();
		}

		int xVal = stack->pop()->getInt();
		int yVal = stack->pop()->getInt();
		bool freezeMusic = stack->pop()->getBool(true);
		bool dropFrames = stack->pop()->getBool(true);

		ScValue *valSub = stack->pop();
		const char *subtitleFile = valSub->isNULL() ? nullptr : valSub->getString();

		if (type < (int)VID_PLAY_POS || type > (int)VID_PLAY_CENTER) {
			type = (int)VID_PLAY_STRETCH;
		}

		delete _theoraPlayer;
		_theoraPlayer = new VideoTheoraPlayer(this);
		if (_theoraPlayer && DID_SUCCEED(_theoraPlayer->initialize(filename, subtitleFile))) {
			_theoraPlayer->_dontDropFrames = !dropFrames;
			if (DID_SUCCEED(_theoraPlayer->play((TVideoPlayback)type, xVal, yVal, true, freezeMusic))) {
				stack->pushBool(true);
				script->sleep(0);
			} else {
				stack->pushBool(false);
			}
		} else {
			stack->pushBool(false);
			delete _theoraPlayer;
			_theoraPlayer = nullptr;
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// QuitGame
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "QuitGame") == 0) {
		stack->correctParams(0);
		stack->pushNULL();
		_quitting = true;
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// RegWriteNumber
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "RegWriteNumber") == 0) {
		stack->correctParams(2);
		const char *key = stack->pop()->getString();
		int val = stack->pop()->getInt();
		Common::String privKey = "priv_" + StringUtil::encodeSetting(key);
		ConfMan.setInt(privKey, val);
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// RegReadNumber
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "RegReadNumber") == 0) {
		stack->correctParams(2);
		const char *key = stack->pop()->getString();
		int initVal = stack->pop()->getInt();
		Common::String privKey = "priv_" + StringUtil::encodeSetting(key);
		int result = initVal;
		if (ConfMan.hasKey(privKey)) {
			result = ConfMan.getInt(privKey);
		}
		stack->pushInt(result);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// RegWriteString
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "RegWriteString") == 0) {
		stack->correctParams(2);
		const char *key = stack->pop()->getString();
		const char *val = stack->pop()->getString();
		Common::String privKey = "wme_" + StringUtil::encodeSetting(key);
		Common::String privVal = StringUtil::encodeSetting(val);
		ConfMan.set(privKey, privVal);
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// RegReadString
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "RegReadString") == 0) {
		stack->correctParams(2);
		const char *key = stack->pop()->getString();
		const char *initVal = stack->pop()->getString();
		Common::String result = readRegistryString(key, initVal);
		stack->pushString(result.c_str());
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SaveGame
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SaveGame") == 0) {
		stack->correctParams(3);
		int slot = stack->pop()->getInt();
		const char *xdesc = stack->pop()->getString();
		bool quick = stack->pop()->getBool(false);

		char *desc = new char[strlen(xdesc) + 1];
		strcpy(desc, xdesc);
		stack->pushBool(true);
		if (DID_FAIL(saveGame(slot, desc, quick))) {
			stack->pop();
			stack->pushBool(false);
		}
		delete[] desc;
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// LoadGame
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "LoadGame") == 0) {
		stack->correctParams(1);
		_scheduledLoadSlot = stack->pop()->getInt();
		_loading = true;
		stack->pushBool(false);
		script->sleep(0);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// IsSaveSlotUsed
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "IsSaveSlotUsed") == 0) {
		stack->correctParams(1);
		int slot = stack->pop()->getInt();
		stack->pushBool(SaveLoad::isSaveSlotUsed(slot));
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetSaveSlotDescription
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetSaveSlotDescription") == 0) {
		stack->correctParams(1);
		int slot = stack->pop()->getInt();
		char desc[512];
		desc[0] = '\0';
		SaveLoad::getSaveSlotDescription(slot, desc);
		stack->pushString(desc);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// EmptySaveSlot
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "EmptySaveSlot") == 0) {
		stack->correctParams(1);
		int slot = stack->pop()->getInt();
		SaveLoad::emptySaveSlot(slot);
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SetGlobalSFXVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SetGlobalSFXVolume") == 0) {
		stack->correctParams(1);
		_gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSFXSoundType, (byte)stack->pop()->getInt());
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SetGlobalSpeechVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SetGlobalSpeechVolume") == 0) {
		stack->correctParams(1);
		_gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSpeechSoundType, (byte)stack->pop()->getInt());
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SetGlobalMusicVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SetGlobalMusicVolume") == 0) {
		stack->correctParams(1);
		_gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kMusicSoundType, (byte)stack->pop()->getInt());
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SetGlobalMasterVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SetGlobalMasterVolume") == 0) {
		stack->correctParams(1);
		_gameRef->_soundMgr->setMasterVolumePercent((byte)stack->pop()->getInt());
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetGlobalSFXVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetGlobalSFXVolume") == 0) {
		stack->correctParams(0);
		stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType));
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetGlobalSpeechVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetGlobalSpeechVolume") == 0) {
		stack->correctParams(0);
		stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kSpeechSoundType));
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetGlobalMusicVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetGlobalMusicVolume") == 0) {
		stack->correctParams(0);
		stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kMusicSoundType));
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetGlobalMasterVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetGlobalMasterVolume") == 0) {
		stack->correctParams(0);
		stack->pushInt(_soundMgr->getMasterVolumePercent());
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SetActiveCursor
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SetActiveCursor") == 0) {
		stack->correctParams(1);
		if (DID_SUCCEED(setActiveCursor(stack->pop()->getString()))) {
			stack->pushBool(true);
		} else {
			stack->pushBool(false);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetActiveCursor
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetActiveCursor") == 0) {
		stack->correctParams(0);
		if (!_activeCursor || !_activeCursor->getFilename()) {
			stack->pushNULL();
		} else {
			stack->pushString(_activeCursor->getFilename());
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetActiveCursorObject
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetActiveCursorObject") == 0) {
		stack->correctParams(0);
		if (!_activeCursor) {
			stack->pushNULL();
		} else {
			stack->pushNative(_activeCursor, true);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// RemoveActiveCursor
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "RemoveActiveCursor") == 0) {
		stack->correctParams(0);
		delete _activeCursor;
		_activeCursor = nullptr;
		stack->pushNULL();

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// HasActiveCursor
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "HasActiveCursor") == 0) {
		stack->correctParams(0);

		if (_activeCursor) {
			stack->pushBool(true);
		} else {
			stack->pushBool(false);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// FileExists
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "FileExists") == 0) {
		stack->correctParams(1);
		const char *filename = stack->pop()->getString();

		bool exists = BaseFileManager::getEngineInstance()->hasFile(filename); // Had absPathWarning = false
		stack->pushBool(exists);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// FadeOut / FadeOutAsync / SystemFadeOut / SystemFadeOutAsync
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "FadeOut") == 0 || strcmp(name, "FadeOutAsync") == 0 || strcmp(name, "SystemFadeOut") == 0 || strcmp(name, "SystemFadeOutAsync") == 0) {
		stack->correctParams(5);
		uint32 duration = stack->pop()->getInt(500);
		byte red = stack->pop()->getInt(0);
		byte green = stack->pop()->getInt(0);
		byte blue = stack->pop()->getInt(0);
		byte alpha = stack->pop()->getInt(0xFF);

		bool system = (strcmp(name, "SystemFadeOut") == 0 || strcmp(name, "SystemFadeOutAsync") == 0);

		_fader->fadeOut(BYTETORGBA(red, green, blue, alpha), duration, system);
		if (strcmp(name, "FadeOutAsync") != 0 && strcmp(name, "SystemFadeOutAsync") != 0) {
			script->waitFor(_fader);
		}

		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// FadeIn / FadeInAsync / SystemFadeIn / SystemFadeInAsync
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "FadeIn") == 0 || strcmp(name, "FadeInAsync") == 0 || strcmp(name, "SystemFadeIn") == 0 || strcmp(name, "SystemFadeInAsync") == 0) {
		stack->correctParams(5);
		uint32 duration = stack->pop()->getInt(500);
		byte red = stack->pop()->getInt(0);
		byte green = stack->pop()->getInt(0);
		byte blue = stack->pop()->getInt(0);
		byte alpha = stack->pop()->getInt(0xFF);

		bool system = (strcmp(name, "SystemFadeIn") == 0 || strcmp(name, "SystemFadeInAsync") == 0);

		_fader->fadeIn(BYTETORGBA(red, green, blue, alpha), duration, system);
		if (strcmp(name, "FadeInAsync") != 0 && strcmp(name, "SystemFadeInAsync") != 0) {
			script->waitFor(_fader);
		}

		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetFadeColor
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetFadeColor") == 0) {
		stack->correctParams(0);
		stack->pushInt(_fader->getCurrentColor());
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// Screenshot
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Screenshot") == 0) {
		stack->correctParams(1);
		char filename[MAX_PATH_LENGTH];

		ScValue *val = stack->pop();

		warning("BGame::ScCallMethod - Screenshot not reimplemented"); //TODO
		int fileNum = 0;

		while (true) {
			sprintf(filename, "%s%03d.bmp", val->isNULL() ? getName() : val->getString(), fileNum);
			if (!Common::File::exists(filename)) {
				break;
			}
			fileNum++;
		}

		bool ret = _gameRef->_renderer->saveScreenShot(filename);

		stack->pushBool(ret);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// ScreenshotEx
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ScreenshotEx") == 0) {
		stack->correctParams(3);
		const char *filename = stack->pop()->getString();
		int sizeX = stack->pop()->getInt(_renderer->getWidth());
		int sizeY = stack->pop()->getInt(_renderer->getHeight());

		bool ret = _gameRef->_renderer->saveScreenShot(filename, sizeX, sizeY);

		stack->pushBool(ret);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// CreateWindow
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "CreateWindow") == 0) {
		stack->correctParams(1);
		ScValue *val = stack->pop();

		UIWindow *win = new UIWindow(_gameRef);
		_windows.add(win);
		registerObject(win);
		if (!val->isNULL()) {
			win->setName(val->getString());
		}
		stack->pushNative(win, true);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// DeleteWindow
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "DeleteWindow") == 0) {
		stack->correctParams(1);
		BaseObject *obj = (BaseObject *)stack->pop()->getNative();
		for (uint32 i = 0; i < _windows.size(); i++) {
			if (_windows[i] == obj) {
				unregisterObject(_windows[i]);
				stack->pushBool(true);
				return STATUS_OK;
			}
		}
		stack->pushBool(false);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// OpenDocument
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "OpenDocument") == 0) {
		stack->correctParams(0);
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// DEBUG_DumpClassRegistry
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "DEBUG_DumpClassRegistry") == 0) {
		stack->correctParams(0);
		DEBUG_DumpClassRegistry();
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SetLoadingScreen
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SetLoadingScreen") == 0) {
		stack->correctParams(3);
		ScValue *val = stack->pop();
		int loadImageX = stack->pop()->getInt();
		int loadImageY = stack->pop()->getInt();

		if (val->isNULL()) {
			_renderer->setLoadingScreen(NULL, loadImageX, loadImageY);
		} else {
			_renderer->setLoadingScreen(val->getString(), loadImageX, loadImageY);
		}
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SetSavingScreen
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SetSavingScreen") == 0) {
		stack->correctParams(3);
		ScValue *val = stack->pop();
		int saveImageX = stack->pop()->getInt();
		int saveImageY = stack->pop()->getInt();

		if (val->isNULL()) {
			_renderer->setSaveImage(NULL, saveImageX, saveImageY);
		} else {
			_renderer->setSaveImage(NULL, saveImageX, saveImageY);
		}
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SetWaitCursor
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SetWaitCursor") == 0) {
		stack->correctParams(1);
		if (DID_SUCCEED(setWaitCursor(stack->pop()->getString()))) {
			stack->pushBool(true);
		} else {
			stack->pushBool(false);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// RemoveWaitCursor
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "RemoveWaitCursor") == 0) {
		stack->correctParams(0);
		delete _cursorNoninteractive;
		_cursorNoninteractive = nullptr;

		stack->pushNULL();

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetWaitCursor
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetWaitCursor") == 0) {
		stack->correctParams(0);
		if (!_cursorNoninteractive || !_cursorNoninteractive->getFilename()) {
			stack->pushNULL();
		} else {
			stack->pushString(_cursorNoninteractive->getFilename());
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetWaitCursorObject
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetWaitCursorObject") == 0) {
		stack->correctParams(0);
		if (!_cursorNoninteractive) {
			stack->pushNULL();
		} else {
			stack->pushNative(_cursorNoninteractive, true);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// ClearScriptCache
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ClearScriptCache") == 0) {
		stack->correctParams(0);
		stack->pushBool(DID_SUCCEED(_scEngine->emptyScriptCache()));
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// DisplayLoadingIcon
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "DisplayLoadingIcon") == 0) {
		stack->correctParams(4);

		const char *filename = stack->pop()->getString();
		_loadingIconX = stack->pop()->getInt();
		_loadingIconY = stack->pop()->getInt();
		_loadingIconPersistent = stack->pop()->getBool();

		delete _loadingIcon;
		_loadingIcon = new BaseSprite(this);
		if (!_loadingIcon || DID_FAIL(_loadingIcon->loadFile(filename))) {
			delete _loadingIcon;
			_loadingIcon = nullptr;
		} else {
			displayContent(false, true);
			_gameRef->_renderer->flip();
			_gameRef->_renderer->initLoop();
		}
		stack->pushNULL();

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// HideLoadingIcon
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "HideLoadingIcon") == 0) {
		stack->correctParams(0);
		delete _loadingIcon;
		_loadingIcon = nullptr;
		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// DumpTextureStats
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "DumpTextureStats") == 0) {
		stack->correctParams(1);
		const char *filename = stack->pop()->getString();

		_renderer->dumpData(filename);

		stack->pushNULL();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// AccOutputText
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "AccOutputText") == 0) {
		stack->correctParams(2);
		/* const char *str = */	stack->pop()->getString();
		/* int type = */ stack->pop()->getInt();
		// do nothing
		stack->pushNULL();

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// StoreSaveThumbnail
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "StoreSaveThumbnail") == 0) {
		stack->correctParams(0);
		delete _cachedThumbnail;
		_cachedThumbnail = new SaveThumbHelper(this);
		if (DID_FAIL(_cachedThumbnail->storeThumbnail())) {
			delete _cachedThumbnail;
			_cachedThumbnail = nullptr;
			stack->pushBool(false);
		} else {
			stack->pushBool(true);
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// DeleteSaveThumbnail
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "DeleteSaveThumbnail") == 0) {
		stack->correctParams(0);
		delete _cachedThumbnail;
		_cachedThumbnail = nullptr;
		stack->pushNULL();

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// GetFileChecksum
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetFileChecksum") == 0) {
		stack->correctParams(2);
		const char *filename = stack->pop()->getString();
		bool asHex = stack->pop()->getBool(false);

		Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(filename, false);
		if (file) {
			crc remainder = crc_initialize();
			byte buf[1024];
			int bytesRead = 0;

			while (bytesRead < file->size()) {
				int bufSize = MIN((uint32)1024, (uint32)(file->size() - bytesRead));
				bytesRead += file->read(buf, bufSize);

				for (int i = 0; i < bufSize; i++) {
					remainder = crc_process_byte(buf[i], remainder);
				}
			}
			crc checksum = crc_finalize(remainder);

			if (asHex) {
				char hex[100];
				sprintf(hex, "%x", checksum);
				stack->pushString(hex);
			} else {
				stack->pushInt(checksum);
			}

			BaseFileManager::getEngineInstance()->closeFile(file);
			file = nullptr;
		} else {
			stack->pushNULL();
		}

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// EnableScriptProfiling
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "EnableScriptProfiling") == 0) {
		stack->correctParams(0);
		_scEngine->enableProfiling();
		stack->pushNULL();

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// DisableScriptProfiling
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "DisableScriptProfiling") == 0) {
		stack->correctParams(0);
		_scEngine->disableProfiling();
		stack->pushNULL();

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// ShowStatusLine
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ShowStatusLine") == 0) {
		stack->correctParams(0);
		// Block kept to show intention of opcode.
		/*#ifdef __IPHONEOS__
		        IOS_ShowStatusLine(TRUE);
		#endif*/
		stack->pushNULL();

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// HideStatusLine
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "HideStatusLine") == 0) {
		stack->correctParams(0);
		// Block kept to show intention of opcode.
		/*#ifdef __IPHONEOS__
		        IOS_ShowStatusLine(FALSE);
		#endif*/
		stack->pushNULL();

		return STATUS_OK;
	} else {
		return BaseObject::scCallMethod(script, stack, thisStack, name);
	}
}


//////////////////////////////////////////////////////////////////////////
ScValue *BaseGame::scGetProperty(const Common::String &name) {
	_scValue->setNULL();

	//////////////////////////////////////////////////////////////////////////
	// Type
	//////////////////////////////////////////////////////////////////////////
	if (name == "Type") {
		_scValue->setString("game");
		return _scValue;
	}
	//////////////////////////////////////////////////////////////////////////
	// Name
	//////////////////////////////////////////////////////////////////////////
	else if (name == "Name") {
		_scValue->setString(getName());
		return _scValue;
	}
	//////////////////////////////////////////////////////////////////////////
	// Hwnd (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "Hwnd") {
		_scValue->setInt((int)_renderer->_window);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// CurrentTime (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "CurrentTime") {
		_scValue->setInt((int)getTimer()->getTime());
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// WindowsTime (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "WindowsTime") {
		_scValue->setInt((int)g_system->getMillis());
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// WindowedMode (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "WindowedMode") {
		_scValue->setBool(_renderer->isWindowed());
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// MouseX
	//////////////////////////////////////////////////////////////////////////
	else if (name == "MouseX") {
		_scValue->setInt(_mousePos.x);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// MouseY
	//////////////////////////////////////////////////////////////////////////
	else if (name == "MouseY") {
		_scValue->setInt(_mousePos.y);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// MainObject
	//////////////////////////////////////////////////////////////////////////
	else if (name == "MainObject") {
		_scValue->setNative(_mainObject, true);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// ActiveObject (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "ActiveObject") {
		_scValue->setNative(_activeObject, true);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// ScreenWidth (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "ScreenWidth") {
		_scValue->setInt(_renderer->getWidth());
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// ScreenHeight (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "ScreenHeight") {
		_scValue->setInt(_renderer->getHeight());
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// Interactive
	//////////////////////////////////////////////////////////////////////////
	else if (name == "Interactive") {
		_scValue->setBool(_interactive);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// DebugMode (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "DebugMode") {
		_scValue->setBool(_debugDebugMode);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// SoundAvailable (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "SoundAvailable") {
		_scValue->setBool(_soundMgr->_soundAvailable);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// SFXVolume
	//////////////////////////////////////////////////////////////////////////
	else if (name == "SFXVolume") {
		_gameRef->LOG(0, "**Warning** The SFXVolume attribute is obsolete");
		_scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType));
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// SpeechVolume
	//////////////////////////////////////////////////////////////////////////
	else if (name == "SpeechVolume") {
		_gameRef->LOG(0, "**Warning** The SpeechVolume attribute is obsolete");
		_scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kSpeechSoundType));
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// MusicVolume
	//////////////////////////////////////////////////////////////////////////
	else if (name == "MusicVolume") {
		_gameRef->LOG(0, "**Warning** The MusicVolume attribute is obsolete");
		_scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kMusicSoundType));
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// MasterVolume
	//////////////////////////////////////////////////////////////////////////
	else if (name == "MasterVolume") {
		_gameRef->LOG(0, "**Warning** The MasterVolume attribute is obsolete");
		_scValue->setInt(_soundMgr->getMasterVolumePercent());
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// Keyboard (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "Keyboard") {
		if (_keyboardState) {
			_scValue->setNative(_keyboardState, true);
		} else {
			_scValue->setNULL();
		}

		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// Subtitles
	//////////////////////////////////////////////////////////////////////////
	else if (name == "Subtitles") {
		_scValue->setBool(_subtitles);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// SubtitlesSpeed
	//////////////////////////////////////////////////////////////////////////
	else if (name == "SubtitlesSpeed") {
		_scValue->setInt(_subtitlesSpeed);
		return _scValue;
	}
	//////////////////////////////////////////////////////////////////////////
	// VideoSubtitles
	//////////////////////////////////////////////////////////////////////////
	else if (name == "VideoSubtitles") {
		_scValue->setBool(_videoSubtitles);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// FPS (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "FPS") {
		_scValue->setInt(_fps);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// AcceleratedMode / Accelerated (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "AcceleratedMode" || name == "Accelerated") {
		_scValue->setBool(_useD3D);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// TextEncoding
	//////////////////////////////////////////////////////////////////////////
	else if (name == "TextEncoding") {
		_scValue->setInt(_textEncoding);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// TextRTL
	//////////////////////////////////////////////////////////////////////////
	else if (name == "TextRTL") {
		_scValue->setBool(_textRTL);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// SoundBufferSize
	//////////////////////////////////////////////////////////////////////////
	else if (name == "SoundBufferSize") {
		_scValue->setInt(_soundBufferSizeSec);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// SuspendedRendering
	//////////////////////////////////////////////////////////////////////////
	else if (name == "SuspendedRendering") {
		_scValue->setBool(_suspendedRendering);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// SuppressScriptErrors
	//////////////////////////////////////////////////////////////////////////
	else if (name == "SuppressScriptErrors") {
		_scValue->setBool(_suppressScriptErrors);
		return _scValue;
	}


	//////////////////////////////////////////////////////////////////////////
	// Frozen
	//////////////////////////////////////////////////////////////////////////
	else if (name == "Frozen") {
		_scValue->setBool(_state == GAME_FROZEN);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// AccTTSEnabled
	//////////////////////////////////////////////////////////////////////////
	else if (name == "AccTTSEnabled") {
		_scValue->setBool(false);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// AccTTSTalk
	//////////////////////////////////////////////////////////////////////////
	else if (name == "AccTTSTalk") {
		_scValue->setBool(false);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// AccTTSCaptions
	//////////////////////////////////////////////////////////////////////////
	else if (name == "AccTTSCaptions") {
		_scValue->setBool(false);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// AccTTSKeypress
	//////////////////////////////////////////////////////////////////////////
	else if (name == "AccTTSKeypress") {
		_scValue->setBool(false);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// AccKeyboardEnabled
	//////////////////////////////////////////////////////////////////////////
	else if (name == "AccKeyboardEnabled") {
		_scValue->setBool(false);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// AccKeyboardCursorSkip
	//////////////////////////////////////////////////////////////////////////
	else if (name == "AccKeyboardCursorSkip") {
		_scValue->setBool(false);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// AccKeyboardPause
	//////////////////////////////////////////////////////////////////////////
	else if (name == "AccKeyboardPause") {
		_scValue->setBool(false);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// AutorunDisabled
	//////////////////////////////////////////////////////////////////////////
	else if (name == "AutorunDisabled") {
		_scValue->setBool(_autorunDisabled);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// SaveDirectory (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "SaveDirectory") {
		AnsiString dataDir = "saves/";	// TODO: This is just to avoid telling the engine actual paths.
		_scValue->setString(dataDir.c_str());
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// AutoSaveOnExit
	//////////////////////////////////////////////////////////////////////////
	else if (name == "AutoSaveOnExit") {
		_scValue->setBool(_autoSaveOnExit);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// AutoSaveSlot
	//////////////////////////////////////////////////////////////////////////
	else if (name == "AutoSaveSlot") {
		_scValue->setInt(_autoSaveSlot);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// CursorHidden
	//////////////////////////////////////////////////////////////////////////
	else if (name == "CursorHidden") {
		_scValue->setBool(_cursorHidden);
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// Platform (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "Platform") {
		_scValue->setString(BasePlatform::getPlatformName().c_str());
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// DeviceType (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "DeviceType") {
		_scValue->setString(getDeviceType().c_str());
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// MostRecentSaveSlot (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "MostRecentSaveSlot") {
		if (!ConfMan.hasKey("most_recent_saveslot")) {
			_scValue->setInt(-1);
		} else {
			_scValue->setInt(ConfMan.getInt("most_recent_saveslot"));
		}
		return _scValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// Store (RO)
	//////////////////////////////////////////////////////////////////////////
	else if (name == "Store") {
		_scValue->setNULL();
		error("Request for a SXStore-object, which is not supported by ScummVM");

		return _scValue;
	} else {
		return BaseObject::scGetProperty(name);
	}
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::scSetProperty(const char *name, ScValue *value) {
	//////////////////////////////////////////////////////////////////////////
	// Name
	//////////////////////////////////////////////////////////////////////////
	if (strcmp(name, "Name") == 0) {
		setName(value->getString());

		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// MouseX
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "MouseX") == 0) {
		_mousePos.x = value->getInt();
		resetMousePos();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// MouseY
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "MouseY") == 0) {
		_mousePos.y = value->getInt();
		resetMousePos();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// Caption
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Name") == 0) {
		bool res = BaseObject::scSetProperty(name, value);
		setWindowTitle();
		return res;
	}

	//////////////////////////////////////////////////////////////////////////
	// MainObject
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "MainObject") == 0) {
		BaseScriptable *obj = value->getNative();
		if (obj == nullptr || validObject((BaseObject *)obj)) {
			_mainObject = (BaseObject *)obj;
		}
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// Interactive
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Interactive") == 0) {
		setInteractive(value->getBool());
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SFXVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SFXVolume") == 0) {
		_gameRef->LOG(0, "**Warning** The SFXVolume attribute is obsolete");
		_gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSFXSoundType, (byte)value->getInt());
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SpeechVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SpeechVolume") == 0) {
		_gameRef->LOG(0, "**Warning** The SpeechVolume attribute is obsolete");
		_gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSpeechSoundType, (byte)value->getInt());
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// MusicVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "MusicVolume") == 0) {
		_gameRef->LOG(0, "**Warning** The MusicVolume attribute is obsolete");
		_gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kMusicSoundType, (byte)value->getInt());
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// MasterVolume
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "MasterVolume") == 0) {
		_gameRef->LOG(0, "**Warning** The MasterVolume attribute is obsolete");
		_gameRef->_soundMgr->setMasterVolumePercent((byte)value->getInt());
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// Subtitles
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Subtitles") == 0) {
		_subtitles = value->getBool();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SubtitlesSpeed
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SubtitlesSpeed") == 0) {
		_subtitlesSpeed = value->getInt();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// VideoSubtitles
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "VideoSubtitles") == 0) {
		_videoSubtitles = value->getBool();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// TextEncoding
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "TextEncoding") == 0) {
		int enc = value->getInt();
		if (enc < 0) {
			enc = 0;
		}
		if (enc >= NUM_TEXT_ENCODINGS) {
			enc = NUM_TEXT_ENCODINGS - 1;
		}
		_textEncoding = (TTextEncoding)enc;
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// TextRTL
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "TextRTL") == 0) {
		_textRTL = value->getBool();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SoundBufferSize
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SoundBufferSize") == 0) {
		_soundBufferSizeSec = value->getInt();
		_soundBufferSizeSec = MAX<int32>(3, _soundBufferSizeSec);
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SuspendedRendering
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SuspendedRendering") == 0) {
		_suspendedRendering = value->getBool();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// SuppressScriptErrors
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SuppressScriptErrors") == 0) {
		_suppressScriptErrors = value->getBool();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// AutorunDisabled
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "AutorunDisabled") == 0) {
		_autorunDisabled = value->getBool();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// AutoSaveOnExit
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "AutoSaveOnExit") == 0) {
		_autoSaveOnExit = value->getBool();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// AutoSaveSlot
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "AutoSaveSlot") == 0) {
		_autoSaveSlot = value->getInt();
		return STATUS_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// CursorHidden
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "CursorHidden") == 0) {
		_cursorHidden = value->getBool();
		return STATUS_OK;
	} else {
		return BaseObject::scSetProperty(name, value);
	}
}


//////////////////////////////////////////////////////////////////////////
const char *BaseGame::scToString() {
	return "[game object]";
}



#define QUICK_MSG_DURATION 3000
//////////////////////////////////////////////////////////////////////////
bool BaseGame::displayQuickMsg() {
	if (_quickMessages.size() == 0 || !_systemFont) {
		return STATUS_OK;
	}

	// update
	for (uint32 i = 0; i < _quickMessages.size(); i++) {
		if (_currentTime - _quickMessages[i]->getStartTime() >= QUICK_MSG_DURATION) {
			delete _quickMessages[i];
			_quickMessages.remove_at(i);
			i--;
		}
	}

	int posY = 20;

	// display
	for (uint32 i = 0; i < _quickMessages.size(); i++) {
		_systemFont->drawText((const byte *)_quickMessages[i]->getText(), 0, posY, _renderer->getWidth());
		posY += _systemFont->getTextHeight((const byte *)_quickMessages[i]->getText(), _renderer->getWidth());
	}
	return STATUS_OK;
}


#define MAX_QUICK_MSG 5
//////////////////////////////////////////////////////////////////////////
void BaseGame::quickMessage(const char *text) {
	if (_quickMessages.size() >= MAX_QUICK_MSG) {
		delete _quickMessages[0];
		_quickMessages.remove_at(0);
	}
	_quickMessages.add(new BaseQuickMsg(_currentTime,  text));
}


//////////////////////////////////////////////////////////////////////////
void BaseGame::quickMessageForm(char *fmt, ...) {
	char buff[256];
	va_list va;

	va_start(va, fmt);
	vsprintf(buff, fmt, va);
	va_end(va);

	quickMessage(buff);
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::registerObject(BaseObject *object) {
	_regObjects.add(object);
	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::unregisterObject(BaseObject *object) {
	if (!object) {
		return STATUS_OK;
	}

	// is it a window?
	for (uint32 i = 0; i < _windows.size(); i++) {
		if ((BaseObject *)_windows[i] == object) {
			_windows.remove_at(i);

			// get new focused window
			if (_focusedWindow == object) {
				_focusedWindow = nullptr;
			}

			break;
		}
	}

	// is it active object?
	if (_activeObject == object) {
		_activeObject = nullptr;
	}

	// is it main object?
	if (_mainObject == object) {
		_mainObject = nullptr;
	}

	// destroy object
	for (uint32 i = 0; i < _regObjects.size(); i++) {
		if (_regObjects[i] == object) {
			_regObjects.remove_at(i);
			if (!_loadInProgress) {
				SystemClassRegistry::getInstance()->enumInstances(invalidateValues, "ScValue", (void *)object);
			}
			delete object;
			return STATUS_OK;
		}
	}

	return STATUS_FAILED;
}


//////////////////////////////////////////////////////////////////////////
void BaseGame::invalidateValues(void *value, void *data) {
	ScValue *val = (ScValue *)value;
	if (val->isNative() && val->getNative() == data) {
		if (!val->_persistent && ((BaseScriptable *)data)->_refCount == 1) {
			((BaseScriptable *)data)->_refCount++;
		}
		val->setNative(nullptr);
		val->setNULL();
	}
}



//////////////////////////////////////////////////////////////////////////
bool BaseGame::validObject(BaseObject *object) {
	if (!object) {
		return false;
	}
	if (object == this) {
		return true;
	}

	for (uint32 i = 0; i < _regObjects.size(); i++) {
		if (_regObjects[i] == object) {
			return true;
		}
	}
	return false;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name) {
	ScValue *thisObj;

	//////////////////////////////////////////////////////////////////////////
	// LOG
	//////////////////////////////////////////////////////////////////////////
	if (strcmp(name, "LOG") == 0) {
		stack->correctParams(1);
		_gameRef->LOG(0, "sc: %s", stack->pop()->getString());
		stack->pushNULL();
	}

	//////////////////////////////////////////////////////////////////////////
	// String
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "String") == 0) {
		thisObj = thisStack->getTop();

		thisObj->setNative(makeSXString(_gameRef,  stack));
		stack->pushNULL();
	}

	//////////////////////////////////////////////////////////////////////////
	// MemBuffer
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "MemBuffer") == 0) {
		thisObj = thisStack->getTop();

		thisObj->setNative(makeSXMemBuffer(_gameRef,  stack));
		stack->pushNULL();
	}

	//////////////////////////////////////////////////////////////////////////
	// File
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "File") == 0) {
		thisObj = thisStack->getTop();

		thisObj->setNative(makeSXFile(_gameRef,  stack));
		stack->pushNULL();
	}

	//////////////////////////////////////////////////////////////////////////
	// Date
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Date") == 0) {
		thisObj = thisStack->getTop();

		thisObj->setNative(makeSXDate(_gameRef,  stack));
		stack->pushNULL();
	}

	//////////////////////////////////////////////////////////////////////////
	// Array
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Array") == 0) {
		thisObj = thisStack->getTop();

		thisObj->setNative(makeSXArray(_gameRef,  stack));
		stack->pushNULL();
	}

	//////////////////////////////////////////////////////////////////////////
	// Object
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Object") == 0) {
		thisObj = thisStack->getTop();

		thisObj->setNative(makeSXObject(_gameRef,  stack));
		stack->pushNULL();
	}

	//////////////////////////////////////////////////////////////////////////
	// Sleep
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Sleep") == 0) {
		stack->correctParams(1);

		script->sleep((uint32)stack->pop()->getInt());
		stack->pushNULL();
	}

	//////////////////////////////////////////////////////////////////////////
	// WaitFor
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "WaitFor") == 0) {
		stack->correctParams(1);

		BaseScriptable *obj = stack->pop()->getNative();
		if (validObject((BaseObject *)obj)) {
			script->waitForExclusive((BaseObject *)obj);
		}
		stack->pushNULL();
	}

	//////////////////////////////////////////////////////////////////////////
	// Random
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Random") == 0) {
		stack->correctParams(2);

		int from = stack->pop()->getInt();
		int to   = stack->pop()->getInt();

		stack->pushInt(BaseUtils::randomInt(from, to));
	}

	//////////////////////////////////////////////////////////////////////////
	// SetScriptTimeSlice
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "SetScriptTimeSlice") == 0) {
		stack->correctParams(1);

		script->_timeSlice = (uint32)stack->pop()->getInt();
		stack->pushNULL();
	}

	//////////////////////////////////////////////////////////////////////////
	// MakeRGBA / MakeRGB / RGB
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "MakeRGBA") == 0 || strcmp(name, "MakeRGB") == 0 || strcmp(name, "RGB") == 0) {
		stack->correctParams(4);
		int r = stack->pop()->getInt();
		int g = stack->pop()->getInt();
		int b = stack->pop()->getInt();
		int a;
		ScValue *val = stack->pop();
		if (val->isNULL()) {
			a = 255;
		} else {
			a = val->getInt();
		}

		stack->pushInt(BYTETORGBA(r, g, b, a));
	}

	//////////////////////////////////////////////////////////////////////////
	// MakeHSL
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "MakeHSL") == 0) {
		stack->correctParams(3);
		int h = stack->pop()->getInt();
		int s = stack->pop()->getInt();
		int l = stack->pop()->getInt();

		stack->pushInt(BaseUtils::HSLtoRGB(h, s, l));
	}

	//////////////////////////////////////////////////////////////////////////
	// GetRValue
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetRValue") == 0) {
		stack->correctParams(1);

		uint32 rgba = (uint32)stack->pop()->getInt();
		stack->pushInt(RGBCOLGetR(rgba));
	}

	//////////////////////////////////////////////////////////////////////////
	// GetGValue
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetGValue") == 0) {
		stack->correctParams(1);

		uint32 rgba = (uint32)stack->pop()->getInt();
		stack->pushInt(RGBCOLGetG(rgba));
	}

	//////////////////////////////////////////////////////////////////////////
	// GetBValue
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetBValue") == 0) {
		stack->correctParams(1);

		uint32 rgba = (uint32)stack->pop()->getInt();
		stack->pushInt(RGBCOLGetB(rgba));
	}

	//////////////////////////////////////////////////////////////////////////
	// GetAValue
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetAValue") == 0) {
		stack->correctParams(1);

		uint32 rgba = (uint32)stack->pop()->getInt();
		stack->pushInt(RGBCOLGetA(rgba));
	}

	//////////////////////////////////////////////////////////////////////////
	// GetHValue
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetHValue") == 0) {
		stack->correctParams(1);
		uint32 rgb = (uint32)stack->pop()->getInt();

		byte H, S, L;
		BaseUtils::RGBtoHSL(rgb, &H, &S, &L);
		stack->pushInt(H);
	}

	//////////////////////////////////////////////////////////////////////////
	// GetSValue
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetSValue") == 0) {
		stack->correctParams(1);
		uint32 rgb = (uint32)stack->pop()->getInt();

		byte H, S, L;
		BaseUtils::RGBtoHSL(rgb, &H, &S, &L);
		stack->pushInt(S);
	}

	//////////////////////////////////////////////////////////////////////////
	// GetLValue
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "GetLValue") == 0) {
		stack->correctParams(1);
		uint32 rgb = (uint32)stack->pop()->getInt();

		byte H, S, L;
		BaseUtils::RGBtoHSL(rgb, &H, &S, &L);
		stack->pushInt(L);
	}

	//////////////////////////////////////////////////////////////////////////
	// Debug
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "Debug") == 0) {
		stack->correctParams(0);
		stack->pushNULL();
	}

	//////////////////////////////////////////////////////////////////////////
	// ToString
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ToString") == 0) {
		stack->correctParams(1);
		const char *str = stack->pop()->getString();
		char *str2 = new char[strlen(str) + 1];
		strcpy(str2, str);
		stack->pushString(str2);
		delete[] str2;
	}

	//////////////////////////////////////////////////////////////////////////
	// ToInt
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ToInt") == 0) {
		stack->correctParams(1);
		int val = stack->pop()->getInt();
		stack->pushInt(val);
	}

	//////////////////////////////////////////////////////////////////////////
	// ToFloat
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ToFloat") == 0) {
		stack->correctParams(1);
		double val = stack->pop()->getFloat();
		stack->pushFloat(val);
	}

	//////////////////////////////////////////////////////////////////////////
	// ToBool
	//////////////////////////////////////////////////////////////////////////
	else if (strcmp(name, "ToBool") == 0) {
		stack->correctParams(1);
		bool val = stack->pop()->getBool();
		stack->pushBool(val);
	}

	//////////////////////////////////////////////////////////////////////////
	// failure
	else {
		script->runtimeError("Call to undefined function '%s'. Ignored.", name);
		stack->correctParams(0);
		stack->pushNULL();
	}

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::showCursor() {
	if (_cursorHidden) {
		return STATUS_OK;
	}

	if (!_interactive && _gameRef->_state == GAME_RUNNING) {
		if (_cursorNoninteractive) {
			return drawCursor(_cursorNoninteractive);
		}
	} else {
		if (_activeObject && !DID_FAIL(_activeObject->showCursor())) {
			return STATUS_OK;
		} else {
			if (_activeObject && _activeCursor && _activeObject->getExtendedFlag("usable")) {
				return drawCursor(_activeCursor);
			} else if (_cursor) {
				return drawCursor(_cursor);
			}
		}
	}
	return STATUS_FAILED;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::saveGame(int32 slot, const char *desc, bool quickSave) {
	return SaveLoad::saveGame(slot, desc, quickSave, _gameRef);
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::loadGame(uint32 slot) {
	//_gameRef->LOG(0, "Load start %d", BaseUtils::GetUsedMemMB());

	_loading = false;
	_scheduledLoadSlot = -1;

	Common::String filename = SaveLoad::getSaveSlotFilename(slot);

	return loadGame(filename.c_str());
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::loadGame(const char *filename) {
	return SaveLoad::loadGame(filename, _gameRef);
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::displayWindows(bool inGame) {
	bool res;

	// did we lose focus? focus topmost window
	if (_focusedWindow == nullptr || !_focusedWindow->isVisible() || _focusedWindow->isDisabled()) {
		_focusedWindow = nullptr;
		for (int i = _windows.size() - 1; i >= 0; i--) {
			if (_windows[i]->isVisible() && !_windows[i]->isDisabled()) {
				_focusedWindow = _windows[i];
				break;
			}
		}
	}

	// display all windows
	for (uint32 i = 0; i < _windows.size(); i++) {
		if (_windows[i]->isVisible() && _windows[i]->getInGame() == inGame) {

			res = _windows[i]->display();
			if (DID_FAIL(res)) {
				return res;
			}
		}
	}

	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::persist(BasePersistenceManager *persistMgr) {
	if (!persistMgr->getIsSaving()) {
		cleanup();
	}

	BaseObject::persist(persistMgr);

	persistMgr->transferPtr(TMEMBER_PTR(_activeObject));
	persistMgr->transferPtr(TMEMBER_PTR(_capturedObject));
	persistMgr->transferPtr(TMEMBER_PTR(_cursorNoninteractive));
	persistMgr->transferBool(TMEMBER(_editorMode));
	persistMgr->transferPtr(TMEMBER_PTR(_fader));
	persistMgr->transferSint32(TMEMBER(_freezeLevel));
	persistMgr->transferPtr(TMEMBER_PTR(_focusedWindow));
	persistMgr->transferPtr(TMEMBER_PTR(_fontStorage));
	persistMgr->transferBool(TMEMBER(_interactive));
	persistMgr->transferPtr(TMEMBER_PTR(_keyboardState));
	persistMgr->transferUint32(TMEMBER(_lastTime));
	persistMgr->transferPtr(TMEMBER_PTR(_mainObject));
	_musicSystem->persistChannels(persistMgr);
	_musicSystem->persistCrossfadeSettings(persistMgr);

	persistMgr->transferSint32(TMEMBER(_offsetX));
	persistMgr->transferSint32(TMEMBER(_offsetY));
	persistMgr->transferFloat(TMEMBER(_offsetPercentX));
	persistMgr->transferFloat(TMEMBER(_offsetPercentY));

	persistMgr->transferBool(TMEMBER(_origInteractive));
	persistMgr->transferSint32(TMEMBER_INT(_origState));
	persistMgr->transferBool(TMEMBER(_personalizedSave));
	persistMgr->transferBool(TMEMBER(_quitting));

	_regObjects.persist(persistMgr);

	persistMgr->transferPtr(TMEMBER_PTR(_scEngine));
	//persistMgr->transfer(TMEMBER(_soundMgr));
	persistMgr->transferSint32(TMEMBER_INT(_state));
	//persistMgr->transfer(TMEMBER(_surfaceStorage));
	persistMgr->transferBool(TMEMBER(_subtitles));
	persistMgr->transferSint32(TMEMBER(_subtitlesSpeed));
	persistMgr->transferPtr(TMEMBER_PTR(_systemFont));
	persistMgr->transferPtr(TMEMBER_PTR(_videoFont));
	persistMgr->transferBool(TMEMBER(_videoSubtitles));

	_timerNormal.persist(persistMgr);
	_timerLive.persist(persistMgr);

	_renderer->persistSaveLoadImages(persistMgr);

	persistMgr->transferSint32(TMEMBER_INT(_textEncoding));
	persistMgr->transferBool(TMEMBER(_textRTL));

	persistMgr->transferSint32(TMEMBER(_soundBufferSizeSec));
	persistMgr->transferBool(TMEMBER(_suspendedRendering));

	persistMgr->transferRect32(TMEMBER(_mouseLockRect));

	_windows.persist(persistMgr);

	persistMgr->transferBool(TMEMBER(_suppressScriptErrors));
	persistMgr->transferBool(TMEMBER(_autorunDisabled));

	persistMgr->transferBool(TMEMBER(_autoSaveOnExit));
	persistMgr->transferUint32(TMEMBER(_autoSaveSlot));
	persistMgr->transferBool(TMEMBER(_cursorHidden));

	if (persistMgr->checkVersion(1, 3, 1)) {
		_settings->persist(persistMgr);
	}

	if (!persistMgr->getIsSaving()) {
		_quitting = false;
	}

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::focusWindow(UIWindow *window) {
	UIWindow *prev = _focusedWindow;

	for (uint32 i = 0; i < _windows.size(); i++) {
		if (_windows[i] == window) {
			if (i < _windows.size() - 1) {
				_windows.remove_at(i);
				_windows.add(window);

				_gameRef->_focusedWindow = window;
			}

			if (window->getMode() == WINDOW_NORMAL && prev != window && _gameRef->validObject(prev) && (prev->getMode() == WINDOW_EXCLUSIVE || prev->getMode() == WINDOW_SYSTEM_EXCLUSIVE)) {
				return focusWindow(prev);
			} else {
				return STATUS_OK;
			}
		}
	}
	return STATUS_FAILED;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::freeze(bool includingMusic) {
	if (_freezeLevel == 0) {
		_scEngine->pauseAll();
		_soundMgr->pauseAll(includingMusic);
		_origState = _state;
		_origInteractive = _interactive;
		_interactive = true;
	}
	_state = GAME_FROZEN;
	_freezeLevel++;

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::unfreeze() {
	if (_freezeLevel == 0) {
		return STATUS_OK;
	}

	_freezeLevel--;
	if (_freezeLevel == 0) {
		_state = _origState;
		_interactive = _origInteractive;
		_scEngine->resumeAll();
		_soundMgr->resumeAll();
	}

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::handleKeypress(Common::Event *event, bool printable) {
	if (isVideoPlaying()) {
		if (event->kbd.keycode == Common::KEYCODE_ESCAPE) {
			stopVideo();
		}
		return true;
	}

	if (event->type == Common::EVENT_QUIT) {
		onWindowClose();
		return true;
	}

	_keyboardState->handleKeyPress(event);
	_keyboardState->readKey(event);
// TODO

	if (_focusedWindow) {
		if (!_gameRef->_focusedWindow->handleKeypress(event, _keyboardState->isCurrentPrintable())) {
			/*if (event->type != SDL_TEXTINPUT) {*/
			if (_gameRef->_focusedWindow->canHandleEvent("Keypress")) {
				_gameRef->_focusedWindow->applyEvent("Keypress");
			} else {
				applyEvent("Keypress");
			}
			/*}*/
		}
		return true;
	} else { /*if (event->type != SDL_TEXTINPUT)*/
		applyEvent("Keypress");
		return true;
	}

	return false;
}

void BaseGame::handleKeyRelease(Common::Event *event) {
	_keyboardState->handleKeyRelease(event);
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::handleMouseWheel(int32 delta) {
	bool handled = false;
	if (_focusedWindow) {
		handled = _gameRef->_focusedWindow->handleMouseWheel(delta);

		if (!handled) {
			if (delta < 0 && _gameRef->_focusedWindow->canHandleEvent("MouseWheelDown")) {
				_gameRef->_focusedWindow->applyEvent("MouseWheelDown");
				handled = true;
			} else if (_gameRef->_focusedWindow->canHandleEvent("MouseWheelUp")) {
				_gameRef->_focusedWindow->applyEvent("MouseWheelUp");
				handled = true;
			}

		}
	}

	if (!handled) {
		if (delta < 0) {
			applyEvent("MouseWheelDown");
		} else {
			applyEvent("MouseWheelUp");
		}
	}

	return true;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor) const {
	if (verMajor) {
		*verMajor = DCGF_VER_MAJOR;
	}
	if (verMinor) {
		*verMinor = DCGF_VER_MINOR;
	}

	if (extMajor) {
		*extMajor = 0;
	}
	if (extMinor) {
		*extMinor = 0;
	}

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
void BaseGame::setWindowTitle() {
	if (_renderer) {
		char title[512];
		Common::strlcpy(title, _caption[0], 512);
		if (title[0] != '\0') {
			Common::strlcat(title, " - ", 512);
		}
		Common::strlcat(title, "WME Lite", 512);


		Utf8String utf8Title;
		if (_textEncoding == TEXT_UTF8) {
			utf8Title = Utf8String(title);
		} else {
			warning("BaseGame::SetWindowTitle - Ignoring textencoding");
			utf8Title = Utf8String(title);
			/*          WideString wstr = StringUtil::AnsiToWide(Title);
			            title = StringUtil::WideToUtf8(wstr);*/
		}
		warning("BaseGame::SetWindowTitle: Ignoring value: %s", utf8Title.c_str());
	}
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::setActiveObject(BaseObject *obj) {
	// not-active when game is frozen
	if (obj && !_gameRef->_interactive && !obj->_nonIntMouseEvents) {
		obj = nullptr;
	}

	if (obj == _activeObject) {
		return STATUS_OK;
	}

	if (_activeObject) {
		_activeObject->applyEvent("MouseLeave");
	}
	//if (ValidObject(_activeObject)) _activeObject->applyEvent("MouseLeave");
	_activeObject = obj;
	if (_activeObject) {
		_activeObject->applyEvent("MouseEntry");
	}

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::pushViewport(BaseViewport *viewport) {
	_viewportSP++;
	if (_viewportSP >= (int32)_viewportStack.size()) {
		_viewportStack.add(viewport);
	} else {
		_viewportStack[_viewportSP] = viewport;
	}

	_renderer->setViewport(viewport->getRect());

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::popViewport() {
	_viewportSP--;
	if (_viewportSP < -1) {
		_gameRef->LOG(0, "Fatal: Viewport stack underflow!");
	}

	if (_viewportSP >= 0 && _viewportSP < (int32)_viewportStack.size()) {
		_renderer->setViewport(_viewportStack[_viewportSP]->getRect());
	} else _renderer->setViewport(_renderer->_drawOffsetX,
		                              _renderer->_drawOffsetY,
		                              _renderer->getWidth() + _renderer->_drawOffsetX,
		                              _renderer->getHeight() + _renderer->_drawOffsetY);

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::getCurrentViewportRect(Rect32 *rect, bool *custom) const {
	if (rect == nullptr) {
		return STATUS_FAILED;
	} else {
		if (_viewportSP >= 0) {
			BasePlatform::copyRect(rect, _viewportStack[_viewportSP]->getRect());
			if (custom) {
				*custom = true;
			}
		} else {
			rect->setRect(_renderer->_drawOffsetX,
						  _renderer->_drawOffsetY,
						  _renderer->getWidth() + _renderer->_drawOffsetX,
						  _renderer->getHeight() + _renderer->_drawOffsetY);
			if (custom) {
				*custom = false;
			}
		}

		return STATUS_OK;
	}
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::getCurrentViewportOffset(int *offsetX, int *offsetY) const {
	if (_viewportSP >= 0) {
		if (offsetX) {
			*offsetX = _viewportStack[_viewportSP]->_offsetX;
		}
		if (offsetY) {
			*offsetY = _viewportStack[_viewportSP]->_offsetY;
		}
	} else {
		if (offsetX) {
			*offsetX = 0;
		}
		if (offsetY) {
			*offsetY = 0;
		}
	}

	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::windowLoadHook(UIWindow *win, char **buf, char **params) {
	return STATUS_FAILED;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name) {
	return STATUS_FAILED;
}


//////////////////////////////////////////////////////////////////////////
void BaseGame::setInteractive(bool state) {
	_interactive = state;
	if (_transMgr) {
		_transMgr->_origInteractive = state;
	}
}


//////////////////////////////////////////////////////////////////////////
void BaseGame::resetMousePos() {
	Common::Point p;
	p.x = _mousePos.x + _renderer->_drawOffsetX;
	p.y = _mousePos.y + _renderer->_drawOffsetY;

	BasePlatform::setCursorPos(p.x, p.y);
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::displayContent(bool doUpdate, bool displayAll) {
	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::displayContentSimple() {
	// fill black
	_renderer->fill(0, 0, 0);
	_renderer->displayIndicator();

	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::resetContent() {
	_scEngine->clearGlobals();
	//_timer = 0;
	//_liveTimer = 0;

	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
void BaseGame::DEBUG_DumpClassRegistry() {
	warning("DEBUG_DumpClassRegistry - untested");
	Common::DumpFile *f = new Common::DumpFile;
	f->open("zz_class_reg_dump.log");

	SystemClassRegistry::getInstance()->dumpClasses(f);

	f->close();
	delete f;
	_gameRef->quickMessage("Classes dump completed.");
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::invalidateDeviceObjects() {
	for (uint32 i = 0; i < _regObjects.size(); i++) {
		_regObjects[i]->invalidateDeviceObjects();
	}
	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::restoreDeviceObjects() {
	for (uint32 i = 0; i < _regObjects.size(); i++) {
		_regObjects[i]->restoreDeviceObjects();
	}
	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::setWaitCursor(const char *filename) {
	delete _cursorNoninteractive;
	_cursorNoninteractive = nullptr;

	_cursorNoninteractive = new BaseSprite(_gameRef);
	if (!_cursorNoninteractive || DID_FAIL(_cursorNoninteractive->loadFile(filename))) {
		delete _cursorNoninteractive;
		_cursorNoninteractive = nullptr;
		return STATUS_FAILED;
	} else {
		return STATUS_OK;
	}
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::isVideoPlaying() {
	if (_videoPlayer->isPlaying()) {
		return true;
	}
	if (_theoraPlayer && _theoraPlayer->isPlaying()) {
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::stopVideo() {
	if (_videoPlayer->isPlaying()) {
		_videoPlayer->stop();
	}
	if (_theoraPlayer && _theoraPlayer->isPlaying()) {
		_theoraPlayer->stop();
		delete _theoraPlayer;
		_theoraPlayer = nullptr;
	}
	return STATUS_OK;
}


//////////////////////////////////////////////////////////////////////////
bool BaseGame::drawCursor(BaseSprite *cursor) {
	if (!cursor) {
		return STATUS_FAILED;
	}
	if (cursor != _lastCursor) {
		cursor->reset();
		_lastCursor = cursor;
	}
	return cursor->draw(_mousePos.x, _mousePos.y);
}


//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
bool BaseGame::onActivate(bool activate, bool refreshMouse) {
	if (_shuttingDown || !_renderer) {
		return STATUS_OK;
	}

	_renderer->_active = activate;

	if (refreshMouse) {
		Point32 p;
		getMousePos(&p);
		setActiveObject(_renderer->getObjectAt(p.x, p.y));
	}

	if (activate) {
		_soundMgr->resumeAll();
	} else {
		_soundMgr->pauseAll();
	}

	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::onMouseLeftDown() {
	if (_activeObject) {
		_activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_LEFT);
	}

	bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftClick"));
	if (!handled) {
		if (_activeObject != nullptr) {
			_activeObject->applyEvent("LeftClick");
		}
	}

	if (_activeObject != nullptr) {
		_capturedObject = _activeObject;
	}
	_mouseLeftDown = true;

	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::onMouseLeftUp() {
	if (_activeObject) {
		_activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_LEFT);
	}

	_capturedObject = nullptr;
	_mouseLeftDown = false;

	bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftRelease"));
	if (!handled) {
		if (_activeObject != nullptr) {
			_activeObject->applyEvent("LeftRelease");
		}
	}
	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::onMouseLeftDblClick() {
	if (_state == GAME_RUNNING && !_interactive) {
		return STATUS_OK;
	}

	if (_activeObject) {
		_activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_LEFT);
	}

	bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftDoubleClick"));
	if (!handled) {
		if (_activeObject != nullptr) {
			_activeObject->applyEvent("LeftDoubleClick");
		}
	}
	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::onMouseRightDblClick() {
	if (_state == GAME_RUNNING && !_interactive) {
		return STATUS_OK;
	}

	if (_activeObject) {
		_activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_RIGHT);
	}

	bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightDoubleClick"));
	if (!handled) {
		if (_activeObject != nullptr) {
			_activeObject->applyEvent("RightDoubleClick");
		}
	}
	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::onMouseRightDown() {
	if (_activeObject) {
		_activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_RIGHT);
	}

	bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightClick"));
	if (!handled) {
		if (_activeObject != nullptr) {
			_activeObject->applyEvent("RightClick");
		}
	}
	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::onMouseRightUp() {
	if (_activeObject) {
		_activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_RIGHT);
	}

	bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightRelease"));
	if (!handled) {
		if (_activeObject != nullptr) {
			_activeObject->applyEvent("RightRelease");
		}
	}
	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::onMouseMiddleDown() {
	if (_state == GAME_RUNNING && !_interactive) {
		return STATUS_OK;
	}

	if (_activeObject) {
		_activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_MIDDLE);
	}

	bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("MiddleClick"));
	if (!handled) {
		if (_activeObject != nullptr) {
			_activeObject->applyEvent("MiddleClick");
		}
	}
	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::onMouseMiddleUp() {
	if (_activeObject) {
		_activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_MIDDLE);
	}

	bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("MiddleRelease"));
	if (!handled) {
		if (_activeObject != nullptr) {
			_activeObject->applyEvent("MiddleRelease");
		}
	}
	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::onPaint() {
	if (_renderer && _renderer->isWindowed() && _renderer->isReady()) {
		_renderer->initLoop();
		displayContent(false, true);
		displayDebugInfo();
		_renderer->windowedBlt();
	}
	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::onWindowClose() {
	if (canHandleEvent("QuitGame")) {
		if (_state != GAME_FROZEN) {
			_gameRef->applyEvent("QuitGame");
		}
		return STATUS_OK;
	} else {
		return STATUS_FAILED;
	}
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::displayDebugInfo() {
	const uint32 strLength = 100;
	char str[strLength];

	if (_debugShowFPS) {
		sprintf(str, "FPS: %d", _gameRef->_fps);
		_systemFont->drawText((byte *)str, 0, 0, 100, TAL_LEFT);
	}

	if (_gameRef->_debugDebugMode) {
		if (!_gameRef->_renderer->isWindowed()) {
			sprintf(str, "Mode: %dx%dx%d", _renderer->getWidth(), _renderer->getHeight(), _renderer->getBPP());
		} else {
			sprintf(str, "Mode: %dx%d windowed", _renderer->getWidth(), _renderer->getHeight());
		}

		Common::strlcat(str, " (", strLength);
		Common::strlcat(str, _renderer->getName().c_str(), strLength);
		Common::strlcat(str, ")", strLength);
		_systemFont->drawText((byte *)str, 0, 0, _renderer->getWidth(), TAL_RIGHT);

		_renderer->displayDebugInfo();

		int scrTotal, scrRunning, scrWaiting, scrPersistent;
		scrTotal = _scEngine->getNumScripts(&scrRunning, &scrWaiting, &scrPersistent);
		sprintf(str, "Running scripts: %d (r:%d w:%d p:%d)", scrTotal, scrRunning, scrWaiting, scrPersistent);
		_systemFont->drawText((byte *)str, 0, 70, _renderer->getWidth(), TAL_RIGHT);


		sprintf(str, "Timer: %d", getTimer()->getTime());
		_gameRef->_systemFont->drawText((byte *)str, 0, 130, _renderer->getWidth(), TAL_RIGHT);

		if (_activeObject != nullptr) {
			_systemFont->drawText((const byte *)_activeObject->getName(), 0, 150, _renderer->getWidth(), TAL_RIGHT);
		}

		sprintf(str, "GfxMem: %dMB", _usedMem / (1024 * 1024));
		_systemFont->drawText((byte *)str, 0, 170, _renderer->getWidth(), TAL_RIGHT);

	}

	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
void BaseGame::getMousePos(Point32 *pos) {
	BasePlatform::getCursorPos(pos);

	pos->x -= _renderer->_drawOffsetX;
	pos->y -= _renderer->_drawOffsetY;

	/*
	// Windows can squish maximized window if it's larger than desktop
	// so we need to modify mouse position appropriately (tnx mRax)
	if (_renderer->_windowed && ::IsZoomed(_renderer->_window)) {
	    Common::Rect rc;
	    ::GetClientRect(_renderer->_window, &rc);
	    Pos->x *= _gameRef->_renderer->_realWidth;
	    Pos->x /= (rc.right - rc.left);
	    Pos->y *= _gameRef->_renderer->_realHeight;
	    Pos->y /= (rc.bottom - rc.top);
	}
	*/

	if (_mouseLockRect.left != 0 && _mouseLockRect.right != 0 && _mouseLockRect.top != 0 && _mouseLockRect.bottom != 0) {
		if (!BasePlatform::ptInRect(&_mouseLockRect, *pos)) {
			pos->x = MAX(_mouseLockRect.left, pos->x);
			pos->y = MAX(_mouseLockRect.top, pos->y);

			pos->x = MIN(_mouseLockRect.right, pos->x);
			pos->y = MIN(_mouseLockRect.bottom, pos->y);

			Point32 newPos = *pos;

			newPos.x += _renderer->_drawOffsetX;
			newPos.y += _renderer->_drawOffsetY;

			BasePlatform::setCursorPos(newPos.x, newPos.y);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void BaseGame::miniUpdate() { // TODO: Is this really necessary, it used to update sound, but the mixer does that now.
	if (!_miniUpdateEnabled) {
		return;
	}

	if (g_system->getMillis() - _lastMiniUpdate > 200) {
		_lastMiniUpdate = g_system->getMillis();
	}
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::onScriptShutdown(ScScript *script) {
	return STATUS_OK;
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::isLeftDoubleClick() {
	return isDoubleClick(0);
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::isRightDoubleClick() {
	return isDoubleClick(1);
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::isDoubleClick(int32 buttonIndex) {
	uint32 maxDoubleCLickTime = 500;
	int maxMoveX = 4;
	int maxMoveY = 4;

	Point32 pos;
	BasePlatform::getCursorPos(&pos);

	int moveX = abs(pos.x - _lastClick[buttonIndex].posX);
	int moveY = abs(pos.y - _lastClick[buttonIndex].posY);


	if (_lastClick[buttonIndex].time == 0 || g_system->getMillis() - _lastClick[buttonIndex].time > maxDoubleCLickTime || moveX > maxMoveX || moveY > maxMoveY) {
		_lastClick[buttonIndex].time = g_system->getMillis();
		_lastClick[buttonIndex].posX = pos.x;
		_lastClick[buttonIndex].posY = pos.y;
		return false;
	} else {
		_lastClick[buttonIndex].time = 0;
		return true;
	}
}

//////////////////////////////////////////////////////////////////////////
void BaseGame::autoSaveOnExit() {
	_soundMgr->saveSettings();
	ConfMan.flushToDisk();

	if (!_autoSaveOnExit) {
		return;
	}
	if (_state == GAME_FROZEN) {
		return;
	}

	saveGame(_autoSaveSlot, "autosave", true);
}

//////////////////////////////////////////////////////////////////////////
void BaseGame::addMem(int32 bytes) {
	_usedMem += bytes;
}

//////////////////////////////////////////////////////////////////////////
AnsiString BaseGame::getDeviceType() const {
	return "computer";
}

//////////////////////////////////////////////////////////////////////////
bool BaseGame::loadSettings(const char *filename) {
	return _settings->loadSettings(filename);
}

//////////////////////////////////////////////////////////////////////////
void BaseGame::expandStringByStringTable(char **str) const {
	_settings->expandStringByStringTable(str);
}

//////////////////////////////////////////////////////////////////////////
void BaseGame::expandStringByStringTable(Common::String &str) const {
	_settings->expandStringByStringTable(str);
}

char *BaseGame::getKeyFromStringTable(const char *str) const {
	return _settings->getKeyFromStringTable(str);
}

Common::String BaseGame::readRegistryString(const Common::String &key, const Common::String &initValue) const {
	// Game specific hacks:
	Common::String result = initValue;
	// James Peris:
	if (BaseEngine::instance().getGameId() == "jamesperis" && key == "Language") {
		Common::Language language = BaseEngine::instance().getLanguage();
		if (language == Common::EN_ANY) {
			result = "english";
		} else if (language == Common::ES_ESP) {
			result = "spanish";
		} else {
			error("Invalid language set for James Peris");
		}
	} else { // Just fallback to using ConfMan for now
		Common::String privKey = "wme_" + StringUtil::encodeSetting(key);
		if (ConfMan.hasKey(privKey)) {
			result = StringUtil::decodeSetting(ConfMan.get(key));
		}
	}
	return result;
}

} // End of namespace Wintermute
