/* 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */
#include "common/config-manager.h"
#include "common/keyboard.h"
#include "common/language.h"
#include "common/translation.h"
#include "common/error.h"
#include "common/events.h"
#include "common/scummsys.h"
#include "common/system.h"
#include "engines/util.h"
#include "graphics/thumbnail.h"
#include "gui/message.h"
#include "dragons/actor.h"
#include "dragons/actorresource.h"
#include "dragons/background.h"
#include "dragons/bigfile.h"
#include "dragons/cursor.h"
#include "dragons/credits.h"
#include "dragons/dragonflg.h"
#include "dragons/dragonimg.h"
#include "dragons/dragonini.h"
#include "dragons/dragonobd.h"
#include "dragons/dragonrms.h"
#include "dragons/dragonvar.h"
#include "dragons/dragons.h"
#include "dragons/font.h"
#include "dragons/inventory.h"
#include "dragons/scene.h"
#include "dragons/screen.h"
#include "dragons/sequenceopcodes.h"
#include "dragons/scriptopcodes.h"
#include "dragons/bag.h"
#include "dragons/talk.h"
#include "dragons/sound.h"
#include "dragons/strplayer.h"

namespace Dragons {

#define DRAGONS_TICK_INTERVAL 17

static DragonsEngine *_engine = nullptr;

DragonsEngine *getEngine() {
	return _engine;
}

DragonsEngine::DragonsEngine(OSystem *syst, const DragonsGameDescription *gd) : Engine(syst) {
	_language = gd->desc.language;
	_bigfileArchive = nullptr;
	_dragonRMS = nullptr;
	_backgroundResourceLoader = nullptr;
	_screen = nullptr;
	_sequenceOpcodes = new SequenceOpcodes(this);
	_scriptOpcodes = nullptr;
	_engine = this;
	_inventory = new Inventory(this);
	_cursor = new Cursor(this);
	_credits = nullptr;
	_talk = nullptr;
	_fontManager = nullptr;
	_strPlayer = nullptr;
	_sceneUpdateFunction = nullptr;
	_vsyncUpdateFunction = nullptr;

	_dragonOBD = nullptr;
	_dragonImg = nullptr;
	_actorManager = nullptr;
	_dragonINIResource = nullptr;
	_scene = nullptr;
	_sound = nullptr;
	_isLoadingDialogAudio = false;
	_sceneId1 = 0;
	_dragonFLG = nullptr;
	_dragonVAR = nullptr;
	_flickerIdleCounter = 0;
	_loadingScreenState = nullptr;

	_leftMouseButtonUp = false;
	_leftMouseButtonDown = false;
	_rightMouseButtonUp = false;
	_iKeyUp = false;
	_downKeyDown = false;
	_downKeyUp = false;
	_upKeyDown = false;
	_upKeyUp = false;
	_enterKeyUp = false;
	_leftKeyDown = false;
	_leftKeyUp = false;
	_rightKeyDown = false;
	_rightKeyUp = false;
	_wKeyDown = false;
	_aKeyDown = false;
	_sKeyDown = false;
	_dKeyDown = false;
	_oKeyDown = false;
	_pKeyDown = false;
	_mouseWheel = MOUSE_WHEEL_NO_EVENT;

	_debugMode = false;
	_isGamePaused = false;
	_inMenu = false;

	_bit_flags_8006fbd8 = 0;

	reset();
}

DragonsEngine::~DragonsEngine() {
	delete _sequenceOpcodes;
	delete _scriptOpcodes;
	delete _cursor;
}

void DragonsEngine::updateEvents() {
	Common::Event event;
	_leftMouseButtonUp = false;
	_rightMouseButtonUp = false;
	_iKeyUp = false;
	_downKeyUp = false;
	_upKeyUp = false;
	_enterKeyUp = false;
	_leftKeyUp = false;
	_rightKeyUp = false;
	_mouseWheel = MOUSE_WHEEL_NO_EVENT;
	while (_eventMan->pollEvent(event)) {
//		_input->processEvent(event);
		switch (event.type) {
		case Common::EVENT_QUIT:
			quitGame();
			break;
		case Common::EVENT_MOUSEMOVE:
			_cursor->updatePosition(event.mouse.x, event.mouse.y);
			break;
		case Common::EVENT_WHEELDOWN:
			_mouseWheel = MOUSE_WHEEL_DOWN;
			break;
		case Common::EVENT_WHEELUP:
			_mouseWheel = MOUSE_WHEEL_UP;
			break;
		case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
			if (event.customType == Dragons::kDragonsActionLeft) {
				_leftKeyDown = true;
			} else if (event.customType == Dragons::kDragonsActionRight) {
				_rightKeyDown = true;
			} else if (event.customType == Dragons::kDragonsActionUp) {
				_upKeyDown = true;
			} else if (event.customType == Dragons::kDragonsActionDown) {
				_downKeyDown = true;
			} else if (event.customType == Dragons::kDragonsActionSquare) {
				_aKeyDown = true;
			} else if (event.customType == Dragons::kDragonsActionTriangle) {
				_wKeyDown = true;
			} else if (event.customType == Dragons::kDragonsActionCircle) {
				_dKeyDown = true;
			} else if (event.customType == Dragons::kDragonsActionCross) {
				_sKeyDown = true;
			} else if (event.customType == Dragons::kDragonsActionL1) {
				_oKeyDown = true;
			} else if (event.customType == Dragons::kDragonsActionR1) {
				_pKeyDown = true;
			} else if (event.customType == Dragons::kDragonsActionSelect) {
				_leftMouseButtonDown = true;
			} else if (event.customType == Dragons::kDragonsActionDebugGfx) {
				_debugMode = !_debugMode;
			}
			break;
		case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
			if (event.customType == Dragons::kDragonsActionLeft) {
				_leftKeyUp = true;
				_leftKeyDown = false;
			} else if (event.customType == Dragons::kDragonsActionRight) {
				_rightKeyUp = true;
				_rightKeyDown = false;
			} else if (event.customType == Dragons::kDragonsActionUp) {
				_upKeyUp = true;
				_upKeyDown = false;
			} else if (event.customType == Dragons::kDragonsActionDown) {
				_downKeyUp = true;
				_downKeyDown = false;
			} else if (event.customType == Dragons::kDragonsActionSquare) {
				_aKeyDown = false;
			} else if (event.customType == Dragons::kDragonsActionTriangle) {
				_wKeyDown = false;
			} else if (event.customType == Dragons::kDragonsActionCircle) {
				_dKeyDown = false;
			} else if (event.customType == Dragons::kDragonsActionCross) {
				_sKeyDown = false;
			} else if (event.customType == Dragons::kDragonsActionL1) {
				_oKeyDown = false;
			} else if (event.customType == Dragons::kDragonsActionR1) {
				_pKeyDown = false;
			} else if (event.customType == Dragons::kDragonsActionSelect) {
				_leftMouseButtonUp = true;
				_leftMouseButtonDown = false;
			} else if (event.customType == Dragons::kDragonsActionChangeCommand) {
				_rightMouseButtonUp = true;
			} else if (event.customType == Dragons::kDragonsActionInventory) {
				_iKeyUp = true;
			} else if (event.customType == Dragons::kDragonsActionEnter) {
				_enterKeyUp = true;
			} else if (event.customType == Dragons::kDragonsActionQuit) {
				quitGame();
			}
			break;
		default:
			break;
		}
	}
}

Common::Error DragonsEngine::run() {
	if(!checkAudioVideoFiles()) {
		return Common::kNoGameDataFoundError;
	}

	_screen = new Screen();
	_bigfileArchive = new BigfileArchive(this, "bigfile.dat");
	_talk = new Talk(this, _bigfileArchive);
	_dragonFLG = new DragonFLG(_bigfileArchive);
	_dragonImg = new DragonImg(_bigfileArchive);
	_dragonOBD = new DragonOBD(_bigfileArchive);
	_dragonRMS = new DragonRMS(_bigfileArchive, _dragonOBD);
	_dragonVAR = new DragonVAR(_bigfileArchive);
	_dragonINIResource = new DragonINIResource(_bigfileArchive);
	_fontManager = new FontManager(this, _screen, _bigfileArchive);
	_credits = new Credits(this, _fontManager, _bigfileArchive);
	ActorResourceLoader *actorResourceLoader = new ActorResourceLoader(_bigfileArchive);
	_actorManager = new ActorManager(actorResourceLoader);
	_scriptOpcodes = new ScriptOpcodes(this, _dragonFLG);
	_backgroundResourceLoader = new BackgroundResourceLoader(_bigfileArchive, _dragonRMS);
	_scene = new Scene(this, _screen, _scriptOpcodes, _actorManager, _dragonRMS, _dragonINIResource, _backgroundResourceLoader);

	_sound = new SoundManager(this, _bigfileArchive, _dragonRMS);
	_strPlayer = new StrPlayer(this, _screen);

	syncSoundSettings();

	if (ConfMan.hasKey("save_slot")) {
		loadGameState(ConfMan.getInt("save_slot"));
	} else {
		_strPlayer->playVideo("crystald.str");
		_strPlayer->playVideo("illusion.str");
		_strPlayer->playVideo("labintro.str");

		init();
		mainMenu();
		loadScene(0);
	}

	if (!shouldQuit()) {
		_scene->draw();
		_screen->updateScreen();

		gameLoop();
	}

	delete _scene;
	delete _actorManager;
	delete _backgroundResourceLoader;
	delete _dragonFLG;
	delete _dragonImg;
	delete _dragonRMS;
	delete _dragonVAR;
	delete _fontManager;
	delete _bigfileArchive;
	delete _screen;
	delete _sound;
	delete _strPlayer;

	return Common::kNoError;
}

uint16 DragonsEngine::ipt_img_file_related() {
	DragonINI *flicker = _dragonINIResource->getFlickerRecord();
	assert(flicker);

	int16 tileX = flicker->actor->_x_pos / 32;
	int16 tileY = flicker->actor->_y_pos / 8;

	for (int i = 0; i < _dragonINIResource->totalRecords(); i++) {
		DragonINI *ini = getINI(i);
		if ((ini->sceneId == getCurrentSceneId()) && (ini->flags == 0)) {
			Img *img = _dragonImg->getImg(ini->imgId);
			if ((img->x <= tileX) && (((tileX <= img->x + img->w && (img->y <= tileY)) && (tileY <= img->y + img->h)))) {
				return i + 1;
			}
		}
	}
	return 0;
}

void DragonsEngine::gameLoop() {
	uint16 prevImgIniId = 0;
	uint16 sequenceId;

	_cursor->_cursorActivationSeqOffset = 0;
	_bit_flags_8006fbd8 = 0;
	_flickerIdleCounter = 0;
	setFlags(ENGINE_FLAG_8);

	while (!shouldQuit()) {
		_scene->draw();
		_screen->updateScreen();
		wait();
		updateHandler();
		updateEvents();
		runSceneUpdaterFunction(); //TODO is this the right place for this logic?

		if (getCurrentSceneId() != 2) {
			_sceneId1 = getCurrentSceneId();
		}

		updateFlickerIdleAnimation();

		if (_bit_flags_8006fbd8 == 0) {
			setFlags(ENGINE_FLAG_8);
		}
		if (_dragonINIResource->getFlickerRecord()->sceneId == getCurrentSceneId()) {
			uint16 imgIniId = ipt_img_file_related();
			if (imgIniId != 0 && imgIniId != (prevImgIniId & 0xffff)) {
				byte *obd = _dragonOBD->getFromOpt(imgIniId - 1);
				ScriptOpCall scriptOpCall(obd + 8, READ_LE_UINT32(obd));

				if (_scriptOpcodes->runScript4(scriptOpCall)) {
					scriptOpCall._codeEnd = scriptOpCall._code + 4 + READ_LE_UINT16(scriptOpCall._code + 2);
					scriptOpCall._code += 4;
					_scriptOpcodes->runScript(scriptOpCall);
					_flickerIdleCounter = 0;
				}
			}
			prevImgIniId = imgIniId;
		} else {
			prevImgIniId = 0;
		}

		if (_cursor->updateINIUnderCursor() == 0 ||
			(!(_cursor->_iniUnderCursor & 0x8000) && (getINI(_cursor->_iniUnderCursor - 1)->flags & 0x4000) != 0)) { //TODO check this. This logic looks a bit strange.
			_cursor->_cursorActivationSeqOffset = 0;
		} else {
			_cursor->_cursorActivationSeqOffset = 5;
		}

		if (_rightMouseButtonUp && isInputEnabled()) {
			_cursor->selectPreviousCursor();
			_flickerIdleCounter = 0;
			continue;
		}
		// TODO implement cycle cursor up.
//		actorId = CheckButtonMapPress_CycleUp(0);
//		if (((actorId & 0xffff) != 0) && isInputEnabled()) {
//			_cursor->_sequenceID = _cursor->_sequenceID + 1;
//			if (_cursor->_iniItemInHand == 0) {
//				bVar1 = _cursor->_sequenceID < 5;
//			}
//			else {
//				bVar1 = _cursor->_sequenceID < 6;
//			}
//			if (!bVar1) {
//				_cursor->_sequenceID = 0;
//			}
//			if ((_cursor->_sequenceID == 0) && ((_inventory->getType() == 1 || (_inventory->getType() == 2)))) {
//				_cursor->_sequenceID = 1;
//			}
//			if (_cursor->_sequenceID == 2) {
//				if (_inventory->getType() == 1) {
//					_cursor->_sequenceID = 4;
//				}
//				uVar6 = 3;
//				if (_cursor->_sequenceID == 2) goto LAB_80026fb0;
//			}
//			_flickerIdleCounter = 0;
//			actorId = uVar3;
//			continue;
//		}

		if (_bit_flags_8006fbd8 == 3) {
			_bit_flags_8006fbd8 = 0;
			DragonINI *flicker = _dragonINIResource->getFlickerRecord();
			if (flicker->sceneId == getCurrentSceneId() && flicker->actor->_direction != -1) {
				int16 iniId = _scriptOpcodes->_scriptTargetINI;
				if (_cursor->_sequenceID != 5) {
					iniId = _cursor->_performActionTargetINI;
				}
				if (iniId > 0) {
					flicker->actor->_direction = getINI(iniId - 1)->direction;
				}
			}

			performAction();
			if ((getCurrentSceneId() == 0x1d) && (getINI(0x178)->objectState != 0)) { //cave of dilemma
				clearFlags(ENGINE_FLAG_8);
			} else {
				setFlags(ENGINE_FLAG_8);
			}
			_flickerIdleCounter = 0;
			continue;
		}
		if (_inventory->getState() != InventoryOpen) {
			if (_inventory->getState() == Closed) {
				if ((checkForInventoryButtonRelease() && isInputEnabled()) &&
					((_bit_flags_8006fbd8 & 3) != 1)) {
					sequenceId = _dragonVAR->getVar(7);
					InventoryState uVar7 = _inventory->_previousState;
					_inventory->_previousState = _inventory->getState();
					_inventory->setState(_inventory->_previousState);
					if (sequenceId == 1) {
						_inventory->_previousState = uVar7;
						_inventory->inventoryMissing();
					} else {
						_flickerIdleCounter = 0;
						_inventory->setState(InventoryOpen);
						_inventory->openInventory();
						if (_cursor->_iniItemInHand == 0) {
							_cursor->_sequenceID = 1;
						} else {
							_cursor->_sequenceID = 5;
						}
					}
					continue;
				}
				InventoryState uVar6 = _inventory->getState();
				if (checkForActionButtonRelease() && isFlagSet(ENGINE_FLAG_8)) {
					_flickerIdleCounter = 0;
					if ((_cursor->_iniUnderCursor & 0x8000) != 0) {
						if (_cursor->_iniUnderCursor == 0x8002) {
							LAB_80027294:
							if (_cursor->_iniItemInHand == 0) {
								if ((_bit_flags_8006fbd8 & 3) != 1) {
									sequenceId = _dragonVAR->getVar(7);
									InventoryState uVar7 = _inventory->_previousState;
									_inventory->_previousState = _inventory->getState();
									_inventory->setState(_inventory->_previousState);
									if (sequenceId == 1) {
										_inventory->_previousState = uVar7;
										_inventory->inventoryMissing();
									} else {
										_flickerIdleCounter = 0;
										_inventory->setState(InventoryOpen);
										_inventory->openInventory();
										if (_cursor->_iniItemInHand == 0) {
											_cursor->_sequenceID = 1;
										} else {
											_cursor->_sequenceID = 5;
										}
									}
									continue;
								}
							} else {
								if (_inventory->addItem(_cursor->_iniItemInHand)) {
									_cursor->_sequenceID = 1;
									waitForFrames(1);
									_cursor->_iniItemInHand = 0;
									_cursor->_iniUnderCursor = 0;
									continue;
								}
							}
						} else {
							if (_cursor->_iniUnderCursor != 0x8001) {
								_flickerIdleCounter = 0;
								_cursor->_performActionTargetINI = _cursor->_iniUnderCursor;
								_cursor->_data_800728b0_cursor_seqID = _cursor->_sequenceID;
								walkFlickerToObject();
								if (_bit_flags_8006fbd8 != 0) {
									clearFlags(ENGINE_FLAG_8);
								}
								if (_cursor->_sequenceID >= 5) {
									_scriptOpcodes->_scriptTargetINI = _cursor->_performActionTargetINI;
									_cursor->_performActionTargetINI = _cursor->_iniItemInHand;
								}
								runINIScripts();
								continue;
							}
							if (_inventory->getSequenceId() == 0) {
								goto LAB_80027294;
							}
						}
						if ((_cursor->_iniUnderCursor == 0x8001) && (_inventory->getSequenceId() == 1)) {
							_inventory->setState(InventionBookOpen);
							_inventory->_previousState = uVar6;
							_inventory->openInventionBook();
							continue;
						}
					}
					_flickerIdleCounter = 0;
					_cursor->_performActionTargetINI = _cursor->_iniUnderCursor;
					_cursor->_data_800728b0_cursor_seqID = _cursor->_sequenceID;
					walkFlickerToObject();
					if (_bit_flags_8006fbd8 != 0) {
						clearFlags(ENGINE_FLAG_8);
					}
					if (_cursor->_sequenceID >= 5) {
						_scriptOpcodes->_scriptTargetINI = _cursor->_performActionTargetINI;
						_cursor->_performActionTargetINI = _cursor->_iniItemInHand;
					}
				}
			}
			if (_inventory->getState() == InventionBookOpen) {
				InventoryState uVar6 = _inventory->getState();
				if (checkForInventoryButtonRelease() && isInputEnabled()) {
					InventoryState uVar7 = _inventory->_previousState;
					if (_dragonVAR->getVar(7) == 1) {
						_inventory->_previousState = uVar7;
						_inventory->inventoryMissing();
						continue;
					}
					_flickerIdleCounter = 0;
					_inventory->setState(InventoryOpen);
					_inventory->_previousState = uVar6;
					_inventory->openInventory();
					if (_cursor->_iniItemInHand == 0) {
						_cursor->_sequenceID = 1;
					} else {
						_cursor->_sequenceID = 5;
					}
					continue;
				}
				if (checkForActionButtonRelease() && isFlagSet(ENGINE_FLAG_8)) {
					_flickerIdleCounter = 0;
					_cursor->_performActionTargetINI = _cursor->_iniUnderCursor;
					_cursor->_data_800728b0_cursor_seqID = _cursor->_sequenceID;
					walkFlickerToObject();
					if (_bit_flags_8006fbd8 != 0) {
						clearFlags(ENGINE_FLAG_8);
					}
					if (_cursor->_sequenceID >= 5) {
						_scriptOpcodes->_scriptTargetINI = _cursor->_performActionTargetINI;
						_cursor->_performActionTargetINI = _cursor->_iniItemInHand;
					}
				}
			}
			runINIScripts();
			continue;
		}
		if (checkForInventoryButtonRelease()) {
			_flickerIdleCounter = 0;
			_inventory->closeInventory();
			_inventory->setPreviousState();
			continue;
		}
		InventoryState uVar6 = _inventory->getState();
		if (checkForActionButtonRelease() && isFlagSet(ENGINE_FLAG_8)) {
			_flickerIdleCounter = 0;
			if ((_cursor->_iniUnderCursor & 0x8000) != 0) {
				if (_cursor->_iniUnderCursor == 0x8001) {
					_inventory->closeInventory();
					_inventory->setState(Closed);
					if (_inventory->_previousState == InventionBookOpen) {
						_inventory->closeInventionBook();
					}
				} else {
					if (_cursor->_iniUnderCursor != 0x8002) {
						if ((_cursor->_iniItemInHand == 0) ||
							(((uint16)(_cursor->_x - 10U) < 300 && ((uint16)(_cursor->_y - 10U) < 0xb4)))) {
							runINIScripts();
						} else {
							_cursor->_sequenceID = 5;
							waitForFrames(2);
							_inventory->closeInventory();
							_inventory->setPreviousState();
						}
						continue;
					}
					_inventory->closeInventory();
					_inventory->setState(InventionBookOpen);
					if (_inventory->_previousState != InventionBookOpen) {
						_inventory->openInventionBook();
					}
				}
				_inventory->_previousState = uVar6;
				continue;
			}
			if (_cursor->_iniUnderCursor != 0) {
				if ((_cursor->_sequenceID != 4) && (_cursor->_sequenceID != 2)) {
					_cursor->_data_800728b0_cursor_seqID = _cursor->_sequenceID;
					_cursor->_performActionTargetINI = _cursor->_iniUnderCursor;
					if (_cursor->_sequenceID >= 4) {
						_cursor->_performActionTargetINI = _cursor->_iniItemInHand;
						_scriptOpcodes->_scriptTargetINI = _cursor->_iniUnderCursor;
					}
					clearFlags(ENGINE_FLAG_8);
					walkFlickerToObject();
					if ((_cursor->_iniItemInHand == 0) ||
						(((uint16)(_cursor->_x - 10U) < 300 && ((uint16)(_cursor->_y - 10U) < 0xb4)))) {
						runINIScripts();
					} else {
						_cursor->_sequenceID = 5;
						waitForFrames(2);
						_inventory->closeInventory();
						_inventory->setPreviousState();
					}
					continue;
				}
				Actor *actor = _inventory->getInventoryItemActor(_cursor->_iniUnderCursor);
				uint16 tmpId = _cursor->_iniItemInHand;
				_inventory->replaceItem(_cursor->_iniUnderCursor, _cursor->_iniItemInHand);
				_cursor->_objectInHandSequenceID = actor->_sequenceID;
				actor->clearFlag(ACTOR_FLAG_40);
				_cursor->_iniItemInHand = _cursor->_iniUnderCursor;
				_cursor->_sequenceID = 5;
				if (tmpId != 0) {
					actor->_flags = 0;
					actor->_priorityLayer = 0;
					actor->_scale = DRAGONS_ENGINE_SPRITE_100_PERCENT_SCALE;
					actor->updateSequence(getINI(tmpId - 1)->inventorySequenceId * 2 + 10);
					actor->setFlag(ACTOR_FLAG_40);
					actor->setFlag(ACTOR_FLAG_80);
					actor->setFlag(ACTOR_FLAG_100);
					actor->setFlag(ACTOR_FLAG_200);
					actor->_priorityLayer = 6;
				}
				continue;
			}
			if (_cursor->_iniItemInHand == 0) {
				runINIScripts();
				continue;
			}
			//drop item back into inventory
			if (_inventory->addItemIfPositionIsEmpty(_cursor->_iniItemInHand, _cursor->_x, _cursor->_y)) {
				Actor *invActor = _inventory->getInventoryItemActor(_cursor->_iniItemInHand);
				invActor->_flags = 0;
				invActor->_priorityLayer = 0;
				invActor->_scale = DRAGONS_ENGINE_SPRITE_100_PERCENT_SCALE;
				invActor->updateSequence(getINI(_cursor->_iniItemInHand - 1)->inventorySequenceId * 2 + 10);
				_cursor->_iniItemInHand = 0;
				invActor->setFlag(ACTOR_FLAG_40);
				invActor->setFlag(ACTOR_FLAG_80);
				invActor->setFlag(ACTOR_FLAG_100);
				invActor->setFlag(ACTOR_FLAG_200);
				invActor->_priorityLayer = 6;
				if (_cursor->_sequenceID == 5) {
					_cursor->_sequenceID = 4;
				}
			}
		}
		if ((_cursor->_iniItemInHand == 0) ||
			(((uint16)(_cursor->_x - 10U) < 300 && ((uint16)(_cursor->_y - 10U) < 0xb4)))) {
			runINIScripts();
		} else {
			_cursor->_sequenceID = 5;
			waitForFrames(2);
			_inventory->closeInventory();
			_inventory->setPreviousState();
		}
	}
}


void DragonsEngine::updateHandler() {
	_videoFlags |= 0x40;
	//TODO logic here

	updateActorSequences();

	updateCamera();

	_cursor->updateVisibility();
	_inventory->updateVisibility();

	_actorManager->updateActorDisplayOrder();

	// 0x8001b200
	if (isFlagSet(ENGINE_FLAG_8000) && !_sound->isSpeechPlaying()) {
		//dialog finished playing.
		clearFlags(ENGINE_FLAG_8000);
		_sound->resumeMusic();
	}

	//TODO logic here
	for (uint16 i = 0; i < 0x17; i++) {
		Actor *actor = _actorManager->getActor(i);
		if (actor->_flags & ACTOR_FLAG_40) {
			if (!(actor->_flags & ACTOR_FLAG_100)) {
				int16 priority = _scene->getPriorityAtPosition(Common::Point(actor->_x_pos, actor->_y_pos));
				DragonINI *flicker = _dragonINIResource->getFlickerRecord();
				if (flicker && _scene->contains(flicker) && flicker->actor->_actorID == i) {
					if ((priority >= 0 && priority < 8) || priority == 0x10) {
						actor->_priorityLayer = priority;
					}
				} else {
					if (priority != -1) {
						actor->_priorityLayer = priority;
					}
				}

				if (actor->_priorityLayer >= 0x11) {
					actor->_priorityLayer = 0;
				}

				if (actor->_priorityLayer >= 9) {
					actor->_priorityLayer -= 8;
				}
			}

			if (actor->_sequenceTimer != 0) {
				actor->_sequenceTimer--;
			}
		}
	}

	if (_flags & ENGINE_FLAG_80) {
		for (uint16 i = 0x17; i < DRAGONS_ENGINE_NUM_ACTORS; i++) {
			Actor *actor = _actorManager->getActor(i);
			if (actor->_sequenceTimer != 0) {
				actor->_sequenceTimer--;
			}
		}
	}

	if (isFlagSet(ENGINE_FLAG_4)) {
		updatePathfindingActors();
	}

	// TODO 0x8001bed0
	updatePaletteCycling();

	// 0x8001c294
	if (!(_unkFlags1 & ENGINE_UNK1_FLAG_8)) {
		//TODO ReadPad();
	}

	if (isFlagSet(ENGINE_FLAG_20)) {
		engineFlag0x20UpdateFunction();
	}

	runVsyncUpdaterFunction();

	// TODO data_8006a3a0 logic. @ 0x8001c2f4

	_videoFlags &= ~0x40;
}

uint32 DragonsEngine::calulateTimeLeft() {
	uint32 now;

	now = _system->getMillis();

	if (_nextUpdatetime <= now) {
		_nextUpdatetime = now + DRAGONS_TICK_INTERVAL;
		return (0);
	}
	uint32 delay = _nextUpdatetime - now;
	_nextUpdatetime += DRAGONS_TICK_INTERVAL;
	return (delay);
}

void DragonsEngine::wait() {
	_system->delayMillis(calulateTimeLeft());
}

void DragonsEngine::updateActorSequences() {
	if (!(_flags & ENGINE_FLAG_4)) {
		return;
	}

	//TODO ResetRCnt(0xf2000001);

	int16 actorId = _flags & ENGINE_FLAG_80 ? (int16) 64 : (int16) 23;

	while (actorId > 0) {
		actorId--;
		Actor *actor = _actorManager->getActor((uint16) actorId);
		if (actorId < 2 && _flags & ENGINE_FLAG_40) {
			continue;
		}

		if (actor->_flags & ACTOR_FLAG_40 &&
				!(actor->_flags & ACTOR_FLAG_4) &&
				!(actor->_flags & ACTOR_FLAG_400) &&
				(actor->_sequenceTimer == 0 || actor->_flags & ACTOR_FLAG_1)) {
			debug(5, "Actor[%d] execute sequenceOp", actorId);

			if (actor->_flags & ACTOR_FLAG_1) {
				actor->resetSequenceIP();
				//clear flag mask 0xeff6;
				actor->clearFlag(ACTOR_FLAG_1);
				actor->clearFlag(ACTOR_FLAG_8);
				actor->clearFlag(ACTOR_FLAG_1000);
				actor->_field_7a = 0;
			}
			OpCall opCall;
			opCall._result = 1;
			while (opCall._result == 1) {
				opCall._op = (byte) READ_LE_UINT16(actor->_seqCodeIp);
				opCall._code = actor->_seqCodeIp + 2;
				_sequenceOpcodes->execOpcode(actor, opCall);
				actor->_seqCodeIp += opCall._deltaOfs;
			}
		}
	}
}

void DragonsEngine::setFlags(uint32 flags) {
	_flags |= flags;
}

void DragonsEngine::clearFlags(uint32 flags) {
	_flags &= ~flags;
}

uint32 DragonsEngine::getMultipleFlags(uint32 flags) {
	return _flags & flags;
}

uint32 DragonsEngine::getAllFlags() {
	return _flags;
}

void DragonsEngine::setAllFlags(uint32 flags) {
	_flags = flags;
}

void DragonsEngine::setUnkFlags(uint32 flags) {
	_unkFlags1 |= flags;
}

void DragonsEngine::clearUnkFlags(uint32 flags) {
	_unkFlags1 &= ~flags;
}

byte *DragonsEngine::getBackgroundPalette() {
	assert(_scene);
	return _scene->getPalette();
}

bool DragonsEngine::isFlagSet(uint32 flag) {
	return (bool) (_flags & flag);
}

bool DragonsEngine::isUnkFlagSet(uint32 flag) {
	return (bool) (_unkFlags1 & flag);
}

DragonINI *DragonsEngine::getINI(uint32 index) {
	return _dragonINIResource->getRecord(index);
}

uint16 DragonsEngine::getVar(uint16 offset) {
	return _dragonVAR->getVar(offset);
}

uint16 DragonsEngine::getCurrentSceneId() const {
	return _scene->getSceneId();
}

void DragonsEngine::setVar(uint16 offset, uint16 value) {
	return _dragonVAR->setVar(offset, value);
}

uint16 DragonsEngine::getIniFromImg() {
	DragonINI *flicker = _dragonINIResource->getFlickerRecord();

	int16 x = flicker->actor->_x_pos / 32;
	int16 y = flicker->actor->_y_pos / 8;

	uint16 currentSceneId = _scene->getSceneId();

	for (uint16 i = 0; i < _dragonINIResource->totalRecords(); i++) {
		DragonINI *ini = getINI(i);
		if (ini->sceneId == currentSceneId && ini->flags == 0) {
			Img *img = _dragonImg->getImg(ini->imgId);
			if (x >= img->x &&
				img->x + img->w >= x &&
				y >= img->y &&
				img->h + img->y >= y) {
				return i + 1;
			}
		}

	}
	return 0;
}

void DragonsEngine::runINIScripts() {
	bool isFlag8Set = isFlagSet(ENGINE_FLAG_8);
	for (uint16 i = 0; i < _dragonINIResource->totalRecords(); i++) {
		DragonINI *ini = getINI(i);
		if (ini->flags & INI_FLAG_10) {
			ini->flags &= ~INI_FLAG_10;
			byte *data = _dragonOBD->getFromOpt(i);
			ScriptOpCall scriptOpCall(data + 8, READ_LE_UINT32(data));
			clearFlags(ENGINE_FLAG_8);
			_scriptOpcodes->runScript3(scriptOpCall);
		}
	}
	if (isFlag8Set) {
		setFlags(ENGINE_FLAG_8);
	}
}

void DragonsEngine::engineFlag0x20UpdateFunction() {
	if (!isFlagSet(ENGINE_FLAG_20)) {
		_run_func_ptr_unk_countdown_timer = 1;
		return;
	}

	if (isFlagSet(ENGINE_FLAG_8) && !isFlagSet(ENGINE_FLAG_8000000)) {
		_cursor->update();
	}

	uint16 currentSceneId = _scene->getSceneId();
	DragonINI *flickerINI = _dragonINIResource->getFlickerRecord();
	DragonINI *ini1 = getINI(1);

	if (flickerINI != nullptr) {
		if (flickerINI->sceneId == currentSceneId) {
			if (flickerINI->actor == nullptr || flickerINI->actor->isFlagSet(ACTOR_FLAG_10)) {
				if (ini1->actor != nullptr) {
					ini1->actor->updateSequence(8);
					ini1->actor->_priorityLayer = 0;
				}
			} else {
				if ((_bit_flags_8006fbd8 & 2u) == 0) {
					_bit_flags_8006fbd8 |= 2u;
				}
				if (flickerINI->actor->isFlagClear(ACTOR_FLAG_2000)
						&& flickerINI->actor->isFlagSet(ACTOR_FLAG_4)
						&& flickerINI->actor->_direction != -1
						&& flickerINI->actor->_direction != flickerINI->actor->_sequenceID) {
					flickerINI->actor->updateSequence(flickerINI->actor->_direction);
				}
				if (ini1->actor != nullptr) {
					ini1->actor->_priorityLayer = 0;
				}
			}
		}
	}

	// 0x80027db8
	if (!_inventory->isOpen()) {
		for (uint16 i = 0; i < _dragonINIResource->totalRecords(); i++) {
			DragonINI *ini = getINI(i);
			if (ini->counter >= 0 && ini->sceneId == currentSceneId) {
				ini->counter--;
				if (ini->counter < 0) {
					ini->flags |= INI_FLAG_10;
				}
			}
		}
	}

	if (_run_func_ptr_unk_countdown_timer != 0) {
		_run_func_ptr_unk_countdown_timer--;
	}
}

void DragonsEngine::waitForFrames(uint16 numFrames) {
	for (uint16 i = 0; i < numFrames && !Engine::shouldQuit(); i++) {
		wait();
		updateHandler();

		_scene->draw();
		_screen->updateScreen();
		runSceneUpdaterFunction();
		updateEvents();
	}
}

void DragonsEngine::waitForFramesAllowSkip(uint16 numFrames) {
	for (int i = 0; i < numFrames; i++) {
		waitForFrames(1);
		if (checkForActionButtonRelease()) { // TODO should this be any input?
			return;
		}
	}
}

void DragonsEngine::playOrStopSound(uint16 soundId) {
	debug(1, "play sound 0x%x", soundId);

	this->_sound->playOrStopSound(soundId);
}

void DragonsEngine::updatePathfindingActors() {
	for (uint16 i = 0; i < 0x17; i++) {
		Actor *actor = _actorManager->getActor(i);
		actor->walkPath();
	}
}

void DragonsEngine::fadeFromBlack(uint32 flags) {
	if (!isFlagSet(ENGINE_FLAG_40)) {
		return;
	}
	bool isUnkFlag2Set = isUnkFlagSet(ENGINE_UNK1_FLAG_2);

	setUnkFlags(ENGINE_UNK1_FLAG_2);
	clearFlags(ENGINE_FLAG_40);

	//TODO 0x80015a1c implement fading logic here.

	if (!isUnkFlag2Set) {
		clearUnkFlags(ENGINE_UNK1_FLAG_2);
	}
}

void DragonsEngine::fadeFromBlack() {
	fadeFromBlack(0x1f);
}

void DragonsEngine::fadeFromBlackExcludingFont() {
	fadeFromBlack(0x1e);
}

void DragonsEngine::fadeToBlack() {
	fadeToBlack(0x1f);
}

void DragonsEngine::fadeToBlackExcludingFont() {
	fadeToBlack(0x1e);
}

void DragonsEngine::fadeToBlack(uint32 flags) {
	bool isFlag2Set = isUnkFlagSet(ENGINE_UNK1_FLAG_2);
	//TODO
//	do {
//		SetShadeTex(poly_ft4_tbl + uVar2,0);
//		uVar5 = uVar5 + 1;
//		uVar2 = (uint)uVar5;
//	} while (uVar5 < 0x40);
//	uVar5 = 0;
//	uVar2 = 0;
//	do {
//		SetShadeTex(poly_f4_tbl + uVar2,0);
//		uVar1 = unkFlags1;
//		uVar5 = uVar5 + 1;
//		uVar2 = (uint)uVar5;
//	} while (uVar5 < 0xf);
	if (!isFlagSet(ENGINE_FLAG_40)) {
		setUnkFlags(ENGINE_UNK1_FLAG_2);

		//TODO fade out here.

		setFlags(ENGINE_FLAG_40);
		if (!isFlag2Set) {
			clearUnkFlags(ENGINE_UNK1_FLAG_2);
		}
	}
}

void DragonsEngine::performAction() {
	uint uVar1;
	uint16 uVar2;
	uint uVar4;
	uint uVar5;
	uint uVar6;
	byte * pvVar7;
	byte * pvVar8;
	byte *local_58_code;
	byte *local_58_codeEnd;
	bool load_58_result = false;

	uVar2 = _scriptOpcodes->_scriptTargetINI;
	uVar1 = _flags;
	local_58_code = nullptr;
	local_58_codeEnd = nullptr;

	uVar6 = 0;
	_scriptOpcodes->_numDialogStackFramesToPop = 0;

	assert(_cursor->_performActionTargetINI > 0);
	byte *obd = _dragonOBD->getFromOpt(_cursor->_performActionTargetINI - 1);


	ScriptOpCall local_48(obd + 8, READ_LE_UINT32(obd));
	pvVar7 = local_48._code;
	pvVar8 = local_48._codeEnd;

	uVar4 = _cursor->executeScript(local_48, 1);
	if (_cursor->_data_800728b0_cursor_seqID > 4) {
		_scriptOpcodes->_numDialogStackFramesToPop = 0;

		obd = _dragonOBD->getFromOpt(_scriptOpcodes->_scriptTargetINI - 1);
		_scriptOpcodes->_scriptTargetINI = _cursor->_performActionTargetINI;

		ScriptOpCall local_38(obd + 8, READ_LE_UINT32(obd));

		uVar6 = _cursor->executeScript(local_38, 1);
		_scriptOpcodes->_scriptTargetINI = uVar2;

		if ((uVar6 & 0xffff) != 0) {
			local_58_code = local_38._code + 8;
			local_58_codeEnd = local_58_code + READ_LE_UINT16(local_38._code + 6);
		}
	}
	if (((uVar4 & 0xffff) != 0) && ((((uVar4 & 2) == 0 || ((uVar6 & 2) != 0)) || ((uVar6 & 0xffff) == 0)))) {
		local_58_code = local_48._code + 8;
		local_58_codeEnd = local_58_code + READ_LE_UINT16(local_48._code + 6);
	}
	uVar4 = uVar4 & 0xfffd;
	if (local_58_code != nullptr && local_58_codeEnd != nullptr) {
		clearFlags(ENGINE_FLAG_8);
		ScriptOpCall local_58(local_58_code, local_58_codeEnd - local_58_code);
		_scriptOpcodes->runScript(local_58);
		load_58_result = local_58._result;
	}
	if (!load_58_result) {
		if (_cursor->_data_800728b0_cursor_seqID == 3) {
			ScriptOpCall local_58(pvVar7, pvVar8 - pvVar7);
			uVar5 = _talk->talkToActor(local_58);
			uVar4 = uVar4 | uVar5;
		}
		if (((uVar4 & 0xffff) == 0) && ((uVar6 & 0xfffd) == 0)) {
			_talk->flickerRandomDefaultResponse();
		}
	} else {
		_scriptOpcodes->_numDialogStackFramesToPop--;
	}
	_flags |= uVar1 & ENGINE_FLAG_8;
	return;
}

bool DragonsEngine::checkForInventoryButtonRelease() {
	return _iKeyUp;
}

bool DragonsEngine::isInputEnabled() {
	return !isFlagSet(ENGINE_FLAG_20000000) && !isFlagSet(ENGINE_FLAG_400);
}

bool DragonsEngine::isActionButtonPressed() {
	return _leftMouseButtonDown;
}

bool DragonsEngine::isLeftKeyPressed() {
	return _leftKeyDown;
}

bool DragonsEngine::isRightKeyPressed() {
	return _rightKeyDown;
}

bool DragonsEngine::isUpKeyPressed() {
	return _upKeyDown;
}

bool DragonsEngine::isDownKeyPressed() {
	return _downKeyDown;
}

bool DragonsEngine::checkForActionButtonRelease() {
	return _leftMouseButtonUp || _enterKeyUp;
}

void DragonsEngine::walkFlickerToObject() {
	uint16 targetX;
	uint16 targetY;
	uint uVar7;
	uint uVar8;
	DragonINI *targetINI;
	DragonINI *flickerINI;

	flickerINI = _dragonINIResource->getFlickerRecord();
	if (flickerINI->sceneId == getCurrentSceneId()) {
		if (_cursor->_performActionTargetINI != 0) {

			if (!(READ_LE_UINT16(_dragonOBD->getFromOpt(_cursor->_performActionTargetINI - 1) + 4) & 8)
				&& (_inventory->getState() == Closed) && !isFlagSet(ENGINE_FLAG_200000)) {
				targetINI = getINI(_cursor->_performActionTargetINI - 1);
				if ((targetINI->flags & 1) == 0) {
					if (targetINI->actorResourceId == -1) {
						return;
					}
					Img *img = _dragonImg->getImg(targetINI->imgId);
					targetX = img->field_a;
					targetY = img->field_c;
				} else {
					targetX = targetINI->actor->_x_pos;
					targetY = targetINI->actor->_y_pos;
				}
				flickerINI->actor->_walkSpeed = 0x10000;
				if (flickerINI->direction2 == -1) {
					flickerINI->actor->setFlag(ACTOR_FLAG_800);
				}
				flickerINI->actor->startWalk((int)(((uint)targetX + (uint)targetINI->baseXOffset) * 0x10000) >> 0x10,
											 (int)(((uint)targetY + (uint)targetINI->baseYOffset) * 0x10000) >> 0x10, 0);
				_bit_flags_8006fbd8 = 1; //walk to perform an action.
				return;
			}
			if (isFlagSet(ENGINE_FLAG_200000)) {
				_bit_flags_8006fbd8 = 3;
				return;
			}
			flickerINI = _dragonINIResource->getFlickerRecord();
			if (flickerINI != nullptr && flickerINI->actor != nullptr) {
				flickerINI->actor->clearFlag(ACTOR_FLAG_10);
				flickerINI->actor->setFlag(ACTOR_FLAG_4);
				targetINI = getINI(_cursor->_performActionTargetINI - 1);
				flickerINI->direction2 = targetINI->direction;
				flickerINI->actor->_direction = targetINI->direction;
			}
			_bit_flags_8006fbd8 = 3;
			return;
		}
		if (_inventory->getState() == Closed && !isFlagSet(ENGINE_FLAG_200000)) {
			uVar7 = (uint)(uint16)_cursor->_x;
			uVar8 = (uint)(uint16)_cursor->_y;
			flickerINI->actor->_walkSpeed = 0x10000;
			flickerINI->actor->startWalk(
					(int)((uVar7 + (uint)_scene->_camera.x) * 0x10000) >> 0x10,
					(int)((uVar8 + (uint)_scene->_camera.y) * 0x10000) >> 0x10, 0);
		}
	} else {
		if (_cursor->_performActionTargetINI != 0) {
			_bit_flags_8006fbd8 = 3;
			return;
		}
	}
	_bit_flags_8006fbd8 = 0;
	return;
}

void DragonsEngine::reset_screen_maybe() {
	_videoFlags &= ~0x10;
	//TODO
}

bool DragonsEngine::canLoadGameStateCurrently(Common::U32String *msg) {
	//player has control and not currently talking to anyone.
	return isInputEnabled() && isFlagSet(ENGINE_FLAG_8) && !isFlagSet(Dragons::ENGINE_FLAG_100);
}

bool DragonsEngine::canSaveGameStateCurrently(Common::U32String *msg) {
	return isInputEnabled() && !_inventory->isOpen() && isFlagSet(ENGINE_FLAG_8) && !isFlagSet(Dragons::ENGINE_FLAG_100);
}

bool DragonsEngine::hasFeature(Engine::EngineFeature f) const {
	return
		// TODO (f == kSupportsReturnToLauncher) ||
		(f == kSupportsLoadingDuringRuntime) ||
		(f == kSupportsSavingDuringRuntime);
}

void DragonsEngine::init() {
	_flags = 0x1046;
	_flags &= 0x1c07040;
	_flags |= 0x26;
	_unkFlags1 = 0;

	initSubtitleFlag();

	_scriptOpcodes->_scriptTargetINI = 0; //TODO this should be reset in scriptopcode.
	_cursor->init(_actorManager, _dragonINIResource);
	_inventory->init(_actorManager, _backgroundResourceLoader, new Bag(_bigfileArchive, _screen), _dragonINIResource);
	_talk->init();

	_screen->loadPalette(1, _cursor->getPalette());
	setupPalette1();

	_screen->loadPalette(2, _cursor->getPalette());
	_screen->updatePaletteTransparency(2, 1, 5, true);
	_screen->updatePaletteTransparency(2, 0xc1, 0xc3, true);

	_screen->loadPalette(4, _cursor->getPalette());
	_screen->updatePaletteTransparency(4, 1, 0xff, true);
}

void DragonsEngine::loadScene(uint16 sceneId) {
	// TODO fun_80017010_update_actor_texture_maybe();
	if (sceneId > 2) {
		_dragonVAR->setVar(1, 1);
	}

	//if (sceneId > 2) { //TODO remove this restriction to enable intro sequence.
		_scene->setSceneId(2);
		byte *obd = _dragonOBD->getFromSpt(3);
		ScriptOpCall scriptOpCall(obd + 4, READ_LE_UINT32(obd));
		_scriptOpcodes->runScript(scriptOpCall);
	//} else {
//		sceneId = 0x12; // HACK the first scene. TODO remove this
//	}

	_inventory->loadInventoryItemsFromSave();

	if (getINI(0)->sceneId == 0) {
		getINI(0)->sceneId = sceneId; //TODO
	} else {
		_scene->setSceneId(getINI(0)->sceneId);
	}
	_sceneId1 = sceneId;
	_scene->loadScene(sceneId ? sceneId : 0x12, 0x1e);
}

void DragonsEngine::reset() {
	seedRandom(0x1dd); //TODO should we randomise this better? I got this value from a couple of runs in the emulator
	_nextUpdatetime = 0;
	_flags = 0;
	_unkFlags1 = 0;
	_run_func_ptr_unk_countdown_timer = 0;
	_videoFlags = 0;
	_flickerInitialSceneDirection = 0;

	for (int i = 0; i < 8; i++) {
		_paletteCyclingTbl[i].paletteType = 0;
		_paletteCyclingTbl[i].startOffset = 0;
		_paletteCyclingTbl[i].endOffset = 0;
		_paletteCyclingTbl[i].updateInterval = 0;
		_paletteCyclingTbl[i].updateCounter = 0;
	}

	clearSceneUpdateFunction();
}

void DragonsEngine::runSceneUpdaterFunction() {
	if ((isFlagSet(ENGINE_FLAG_20) && (_run_func_ptr_unk_countdown_timer == 0)) &&
			(_run_func_ptr_unk_countdown_timer = 1, _sceneUpdateFunction != nullptr)) {
		_sceneUpdateFunction();
	}
}

void DragonsEngine::setSceneUpdateFunction(void (*newUpdateFunction)()) {
	_sceneUpdateFunction = newUpdateFunction;
}

void DragonsEngine::clearSceneUpdateFunction() {
	setSceneUpdateFunction(nullptr);
}

void DragonsEngine::setVsyncUpdateFunction(void (*newUpdateFunction)()) {
	_vsyncUpdateFunction = newUpdateFunction;
}

void DragonsEngine::seedRandom(int32 seed) {
	_randomState = seed * -0x2b0e2b0f;
}

uint32 DragonsEngine::shuffleRandState() {
	uint32 returnBit;

	returnBit = _randomState & 1;
	_randomState = _randomState >> 1 |
			((_randomState << 0x1e ^ _randomState ^ _randomState << 0x1d ^ _randomState << 0x1b ^
						  _randomState << 0x19) & 0x80000000);
	return returnBit;
}

uint16 DragonsEngine::getRand(uint16 max) {
	uint16 rand = 0;

	for (int i = 0; i < 0x10; i++) {
		rand |= shuffleRandState() << i;
	}
	return rand % max;
}

bool DragonsEngine::checkForDownKeyRelease() {
	return _downKeyUp;
}

bool DragonsEngine::checkForUpKeyRelease() {
	return _upKeyUp;
}

bool DragonsEngine::isSquareButtonPressed() {
	return _aKeyDown;
}

bool DragonsEngine::isTriangleButtonPressed() {
	return _wKeyDown;
}

bool DragonsEngine::isCircleButtonPressed() {
	return _dKeyDown;
}

bool DragonsEngine::isCrossButtonPressed() {
	return _sKeyDown;
}

bool DragonsEngine::isL1ButtonPressed() {
	return _oKeyDown;
}

bool DragonsEngine::isR1ButtonPressed() {
	return _pKeyDown;
}

void DragonsEngine::setupPalette1() {
	byte palette[512];
	memcpy(palette, _cursor->getPalette(), 0x100);
	memcpy(palette + 0x100, _cursor->getPalette(), 0x100);
	_screen->loadPalette(1, palette);
	_screen->updatePaletteTransparency(1, 0x40, 0x7f, true);
}

bool DragonsEngine::isDebugMode() {
	return _debugMode;
}

bool DragonsEngine::isVsyncUpdaterFunctionRunning() {
	return _vsyncUpdateFunction != nullptr;
}

void DragonsEngine::runVsyncUpdaterFunction() {
	if (isVsyncUpdaterFunctionRunning()) {
		_vsyncUpdateFunction();
	}
}

void DragonsEngine::loadCurrentSceneMsf() {
	_sound->loadMsf(getCurrentSceneId());
}

void DragonsEngine::updatePaletteCycling() {
	if (!_isGamePaused) {
		for (int loopIndex = 0; loopIndex < 8 ; loopIndex++) {
			if (_paletteCyclingTbl[loopIndex].updateInterval != 0) {
				if (_paletteCyclingTbl[loopIndex].updateCounter == 0) {
					uint16 *palette = (uint16 *)_screen->getPalette(_paletteCyclingTbl[loopIndex].paletteType);
					int16 uVar14 = _paletteCyclingTbl[loopIndex].startOffset;
					int16 uVar8 = _paletteCyclingTbl[loopIndex].endOffset;
					if (uVar14 < uVar8) {
						uint16 uVar11 = palette[uVar8];
						int uVar15 = uVar8;
						if (uVar14 < uVar8) {
							do {
								uVar8--;
								palette[uVar15] = palette[uVar15 - 1];
								uVar15 = uVar8 & 0xffff;
							} while (_paletteCyclingTbl[loopIndex].startOffset < (uVar8 & 0xffff));
						}
						palette[(uint16)_paletteCyclingTbl[loopIndex].startOffset] = uVar11;
						_paletteCyclingTbl[loopIndex].updateCounter = _paletteCyclingTbl[loopIndex].updateInterval;
					} else {
						if (uVar8 < uVar14) {
							uint16 uVar11 = palette[uVar14];
							uint16 uVar15 = uVar8;
							if (uVar8 < uVar14) {
								do {
									uVar8--;
									palette[uVar15] = palette[uVar15 + 1];
									uVar15 = uVar8 & 0xffff;
								} while ((uVar8 & 0xffff) < _paletteCyclingTbl[loopIndex].startOffset);
							}
							palette[(uint16)_paletteCyclingTbl[loopIndex].endOffset] = uVar11;
							_paletteCyclingTbl[loopIndex].updateCounter =
									_paletteCyclingTbl[loopIndex].updateInterval;
						}
					}
				} else {
					_paletteCyclingTbl[loopIndex].updateCounter = _paletteCyclingTbl[loopIndex].updateCounter + -1;
				}
			}
		}
	}
}

uint32 DragonsEngine::getFontOffsetFromDragonEXE() {
	switch (_language) {
	case Common::EN_USA :
	case Common::RU_RUS : return 0x4a144;
	case Common::EN_GRB : return 0x4b4fc;
	case Common::DE_DEU : return 0x4af5c;
	case Common::FR_FRA : return 0x4b158;
	default : error("Unable to get font offset from dragon.exe for %s", getLanguageCode(_language));
	}
}

uint32 DragonsEngine::getSpeechTblOffsetFromDragonEXE() {
	switch (_language) {
	case Common::EN_USA :
	case Common::RU_RUS : return 0x4e138;
	case Common::EN_GRB : return 0x4f4f4;
	case Common::DE_DEU : return 0x4f0a4;
	case Common::FR_FRA : return 0x4f2a0;
	default : error("Unable to get speech table offset from dragon.exe for %s", getLanguageCode(_language));
	}
}

uint16 DragonsEngine::getBigFileTotalRecords() {
	if (_language == Common::EN_USA || _language == Common::EN_GRB || _language == Common::RU_RUS) {
		return 576;
	}
	return 588;
}

uint32 DragonsEngine::getBigFileInfoTblFromDragonEXE() {
	switch (_language) {
	case Common::EN_USA :
	case Common::RU_RUS : return 0x4a238;
	case Common::EN_GRB : return 0x4b5f4;
	case Common::DE_DEU : return 0x4b054;
	case Common::FR_FRA : return 0x4b250;
	default :
		error("Unable to get bigfile info table offset from dragon.exe for %s", getLanguageCode(_language));
	}
}

uint32 DragonsEngine::getCutscenePaletteOffsetFromDragonEXE() {
	switch (_language) {
	case Common::EN_USA :
	case Common::RU_RUS : return 0x5336c;
	case Common::EN_GRB : return 0x54628;
	case Common::DE_DEU : return 0x541d8;
	case Common::FR_FRA : return 0x543d4;
	default :
		error("Unable to get cutscene palette table offset from dragon.exe for %s", getLanguageCode(_language));
	}
}

uint32 DragonsEngine::defaultResponseOffsetFromDragonEXE() {
	switch (_language) {
	case Common::EN_USA :
	case Common::RU_RUS : return 0x541b0;
	case Common::EN_GRB : return 0x55470;
	case Common::DE_DEU : return 0x55020;
	case Common::FR_FRA : return 0x5521c;
	default :
		error("Unable to get response offset table offset from dragon.exe for %s", getLanguageCode(_language));
	}
}

void DragonsEngine::updateFlickerIdleAnimation() {
	_flickerIdleCounter++;
	if (_flickerIdleCounter >= 0x4af) {
		DragonINI *flicker = _dragonINIResource->getFlickerRecord();
		if (flicker->actor->_resourceID == 0xe) {
			flicker->actor->_direction = 2;
			flicker->direction2 = 2;
			if (getINI(0xc2)->sceneId == 1) {
				flicker->actor->updateSequence(0x30);
			} else {
				flicker->actor->updateSequence(2);
			}
			_flickerIdleCounter = 0;
			setFlags(ENGINE_FLAG_80000000);
		}
	}
	if (isFlagSet(ENGINE_FLAG_80000000)
		&& _dragonINIResource->getFlickerRecord()->actor->isFlagSet(ACTOR_FLAG_4)) {
		_flickerIdleCounter = 0;
		clearFlags(ENGINE_FLAG_80000000);
	}
}

void DragonsEngine::updateCamera() {
	if (isFlagSet(ENGINE_FLAG_40) && !isUnkFlagSet(ENGINE_UNK1_FLAG_1)) {
		return;
	}

	if (isFlagSet(ENGINE_FLAG_1) && !isUnkFlagSet(ENGINE_UNK1_FLAG_2)) { //TODO original doesn't seem to check for flag 2 here. adding to get cutscenes to work.
		DragonINI *flicker = _dragonINIResource->getFlickerRecord();
		if (flicker && flicker->sceneId != 0) {
			if ((flicker->actor->_x_pos - _scene->_camera.x >= 0x4f)) {
				if (flicker->actor->_x_pos - _scene->_camera.x >= 0xf0) {
					_scene->_camera.x = flicker->actor->_x_pos - 0xf0;
				}
			} else {
				_scene->_camera.x = flicker->actor->_x_pos - 0x50;
			}

			int16 sVar4 = flicker->actor->_y_pos + -0x1e;
			if (((int)flicker->actor->_y_pos - (int)_scene->_camera.y < 0x1e) ||
				(sVar4 = flicker->actor->_y_pos + -0xaa, 0xaa < (int)flicker->actor->_y_pos - (int)_scene->_camera.y)) {
				_scene->_camera.y = sVar4;
			}
		}

		if (_scene->_camera.x < 0) {
			_scene->_camera.x = 0;
		}
		if (_scene->getStageWidth() < _scene->_camera.x + 0x140) {
			_scene->_camera.x = _scene->getStageWidth() - 0x140;
		}
		if (_scene->_camera.y < 0) {
			_scene->_camera.y = 0;
		}
		if (_scene->getStageHeight() < _scene->_camera.y + 200) {
			_scene->_camera.y = _scene->getStageHeight() + -200;
		}
	}
}

void DragonsEngine::initSubtitleFlag() {
	bool showSubtitles = ConfMan.getBool("subtitles");
	if (showSubtitles) {
		clearFlags(ENGINE_FLAG_1000_SUBTITLES_DISABLED);
	} else {
		setFlags(ENGINE_FLAG_1000_SUBTITLES_DISABLED);
	}
}

int centerText(const char *text) {
	return 0x14 - (strlen(text) / 2 + 1);
}

void DragonsEngine::mainMenu() {
	_inMenu = true;

	int lang_index, y_offset;

	int lastMenuItem = 1;

	const char copyright[4][11][40] = {
		{
			// US
			"Crystal Dynamics is a trademark",
			"of Crystal Dynamics.",
			"Blazing Dragons is a trademark and",
			"copyright of Terry Jones and is",
			"used with permission.",
			"Licensed by Nelvana Marketing Inc."
		},
		{
			// UK
			"Crystal Dynamics is a trademark",
			"of Crystal Dynamics. Blazing",
			"Dragons' characters are trademarks",
			"and copyrights of Terry Jones and",
			"are used with permission.",
			"Licensed by Nelvana Marketing Inc.",
			" ",
			"Game Copyright (C) Crystal Dynamics.",
			"All rights reserved.",
		},
		{
			// GERMANY
			"Crystal Dynamics ist ein Warenzeichen",
			"von Crystal Dynamics",
			"Blazing Dragons ist ein gesch\x81tztes",
			"Warenzeichen von Terry Jones",
			"und wird unter Lizenz verwendet.",
			"Lizenziert durch",
			"Nelvana Marketing Inc.",
			" ",
			"Spiel Copyright",
			"c 1996 Crystal Dynamics.",
			"Alle Rechte Vorbehalten.",
		},
		{
			// FRANCE
			"Crystal Dynamics est une marque",
			"de Crystal Dynamics. Blazing Dragons",
			"est une marque et un copyright de",
			"Terry Jones et est utilis\x82 avec",
			"sa permission.",
			"Licence de Nelvana Marketing Inc.",
			" ",
			"Copyright Du Jeu",
			"c1996 Crystal Dynamics.",
			"Tous Droids R\x82serv\x82s."
		},
	};

	const char menuItems[4][3][40] = {
		{
			// US
			"Start",
			"Options",
			"Previews",
		},
		{
			// UK
			"Start",
			"Options",
		},
		{	// GERMANY
			"Start",
			"Optionen",
		},
		{
			// FRANCE
			"D\x82marrer",
			"Options",
		},
	};

	if (_language == Common::EN_GRB) {
		lang_index = 1;
		y_offset = 0x0f;
	}
	else if (_language == Common::DE_DEU) {
		lang_index = 2;
		y_offset = 0x0e;
	}
	else if (_language == Common::FR_FRA) {
		lang_index = 3;
		y_offset = 0x0e;
	}
	else {
		lang_index = 0;
		y_offset = 0x12;
		lastMenuItem = 2;
	}

	_screen->clearScreen();
	Actor *actor = _actorManager->loadActor(0xd9,0,0,0,3);
	actor->setFlag(ACTOR_FLAG_8000);
	actor->setFlag(ACTOR_FLAG_100);
	actor->setFlag(ACTOR_FLAG_80);

	//TODO fix palette for copyright image.
	_screen->loadPalette(0, _cursor->getPalette()); //actor->_actorResource->getPalette());

	for (int i = 0; (i < 11) && (copyright[lang_index][i][0] != 0); i++) {
		_fontManager->addAsciiText(centerText(&copyright[lang_index][i][0]) * 8, (y_offset + i) * 8, &copyright[lang_index][i][0], strlen(copyright[lang_index][i]), 1);
	}

	waitForFramesAllowSkip(400);
	_fontManager->clearText();
	actor->updateSequence(1);

	bool startGame = false;
	do {
		uint16 curMenuItem = 0;
		do {
			for (int i = 0; i <= lastMenuItem; i++) {
				_fontManager->addAsciiText(((i == 0) && (lang_index != 3) ? 17 : 16) * 8, (0x12 + i) * 8, &menuItems[lang_index][i][0], strlen(menuItems[lang_index][i]),
										   i == curMenuItem ? 0 : 1);
			}
			if (checkForDownKeyRelease() || checkForWheelDown()) {
				if (curMenuItem < lastMenuItem) {
					curMenuItem++;
				} else {
					curMenuItem = 0;
				}
				playOrStopSound(0x8009);
			}

			if (checkForUpKeyRelease() || checkForWheelUp()) {
				if (curMenuItem > 0) {
					curMenuItem--;
				} else {
					curMenuItem = lastMenuItem;
				}
				playOrStopSound(0x8009);
			}
			waitForFrames(1);
		} while (!checkForActionButtonRelease() && !shouldQuit());

		if (!shouldQuit()) {
			if (curMenuItem == 0) {
				_screen->clearScreen();
				loadingScreen();
				startGame = true;
			} else if (curMenuItem == 1) {
				openMainMenuDialog();  //TODO original options menu
			} else if (curMenuItem == 2) {
				_strPlayer->playVideo("previews.str");
			}
		}
	} while (!shouldQuit() && !startGame);

	_inMenu = false;
}

bool DragonsEngine::isInMenu() {
	return _inMenu;
}

void loadingScreenUpdateFunction() {
	getEngine()->loadingScreenUpdate();
}

void DragonsEngine::loadingScreen() {
	const int flamesActorOffset[4] = {2, 0, 3, 1};
	_loadingScreenState = new LoadingScreenState;
	_fontManager->clearText();
	_actorManager->clearActorFlags(2);
	_screen->addFlatQuad(0,0,0x13f,0,0x13f,199,0,199,0x34a2,6,0);
	Actor *actor = _actorManager->loadActor(0,0x82,0,0,6);
	actor->setFlag(ACTOR_FLAG_100);
	actor->setFlag(ACTOR_FLAG_200);
	actor->setFlag(ACTOR_FLAG_80);
	actor = _actorManager->loadActor(0,0x83,0,0,6);
	actor->setFlag(ACTOR_FLAG_100);
	actor->setFlag(ACTOR_FLAG_200);
	actor->setFlag(ACTOR_FLAG_80);

	if (_language == Common::DE_DEU || _language == Common::FR_FRA) {
		actor = _actorManager->loadActor(0,0x84,0,0,6);
		actor->setFlag(ACTOR_FLAG_100);
		actor->setFlag(ACTOR_FLAG_200);
		actor->setFlag(ACTOR_FLAG_80);
		actor = _actorManager->loadActor(0,0x85,0,0,6);
		actor->setFlag(ACTOR_FLAG_100);
		actor->setFlag(ACTOR_FLAG_200);
		actor->setFlag(ACTOR_FLAG_80);
	}

	for (int i = 0; i < 10; i++) {
		actor = _actorManager->loadActor(0,flamesActorOffset[(i % 4)] + 0x7e,i * 0x20 + 0x10,0xbe,6);
		actor->setFlag(ACTOR_FLAG_100);
		actor->setFlag(ACTOR_FLAG_200);
		actor->setFlag(ACTOR_FLAG_80);
		_loadingScreenState->flames[i] = actor;
		_loadingScreenState->baseYOffset = 0xbe;
		int x0 = i * 0x20;
		_loadingScreenState->quads[i] = _screen->addFlatQuad(x0,0,x0 + 0x20,0,x0 + 0x20,199,x0,199,0,7,0);
	}

	setVsyncUpdateFunction(loadingScreenUpdateFunction);
	waitForFramesAllowSkip(400);
	_actorManager->clearActorFlags(2);
	_screen->clearAllFlatQuads();
	setVsyncUpdateFunction(nullptr);
	delete _loadingScreenState;
	_loadingScreenState = nullptr;
}

void DragonsEngine::loadingScreenUpdate() {
	const int16 flameOffsetTbl[26] = {
			3,      4,      5,      6,
			7,      5,      4,      3,
			3,      4,      6,      7,
			6,      5,      5,      6,
			7,      6,      4,      3,
			2,      3,      4,      5,
			6,      5
	};
	FlatQuad *quad;
	int16 flameYOffset;

	if (_loadingScreenState->loadingFlamesUpdateCounter == 0) {
		_loadingScreenState->loadingFlamesUpdateCounter = 4;
		for (int i = 0; i < 10 ; i++) {
			flameYOffset = _loadingScreenState->baseYOffset - flameOffsetTbl[(i + _loadingScreenState->flameOffsetIdx) % 26];
			if (_loadingScreenState->flames[i]->_y_pos >= -0xb) {
				_loadingScreenState->flames[i]->_y_pos = flameYOffset;
			}
			quad = _screen->getFlatQuad(_loadingScreenState->quads[i]);
			if (quad->points[0].y >= -1) {
				quad->points[0].y = flameYOffset + 2;
				quad->points[1].y = flameYOffset + 2;
			}
		}
		_loadingScreenState->flameOffsetIdx = (_loadingScreenState->flameOffsetIdx + 1) % 26;
	} else {
		_loadingScreenState->loadingFlamesUpdateCounter--;
	}

	if (_loadingScreenState->loadingFlamesRiseCounter == 0) {
		_loadingScreenState->loadingFlamesRiseCounter = 1;
		_loadingScreenState->baseYOffset--;
	} else {
		_loadingScreenState->loadingFlamesRiseCounter--;
	}
}

bool DragonsEngine::checkAudioVideoFiles() {
	return validateAVFile("crystald.str") &&
		   validateAVFile("illusion.str") &&
		   validateAVFile("labintro.str") &&
		   validateAVFile("previews.str") &&
		   validateAVFile("dtspeech.xa");
}

bool DragonsEngine::validateAVFile(const char *filename) {
	const byte fileSignature[12] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
	byte buf[12];
	Common::File file;
	bool fileValid = false;

	if (!file.open(filename)) {
		error("Failed to open %s", filename);
	}

	if ((file.size() % 2352) == 0) {
		file.read(buf, 12);
		if (!memcmp(fileSignature, buf, 12)) {
			fileValid = true;
		}
	}

	file.close();

	if(!fileValid) {
		const char* urlForFileExtractionInfo = "https://wiki.scummvm.org/index.php?title=HOWTO-PlayStation_Videos";
		GUIErrorMessageWithURL(Common::U32String::format(_("Error: The file '%s' hasn't been extracted properly.\nPlease refer to the wiki page\n%s for details on how to properly extract the DTSPEECH.XA and *.STR files from your game disc."), filename, urlForFileExtractionInfo), urlForFileExtractionInfo);
	}
	return fileValid;
}

bool DragonsEngine::checkForWheelUp() {
	return _mouseWheel == MOUSE_WHEEL_UP;
}

bool DragonsEngine::checkForWheelDown() {
	return _mouseWheel == MOUSE_WHEEL_DOWN;
}

void DragonsEngine::clearAllText() {
	_fontManager->clearText();
}

void DragonsEngine::syncSoundSettings() {
	Engine::syncSoundSettings();
	if (_sound) {
		_sound->syncSoundSettings();
	}
}

uint16 DragonsEngine::getCursorHandPointerSequenceID() {
	return _language == Common::DE_DEU || _language == Common::FR_FRA ? 0x86 : 0x84;
}

uint32 DragonsEngine::getMiniGame3StartingDialog() {
	switch (_language) {
	case Common::DE_DEU : return 0x5456;
	case Common::FR_FRA : return 0x509C;
	default : break;
	}
	return 0x479A;
}

uint32 DragonsEngine::getMiniGame3PickAHatDialog() {
	switch (_language) {
	case Common::DE_DEU : return 0x2E32E;
	case Common::FR_FRA : return 0x2F180;
	default : break;
	}
	return 0x2958A;
}

uint32 DragonsEngine::getMiniGame3DataOffset() {
	if (_language == Common::DE_DEU || _language == Common::FR_FRA) {
		return 0x265c;
	}
	return 0x4914;
}

uint32 DragonsEngine::getDialogTextId(uint32 textId) {
	switch (_language) {
	case Common::EN_GRB : return getDialogTextIdGrb(textId);
	case Common::DE_DEU : return getDialogTextIdDe(textId);
	case Common::FR_FRA : return getDialogTextIdFr(textId);
	default : break;
	}
	return textId;
}

uint32 DragonsEngine::getDialogTextIdGrb(uint32 textId) {
	switch (textId) {

		//scene 1
	case 0x5ef2 : return 0x5ef0;
	case 0x5ea2 : return 0x5ea0;
	case 0x5ecc : return 0x5eca;

	default: break;
	}
	return textId;
}

uint32 DragonsEngine::getDialogTextIdDe(uint32 textId) {
	switch (textId) {
	//diamond cutscene
	case 0x4294a : return 0x4a584;
	case 0x42A66 : return 0x4a6c4;
	case 0x42AC2 : return 0x4a744;
	case 0x42B56 : return 0x4a7f0;

	//scene 1
	case 0x4e26 : return 0x5c2c;
	case 0x4ea2 : return 0x5ca0;
	case 0x4eec : return 0x5cfa;
	case 0x5000 : return 0x5de4;
	case 0x5074 : return 0x5e78;
	case 0x511c : return 0x5f0e;
	case 0x5138 : return 0x5f28;
	case 0x5152 : return 0x6000;
	case 0x51fc : return 0x60e4;
	case 0x52d6 : return 0x6208;
	case 0x530c : return 0x6244;
	case 0x54dc : return 0x6466;
	case 0x55d4 : return 0x654e;
	case 0x562c : return 0x65a6;
	case 0x5780 : return 0x66f4;
	case 0x581c : return 0x679c;
	case 0x5942 : return 0x6910;
	case 0x5aaa : return 0x6aa8;
	case 0x5afc : return 0x6af4;
	case 0x5b96 : return 0x6bc0;
	case 0x5c4a : return 0x6c66;
	case 0x5dc8 : return 0x6e30;
	case 0x5ef2 : return 0x6f9c;
	case 0x6000 : return 0x7000;
	case 0x7dcc : return 0x92da;
	case 0x60ee : return 0x710e;
	case 0x5de8 : return 0x6e58;
	case 0x5ea2 : return 0x6f46;
	case 0x5ecc : return 0x6f6e;

	// knights saved
	case 0x7854 : return 0x8bb4;
	case 0x78c6 : return 0x8c32;
	case 0x78e8 : return 0x8c56;
	case 0x7a1e : return 0x8d9e;
	case 0x7aba : return 0x8e64;
	case 0x7b60 : return 0x9000;
	case 0x7c20 : return 0x90fa;
	case 0x7c9c : return 0x9196;
	case 0x7cf2 : return 0x91fa;
	case 0x7e1a : return 0x9324;
	case 0x7e96 : return 0x93e2;
	case 0x7f0a : return 0x9464;

	// flame returns
	case 0x8ab2 : return 0xa190;
	case 0x8b40 : return 0xa274;
	case 0x8bb6 : return 0xa2e0;
	case 0x8bd8 : return 0xa304;
	case 0x8c70 : return 0xa3d2;
	case 0x8cd2 : return 0xa450;
	case 0x8e1e : return 0xa5a2;

	// knights saved again
	case 0x9000 : return 0xa806;
	case 0x90de : return 0xa8f8;
	case 0x921c : return 0xaa8e;
	case 0x92aa : return 0xab3c;
	case 0x932c : return 0xabc0;
	case 0x93d6 : return 0xac52;
	case 0x948c : return 0xad16;

	// tournament
	case 0x4c40c : return 0x552f8;
	case 0x4c530 : return 0x55430;
	case 0x4c588 : return 0x5547e;
	case 0x4c6b0 : return 0x5557a;
	case 0x4c6e8 : return 0x555a2;
	case 0x4c814 : return 0x556c4;
	case 0x4c852 : return 0x556fc;

	//for camelhot
	case 0x30DD8 : return 0x36594;

	// castle garden bg dialog
	case 0x22660 : return 0x267ec;
	case 0x226CA : return 0x2687a;
	case 0x22738 : return 0x2690a;
	case 0x22790 : return 0x26972;
	case 0x227E8 : return 0x269e6;
	case 0x2283C : return 0x26a44;
	case 0x228A0 : return 0x26ac4;
	case 0x228EC : return 0x26b1e;

	// knight pool reflection logic
	case 0x23E90 : return 0x28456;
	case 0x23EE6 : return 0x284aa;
	case 0x23F0C : return 0x284e0;
	case 0x23F86 : return 0x2856c;
	case 0x24000 : return 0x285c4;
	case 0x2406A : return 0x2863c;
	case 0x240C2 : return 0x286a0;
	case 0x2411E : return 0x28706;
	case 0x24158 : return 0x28746;
	case 0x241BC : return 0x287a6;
	case 0x241EE : return 0x287d6;
	case 0x24240 : return 0x28832;
	case 0x24286 : return 0x2887e;
	case 0x242B0 : return 0x288ae;
	case 0x2431C : return 0x2891e;
	case 0x2437C : return 0x28994;
	case 0x243B2 : return 0x289dc;
	case 0x2440A : return 0x28a46;
	case 0x24432 : return 0x28a7e;
	case 0x24480 : return 0x28ac8;
	case 0x244EE : return 0x28b40;
	case 0x2453C : return 0x28b9e;

	// Zigmond fraud logic
	case 0x2D000 : return 0x32312;
	case 0x2D044 : return 0x32364;
	case 0x2D0B2 : return 0x323d0;
	case 0x2D0D6 : return 0x323fc;
	case 0x2D152 : return 0x32490;
	case 0x2D1A4 : return 0x324f0;
	case 0x2D20A : return 0x32566;
	case 0x2D27C : return 0x325f0;
	case 0x2D2EC : return 0x32674;
	case 0x2D336 : return 0x326c6;
	case 0x2D3E0 : return 0x3274c;
	case 0x2D456 : return 0x327d2;
	case 0x2D4A8 : return 0x3283c;
	case 0x2D504 : return 0x328a0;

	// Zigmond fraud logic 1
	case 0x2F422 : return 0x348c6;

	// Black dragon on hill
	case 0x325EA : return 0x37f94;
	case 0x3262A : return 0x38000;
	case 0x32686 : return 0x3806c;
	case 0x326D8 : return 0x380fc;
	case 0x3270E : return 0x3814a;
	case 0x32774 : return 0x381a2;
	case 0x32D72 : return 0x38850;

	// Jester in library
	case 0x18502 : return 0x1b7f8;
	case 0x185E0 : return 0x1b900;
	case 0x18596 : return 0x1b8a4;

	// Broken black dragon
	case 0x40802 : return 0x47ec2;
	case 0x40852 : return 0x47f2a;
	case 0x40896 : return 0x47f70;
	case 0x408C0 : return 0x48000;
	case 0x4092A : return 0x48084;

	// Dodo under attack
	case 0x3353A : return 0x39100;
	case 0x335AC : return 0x3917e;
	case 0x335F8 : return 0x391e4;
	case 0x33660 : return 0x39252;
	case 0x336DE : return 0x392da;
	case 0x3375C : return 0x39364;

	// Forest without dodo
	case 0x33EA0 : return 0x39c02;
	case 0x33EFC : return 0x39c98;
	case 0x33F34 : return 0x39cd0;
	case 0x34000 : return 0x39d30;
	case 0x34074 : return 0x39dc4;

	// Angry villagers
	case 0x35946 : return 0x3ba7e;
	case 0x359BC : return 0x3baee;
	case 0x35A38 : return 0x3bb68;
	case 0x35ABC : return 0x3bbd4;
	case 0x35B28 : return 0x3bc68;
	case 0x35B9C : return 0x3bcd4;
	case 0x35C10 : return 0x3bd78;
	case 0x35C80 : return 0x3be0e;
	case 0x35CFA : return 0x3be96;
	case 0x35D64 : return 0x3bf26;

	// Flame bedroom escape
	case 0x10458 : return 0x12ac2;
	case 0x104A0 : return 0x12b16;
	case 0x10500 : return 0x12b72;
	case 0x10550 : return 0x12bb6;
	case 0x10578 : return 0x12be6;

	// Lady of the lake captured
	case 0x490C8 : return 0x51ace;
	case 0x490FC : return 0x51b04;
	case 0x4913A : return 0x51b3e;

	// Men in mines
	case 0x4590A : return 0x4db1c;
	case 0x45994 : return 0x4dbba;
	case 0x459F4 : return 0x4dc2c;
	case 0x45A60 : return 0x4dc92;

	// Moat drained
	case 0x3C97A : return 0x43830;
	case 0x3C9AC : return 0x43870;
	case 0x3C9F8 : return 0x438dc;
	case 0x3CA48 : return 0x43966;

	// Monks at bar
	case 0x37800 : return 0x3db58;
	case 0x37854 : return 0x3dba0;
	case 0x378CA : return 0x3dc1e;
	case 0x39152 : return 0x3f754;
	case 0x3919A : return 0x3f78a;
	case 0x3922C : return 0x3f82a;

	case 0x38F2A : return 0x3f52c;
	case 0x39000 : return 0x3f5a0;
	case 0x39084 : return 0x3f65a;
	case 0x390E8 : return 0x3f6d6;

	case 0x38C68 : return 0x3f25e;
	case 0x38CE2 : return 0x3f2da;
	case 0x38D4E : return 0x3f348;
	case 0x38DC2 : return 0x3f3ba;
	case 0x38E0C : return 0x3f3f4;
	case 0x38E5C : return 0x3f44a;
	case 0x38ED0 : return 0x3f4c0;

	// Mini game 1
	case 0x21312 : return 0x25258;
	case 0x2134C : return 0x252A0;
	case 0x21386 : return 0x252D6;
	case 0x213C0 : return 0x25324;
	case 0x213E2 : return 0x25354;
	case 0x21428 : return 0x253A4;
	case 0x2146C : return 0x253EC;
	case 0x214B4 : return 0x25446;
	case 0x214E4 : return 0x25472;
	case 0x21514 : return 0x254AC;
	case 0x21540 : return 0x254DC;
	case 0x21590 : return 0x25530;
	case 0x215E2 : return 0x2559A;
	case 0x2164E : return 0x25626;
	case 0x216AA : return 0x25690;
	case 0x216D2 : return 0x256D6;
	case 0x217D8 : return 0x257DC;

	// Mini game 2
	case 0x4500 : return 0x50F4;
	case 0x454A : return 0x5164;
	case 0x4576 : return 0x519E;
	case 0x46BC : return 0x532C;
	case 0x4718 : return 0x53B6;
	case 0x475E : return 0x5420;
	case 0x4774 : return 0x5436;

	// Mini game 4
	case 0x49A2 : return 0x5706;
	case 0x4A84 : return 0x580C;
	case 0x4ADE : return 0x5876;
	case 0x4B6A : return 0x5904;
	case 0x4C0C : return 0x59C6;
	case 0x4D50 : return 0x5B24;
	case 0x4DD4 : return 0x5BC6;
	case 0x4DEE : return 0x5BE8;
	case 0x4CC8 : return 0x5A7E;
	case 0x4A56 : return 0x57D4;

	// Mini game 5
	case 0x21DAE : return 0x25E68;
	case 0x21BF0 : return 0x25C34;
	case 0x21E3E : return 0x25F10;
	case 0x475DA : return 0x4FC9E;

	default: break;
	}
	warning("Unhandled textID 0x%x", textId);
	return textId;
}

uint32 DragonsEngine::getDialogTextIdFr(uint32 textId) {
	switch (textId) {
	//diamond cutscene
	case 0x4294a : return 0x4c8b2;
	case 0x42A66 : return 0x4ca14;
	case 0x42AC2 : return 0x4ca76;
	case 0x42B56 : return 0x4cb34;

	//scene 1
	case 0x4e26 : return 0x5844;
	case 0x4ea2 : return 0x58e2;
	case 0x4eec : return 0x5950;
	case 0x5000 : return 0x5a32;
	case 0x5074 : return 0x5ac0;
	case 0x511c : return 0x5b7a;
	case 0x5138 : return 0x5b98;
	case 0x5152 : return 0x5bb4;
	case 0x51fc : return 0x5c6c;
	case 0x52d6 : return 0x5d8a;
	case 0x530c : return 0x5dc8;
	case 0x54dc : return 0x6000;
	case 0x55d4 : return 0x610c;
	case 0x562c : return 0x616a;
	case 0x5780 : return 0x62ba;
	case 0x581c : return 0x6332;
	case 0x5942 : return 0x64b4;
	case 0x5aaa : return 0x660e;
	case 0x5afc : return 0x665e;
	case 0x5b96 : return 0x6752;
	case 0x5c4a : return 0x6838;
	case 0x5dc8 : return 0x69d0;
	case 0x5ef2 : return 0x6b1a;
	case 0x6000 : return 0x6b76;
	case 0x7dcc : return 0x8cc0;
	case 0x60ee : return 0x6c56;
	case 0x5de8 : return 0x69f2;
	case 0x5ea2 : return 0x6ac4;
	case 0x5ecc : return 0x6aec;

	// knights saved
	case 0x7854 : return 0x86d6;
	case 0x78c6 : return 0x8736;
	case 0x78e8 : return 0x8750;
	case 0x7a1e : return 0x88b0;
	case 0x7aba : return 0x8974;
	case 0x7b60 : return 0x8a22;
	case 0x7c20 : return 0x8afe;
	case 0x7c9c : return 0x8b92;
	case 0x7cf2 : return 0x8bda;
	case 0x7e1a : return 0x8d0e;
	case 0x7e96 : return 0x8da6;
	case 0x7f0a : return 0x8e30;

	// flame returns
	case 0x8ab2 : return 0x9ae2;
	case 0x8b40 : return 0x9ba6;
	case 0x8bb6 : return 0x9c16;
	case 0x8bd8 : return 0x9c3c;
	case 0x8c70 : return 0x9ccc;
	case 0x8cd2 : return 0x9d22;
	case 0x8e1e : return 0x9e8c;

	// knights saved again
	case 0x9000 : return 0xa086;
	case 0x90de : return 0xa162;
	case 0x921c : return 0xa2b8;
	case 0x92aa : return 0xa35e;
	case 0x932c : return 0xa400;
	case 0x93d6 : return 0xa4d2;
	case 0x948c : return 0xa5b6;

	// tournament
	case 0x4c40c : return 0x577b4;
	case 0x4c530 : return 0x578be;
	case 0x4c588 : return 0x57908;
	case 0x4c6b0 : return 0x57a26;
	case 0x4c6e8 : return 0x57a4a;
	case 0x4c814 : return 0x57bd6;
	case 0x4c852 : return 0x57c08;

	//for camelhot
	case 0x30DD8 : return 0x37cb8;

	// castle garden bg dialog
	case 0x22660 : return 0x271a4;
	case 0x226CA : return 0x27214;
	case 0x22738 : return 0x27292;
	case 0x22790 : return 0x272ee;
	case 0x227E8 : return 0x27366;
	case 0x2283C : return 0x273c8;
	case 0x228A0 : return 0x2743a;
	case 0x228EC : return 0x274a2;

	// knight pool reflection logic
	case 0x23E90 : return 0x28da8;
	case 0x23EE6 : return 0x28e16;
	case 0x23F0C : return 0x28e42;
	case 0x23F86 : return 0x28ec0;
	case 0x24000 : return 0x28efa;
	case 0x2406A : return 0x28f64;
	case 0x240C2 : return 0x29000;
	case 0x2411E : return 0x29068;
	case 0x24158 : return 0x290a6;
	case 0x241BC : return 0x29114;
	case 0x241EE : return 0x2914a;
	case 0x24240 : return 0x291b2;
	case 0x24286 : return 0x29206;
	case 0x242B0 : return 0x29234;
	case 0x2431C : return 0x292a8;
	case 0x2437C : return 0x29332;
	case 0x243B2 : return 0x2937e;
	case 0x2440A : return 0x293d0;
	case 0x24432 : return 0x293f2;
	case 0x24480 : return 0x29434;
	case 0x244EE : return 0x294a8;
	case 0x2453C : return 0x29500;

	// Zigmond fraud logic
	case 0x2D000 : return 0x33602;
	case 0x2D044 : return 0x33654;
	case 0x2D0B2 : return 0x336d2;
	case 0x2D0D6 : return 0x336f6;
	case 0x2D152 : return 0x337c2;
	case 0x2D1A4 : return 0x33818;
	case 0x2D20A : return 0x338a2;
	case 0x2D27C : return 0x33952;
	case 0x2D2EC : return 0x33a06;
	case 0x2D336 : return 0x33a56;
	case 0x2D3E0 : return 0x33b24;
	case 0x2D456 : return 0x33bd0;
	case 0x2D4A8 : return 0x33c36;
	case 0x2D504 : return 0x33c9e;

	// Zigmond fraud logic 1
	case 0x2F422 : return 0x3608a;

	// Black dragon on hill
	case 0x325EA : return 0x39822;
	case 0x3262A : return 0x3986e;
	case 0x32686 : return 0x398dc;
	case 0x326D8 : return 0x39944;
	case 0x3270E : return 0x39982;
	case 0x32774 : return 0x39a08;
	case 0x32D72 : return 0x3a162;

	// Jester in library
	case 0x18502 : return 0x1b6d6;
	case 0x185E0 : return 0x1b7e2;
	case 0x18596 : return 0x1b790;

	// Broken black dragon
	case 0x40802 : return 0x4a178;
	case 0x40852 : return 0x4a1d2;
	case 0x40896 : return 0x4a212;
	case 0x408C0 : return 0x4a252;
	case 0x4092A : return 0x4a310;

	// Dodo under attack
	case 0x3353A : return 0x3aa2a;
	case 0x335AC : return 0x3aaae;
	case 0x335F8 : return 0x3ab02;
	case 0x33660 : return 0x3ab9c;
	case 0x336DE : return 0x3ac46;
	case 0x3375C : return 0x3acf0;

	// Forest without dodo
	case 0x33EA0 : return 0x3b63a;
	case 0x33EFC : return 0x3b6b2;
	case 0x33F34 : return 0x3b6e8;
	case 0x34000 : return 0x3b76a;
	case 0x34074 : return 0x3b7e2;

	// Angry villagers
	case 0x35946 : return 0x3d476;
	case 0x359BC : return 0x3d4fa;
	case 0x35A38 : return 0x3d570;
	case 0x35ABC : return 0x3d64e;
	case 0x35B28 : return 0x3d6cc;
	case 0x35B9C : return 0x3d71a;
	case 0x35C10 : return 0x3d79e;
	case 0x35C80 : return 0x3d81e;
	case 0x35CFA : return 0x3d8be;
	case 0x35D64 : return 0x3d94c;

	// Flame bedroom escape
	case 0x10458 : return 0x12598;
	case 0x104A0 : return 0x125f4;
	case 0x10500 : return 0x12664;
	case 0x10550 : return 0x12692;
	case 0x10578 : return 0x126d4;

	// Lady of the lake captured
	case 0x490C8 : return 0x53ef8;
	case 0x490FC : return 0x53f42;
	case 0x4913A : return 0x54000;

	// Men in mines
	case 0x4590A : return 0x50000;
	case 0x45994 : return 0x500be;
	case 0x459F4 : return 0x50142;
	case 0x45A60 : return 0x501a0;

	// Moat drained
	case 0x3C97A : return 0x45826;
	case 0x3C9AC : return 0x45862;
	case 0x3C9F8 : return 0x458c0;
	case 0x3CA48 : return 0x4595e;

		// Monks at bar
	case 0x37800 : return 0x3f7d4;
	case 0x37854 : return 0x3f84e;
	case 0x378CA : return 0x3f8b2;
	case 0x39152 : return 0x4153a;
	case 0x3919A : return 0x4157e;
	case 0x3922C : return 0x41616;

	case 0x38F2A : return 0x41388;
	case 0x39000 : return 0x413f4;
	case 0x39084 : return 0x4147e;
	case 0x390E8 : return 0x414d4;

	case 0x38C68 : return 0x41000;
	case 0x38CE2 : return 0x41098;
	case 0x38D4E : return 0x41126;
	case 0x38DC2 : return 0x411b2;
	case 0x38E0C : return 0x41218;
	case 0x38E5C : return 0x41278;
	case 0x38ED0 : return 0x412fa;

	// Mini game 1
	case 0x21312 : return 0x25B60;
	case 0x2134C : return 0x25B98;
	case 0x21386 : return 0x25BD6;
	case 0x213C0 : return 0x25C10;
	case 0x213E2 : return 0x25C38;
	case 0x21428 : return 0x25C7E;
	case 0x2146C : return 0x25CE0;
	case 0x214B4 : return 0x25D36;
	case 0x214E4 : return 0x25D5E;
	case 0x21514 : return 0x25D8A;
	case 0x21540 : return 0x25DCE;
	case 0x21590 : return 0x25E4E;
	case 0x215E2 : return 0x25EB8;
	case 0x2164E : return 0x25F2C;
	case 0x216AA : return 0x25F9C;
	case 0x216D2 : return 0x26000;
	case 0x217D8 : return 0x260F4;

	// Mini game 2
	case 0x4500 : return 0x4D78;
	case 0x454A : return 0x4DCC;
	case 0x4576 : return 0x4E0A;
	case 0x46BC : return 0x4F32;
	case 0x4718 : return 0x5000;
	case 0x475E : return 0x505A;
	case 0x4774 : return 0x5070;

	// Mini game 4
	case 0x49A2 : return 0x5360;
	case 0x4A84 : return 0x5476;
	case 0x4ADE : return 0x54C0;
	case 0x4B6A : return 0x554C;
	case 0x4C0C : return 0x55EA;
	case 0x4D50 : return 0x5742;
	case 0x4DD4 : return 0x57F0;
	case 0x4DEE : return 0x580E;
	case 0x4CC8 : return 0x569C;
	case 0x4A56 : return 0x5438;

	// Mini game 5
	case 0x21DAE : return 0x267C2;
	case 0x21BF0 : return 0x265BC;
	case 0x21E3E : return 0x2686A;
	case 0x475DA : return 0x51F88;

	default: break;
	}
	warning("Unhandled textID 0x%x", textId);
	return textId;
}

void (*DragonsEngine::getSceneUpdateFunction())() {
	return _sceneUpdateFunction;
}

} // End of namespace Dragons
