/* 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 "gui/message.h"
#include "common/config-manager.h"
#include "common/keyboard.h"
#include "common/language.h"
#include "common/translation.h"
#include "engines/util.h"
#include "graphics/thumbnail.h"
#include "common/error.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 ADGameDescription *desc) : Engine(syst) {
	_language = 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;

	debug("Ok");
	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() {
	//player has control and not currently talking to anyone.
	return isInputEnabled() && isFlagSet(ENGINE_FLAG_8) && !isFlagSet(Dragons::ENGINE_FLAG_100);
}

bool DragonsEngine::canSaveGameStateCurrently() {
	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;
	//TODO need to support other languages.
	const char copyright[6][40] = {
			"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."
	};
	const char menuItems[3][40] = {
			"Start",
			"Options",
			"Previews"
	};

	_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 < 6; i++) {
		_fontManager->addAsciiText(centerText(&copyright[i][0]) * 8, (0x12 + i) * 8, &copyright[i][0], strlen(copyright[i]), 1);
	}

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

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

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

		if (!shouldQuit()) {
			if (curMenuItem == 0) {
				_screen->clearScreen();
				loadingScreen();
				startGame = true;
			} else if (curMenuItem == 1) {
				//TODO 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) {
		GUIErrorMessageWithURL(Common::U32String::format(_("Error: The file '%s' hasn't been extracted properly.\nPlease refer to the wiki page\nhttps://wiki.scummvm.org/index.php?title=HOWTO-PlayStation_Videos for details on how to properly extract the DTSPEECH.XA and *.STR files from your game disc."), filename),
				"https://wiki.scummvm.org/index.php?title=HOWTO-PlayStation_Videos");
	}
	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
